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.api.*;
030import com.github.theholywaffle.teamspeak3.api.event.TS3EventType;
031import com.github.theholywaffle.teamspeak3.api.event.TS3Listener;
032import com.github.theholywaffle.teamspeak3.api.exception.TS3CommandFailedException;
033import com.github.theholywaffle.teamspeak3.api.exception.TS3Exception;
034import com.github.theholywaffle.teamspeak3.api.exception.TS3FileTransferFailedException;
035import com.github.theholywaffle.teamspeak3.api.wrapper.*;
036import com.github.theholywaffle.teamspeak3.commands.*;
037import com.github.theholywaffle.teamspeak3.commands.response.DefaultArrayResponse;
038
039import java.io.ByteArrayInputStream;
040import java.io.ByteArrayOutputStream;
041import java.io.IOException;
042import java.io.InputStream;
043import java.io.OutputStream;
044import java.lang.invoke.MethodHandle;
045import java.lang.invoke.MethodHandles;
046import java.lang.invoke.MethodType;
047import java.util.ArrayList;
048import java.util.Collection;
049import java.util.Collections;
050import java.util.List;
051import java.util.Locale;
052import java.util.Map;
053import java.util.concurrent.TimeUnit;
054import java.util.regex.Pattern;
055
056/**
057 * Asynchronous version of {@link TS3Api} to interact with the {@link TS3Query}.
058 * <p>
059 * This class is used to easily interact with a {@link TS3Query}. It constructs commands,
060 * sends them to the TeamSpeak3 server, processes the response and returns the result.
061 * </p><p>
062 * All methods in this class are asynchronous (so they won't block) and
063 * will return a {@link CommandFuture} of the corresponding return type in {@link TS3Api}.
064 * If a command fails, no exception will be thrown directly. It will however be rethrown in
065 * {@link CommandFuture#get()} and {@link CommandFuture#get(long, TimeUnit)}.
066 * Usually, the thrown exception is a {@link TS3CommandFailedException}, which will get you
067 * access to the {@link QueryError} from which more information about the error can be obtained.
068 * </p><p>
069 * Also note that while these methods are asynchronous, the commands will still be sent through a
070 * synchronous command pipeline. That means if an asynchronous method is called immediately
071 * followed by a synchronous method, the synchronous method will first have to wait until the
072 * asynchronous method completed until it its command is sent.
073 * </p><p>
074 * You won't be able to execute most commands while you're not logged in due to missing permissions.
075 * Make sure to either pass your login credentials to the {@link TS3Config} object when
076 * creating the {@code TS3Query} or to call {@link #login(String, String)} to log in.
077 * </p><p>
078 * After that, most commands also require you to select a {@linkplain VirtualServer virtual server}.
079 * To do so, call either {@link #selectVirtualServerByPort(int)} or {@link #selectVirtualServerById(int)}.
080 * </p>
081 *
082 * @see TS3Api The synchronous version of the API
083 */
084public class TS3ApiAsync {
085
086        /**
087         * The TS3 query to which this API sends its commands.
088         */
089        private final TS3Query query;
090
091        /**
092         * Creates a new asynchronous API object for the given {@code TS3Query}.
093         * <p>
094         * <b>Usually, this constructor should not be called.</b> Use {@link TS3Query#getAsyncApi()} instead.
095         * </p>
096         *
097         * @param query
098         *              the TS3Query to call
099         */
100        public TS3ApiAsync(TS3Query query) {
101                this.query = query;
102        }
103
104        /**
105         * Adds a new ban entry. At least one of the parameters {@code ip}, {@code name} or {@code uid} needs to be not null.
106         * Returns the ID of the newly created ban.
107         *
108         * @param ip
109         *              a RegEx pattern to match a client's IP against, can be null
110         * @param name
111         *              a RegEx pattern to match a client's name against, can be null
112         * @param uid
113         *              the unique identifier of a client, can be null
114         * @param timeInSeconds
115         *              the duration of the ban in seconds. 0 equals a permanent ban
116         * @param reason
117         *              the reason for the ban, can be null
118         *
119         * @return the ID of the newly created ban entry
120         *
121         * @throws TS3CommandFailedException
122         *              if the execution of a command fails
123         * @querycommands 1
124         * @see Pattern RegEx Pattern
125         * @see Client#getId()
126         * @see Client#getUniqueIdentifier()
127         * @see ClientInfo#getIp()
128         */
129        public CommandFuture<Integer> addBan(String ip, String name, String uid, long timeInSeconds, String reason) {
130                final Command cmd = BanCommands.banAdd(ip, name, uid, timeInSeconds, reason);
131                return executeAndReturnIntProperty(cmd, "banid");
132        }
133
134        /**
135         * Adds a specified permission to a client in a specific channel.
136         *
137         * @param channelId
138         *              the ID of the channel wherein the permission should be granted
139         * @param clientDBId
140         *              the database ID of the client to add a permission to
141         * @param permName
142         *              the name of the permission to grant
143         * @param permValue
144         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
145         *
146         * @return a future to track the progress of this command
147         *
148         * @throws TS3CommandFailedException
149         *              if the execution of a command fails
150         * @querycommands 1
151         * @see Channel#getId()
152         * @see Client#getDatabaseId()
153         * @see Permission
154         */
155        public CommandFuture<Void> addChannelClientPermission(int channelId, int clientDBId, String permName, int permValue) {
156                final Command cmd = PermissionCommands.channelClientAddPerm(channelId, clientDBId, permName, permValue);
157                return executeAndReturnError(cmd);
158        }
159
160        /**
161         * Creates a new channel group for clients using a given name and returns its ID.
162         * <p>
163         * To create channel group templates or ones for server queries,
164         * use {@link #addChannelGroup(String, PermissionGroupDatabaseType)}.
165         * </p>
166         *
167         * @param name
168         *              the name of the new channel group
169         *
170         * @return the ID of the newly created channel group
171         *
172         * @throws TS3CommandFailedException
173         *              if the execution of a command fails
174         * @querycommands 1
175         * @see ChannelGroup
176         */
177        public CommandFuture<Integer> addChannelGroup(String name) {
178                return addChannelGroup(name, null);
179        }
180
181        /**
182         * Creates a new channel group using a given name and returns its ID.
183         *
184         * @param name
185         *              the name of the new channel group
186         * @param type
187         *              the desired type of channel group
188         *
189         * @return the ID of the newly created channel group
190         *
191         * @throws TS3CommandFailedException
192         *              if the execution of a command fails
193         * @querycommands 1
194         * @see ChannelGroup
195         */
196        public CommandFuture<Integer> addChannelGroup(String name, PermissionGroupDatabaseType type) {
197                final Command cmd = ChannelGroupCommands.channelGroupAdd(name, type);
198                return executeAndReturnIntProperty(cmd, "cgid");
199        }
200
201        /**
202         * Adds a specified permission to a channel group.
203         *
204         * @param groupId
205         *              the ID of the channel group to grant the permission
206         * @param permName
207         *              the name of the permission to be granted
208         * @param permValue
209         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
210         *
211         * @return a future to track the progress of this command
212         *
213         * @throws TS3CommandFailedException
214         *              if the execution of a command fails
215         * @querycommands 1
216         * @see ChannelGroup#getId()
217         * @see Permission
218         */
219        public CommandFuture<Void> addChannelGroupPermission(int groupId, String permName, int permValue) {
220                final Command cmd = PermissionCommands.channelGroupAddPerm(groupId, permName, permValue);
221                return executeAndReturnError(cmd);
222        }
223
224        /**
225         * Adds a specified permission to a channel.
226         *
227         * @param channelId
228         *              the ID of the channel wherein the permission should be granted
229         * @param permName
230         *              the name of the permission to grant
231         * @param permValue
232         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
233         *
234         * @return a future to track the progress of this command
235         *
236         * @throws TS3CommandFailedException
237         *              if the execution of a command fails
238         * @querycommands 1
239         * @see Channel#getId()
240         * @see Permission
241         */
242        public CommandFuture<Void> addChannelPermission(int channelId, String permName, int permValue) {
243                final Command cmd = PermissionCommands.channelAddPerm(channelId, permName, permValue);
244                return executeAndReturnError(cmd);
245        }
246
247        /**
248         * Adds a specified permission to a channel.
249         *
250         * @param clientDBId
251         *              the database ID of the client to grant the permission
252         * @param permName
253         *              the name of the permission to grant
254         * @param value
255         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
256         * @param skipped
257         *              if set to {@code true}, the permission will not be overridden by channel group permissions
258         *
259         * @return a future to track the progress of this command
260         *
261         * @throws TS3CommandFailedException
262         *              if the execution of a command fails
263         * @querycommands 1
264         * @see Client#getDatabaseId()
265         * @see Permission
266         */
267        public CommandFuture<Void> addClientPermission(int clientDBId, String permName, int value, boolean skipped) {
268                final Command cmd = PermissionCommands.clientAddPerm(clientDBId, permName, value, skipped);
269                return executeAndReturnError(cmd);
270        }
271
272        /**
273         * Adds a client to the specified server group.
274         * <p>
275         * Please note that a client cannot be added to default groups or template groups.
276         * </p>
277         *
278         * @param groupId
279         *              the ID of the server group to add the client to
280         * @param clientDatabaseId
281         *              the database ID of the client to add
282         *
283         * @return a future to track the progress of this command
284         *
285         * @throws TS3CommandFailedException
286         *              if the execution of a command fails
287         * @querycommands 1
288         * @see ServerGroup#getId()
289         * @see Client#getDatabaseId()
290         */
291        public CommandFuture<Void> addClientToServerGroup(int groupId, int clientDatabaseId) {
292                final Command cmd = ServerGroupCommands.serverGroupAddClient(groupId, clientDatabaseId);
293                return executeAndReturnError(cmd);
294        }
295
296        /**
297         * Submits a complaint about the specified client.
298         * The length of the message is limited to 200 UTF-8 bytes and BB codes in it will be ignored.
299         *
300         * @param clientDBId
301         *              the database ID of the client
302         * @param message
303         *              the message of the complaint, may not contain BB codes
304         *
305         * @return a future to track the progress of this command
306         *
307         * @throws TS3CommandFailedException
308         *              if the execution of a command fails
309         * @querycommands 1
310         * @see Client#getDatabaseId()
311         * @see Complaint#getMessage()
312         */
313        public CommandFuture<Void> addComplaint(int clientDBId, String message) {
314                final Command cmd = ComplaintCommands.complainAdd(clientDBId, message);
315                return executeAndReturnError(cmd);
316        }
317
318        /**
319         * Adds a specified permission to all server groups of the type specified by {@code type} on all virtual servers.
320         *
321         * @param type
322         *              the kind of server group this permission should be added to
323         * @param permName
324         *              the name of the permission to be granted
325         * @param value
326         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
327         * @param negated
328         *              if set to true, the lowest permission value will be selected instead of the highest
329         * @param skipped
330         *              if set to true, this permission will not be overridden by client or channel group permissions
331         *
332         * @return a future to track the progress of this command
333         *
334         * @throws TS3CommandFailedException
335         *              if the execution of a command fails
336         * @querycommands 1
337         * @see ServerGroupType
338         * @see Permission
339         */
340        public CommandFuture<Void> addPermissionToAllServerGroups(ServerGroupType type, String permName, int value, boolean negated, boolean skipped) {
341                final Command cmd = PermissionCommands.serverGroupAutoAddPerm(type, permName, value, negated, skipped);
342                return executeAndReturnError(cmd);
343        }
344
345        /**
346         * Create a new privilege key that allows one client to join a server or channel group.
347         * <ul>
348         * <li>If {@code type} is set to {@linkplain PrivilegeKeyType#SERVER_GROUP SERVER_GROUP},
349         * {@code groupId} is used as a server group ID and {@code channelId} is ignored.</li>
350         * <li>If {@code type} is set to {@linkplain PrivilegeKeyType#CHANNEL_GROUP CHANNEL_GROUP},
351         * {@code groupId} is used as a channel group ID and {@code channelId} is used as the channel in which the group should be set.</li>
352         * </ul>
353         *
354         * @param type
355         *              the type of token that should be created
356         * @param groupId
357         *              the ID of the server or channel group
358         * @param channelId
359         *              the ID of the channel, in case the token is channel group token
360         * @param description
361         *              the description for the token, can be null
362         *
363         * @return the created token for a client to use
364         *
365         * @throws TS3CommandFailedException
366         *              if the execution of a command fails
367         * @querycommands 1
368         * @see PrivilegeKeyType
369         * @see #addPrivilegeKeyServerGroup(int, String)
370         * @see #addPrivilegeKeyChannelGroup(int, int, String)
371         */
372        public CommandFuture<String> addPrivilegeKey(PrivilegeKeyType type, int groupId, int channelId, String description) {
373                final Command cmd = PrivilegeKeyCommands.privilegeKeyAdd(type, groupId, channelId, description);
374                return executeAndReturnStringProperty(cmd, "token");
375        }
376
377        /**
378         * Creates a new privilege key for a channel group.
379         *
380         * @param channelGroupId
381         *              the ID of the channel group
382         * @param channelId
383         *              the ID of the channel in which the channel group should be set
384         * @param description
385         *              the description for the token, can be null
386         *
387         * @return the created token for a client to use
388         *
389         * @throws TS3CommandFailedException
390         *              if the execution of a command fails
391         * @querycommands 1
392         * @see ChannelGroup#getId()
393         * @see Channel#getId()
394         * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String)
395         * @see #addPrivilegeKeyServerGroup(int, String)
396         */
397        public CommandFuture<String> addPrivilegeKeyChannelGroup(int channelGroupId, int channelId, String description) {
398                return addPrivilegeKey(PrivilegeKeyType.CHANNEL_GROUP, channelGroupId, channelId, description);
399        }
400
401        /**
402         * Creates a new privilege key for a server group.
403         *
404         * @param serverGroupId
405         *              the ID of the server group
406         * @param description
407         *              the description for the token, can be null
408         *
409         * @return the created token for a client to use
410         *
411         * @throws TS3CommandFailedException
412         *              if the execution of a command fails
413         * @querycommands 1
414         * @see ServerGroup#getId()
415         * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String)
416         * @see #addPrivilegeKeyChannelGroup(int, int, String)
417         */
418        public CommandFuture<String> addPrivilegeKeyServerGroup(int serverGroupId, String description) {
419                return addPrivilegeKey(PrivilegeKeyType.SERVER_GROUP, serverGroupId, 0, description);
420        }
421
422        /**
423         * Creates a new server group for clients using a given name and returns its ID.
424         * <p>
425         * To create server group templates or ones for server queries,
426         * use {@link #addServerGroup(String, PermissionGroupDatabaseType)}.
427         * </p>
428         *
429         * @param name
430         *              the name of the new server group
431         *
432         * @return the ID of the newly created server group
433         *
434         * @throws TS3CommandFailedException
435         *              if the execution of a command fails
436         * @querycommands 1
437         * @see ServerGroup
438         */
439        public CommandFuture<Integer> addServerGroup(String name) {
440                return addServerGroup(name, PermissionGroupDatabaseType.REGULAR);
441        }
442
443        /**
444         * Creates a new server group using a given name and returns its ID.
445         *
446         * @param name
447         *              the name of the new server group
448         * @param type
449         *              the desired type of server group
450         *
451         * @return the ID of the newly created server group
452         *
453         * @throws TS3CommandFailedException
454         *              if the execution of a command fails
455         * @querycommands 1
456         * @see ServerGroup
457         * @see PermissionGroupDatabaseType
458         */
459        public CommandFuture<Integer> addServerGroup(String name, PermissionGroupDatabaseType type) {
460                final Command cmd = ServerGroupCommands.serverGroupAdd(name, type);
461                return executeAndReturnIntProperty(cmd, "sgid");
462        }
463
464        /**
465         * Adds a specified permission to a server group.
466         *
467         * @param groupId
468         *              the ID of the channel group to which the permission should be added
469         * @param permName
470         *              the name of the permission to add
471         * @param value
472         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
473         * @param negated
474         *              if set to true, the lowest permission value will be selected instead of the highest
475         * @param skipped
476         *              if set to true, this permission will not be overridden by client or channel group permissions
477         *
478         * @return a future to track the progress of this command
479         *
480         * @throws TS3CommandFailedException
481         *              if the execution of a command fails
482         * @querycommands 1
483         * @see ServerGroup#getId()
484         * @see Permission
485         */
486        public CommandFuture<Void> addServerGroupPermission(int groupId, String permName, int value, boolean negated, boolean skipped) {
487                final Command cmd = PermissionCommands.serverGroupAddPerm(groupId, permName, value, negated, skipped);
488                return executeAndReturnError(cmd);
489        }
490
491        /**
492         * Adds one or more {@link TS3Listener}s to the event manager of the query.
493         * These listeners will be notified when the TS3 server fires an event.
494         * <p>
495         * Note that for the TS3 server to fire events, you must first also register
496         * the event types you want to listen to.
497         * </p>
498         *
499         * @param listeners
500         *              one or more listeners to register
501         *
502         * @see #registerAllEvents()
503         * @see #registerEvent(TS3EventType, int)
504         * @see TS3Listener
505         * @see TS3EventType
506         */
507        public void addTS3Listeners(TS3Listener... listeners) {
508                query.getEventManager().addListeners(listeners);
509        }
510
511        /**
512         * Bans a client with a given client ID for a given time.
513         * <p>
514         * Please note that this will create two separate ban rules,
515         * one for the targeted client's IP address and their unique identifier.
516         * </p><p>
517         * <i>Exception:</i> If the banned client connects via a loopback address
518         * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created
519         * and the returned array will only have 1 entry.
520         * </p>
521         *
522         * @param clientId
523         *              the ID of the client
524         * @param timeInSeconds
525         *              the duration of the ban in seconds. 0 equals a permanent ban
526         *
527         * @return an array containing the IDs of the first and the second ban entry
528         *
529         * @throws TS3CommandFailedException
530         *              if the execution of a command fails
531         * @querycommands 1
532         * @see Client#getId()
533         * @see #addBan(String, String, String, long, String)
534         */
535        public CommandFuture<int[]> banClient(int clientId, long timeInSeconds) {
536                return banClient(clientId, timeInSeconds, null);
537        }
538
539        /**
540         * Bans a client with a given client ID for a given time for the specified reason.
541         * <p>
542         * Please note that this will create two separate ban rules,
543         * one for the targeted client's IP address and their unique identifier.
544         * </p><p>
545         * <i>Exception:</i> If the banned client connects via a loopback address
546         * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created
547         * and the returned array will only have 1 entry.
548         * </p>
549         *
550         * @param clientId
551         *              the ID of the client
552         * @param timeInSeconds
553         *              the duration of the ban in seconds. 0 equals a permanent ban
554         * @param reason
555         *              the reason for the ban, can be null
556         *
557         * @return an array containing the IDs of the first and the second ban entry
558         *
559         * @throws TS3CommandFailedException
560         *              if the execution of a command fails
561         * @querycommands 1
562         * @see Client#getId()
563         * @see #addBan(String, String, String, long, String)
564         */
565        public CommandFuture<int[]> banClient(int clientId, long timeInSeconds, String reason) {
566                final Command cmd = BanCommands.banClient(clientId, timeInSeconds, reason);
567                final CommandFuture<int[]> future = new CommandFuture<>();
568
569                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
570                        @Override
571                        public void handleSuccess(DefaultArrayResponse result) {
572                                final List<Wrapper> response = result.getResponses();
573                                final int[] banIds = new int[response.size()];
574                                for (int i = 0; i < banIds.length; ++i) {
575                                        banIds[i] = response.get(i).getInt("banid");
576                                }
577                                future.set(banIds);
578                        }
579                }).forwardFailure(future);
580
581                query.doCommandAsync(cmd);
582                return future;
583        }
584
585        /**
586         * Bans a client with a given client ID permanently for the specified reason.
587         * <p>
588         * Please note that this will create two separate ban rules,
589         * one for the targeted client's IP address and their unique identifier.
590         * </p><p>
591         * <i>Exception:</i> If the banned client connects via a loopback address
592         * (i.e. {@code 127.0.0.1} or {@code localhost}), no IP ban is created
593         * and the returned array will only have 1 entry.
594         * </p>
595         *
596         * @param clientId
597         *              the ID of the client
598         * @param reason
599         *              the reason for the ban, can be null
600         *
601         * @return an array containing the IDs of the first and the second ban entry
602         *
603         * @throws TS3CommandFailedException
604         *              if the execution of a command fails
605         * @querycommands 1
606         * @see Client#getId()
607         * @see #addBan(String, String, String, long, String)
608         */
609        public CommandFuture<int[]> banClient(int clientId, String reason) {
610                return banClient(clientId, 0, reason);
611        }
612
613        /**
614         * Sends a text message to all clients on all virtual servers.
615         * These messages will appear to clients in the tab for server messages.
616         *
617         * @param message
618         *              the message to be sent
619         *
620         * @return a future to track the progress of this command
621         *
622         * @throws TS3CommandFailedException
623         *              if the execution of a command fails
624         * @querycommands 1
625         */
626        public CommandFuture<Void> broadcast(String message) {
627                final Command cmd = ServerCommands.gm(message);
628                return executeAndReturnError(cmd);
629        }
630
631        /**
632         * Creates a copy of the channel group specified by {@code sourceGroupId},
633         * overwriting any other channel group specified by {@code targetGroupId}.
634         * <p>
635         * The parameter {@code type} can be used to create server query and template groups.
636         * </p>
637         *
638         * @param sourceGroupId
639         *              the ID of the channel group to copy
640         * @param targetGroupId
641         *              the ID of another channel group to overwrite
642         * @param type
643         *              the desired type of channel group
644         *
645         * @return a future to track the progress of this command
646         *
647         * @throws TS3CommandFailedException
648         *              if the execution of a command fails
649         * @querycommands 1
650         * @see ChannelGroup#getId()
651         */
652        public CommandFuture<Void> copyChannelGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) {
653                if (targetGroupId <= 0) {
654                        throw new IllegalArgumentException("To create a new channel group, use the method with a String argument");
655                }
656
657                final Command cmd = ChannelGroupCommands.channelGroupCopy(sourceGroupId, targetGroupId, type);
658                return executeAndReturnError(cmd);
659        }
660
661        /**
662         * Creates a copy of the channel group specified by {@code sourceGroupId} with a given name
663         * and returns the ID of the newly created channel group.
664         *
665         * @param sourceGroupId
666         *              the ID of the channel group to copy
667         * @param targetName
668         *              the name for the copy of the channel group
669         * @param type
670         *              the desired type of channel group
671         *
672         * @return the ID of the newly created channel group
673         *
674         * @throws TS3CommandFailedException
675         *              if the execution of a command fails
676         * @querycommands 1
677         * @see ChannelGroup#getId()
678         */
679        public CommandFuture<Integer> copyChannelGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) {
680                final Command cmd = ChannelGroupCommands.channelGroupCopy(sourceGroupId, targetName, type);
681                return executeAndReturnIntProperty(cmd, "cgid");
682        }
683
684        /**
685         * Creates a copy of the server group specified by {@code sourceGroupId},
686         * overwriting another server group specified by {@code targetGroupId}.
687         * <p>
688         * The parameter {@code type} can be used to create server query and template groups.
689         * </p>
690         *
691         * @param sourceGroupId
692         *              the ID of the server group to copy
693         * @param targetGroupId
694         *              the ID of another server group to overwrite
695         * @param type
696         *              the desired type of server group
697         *
698         * @return a future to track the progress of this command
699         *
700         * @throws TS3CommandFailedException
701         *              if the execution of a command fails
702         * @querycommands 1
703         * @see ServerGroup#getId()
704         */
705        public CommandFuture<Integer> copyServerGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) {
706                if (targetGroupId <= 0) {
707                        throw new IllegalArgumentException("To create a new server group, use the method with a String argument");
708                }
709
710                final Command cmd = ServerGroupCommands.serverGroupCopy(sourceGroupId, targetGroupId, type);
711                return executeAndReturnIntProperty(cmd, "sgid");
712        }
713
714        /**
715         * Creates a copy of the server group specified by {@code sourceGroupId} with a given name
716         * and returns the ID of the newly created server group.
717         *
718         * @param sourceGroupId
719         *              the ID of the server group to copy
720         * @param targetName
721         *              the name for the copy of the server group
722         * @param type
723         *              the desired type of server group
724         *
725         * @return the ID of the newly created server group
726         *
727         * @throws TS3CommandFailedException
728         *              if the execution of a command fails
729         * @querycommands 1
730         * @see ServerGroup#getId()
731         */
732        public CommandFuture<Integer> copyServerGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) {
733                final Command cmd = ServerGroupCommands.serverGroupCopy(sourceGroupId, targetName, type);
734                return executeAndReturnIntProperty(cmd, "sgid");
735        }
736
737        /**
738         * Creates a new channel with a given name using the given properties and returns its ID.
739         *
740         * @param name
741         *              the name for the new channel
742         * @param options
743         *              a map of options that should be set for the channel
744         *
745         * @return the ID of the newly created channel
746         *
747         * @throws TS3CommandFailedException
748         *              if the execution of a command fails
749         * @querycommands 1
750         * @see Channel
751         */
752        public CommandFuture<Integer> createChannel(String name, Map<ChannelProperty, String> options) {
753                final Command cmd = ChannelCommands.channelCreate(name, options);
754                return executeAndReturnIntProperty(cmd, "cid");
755        }
756
757        /**
758         * Creates a new directory on the file repository in the specified channel.
759         *
760         * @param directoryPath
761         *              the path to the directory that should be created
762         * @param channelId
763         *              the ID of the channel the directory should be created in
764         *
765         * @return a future to track the progress of this command
766         *
767         * @throws TS3CommandFailedException
768         *              if the execution of a command fails
769         * @querycommands 1
770         * @see FileInfo#getPath()
771         * @see Channel#getId()
772         */
773        public CommandFuture<Void> createFileDirectory(String directoryPath, int channelId) {
774                return createFileDirectory(directoryPath, channelId, null);
775        }
776
777        /**
778         * Creates a new directory on the file repository in the specified channel.
779         *
780         * @param directoryPath
781         *              the path to the directory that should be created
782         * @param channelId
783         *              the ID of the channel the directory should be created in
784         * @param channelPassword
785         *              the password of that channel
786         *
787         * @return a future to track the progress of this command
788         *
789         * @throws TS3CommandFailedException
790         *              if the execution of a command fails
791         * @querycommands 1
792         * @see FileInfo#getPath()
793         * @see Channel#getId()
794         */
795        public CommandFuture<Void> createFileDirectory(String directoryPath, int channelId, String channelPassword) {
796                final Command cmd = FileCommands.ftCreateDir(directoryPath, channelId, channelPassword);
797                return executeAndReturnError(cmd);
798        }
799
800        /**
801         * Creates a new virtual server with the given name and returns an object containing the ID of the newly
802         * created virtual server, the default server admin token and the virtual server's voice port. Usually,
803         * the virtual server is also automatically started. This can be turned off on the TS3 server, though.
804         * <p>
805         * If {@link VirtualServerProperty#VIRTUALSERVER_PORT} is not specified in the virtual server properties,
806         * the server will test for the first unused UDP port.
807         * </p><p>
808         * Please also note that creating virtual servers usually requires the server query admin account
809         * and that there is a limit to how many virtual servers can be created, which is dependent on your license.
810         * Unlicensed TS3 server instances are limited to 1 virtual server with up to 32 client slots.
811         * </p>
812         *
813         * @param name
814         *              the name for the new virtual server
815         * @param options
816         *              a map of options that should be set for the virtual server
817         *
818         * @return information about the newly created virtual server
819         *
820         * @throws TS3CommandFailedException
821         *              if the execution of a command fails
822         * @querycommands 1
823         * @see VirtualServer
824         */
825        public CommandFuture<CreatedVirtualServer> createServer(String name, Map<VirtualServerProperty, String> options) {
826                final Command cmd = VirtualServerCommands.serverCreate(name, options);
827                return executeAndTransformFirst(cmd, Transformer.CREATED_VIRTUAL_SERVER /* CreatedVirtualServer::new */);
828        }
829
830        /**
831         * Creates a {@link Snapshot} of the selected virtual server containing all settings,
832         * groups and known client identities. The data from a server snapshot can be
833         * used to restore a virtual servers configuration.
834         *
835         * @return a snapshot of the virtual server
836         *
837         * @throws TS3CommandFailedException
838         *              if the execution of a command fails
839         * @querycommands 1
840         * @see #deployServerSnapshot(Snapshot)
841         */
842        public CommandFuture<Snapshot> createServerSnapshot() {
843                final Command cmd = VirtualServerCommands.serverSnapshotCreate();
844                final CommandFuture<Snapshot> future = new CommandFuture<>();
845
846                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
847                        @Override
848                        public void handleSuccess(DefaultArrayResponse result) {
849                                future.set(new Snapshot(result.getRawResponse()));
850                        }
851                }).forwardFailure(future);
852
853                query.doCommandAsync(cmd);
854                return future;
855        }
856
857        /**
858         * Deletes all active ban rules from the server. Use with caution.
859         *
860         * @return a future to track the progress of this command
861         *
862         * @throws TS3CommandFailedException
863         *              if the execution of a command fails
864         * @querycommands 1
865         */
866        public CommandFuture<Void> deleteAllBans() {
867                final Command cmd = BanCommands.banDelAll();
868                return executeAndReturnError(cmd);
869        }
870
871        /**
872         * Deletes all complaints about the client with specified database ID from the server.
873         *
874         * @param clientDBId
875         *              the database ID of the client
876         *
877         * @return a future to track the progress of this command
878         *
879         * @throws TS3CommandFailedException
880         *              if the execution of a command fails
881         * @querycommands 1
882         * @see Client#getDatabaseId()
883         * @see Complaint
884         */
885        public CommandFuture<Void> deleteAllComplaints(int clientDBId) {
886                final Command cmd = ComplaintCommands.complainDelAll(clientDBId);
887                return executeAndReturnError(cmd);
888        }
889
890        /**
891         * Deletes the ban rule with the specified ID from the server.
892         *
893         * @param banId
894         *              the ID of the ban to delete
895         *
896         * @return a future to track the progress of this command
897         *
898         * @throws TS3CommandFailedException
899         *              if the execution of a command fails
900         * @querycommands 1
901         * @see Ban#getId()
902         */
903        public CommandFuture<Void> deleteBan(int banId) {
904                final Command cmd = BanCommands.banDel(banId);
905                return executeAndReturnError(cmd);
906        }
907
908        /**
909         * Deletes an existing channel specified by its ID, kicking all clients out of the channel.
910         *
911         * @param channelId
912         *              the ID of the channel to delete
913         *
914         * @return a future to track the progress of this command
915         *
916         * @throws TS3CommandFailedException
917         *              if the execution of a command fails
918         * @querycommands 1
919         * @see Channel#getId()
920         * @see #deleteChannel(int, boolean)
921         * @see #kickClientFromChannel(String, int...)
922         */
923        public CommandFuture<Void> deleteChannel(int channelId) {
924                return deleteChannel(channelId, true);
925        }
926
927        /**
928         * Deletes an existing channel with a given ID.
929         * If {@code force} is true, the channel will be deleted even if there are clients within,
930         * else the command will fail in this situation.
931         *
932         * @param channelId
933         *              the ID of the channel to delete
934         * @param force
935         *              whether clients should be kicked out of the channel
936         *
937         * @return a future to track the progress of this command
938         *
939         * @throws TS3CommandFailedException
940         *              if the execution of a command fails
941         * @querycommands 1
942         * @see Channel#getId()
943         * @see #kickClientFromChannel(String, int...)
944         */
945        public CommandFuture<Void> deleteChannel(int channelId, boolean force) {
946                final Command cmd = ChannelCommands.channelDelete(channelId, force);
947                return executeAndReturnError(cmd);
948        }
949
950        /**
951         * Removes a specified permission from a client in a specific channel.
952         *
953         * @param channelId
954         *              the ID of the channel wherein the permission should be removed
955         * @param clientDBId
956         *              the database ID of the client
957         * @param permName
958         *              the name of the permission to revoke
959         *
960         * @return a future to track the progress of this command
961         *
962         * @throws TS3CommandFailedException
963         *              if the execution of a command fails
964         * @querycommands 1
965         * @see Channel#getId()
966         * @see Client#getDatabaseId()
967         * @see Permission#getName()
968         */
969        public CommandFuture<Void> deleteChannelClientPermission(int channelId, int clientDBId, String permName) {
970                final Command cmd = PermissionCommands.channelClientDelPerm(channelId, clientDBId, permName);
971                return executeAndReturnError(cmd);
972        }
973
974        /**
975         * Removes the channel group with the given ID.
976         *
977         * @param groupId
978         *              the ID of the channel group
979         *
980         * @return a future to track the progress of this command
981         *
982         * @throws TS3CommandFailedException
983         *              if the execution of a command fails
984         * @querycommands 1
985         * @see ChannelGroup#getId()
986         */
987        public CommandFuture<Void> deleteChannelGroup(int groupId) {
988                return deleteChannelGroup(groupId, true);
989        }
990
991        /**
992         * Removes the channel group with the given ID.
993         * If {@code force} is true, the channel group will be deleted even if it still contains clients,
994         * else the command will fail in this situation.
995         *
996         * @param groupId
997         *              the ID of the channel group
998         * @param force
999         *              whether the channel group should be deleted even if it still contains clients
1000         *
1001         * @return a future to track the progress of this command
1002         *
1003         * @throws TS3CommandFailedException
1004         *              if the execution of a command fails
1005         * @querycommands 1
1006         * @see ChannelGroup#getId()
1007         */
1008        public CommandFuture<Void> deleteChannelGroup(int groupId, boolean force) {
1009                final Command cmd = ChannelGroupCommands.channelGroupDel(groupId, force);
1010                return executeAndReturnError(cmd);
1011        }
1012
1013        /**
1014         * Removes a permission from the channel group with the given ID.
1015         *
1016         * @param groupId
1017         *              the ID of the channel group
1018         * @param permName
1019         *              the name of the permission to revoke
1020         *
1021         * @return a future to track the progress of this command
1022         *
1023         * @throws TS3CommandFailedException
1024         *              if the execution of a command fails
1025         * @querycommands 1
1026         * @see ChannelGroup#getId()
1027         * @see Permission#getName()
1028         */
1029        public CommandFuture<Void> deleteChannelGroupPermission(int groupId, String permName) {
1030                final Command cmd = PermissionCommands.channelGroupDelPerm(groupId, permName);
1031                return executeAndReturnError(cmd);
1032        }
1033
1034        /**
1035         * Removes a permission from the channel with the given ID.
1036         *
1037         * @param channelId
1038         *              the ID of the channel
1039         * @param permName
1040         *              the name of the permission to revoke
1041         *
1042         * @return a future to track the progress of this command
1043         *
1044         * @throws TS3CommandFailedException
1045         *              if the execution of a command fails
1046         * @querycommands 1
1047         * @see Channel#getId()
1048         * @see Permission#getName()
1049         */
1050        public CommandFuture<Void> deleteChannelPermission(int channelId, String permName) {
1051                final Command cmd = PermissionCommands.channelDelPerm(channelId, permName);
1052                return executeAndReturnError(cmd);
1053        }
1054
1055        /**
1056         * Removes a permission from a client.
1057         *
1058         * @param clientDBId
1059         *              the database ID of the client
1060         * @param permName
1061         *              the name of the permission to revoke
1062         *
1063         * @return a future to track the progress of this command
1064         *
1065         * @throws TS3CommandFailedException
1066         *              if the execution of a command fails
1067         * @querycommands 1
1068         * @see Client#getDatabaseId()
1069         * @see Permission#getName()
1070         */
1071        public CommandFuture<Void> deleteClientPermission(int clientDBId, String permName) {
1072                final Command cmd = PermissionCommands.clientDelPerm(clientDBId, permName);
1073                return executeAndReturnError(cmd);
1074        }
1075
1076        /**
1077         * Deletes the complaint about the client with database ID {@code targetClientDBId} submitted by
1078         * the client with database ID {@code fromClientDBId} from the server.
1079         *
1080         * @param targetClientDBId
1081         *              the database ID of the client the complaint is about
1082         * @param fromClientDBId
1083         *              the database ID of the client who added the complaint
1084         *
1085         * @return a future to track the progress of this command
1086         *
1087         * @throws TS3CommandFailedException
1088         *              if the execution of a command fails
1089         * @querycommands 1
1090         * @see Complaint
1091         * @see Client#getDatabaseId()
1092         */
1093        public CommandFuture<Void> deleteComplaint(int targetClientDBId, int fromClientDBId) {
1094                final Command cmd = ComplaintCommands.complainDel(targetClientDBId, fromClientDBId);
1095                return executeAndReturnError(cmd);
1096        }
1097
1098        /**
1099         * Removes all stored database information about the specified client.
1100         * Please note that this data is also automatically removed after a configured time (usually 90 days).
1101         * <p>
1102         * See {@link DatabaseClientInfo} for a list of stored information about a client.
1103         * </p>
1104         *
1105         * @param clientDBId
1106         *              the database ID of the client
1107         *
1108         * @return a future to track the progress of this command
1109         *
1110         * @throws TS3CommandFailedException
1111         *              if the execution of a command fails
1112         * @querycommands 1
1113         * @see Client#getDatabaseId()
1114         * @see #getDatabaseClientInfo(int)
1115         * @see DatabaseClientInfo
1116         */
1117        public CommandFuture<Void> deleteDatabaseClientProperties(int clientDBId) {
1118                final Command cmd = DatabaseClientCommands.clientDBDelete(clientDBId);
1119                return executeAndReturnError(cmd);
1120        }
1121
1122        /**
1123         * Deletes a file or directory from the file repository in the specified channel.
1124         *
1125         * @param filePath
1126         *              the path to the file or directory
1127         * @param channelId
1128         *              the ID of the channel the file or directory resides in
1129         *
1130         * @return a future to track the progress of this command
1131         *
1132         * @throws TS3CommandFailedException
1133         *              if the execution of a command fails
1134         * @querycommands 1
1135         * @see FileInfo#getPath()
1136         * @see Channel#getId()
1137         */
1138        public CommandFuture<Void> deleteFile(String filePath, int channelId) {
1139                return deleteFile(filePath, channelId, null);
1140        }
1141
1142        /**
1143         * Deletes a file or directory from the file repository in the specified channel.
1144         *
1145         * @param filePath
1146         *              the path to the file or directory
1147         * @param channelId
1148         *              the ID of the channel the file or directory resides in
1149         * @param channelPassword
1150         *              the password of that channel
1151         *
1152         * @return a future to track the progress of this command
1153         *
1154         * @throws TS3CommandFailedException
1155         *              if the execution of a command fails
1156         * @querycommands 1
1157         * @see FileInfo#getPath()
1158         * @see Channel#getId()
1159         */
1160        public CommandFuture<Void> deleteFile(String filePath, int channelId, String channelPassword) {
1161                final Command cmd = FileCommands.ftDeleteFile(channelId, channelPassword, filePath);
1162                return executeAndReturnError(cmd);
1163        }
1164
1165        /**
1166         * Deletes multiple files or directories from the file repository in the specified channel.
1167         *
1168         * @param filePaths
1169         *              the paths to the files or directories
1170         * @param channelId
1171         *              the ID of the channel the file or directory resides in
1172         *
1173         * @return a future to track the progress of this command
1174         *
1175         * @throws TS3CommandFailedException
1176         *              if the execution of a command fails
1177         * @querycommands 1
1178         * @see FileInfo#getPath()
1179         * @see Channel#getId()
1180         */
1181        public CommandFuture<Void> deleteFiles(String[] filePaths, int channelId) {
1182                return deleteFiles(filePaths, channelId, null);
1183        }
1184
1185        /**
1186         * Deletes multiple files or directories from the file repository in the specified channel.
1187         *
1188         * @param filePaths
1189         *              the paths to the files or directories
1190         * @param channelId
1191         *              the ID of the channel the file or directory resides in
1192         * @param channelPassword
1193         *              the password of that channel
1194         *
1195         * @return a future to track the progress of this command
1196         *
1197         * @throws TS3CommandFailedException
1198         *              if the execution of a command fails
1199         * @querycommands 1
1200         * @see FileInfo#getPath()
1201         * @see Channel#getId()
1202         */
1203        public CommandFuture<Void> deleteFiles(String[] filePaths, int channelId, String channelPassword) {
1204                final Command cmd = FileCommands.ftDeleteFile(channelId, channelPassword, filePaths);
1205                return executeAndReturnError(cmd);
1206        }
1207
1208        /**
1209         * Deletes an icon from the icon directory in the file repository.
1210         *
1211         * @param iconId
1212         *              the ID of the icon to delete
1213         *
1214         * @return a future to track the progress of this command
1215         *
1216         * @throws TS3CommandFailedException
1217         *              if the execution of a command fails
1218         * @querycommands 1
1219         * @see IconFile#getIconId()
1220         */
1221        public CommandFuture<Void> deleteIcon(long iconId) {
1222                final String iconPath = "/icon_" + iconId;
1223                return deleteFile(iconPath, 0);
1224        }
1225
1226        /**
1227         * Deletes multiple icons from the icon directory in the file repository.
1228         *
1229         * @param iconIds
1230         *              the IDs of the icons to delete
1231         *
1232         * @return a future to track the progress of this command
1233         *
1234         * @throws TS3CommandFailedException
1235         *              if the execution of a command fails
1236         * @querycommands 1
1237         * @see IconFile#getIconId()
1238         */
1239        public CommandFuture<Void> deleteIcons(long... iconIds) {
1240                final String[] iconPaths = new String[iconIds.length];
1241                for (int i = 0; i < iconIds.length; ++i) {
1242                        iconPaths[i] = "/icon_" + iconIds[i];
1243                }
1244                return deleteFiles(iconPaths, 0);
1245        }
1246
1247        /**
1248         * Deletes the offline message with the specified ID.
1249         *
1250         * @param messageId
1251         *              the ID of the offline message to delete
1252         *
1253         * @return a future to track the progress of this command
1254         *
1255         * @throws TS3CommandFailedException
1256         *              if the execution of a command fails
1257         * @querycommands 1
1258         * @see Message#getId()
1259         */
1260        public CommandFuture<Void> deleteOfflineMessage(int messageId) {
1261                final Command cmd = MessageCommands.messageDel(messageId);
1262                return executeAndReturnError(cmd);
1263        }
1264
1265        /**
1266         * Removes a specified permission from all server groups of the type specified by {@code type} on all virtual servers.
1267         *
1268         * @param type
1269         *              the kind of server group this permission should be removed from
1270         * @param permName
1271         *              the name of the permission to remove
1272         *
1273         * @return a future to track the progress of this command
1274         *
1275         * @throws TS3CommandFailedException
1276         *              if the execution of a command fails
1277         * @querycommands 1
1278         * @see ServerGroupType
1279         * @see Permission#getName()
1280         */
1281        public CommandFuture<Void> deletePermissionFromAllServerGroups(ServerGroupType type, String permName) {
1282                final Command cmd = PermissionCommands.serverGroupAutoDelPerm(type, permName);
1283                return executeAndReturnError(cmd);
1284        }
1285
1286        /**
1287         * Deletes the privilege key with the given token.
1288         *
1289         * @param token
1290         *              the token of the privilege key
1291         *
1292         * @return a future to track the progress of this command
1293         *
1294         * @throws TS3CommandFailedException
1295         *              if the execution of a command fails
1296         * @querycommands 1
1297         * @see PrivilegeKey
1298         */
1299        public CommandFuture<Void> deletePrivilegeKey(String token) {
1300                final Command cmd = PrivilegeKeyCommands.privilegeKeyDelete(token);
1301                return executeAndReturnError(cmd);
1302        }
1303
1304        /**
1305         * Deletes the virtual server with the specified ID.
1306         * <p>
1307         * Only stopped virtual servers can be deleted.
1308         * </p>
1309         *
1310         * @param serverId
1311         *              the ID of the virtual server
1312         *
1313         * @return a future to track the progress of this command
1314         *
1315         * @throws TS3CommandFailedException
1316         *              if the execution of a command fails
1317         * @querycommands 1
1318         * @see VirtualServer#getId()
1319         * @see #stopServer(int)
1320         */
1321        public CommandFuture<Void> deleteServer(int serverId) {
1322                final Command cmd = VirtualServerCommands.serverDelete(serverId);
1323                return executeAndReturnError(cmd);
1324        }
1325
1326        /**
1327         * Deletes the server group with the specified ID, even if the server group still contains clients.
1328         *
1329         * @param groupId
1330         *              the ID of the server group
1331         *
1332         * @return a future to track the progress of this command
1333         *
1334         * @throws TS3CommandFailedException
1335         *              if the execution of a command fails
1336         * @querycommands 1
1337         * @see ServerGroup#getId()
1338         */
1339        public CommandFuture<Void> deleteServerGroup(int groupId) {
1340                return deleteServerGroup(groupId, true);
1341        }
1342
1343        /**
1344         * Deletes a server group with the specified ID.
1345         * <p>
1346         * If {@code force} is true, the server group will be deleted even if it contains clients,
1347         * else the command will fail in this situation.
1348         * </p>
1349         *
1350         * @param groupId
1351         *              the ID of the server group
1352         * @param force
1353         *              whether the server group should be deleted if it still contains clients
1354         *
1355         * @return a future to track the progress of this command
1356         *
1357         * @throws TS3CommandFailedException
1358         *              if the execution of a command fails
1359         * @querycommands 1
1360         * @see ServerGroup#getId()
1361         */
1362        public CommandFuture<Void> deleteServerGroup(int groupId, boolean force) {
1363                final Command cmd = ServerGroupCommands.serverGroupDel(groupId, force);
1364                return executeAndReturnError(cmd);
1365        }
1366
1367        /**
1368         * Removes a permission from the server group with the given ID.
1369         *
1370         * @param groupId
1371         *              the ID of the server group
1372         * @param permName
1373         *              the name of the permission to revoke
1374         *
1375         * @return a future to track the progress of this command
1376         *
1377         * @throws TS3CommandFailedException
1378         *              if the execution of a command fails
1379         * @querycommands 1
1380         * @see ServerGroup#getId()
1381         * @see Permission#getName()
1382         */
1383        public CommandFuture<Void> deleteServerGroupPermission(int groupId, String permName) {
1384                final Command cmd = PermissionCommands.serverGroupDelPerm(groupId, permName);
1385                return executeAndReturnError(cmd);
1386        }
1387
1388        /**
1389         * Restores the selected virtual servers configuration using the data from a
1390         * previously created server snapshot.
1391         *
1392         * @param snapshot
1393         *              the snapshot to restore
1394         *
1395         * @return a future to track the progress of this command
1396         *
1397         * @throws TS3CommandFailedException
1398         *              if the execution of a command fails
1399         * @querycommands 1
1400         * @see #createServerSnapshot()
1401         */
1402        public CommandFuture<Void> deployServerSnapshot(Snapshot snapshot) {
1403                return deployServerSnapshot(snapshot.get());
1404        }
1405
1406        /**
1407         * Restores the configuration of the selected virtual server using the data from a
1408         * previously created server snapshot.
1409         *
1410         * @param snapshot
1411         *              the snapshot to restore
1412         *
1413         * @return a future to track the progress of this command
1414         *
1415         * @throws TS3CommandFailedException
1416         *              if the execution of a command fails
1417         * @querycommands 1
1418         * @see #createServerSnapshot()
1419         */
1420        public CommandFuture<Void> deployServerSnapshot(String snapshot) {
1421                final Command cmd = VirtualServerCommands.serverSnapshotDeploy(snapshot);
1422                return executeAndReturnError(cmd);
1423        }
1424
1425        /**
1426         * Downloads a file from the file repository at a given path and channel
1427         * and writes the file's bytes to an open {@link OutputStream}.
1428         * <p>
1429         * It is the user's responsibility to ensure that the given {@code OutputStream} is
1430         * open and to close the stream again once the download has finished.
1431         * </p><p>
1432         * Note that this method will not read the entire file to memory and can thus
1433         * download arbitrarily sized files from the file repository.
1434         * </p>
1435         *
1436         * @param dataOut
1437         *              a stream that the downloaded data should be written to
1438         * @param filePath
1439         *              the path of the file on the file repository
1440         * @param channelId
1441         *              the ID of the channel to download the file from
1442         *
1443         * @return how many bytes were downloaded
1444         *
1445         * @throws TS3CommandFailedException
1446         *              if the execution of a command fails
1447         * @throws TS3FileTransferFailedException
1448         *              if the file transfer fails for any reason
1449         * @querycommands 1
1450         * @see FileInfo#getPath()
1451         * @see Channel#getId()
1452         * @see #downloadFileDirect(String, int)
1453         */
1454        public CommandFuture<Long> downloadFile(OutputStream dataOut, String filePath, int channelId) {
1455                return downloadFile(dataOut, filePath, channelId, null);
1456        }
1457
1458        /**
1459         * Downloads a file from the file repository at a given path and channel
1460         * and writes the file's bytes to an open {@link OutputStream}.
1461         * <p>
1462         * It is the user's responsibility to ensure that the given {@code OutputStream} is
1463         * open and to close the stream again once the download has finished.
1464         * </p><p>
1465         * Note that this method will not read the entire file to memory and can thus
1466         * download arbitrarily sized files from the file repository.
1467         * </p>
1468         *
1469         * @param dataOut
1470         *              a stream that the downloaded data should be written to
1471         * @param filePath
1472         *              the path of the file on the file repository
1473         * @param channelId
1474         *              the ID of the channel to download the file from
1475         * @param channelPassword
1476         *              that channel's password
1477         *
1478         * @return how many bytes were downloaded
1479         *
1480         * @throws TS3CommandFailedException
1481         *              if the execution of a command fails
1482         * @throws TS3FileTransferFailedException
1483         *              if the file transfer fails for any reason
1484         * @querycommands 1
1485         * @see FileInfo#getPath()
1486         * @see Channel#getId()
1487         * @see #downloadFileDirect(String, int, String)
1488         */
1489        public CommandFuture<Long> downloadFile(final OutputStream dataOut, String filePath, int channelId, String channelPassword) {
1490                final FileTransferHelper helper = query.getFileTransferHelper();
1491                final int transferId = helper.getClientTransferId();
1492                final Command cmd = FileCommands.ftInitDownload(transferId, filePath, channelId, channelPassword);
1493                final CommandFuture<Long> future = new CommandFuture<>();
1494
1495                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
1496                        @Override
1497                        public void handleSuccess(DefaultArrayResponse result) {
1498                                FileTransferParameters params = new FileTransferParameters(result.getFirstResponse().getMap());
1499                                QueryError error = params.getQueryError();
1500                                if (!error.isSuccessful()) {
1501                                        future.fail(new TS3CommandFailedException(error));
1502                                        return;
1503                                }
1504
1505                                try {
1506                                        query.getFileTransferHelper().downloadFile(dataOut, params);
1507                                } catch (IOException e) {
1508                                        future.fail(new TS3FileTransferFailedException("Download failed", e));
1509                                        return;
1510                                }
1511                                future.set(params.getFileSize());
1512                        }
1513                }).forwardFailure(future);
1514
1515                query.doCommandAsync(cmd);
1516                return future;
1517        }
1518
1519        /**
1520         * Downloads a file from the file repository at a given path and channel
1521         * and returns the file's bytes as a byte array.
1522         * <p>
1523         * Note that this method <strong>will read the entire file to memory</strong>.
1524         * That means that if a file is larger than 2<sup>31</sup>-1 bytes in size,
1525         * the download will fail.
1526         * </p>
1527         *
1528         * @param filePath
1529         *              the path of the file on the file repository
1530         * @param channelId
1531         *              the ID of the channel to download the file from
1532         *
1533         * @return a byte array containing the file's data
1534         *
1535         * @throws TS3CommandFailedException
1536         *              if the execution of a command fails
1537         * @throws TS3FileTransferFailedException
1538         *              if the file transfer fails for any reason
1539         * @querycommands 1
1540         * @see FileInfo#getPath()
1541         * @see Channel#getId()
1542         * @see #downloadFile(OutputStream, String, int)
1543         */
1544        public CommandFuture<byte[]> downloadFileDirect(String filePath, int channelId) {
1545                return downloadFileDirect(filePath, channelId, null);
1546        }
1547
1548        /**
1549         * Downloads a file from the file repository at a given path and channel
1550         * and returns the file's bytes as a byte array.
1551         * <p>
1552         * Note that this method <strong>will read the entire file to memory</strong>.
1553         * That means that if a file is larger than 2<sup>31</sup>-1 bytes in size,
1554         * the download will fail.
1555         * </p>
1556         *
1557         * @param filePath
1558         *              the path of the file on the file repository
1559         * @param channelId
1560         *              the ID of the channel to download the file from
1561         * @param channelPassword
1562         *              that channel's password
1563         *
1564         * @return a byte array containing the file's data
1565         *
1566         * @throws TS3CommandFailedException
1567         *              if the execution of a command fails
1568         * @throws TS3FileTransferFailedException
1569         *              if the file transfer fails for any reason
1570         * @querycommands 1
1571         * @see FileInfo#getPath()
1572         * @see Channel#getId()
1573         * @see #downloadFile(OutputStream, String, int, String)
1574         */
1575        public CommandFuture<byte[]> downloadFileDirect(String filePath, int channelId, String channelPassword) {
1576                final FileTransferHelper helper = query.getFileTransferHelper();
1577                final int transferId = helper.getClientTransferId();
1578                final Command cmd = FileCommands.ftInitDownload(transferId, filePath, channelId, channelPassword);
1579                final CommandFuture<byte[]> future = new CommandFuture<>();
1580
1581                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
1582                        @Override
1583                        public void handleSuccess(DefaultArrayResponse result) {
1584                                FileTransferParameters params = new FileTransferParameters(result.getFirstResponse().getMap());
1585                                QueryError error = params.getQueryError();
1586                                if (!error.isSuccessful()) {
1587                                        future.fail(new TS3CommandFailedException(error));
1588                                        return;
1589                                }
1590
1591                                long fileSize = params.getFileSize();
1592                                if (fileSize > Integer.MAX_VALUE) {
1593                                        future.fail(new TS3FileTransferFailedException("File too big for byte array"));
1594                                        return;
1595                                }
1596                                ByteArrayOutputStream dataOut = new ByteArrayOutputStream((int) fileSize);
1597
1598                                try {
1599                                        query.getFileTransferHelper().downloadFile(dataOut, params);
1600                                } catch (IOException e) {
1601                                        future.fail(new TS3FileTransferFailedException("Download failed", e));
1602                                        return;
1603                                }
1604                                future.set(dataOut.toByteArray());
1605                        }
1606                }).forwardFailure(future);
1607
1608                query.doCommandAsync(cmd);
1609                return future;
1610        }
1611
1612        /**
1613         * Downloads an icon from the icon directory in the file repository
1614         * and writes the file's bytes to an open {@link OutputStream}.
1615         * <p>
1616         * It is the user's responsibility to ensure that the given {@code OutputStream} is
1617         * open and to close the stream again once the download has finished.
1618         * </p>
1619         *
1620         * @param dataOut
1621         *              a stream that the downloaded data should be written to
1622         * @param iconId
1623         *              the ID of the icon that should be downloaded
1624         *
1625         * @return a byte array containing the icon file's data
1626         *
1627         * @throws TS3CommandFailedException
1628         *              if the execution of a command fails
1629         * @throws TS3FileTransferFailedException
1630         *              if the file transfer fails for any reason
1631         * @querycommands 1
1632         * @see IconFile#getIconId()
1633         * @see #downloadIconDirect(long)
1634         * @see #uploadIcon(InputStream, long)
1635         */
1636        public CommandFuture<Long> downloadIcon(OutputStream dataOut, long iconId) {
1637                final String iconPath = "/icon_" + iconId;
1638                return downloadFile(dataOut, iconPath, 0);
1639        }
1640
1641        /**
1642         * Downloads an icon from the icon directory in the file repository
1643         * and returns the file's bytes as a byte array.
1644         * <p>
1645         * Note that this method <strong>will read the entire file to memory</strong>.
1646         * </p>
1647         *
1648         * @param iconId
1649         *              the ID of the icon that should be downloaded
1650         *
1651         * @return a byte array containing the icon file's data
1652         *
1653         * @throws TS3CommandFailedException
1654         *              if the execution of a command fails
1655         * @throws TS3FileTransferFailedException
1656         *              if the file transfer fails for any reason
1657         * @querycommands 1
1658         * @see IconFile#getIconId()
1659         * @see #downloadIcon(OutputStream, long)
1660         * @see #uploadIconDirect(byte[])
1661         */
1662        public CommandFuture<byte[]> downloadIconDirect(long iconId) {
1663                final String iconPath = "/icon_" + iconId;
1664                return downloadFileDirect(iconPath, 0);
1665        }
1666
1667        /**
1668         * Changes a channel's configuration using the given properties.
1669         *
1670         * @param channelId
1671         *              the ID of the channel to edit
1672         * @param options
1673         *              the map of properties to modify
1674         *
1675         * @return a future to track the progress of this command
1676         *
1677         * @throws TS3CommandFailedException
1678         *              if the execution of a command fails
1679         * @querycommands 1
1680         * @see Channel#getId()
1681         */
1682        public CommandFuture<Void> editChannel(int channelId, Map<ChannelProperty, String> options) {
1683                final Command cmd = ChannelCommands.channelEdit(channelId, options);
1684                return executeAndReturnError(cmd);
1685        }
1686
1687        /**
1688         * Changes a single property of the given channel.
1689         * <p>
1690         * Note that one can set many properties at once with the overloaded method that
1691         * takes a map of channel properties and strings.
1692         * </p>
1693         *
1694         * @param channelId
1695         *              the ID of the channel to edit
1696         * @param property
1697         *              the channel property to modify, make sure it is editable
1698         * @param value
1699         *              the new value of the property
1700         *
1701         * @return a future to track the progress of this command
1702         *
1703         * @throws TS3CommandFailedException
1704         *              if the execution of a command fails
1705         * @querycommands 1
1706         * @see Channel#getId()
1707         * @see #editChannel(int, Map)
1708         */
1709        public CommandFuture<Void> editChannel(int channelId, ChannelProperty property, String value) {
1710                return editChannel(channelId, Collections.singletonMap(property, value));
1711        }
1712
1713        /**
1714         * Changes a client's configuration using given properties.
1715         * <p>
1716         * Only {@link ClientProperty#CLIENT_DESCRIPTION} can be changed for other clients.
1717         * To update the current client's properties, use {@link #updateClient(Map)}
1718         * or {@link #updateClient(ClientProperty, String)}.
1719         * </p>
1720         *
1721         * @param clientId
1722         *              the ID of the client to edit
1723         * @param options
1724         *              the map of properties to modify
1725         *
1726         * @return a future to track the progress of this command
1727         *
1728         * @throws TS3CommandFailedException
1729         *              if the execution of a command fails
1730         * @querycommands 1
1731         * @see Client#getId()
1732         * @see #updateClient(Map)
1733         */
1734        public CommandFuture<Void> editClient(int clientId, Map<ClientProperty, String> options) {
1735                final Command cmd = ClientCommands.clientEdit(clientId, options);
1736                return executeAndReturnError(cmd);
1737        }
1738
1739        /**
1740         * Changes a single property of the given client.
1741         * <p>
1742         * Only {@link ClientProperty#CLIENT_DESCRIPTION} can be changed for other clients.
1743         * To update the current client's properties, use {@link #updateClient(Map)}
1744         * or {@link #updateClient(ClientProperty, String)}.
1745         * </p>
1746         *
1747         * @param clientId
1748         *              the ID of the client to edit
1749         * @param property
1750         *              the client property to modify, make sure it is editable
1751         * @param value
1752         *              the new value of the property
1753         *
1754         * @return a future to track the progress of this command
1755         *
1756         * @throws TS3CommandFailedException
1757         *              if the execution of a command fails
1758         * @querycommands 1
1759         * @see Client#getId()
1760         * @see #editClient(int, Map)
1761         * @see #updateClient(Map)
1762         */
1763        public CommandFuture<Void> editClient(int clientId, ClientProperty property, String value) {
1764                return editClient(clientId, Collections.singletonMap(property, value));
1765        }
1766
1767        /**
1768         * Changes a client's database settings using given properties.
1769         *
1770         * @param clientDBId
1771         *              the database ID of the client to edit
1772         * @param options
1773         *              the map of properties to modify
1774         *
1775         * @return a future to track the progress of this command
1776         *
1777         * @throws TS3CommandFailedException
1778         *              if the execution of a command fails
1779         * @querycommands 1
1780         * @see DatabaseClientInfo
1781         * @see Client#getDatabaseId()
1782         */
1783        public CommandFuture<Void> editDatabaseClient(int clientDBId, Map<ClientProperty, String> options) {
1784                final Command cmd = DatabaseClientCommands.clientDBEdit(clientDBId, options);
1785                return executeAndReturnError(cmd);
1786        }
1787
1788        /**
1789         * Changes the server instance configuration using given properties.
1790         * If the given property is not changeable, {@code IllegalArgumentException} will be thrown.
1791         *
1792         * @param property
1793         *              the property to edit, must be changeable
1794         * @param value
1795         *              the new value for the edit
1796         *
1797         * @return a future to track the progress of this command
1798         *
1799         * @throws IllegalArgumentException
1800         *              if {@code property} is not changeable
1801         * @throws TS3CommandFailedException
1802         *              if the execution of a command fails
1803         * @querycommands 1
1804         * @see ServerInstanceProperty#isChangeable()
1805         */
1806        public CommandFuture<Void> editInstance(ServerInstanceProperty property, String value) {
1807                final Command cmd = ServerCommands.instanceEdit(Collections.singletonMap(property, value));
1808                return executeAndReturnError(cmd);
1809        }
1810
1811        /**
1812         * Changes the configuration of the selected virtual server using given properties.
1813         *
1814         * @param options
1815         *              the map of properties to edit
1816         *
1817         * @return a future to track the progress of this command
1818         *
1819         * @throws TS3CommandFailedException
1820         *              if the execution of a command fails
1821         * @querycommands 1
1822         * @see VirtualServerProperty
1823         */
1824        public CommandFuture<Void> editServer(Map<VirtualServerProperty, String> options) {
1825                final Command cmd = VirtualServerCommands.serverEdit(options);
1826                return executeAndReturnError(cmd);
1827        }
1828
1829        /**
1830         * Gets a list of all bans on the selected virtual server.
1831         *
1832         * @return a list of all bans on the virtual server
1833         *
1834         * @throws TS3CommandFailedException
1835         *              if the execution of a command fails
1836         * @querycommands 1
1837         * @see Ban
1838         */
1839        public CommandFuture<List<Ban>> getBans() {
1840                final Command cmd = BanCommands.banList();
1841                return executeAndTransform(cmd, Transformer.BAN /* Ban::new */);
1842        }
1843
1844        /**
1845         * Gets a list of IP addresses used by the server instance.
1846         *
1847         * @return the list of bound IP addresses
1848         *
1849         * @throws TS3CommandFailedException
1850         *              if the execution of a command fails
1851         * @querycommands 1
1852         * @see Binding
1853         */
1854        public CommandFuture<List<Binding>> getBindings() {
1855                final Command cmd = ServerCommands.bindingList();
1856                return executeAndTransform(cmd, Transformer.BINDING /* Binding::new */);
1857        }
1858
1859        /**
1860         * Finds and returns the channel matching the given name exactly.
1861         *
1862         * @param name
1863         *              the name of the channel
1864         * @param ignoreCase
1865         *              whether the case of the name should be ignored
1866         *
1867         * @return the found channel or {@code null} if no channel was found
1868         *
1869         * @throws TS3CommandFailedException
1870         *              if the execution of a command fails
1871         * @querycommands 1
1872         * @see Channel
1873         * @see #getChannelsByName(String)
1874         */
1875        public CommandFuture<Channel> getChannelByNameExact(String name, final boolean ignoreCase) {
1876                final CommandFuture<Channel> future = new CommandFuture<>();
1877                final String caseName = ignoreCase ? name.toLowerCase(Locale.ROOT) : name;
1878
1879                getChannels().onSuccess(new CommandFuture.SuccessListener<List<Channel>>() {
1880                        @Override
1881                        public void handleSuccess(final List<Channel> allChannels) {
1882                                for (final Channel c : allChannels) {
1883                                        final String channelName = ignoreCase ? c.getName().toLowerCase(Locale.ROOT) : c.getName();
1884                                        if (caseName.equals(channelName)) {
1885                                                future.set(c);
1886                                                return;
1887                                        }
1888                                }
1889                                future.set(null); // Not found
1890                        }
1891                }).forwardFailure(future);
1892                return future;
1893        }
1894
1895        /**
1896         * Gets a list of channels whose names contain the given search string.
1897         *
1898         * @param name
1899         *              the name to search
1900         *
1901         * @return a list of all channels with names matching the search pattern
1902         *
1903         * @throws TS3CommandFailedException
1904         *              if the execution of a command fails
1905         * @querycommands 2
1906         * @see Channel
1907         * @see #getChannelByNameExact(String, boolean)
1908         */
1909        public CommandFuture<List<Channel>> getChannelsByName(String name) {
1910                final Command cmd = ChannelCommands.channelFind(name);
1911                final CommandFuture<List<Channel>> future = new CommandFuture<>();
1912
1913                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
1914                        @Override
1915                        public void handleSuccess(final DefaultArrayResponse result) {
1916                                getChannels().onSuccess(new CommandFuture.SuccessListener<List<Channel>>() {
1917                                        @Override
1918                                        public void handleSuccess(List<Channel> allChannels) {
1919                                                final List<Wrapper> responses = result.getResponses();
1920                                                final List<Channel> channels = new ArrayList<>(responses.size());
1921
1922                                                for (final Wrapper response : responses) {
1923                                                        final int channelId = response.getInt("cid");
1924                                                        for (final Channel c : allChannels) {
1925                                                                if (c.getId() == channelId) {
1926                                                                        channels.add(c);
1927                                                                        break;
1928                                                                }
1929                                                        }
1930                                                }
1931                                                future.set(channels);
1932                                        }
1933                                }).forwardFailure(future);
1934                        }
1935                }).onFailure(transformError(future, 768, Collections.<Channel>emptyList()));
1936
1937                query.doCommandAsync(cmd);
1938                return future;
1939        }
1940
1941        /**
1942         * Displays a list of permissions defined for a client in a specific channel.
1943         *
1944         * @param channelId
1945         *              the ID of the channel
1946         * @param clientDBId
1947         *              the database ID of the client
1948         *
1949         * @return a list of permissions for the user in the specified channel
1950         *
1951         * @throws TS3CommandFailedException
1952         *              if the execution of a command fails
1953         * @querycommands 1
1954         * @see Channel#getId()
1955         * @see Client#getDatabaseId()
1956         * @see Permission
1957         */
1958        public CommandFuture<List<Permission>> getChannelClientPermissions(int channelId, int clientDBId) {
1959                final Command cmd = PermissionCommands.channelClientPermList(channelId, clientDBId);
1960                return executeAndTransform(cmd, Transformer.PERMISSION /* Permission::new */);
1961        }
1962
1963        /**
1964         * Gets all client / channel ID combinations currently assigned to channel groups.
1965         * All three parameters are optional and can be turned off by setting it to {@code -1}.
1966         *
1967         * @param channelId
1968         *              restricts the search to the channel with a specified ID. Set to {@code -1} to ignore.
1969         * @param clientDBId
1970         *              restricts the search to the client with a specified database ID. Set to {@code -1} to ignore.
1971         * @param groupId
1972         *              restricts the search to the channel group with the specified ID. Set to {@code -1} to ignore.
1973         *
1974         * @return a list of combinations of channel ID, client database ID and channel group ID
1975         *
1976         * @throws TS3CommandFailedException
1977         *              if the execution of a command fails
1978         * @querycommands 1
1979         * @see Channel#getId()
1980         * @see Client#getDatabaseId()
1981         * @see ChannelGroup#getId()
1982         * @see ChannelGroupClient
1983         */
1984        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClients(int channelId, int clientDBId, int groupId) {
1985                final Command cmd = ChannelGroupCommands.channelGroupClientList(channelId, clientDBId, groupId);
1986                return executeAndTransform(cmd, Transformer.CHANNEL_GROUP_CLIENT /* ChannelGroupClient::new */);
1987        }
1988
1989        /**
1990         * Gets all client / channel ID combinations currently assigned to the specified channel group.
1991         *
1992         * @param groupId
1993         *              the ID of the channel group whose client / channel assignments should be returned.
1994         *
1995         * @return a list of combinations of channel ID, client database ID and channel group ID
1996         *
1997         * @throws TS3CommandFailedException
1998         *              if the execution of a command fails
1999         * @querycommands 1
2000         * @see ChannelGroup#getId()
2001         * @see ChannelGroupClient
2002         * @see #getChannelGroupClients(int, int, int)
2003         */
2004        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClientsByChannelGroupId(int groupId) {
2005                return getChannelGroupClients(-1, -1, groupId);
2006        }
2007
2008        /**
2009         * Gets all channel group assignments in the specified channel.
2010         *
2011         * @param channelId
2012         *              the ID of the channel whose channel group assignments should be returned.
2013         *
2014         * @return a list of combinations of channel ID, client database ID and channel group ID
2015         *
2016         * @throws TS3CommandFailedException
2017         *              if the execution of a command fails
2018         * @querycommands 1
2019         * @see Channel#getId()
2020         * @see ChannelGroupClient
2021         * @see #getChannelGroupClients(int, int, int)
2022         */
2023        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClientsByChannelId(int channelId) {
2024                return getChannelGroupClients(channelId, -1, -1);
2025        }
2026
2027        /**
2028         * Gets all channel group assignments for the specified client.
2029         *
2030         * @param clientDBId
2031         *              the database ID of the client whose channel group
2032         *
2033         * @return a list of combinations of channel ID, client database ID and channel group ID
2034         *
2035         * @throws TS3CommandFailedException
2036         *              if the execution of a command fails
2037         * @querycommands 1
2038         * @see Client#getDatabaseId()
2039         * @see ChannelGroupClient
2040         * @see #getChannelGroupClients(int, int, int)
2041         */
2042        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClientsByClientDBId(int clientDBId) {
2043                return getChannelGroupClients(-1, clientDBId, -1);
2044        }
2045
2046        /**
2047         * Gets a list of all permissions assigned to the specified channel group.
2048         *
2049         * @param groupId
2050         *              the ID of the channel group.
2051         *
2052         * @return a list of permissions assigned to the channel group
2053         *
2054         * @throws TS3CommandFailedException
2055         *              if the execution of a command fails
2056         * @querycommands 1
2057         * @see ChannelGroup#getId()
2058         * @see Permission
2059         */
2060        public CommandFuture<List<Permission>> getChannelGroupPermissions(int groupId) {
2061                final Command cmd = PermissionCommands.channelGroupPermList(groupId);
2062                return executeAndTransform(cmd, Transformer.PERMISSION /* Permission::new */);
2063        }
2064
2065        /**
2066         * Gets a list of all channel groups on the selected virtual server.
2067         *
2068         * @return a list of all channel groups on the virtual server
2069         *
2070         * @throws TS3CommandFailedException
2071         *              if the execution of a command fails
2072         * @querycommands 1
2073         * @see ChannelGroup
2074         */
2075        public CommandFuture<List<ChannelGroup>> getChannelGroups() {
2076                final Command cmd = ChannelGroupCommands.channelGroupList();
2077                return executeAndTransform(cmd, Transformer.CHANNEL_GROUP /* ChannelGroup::new */);
2078        }
2079
2080        /**
2081         * Gets detailed configuration information about the channel specified channel.
2082         *
2083         * @param channelId
2084         *              the ID of the channel
2085         *
2086         * @return information about the channel
2087         *
2088         * @throws TS3CommandFailedException
2089         *              if the execution of a command fails
2090         * @querycommands 1
2091         * @see Channel#getId()
2092         * @see ChannelInfo
2093         */
2094        public CommandFuture<ChannelInfo> getChannelInfo(final int channelId) {
2095                final Command cmd = ChannelCommands.channelInfo(channelId);
2096                final CommandFuture<ChannelInfo> future = new CommandFuture<>();
2097
2098                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2099                        @Override
2100                        public void handleSuccess(DefaultArrayResponse result) {
2101                                future.set(new ChannelInfo(channelId, result.getFirstResponse().getMap()));
2102                        }
2103                }).forwardFailure(future);
2104
2105                query.doCommandAsync(cmd);
2106                return future;
2107        }
2108
2109        /**
2110         * Gets a list of all permissions assigned to the specified channel.
2111         *
2112         * @param channelId
2113         *              the ID of the channel
2114         *
2115         * @return a list of all permissions assigned to the channel
2116         *
2117         * @throws TS3CommandFailedException
2118         *              if the execution of a command fails
2119         * @querycommands 1
2120         * @see Channel#getId()
2121         * @see Permission
2122         */
2123        public CommandFuture<List<Permission>> getChannelPermissions(int channelId) {
2124                final Command cmd = PermissionCommands.channelPermList(channelId);
2125                return executeAndTransform(cmd, Transformer.PERMISSION /* Permission::new */);
2126        }
2127
2128        /**
2129         * Gets a list of all channels on the selected virtual server.
2130         *
2131         * @return a list of all channels on the virtual server
2132         *
2133         * @throws TS3CommandFailedException
2134         *              if the execution of a command fails
2135         * @querycommands 1
2136         * @see Channel
2137         */
2138        public CommandFuture<List<Channel>> getChannels() {
2139                final Command cmd = ChannelCommands.channelList();
2140                return executeAndTransform(cmd, Transformer.CHANNEL /* Channel::new */);
2141        }
2142
2143        /**
2144         * Finds and returns the client whose nickname matches the given name exactly.
2145         *
2146         * @param name
2147         *              the name of the client
2148         * @param ignoreCase
2149         *              whether the case of the name should be ignored
2150         *
2151         * @return the found client or {@code null} if no client was found
2152         *
2153         * @throws TS3CommandFailedException
2154         *              if the execution of a command fails
2155         * @querycommands 1
2156         * @see Client
2157         * @see #getClientsByName(String)
2158         */
2159        public CommandFuture<Client> getClientByNameExact(String name, final boolean ignoreCase) {
2160                final CommandFuture<Client> future = new CommandFuture<>();
2161                final String caseName = ignoreCase ? name.toLowerCase(Locale.ROOT) : name;
2162
2163                getClients().onSuccess(new CommandFuture.SuccessListener<List<Client>>() {
2164                        @Override
2165                        public void handleSuccess(final List<Client> allClients) {
2166                                for (final Client c : allClients) {
2167                                        final String clientName = ignoreCase ? c.getNickname().toLowerCase(Locale.ROOT) : c.getNickname();
2168                                        if (caseName.equals(clientName)) {
2169                                                future.set(c);
2170                                                return;
2171                                        }
2172                                }
2173                                future.set(null); // Not found
2174                        }
2175                }).forwardFailure(future);
2176                return future;
2177        }
2178
2179        /**
2180         * Gets a list of clients whose nicknames contain the given search string.
2181         *
2182         * @param name
2183         *              the name to search
2184         *
2185         * @return a list of all clients with nicknames matching the search pattern
2186         *
2187         * @throws TS3CommandFailedException
2188         *              if the execution of a command fails
2189         * @querycommands 2
2190         * @see Client
2191         * @see #getClientByNameExact(String, boolean)
2192         */
2193        public CommandFuture<List<Client>> getClientsByName(String name) {
2194                final Command cmd = ClientCommands.clientFind(name);
2195                final CommandFuture<List<Client>> future = new CommandFuture<>();
2196
2197                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2198                        @Override
2199                        public void handleSuccess(final DefaultArrayResponse result) {
2200                                getClients().onSuccess(new CommandFuture.SuccessListener<List<Client>>() {
2201                                        @Override
2202                                        public void handleSuccess(List<Client> allClients) {
2203                                                final List<Wrapper> responses = result.getResponses();
2204                                                final List<Client> clients = new ArrayList<>(responses.size());
2205
2206                                                for (final Wrapper response : responses) {
2207                                                        for (final Client c : allClients) {
2208                                                                if (c.getId() == response.getInt("clid")) {
2209                                                                        clients.add(c);
2210                                                                        break;
2211                                                                }
2212                                                        }
2213                                                }
2214                                                future.set(clients);
2215                                        }
2216                                }).forwardFailure(future);
2217                        }
2218                }).onFailure(transformError(future, 512, Collections.<Client>emptyList()));
2219
2220                query.doCommandAsync(cmd);
2221                return future;
2222        }
2223
2224        /**
2225         * Gets information about the client with the specified unique identifier.
2226         *
2227         * @param clientUId
2228         *              the unique identifier of the client
2229         *
2230         * @return information about the client
2231         *
2232         * @throws TS3CommandFailedException
2233         *              if the execution of a command fails
2234         * @querycommands 2
2235         * @see Client#getUniqueIdentifier()
2236         * @see ClientInfo
2237         */
2238        public CommandFuture<ClientInfo> getClientByUId(String clientUId) {
2239                final Command cmd = ClientCommands.clientGetIds(clientUId);
2240                final CommandFuture<ClientInfo> future = new CommandFuture<>();
2241
2242                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2243                        @Override
2244                        public void handleSuccess(DefaultArrayResponse result) {
2245                                final int clientId = result.getFirstResponse().getInt("clid");
2246                                getClientInfo(clientId).forwardResult(future);
2247                        }
2248                }).forwardFailure(future);
2249
2250                query.doCommandAsync(cmd);
2251                return future;
2252        }
2253
2254        /**
2255         * Gets information about the client with the specified client ID.
2256         *
2257         * @param clientId
2258         *              the client ID of the client
2259         *
2260         * @return information about the client
2261         *
2262         * @throws TS3CommandFailedException
2263         *              if the execution of a command fails
2264         * @querycommands 1
2265         * @see Client#getId()
2266         * @see ClientInfo
2267         */
2268        public CommandFuture<ClientInfo> getClientInfo(final int clientId) {
2269                final Command cmd = ClientCommands.clientInfo(clientId);
2270                final CommandFuture<ClientInfo> future = new CommandFuture<>();
2271
2272                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2273                        @Override
2274                        public void handleSuccess(DefaultArrayResponse result) {
2275                                future.set(new ClientInfo(clientId, result.getFirstResponse().getMap()));
2276                        }
2277                }).forwardFailure(future);
2278
2279                query.doCommandAsync(cmd);
2280                return future;
2281        }
2282
2283        /**
2284         * Gets a list of all permissions assigned to the specified client.
2285         *
2286         * @param clientDBId
2287         *              the database ID of the client
2288         *
2289         * @return a list of all permissions assigned to the client
2290         *
2291         * @throws TS3CommandFailedException
2292         *              if the execution of a command fails
2293         * @querycommands 1
2294         * @see Client#getDatabaseId()
2295         * @see Permission
2296         */
2297        public CommandFuture<List<Permission>> getClientPermissions(int clientDBId) {
2298                final Command cmd = PermissionCommands.clientPermList(clientDBId);
2299                return executeAndTransform(cmd, Transformer.PERMISSION /* Permission::new */);
2300        }
2301
2302        /**
2303         * Gets a list of all clients on the selected virtual server.
2304         *
2305         * @return a list of all clients on the virtual server
2306         *
2307         * @throws TS3CommandFailedException
2308         *              if the execution of a command fails
2309         * @querycommands 1
2310         * @see Client
2311         */
2312        public CommandFuture<List<Client>> getClients() {
2313                final Command cmd = ClientCommands.clientList();
2314                return executeAndTransform(cmd, Transformer.CLIENT /* Client::new */);
2315        }
2316
2317        /**
2318         * Gets a list of all complaints on the selected virtual server.
2319         *
2320         * @return a list of all complaints on the virtual server
2321         *
2322         * @throws TS3CommandFailedException
2323         *              if the execution of a command fails
2324         * @querycommands 1
2325         * @see Complaint
2326         * @see #getComplaints(int)
2327         */
2328        public CommandFuture<List<Complaint>> getComplaints() {
2329                return getComplaints(-1);
2330        }
2331
2332        /**
2333         * Gets a list of all complaints about the specified client.
2334         *
2335         * @param clientDBId
2336         *              the database ID of the client
2337         *
2338         * @return a list of all complaints about the specified client
2339         *
2340         * @throws TS3CommandFailedException
2341         *              if the execution of a command fails
2342         * @querycommands 1
2343         * @see Client#getDatabaseId()
2344         * @see Complaint
2345         */
2346        public CommandFuture<List<Complaint>> getComplaints(int clientDBId) {
2347                final Command cmd = ComplaintCommands.complainList(clientDBId);
2348                return executeAndTransform(cmd, Transformer.COMPLAINT /* Complaint::new */);
2349        }
2350
2351        /**
2352         * Gets detailed connection information about the selected virtual server.
2353         *
2354         * @return connection information about the selected virtual server
2355         *
2356         * @throws TS3CommandFailedException
2357         *              if the execution of a command fails
2358         * @querycommands 1
2359         * @see ConnectionInfo
2360         * @see #getServerInfo()
2361         */
2362        public CommandFuture<ConnectionInfo> getConnectionInfo() {
2363                final Command cmd = VirtualServerCommands.serverRequestConnectionInfo();
2364                return executeAndTransformFirst(cmd, Transformer.CONNECTION_INFO /* ConnectionInfo::new */);
2365        }
2366
2367        /**
2368         * Gets all clients in the database whose last nickname matches the specified name <b>exactly</b>.
2369         *
2370         * @param name
2371         *              the nickname for the clients to match
2372         *
2373         * @return a list of all clients with a matching nickname
2374         *
2375         * @throws TS3CommandFailedException
2376         *              if the execution of a command fails
2377         * @querycommands 1 + n,
2378         * where n is the amount of database clients with a matching nickname
2379         * @see Client#getNickname()
2380         */
2381        public CommandFuture<List<DatabaseClientInfo>> getDatabaseClientsByName(String name) {
2382                final Command cmd = DatabaseClientCommands.clientDBFind(name, false);
2383                final CommandFuture<List<DatabaseClientInfo>> future = new CommandFuture<>();
2384
2385                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2386                        @Override
2387                        public void handleSuccess(DefaultArrayResponse result) {
2388                                final List<Wrapper> responses = result.getResponses();
2389                                final Collection<CommandFuture<DatabaseClientInfo>> infoFutures = new ArrayList<>(responses.size());
2390                                for (Wrapper response : responses) {
2391                                        final int databaseId = response.getInt("cldbid");
2392                                        infoFutures.add(getDatabaseClientInfo(databaseId));
2393                                }
2394
2395                                CommandFuture.ofAll(infoFutures).forwardResult(future);
2396                        }
2397                }).forwardFailure(future);
2398
2399                query.doCommandAsync(cmd);
2400                return future;
2401        }
2402
2403        /**
2404         * Gets information about the client with the specified unique identifier in the server database.
2405         *
2406         * @param clientUId
2407         *              the unique identifier of the client
2408         *
2409         * @return the database client or {@code null} if no client was found
2410         *
2411         * @throws TS3CommandFailedException
2412         *              if the execution of a command fails
2413         * @querycommands 2
2414         * @see Client#getUniqueIdentifier()
2415         * @see DatabaseClientInfo
2416         */
2417        public CommandFuture<DatabaseClientInfo> getDatabaseClientByUId(String clientUId) {
2418                final Command cmd = DatabaseClientCommands.clientDBFind(clientUId, true);
2419                final CommandFuture<DatabaseClientInfo> future = new CommandFuture<>();
2420
2421                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2422                        @Override
2423                        public void handleSuccess(DefaultArrayResponse result) {
2424                                if (result.getResponses().isEmpty()) {
2425                                        future.set(null);
2426                                } else {
2427                                        final int databaseId = result.getFirstResponse().getInt("cldbid");
2428                                        getDatabaseClientInfo(databaseId).forwardResult(future);
2429                                }
2430                        }
2431                }).forwardFailure(future);
2432
2433                query.doCommandAsync(cmd);
2434                return future;
2435        }
2436
2437        /**
2438         * Gets information about the client with the specified database ID in the server database.
2439         *
2440         * @param clientDBId
2441         *              the database ID of the client
2442         *
2443         * @return the database client or {@code null} if no client was found
2444         *
2445         * @throws TS3CommandFailedException
2446         *              if the execution of a command fails
2447         * @querycommands 1
2448         * @see Client#getDatabaseId()
2449         * @see DatabaseClientInfo
2450         */
2451        public CommandFuture<DatabaseClientInfo> getDatabaseClientInfo(int clientDBId) {
2452                final Command cmd = DatabaseClientCommands.clientDBInfo(clientDBId);
2453                return executeAndTransformFirst(cmd, Transformer.DATABASE_CLIENT_INFO /* DatabaseClientInfo::new */);
2454        }
2455
2456        /**
2457         * Gets information about all clients in the server database.
2458         * <p>
2459         * As this method uses internal commands which can only return 200 clients at once,
2460         * this method can take quite some time to execute.
2461         * </p><p>
2462         * Also keep in mind that the client database can easily accumulate several thousand entries.
2463         * </p>
2464         *
2465         * @return a {@link List} of all database clients
2466         *
2467         * @throws TS3CommandFailedException
2468         *              if the execution of a command fails
2469         * @querycommands 1 + n,
2470         * where n = Math.ceil([amount of database clients] / 200)
2471         * @see DatabaseClient
2472         */
2473        public CommandFuture<List<DatabaseClient>> getDatabaseClients() {
2474                final Command cmd = DatabaseClientCommands.clientDBList(0, 1, true);
2475                final CommandFuture<List<DatabaseClient>> future = new CommandFuture<>();
2476
2477                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2478                        @Override
2479                        public void handleSuccess(DefaultArrayResponse result) {
2480                                final int count = result.getFirstResponse().getInt("count");
2481                                final int futuresCount = ((count - 1) / 200) + 1;
2482                                final Collection<CommandFuture<List<DatabaseClient>>> futures = new ArrayList<>(futuresCount);
2483                                for (int i = 0; i < count; i += 200) {
2484                                        futures.add(getDatabaseClients(i, 200));
2485                                }
2486
2487                                CommandFuture.ofAll(futures).onSuccess(new CommandFuture.SuccessListener<List<List<DatabaseClient>>>() {
2488                                        @Override
2489                                        public void handleSuccess(List<List<DatabaseClient>> result) {
2490                                                int total = 0;
2491                                                for (List<DatabaseClient> list : result) {
2492                                                        total += list.size();
2493                                                }
2494
2495                                                final List<DatabaseClient> combination = new ArrayList<>(total);
2496                                                for (List<DatabaseClient> list : result) {
2497                                                        combination.addAll(list);
2498                                                }
2499                                                future.set(combination);
2500                                        }
2501                                }).forwardFailure(future);
2502                        }
2503                }).forwardFailure(future);
2504
2505                query.doCommandAsync(cmd);
2506                return future;
2507        }
2508
2509        /**
2510         * Gets information about a set number of clients in the server database, starting at {@code offset}.
2511         *
2512         * @param offset
2513         *              the index of the first database client to be returned.
2514         *              Note that this is <b>not</b> a database ID, but an arbitrary, 0-based index.
2515         * @param count
2516         *              the number of database clients that should be returned.
2517         *              Any integer greater than 200 might cause problems with the connection
2518         *
2519         * @return a {@link List} of database clients
2520         *
2521         * @throws TS3CommandFailedException
2522         *              if the execution of a command fails
2523         * @querycommands 1
2524         * @see DatabaseClient
2525         */
2526        public CommandFuture<List<DatabaseClient>> getDatabaseClients(final int offset, final int count) {
2527                final Command cmd = DatabaseClientCommands.clientDBList(offset, count, false);
2528                return executeAndTransform(cmd, Transformer.DATABASE_CLIENT /* DatabaseClient::new */);
2529        }
2530
2531        /**
2532         * Gets information about a file on the file repository in the specified channel.
2533         * <p>
2534         * Note that this method does not work on directories and the information returned by this
2535         * method is identical to the one returned by {@link #getFileList(String, int, String)}
2536         * </p>
2537         *
2538         * @param filePath
2539         *              the path to the file
2540         * @param channelId
2541         *              the ID of the channel the file resides in
2542         *
2543         * @return some information about the file
2544         *
2545         * @throws TS3CommandFailedException
2546         *              if the execution of a command fails
2547         * @querycommands 1
2548         * @see FileInfo#getPath()
2549         * @see Channel#getId()
2550         */
2551        public CommandFuture<FileInfo> getFileInfo(String filePath, int channelId) {
2552                return getFileInfo(filePath, channelId, null);
2553        }
2554
2555        /**
2556         * Gets information about a file on the file repository in the specified channel.
2557         * <p>
2558         * Note that this method does not work on directories and the information returned by this
2559         * method is identical to the one returned by {@link #getFileList(String, int, String)}
2560         * </p>
2561         *
2562         * @param filePath
2563         *              the path to the file
2564         * @param channelId
2565         *              the ID of the channel the file resides in
2566         * @param channelPassword
2567         *              the password of that channel
2568         *
2569         * @return some information about the file
2570         *
2571         * @throws TS3CommandFailedException
2572         *              if the execution of a command fails
2573         * @querycommands 1
2574         * @see FileInfo#getPath()
2575         * @see Channel#getId()
2576         */
2577        public CommandFuture<FileInfo> getFileInfo(String filePath, int channelId, String channelPassword) {
2578                final Command cmd = FileCommands.ftGetFileInfo(channelId, channelPassword, filePath);
2579                return executeAndTransformFirst(cmd, Transformer.FILE_INFO /* FileInfo::new */);
2580        }
2581
2582        /**
2583         * Gets information about multiple files on the file repository in the specified channel.
2584         * <p>
2585         * Note that this method does not work on directories and the information returned by this
2586         * method is identical to the one returned by {@link #getFileList(String, int, String)}
2587         * </p>
2588         *
2589         * @param filePaths
2590         *              the paths to the files
2591         * @param channelId
2592         *              the ID of the channel the file resides in
2593         *
2594         * @return some information about the file
2595         *
2596         * @throws TS3CommandFailedException
2597         *              if the execution of a command fails
2598         * @querycommands 1
2599         * @see FileInfo#getPath()
2600         * @see Channel#getId()
2601         */
2602        public CommandFuture<List<FileInfo>> getFileInfos(String filePaths[], int channelId) {
2603                return getFileInfos(filePaths, channelId, null);
2604        }
2605
2606        /**
2607         * Gets information about multiple files on the file repository in the specified channel.
2608         * <p>
2609         * Note that this method does not work on directories and the information returned by this
2610         * method is identical to the one returned by {@link #getFileList(String, int, String)}
2611         * </p>
2612         *
2613         * @param filePaths
2614         *              the paths to the files
2615         * @param channelId
2616         *              the ID of the channel the file resides in
2617         * @param channelPassword
2618         *              the password of that channel
2619         *
2620         * @return some information about the file
2621         *
2622         * @throws TS3CommandFailedException
2623         *              if the execution of a command fails
2624         * @querycommands 1
2625         * @see FileInfo#getPath()
2626         * @see Channel#getId()
2627         */
2628        public CommandFuture<List<FileInfo>> getFileInfos(String filePaths[], int channelId, String channelPassword) {
2629                final Command cmd = FileCommands.ftGetFileInfo(channelId, channelPassword, filePaths);
2630                return executeAndTransform(cmd, Transformer.FILE_INFO /* FileInfo::new */);
2631        }
2632
2633        /**
2634         * Gets information about multiple files on the file repository in multiple channels.
2635         * <p>
2636         * Note that this method does not work on directories and the information returned by this
2637         * method is identical to the one returned by {@link #getFileList(String, int, String)}
2638         * </p>
2639         *
2640         * @param filePaths
2641         *              the paths to the files, may not be {@code null} and may not contain {@code null} elements
2642         * @param channelIds
2643         *              the IDs of the channels the file resides in, may not be {@code null}
2644         * @param channelPasswords
2645         *              the passwords of those channels, may be {@code null} and may contain {@code null} elements
2646         *
2647         * @return some information about the files
2648         *
2649         * @throws IllegalArgumentException
2650         *              if the dimensions of {@code filePaths}, {@code channelIds} and {@code channelPasswords} don't match
2651         * @throws TS3CommandFailedException
2652         *              if the execution of a command fails
2653         * @querycommands 1
2654         * @see FileInfo#getPath()
2655         * @see Channel#getId()
2656         */
2657        public CommandFuture<List<FileInfo>> getFileInfos(String filePaths[], int[] channelIds, String[] channelPasswords) {
2658                final Command cmd = FileCommands.ftGetFileInfo(channelIds, channelPasswords, filePaths);
2659                return executeAndTransform(cmd, Transformer.FILE_INFO /* FileInfo::new */);
2660        }
2661
2662        /**
2663         * Gets a list of files and directories in the specified parent directory and channel.
2664         *
2665         * @param directoryPath
2666         *              the path to the parent directory
2667         * @param channelId
2668         *              the ID of the channel the directory resides in
2669         *
2670         * @return the files and directories in the parent directory
2671         *
2672         * @throws TS3CommandFailedException
2673         *              if the execution of a command fails
2674         * @querycommands 1
2675         * @see FileInfo#getPath()
2676         * @see Channel#getId()
2677         */
2678        public CommandFuture<List<FileListEntry>> getFileList(String directoryPath, int channelId) {
2679                return getFileList(directoryPath, channelId, null);
2680        }
2681
2682        /**
2683         * Gets a list of files and directories in the specified parent directory and channel.
2684         *
2685         * @param directoryPath
2686         *              the path to the parent directory
2687         * @param channelId
2688         *              the ID of the channel the directory resides in
2689         * @param channelPassword
2690         *              the password of that channel
2691         *
2692         * @return the files and directories in the parent directory
2693         *
2694         * @throws TS3CommandFailedException
2695         *              if the execution of a command fails
2696         * @querycommands 1
2697         * @see FileInfo#getPath()
2698         * @see Channel#getId()
2699         */
2700        public CommandFuture<List<FileListEntry>> getFileList(final String directoryPath, final int channelId, String channelPassword) {
2701                final Command cmd = FileCommands.ftGetFileList(directoryPath, channelId, channelPassword);
2702                return executeAndTransform(cmd, Transformer.FILE_LIST_ENTRY /* FileListEntry::new */);
2703        }
2704
2705        /**
2706         * Gets a list of active or recently active file transfers.
2707         *
2708         * @return a list of file transfers
2709         *
2710         * @throws TS3CommandFailedException
2711         *              if the execution of a command fails
2712         * @querycommands 1
2713         */
2714        public CommandFuture<List<FileTransfer>> getFileTransfers() {
2715                final Command cmd = FileCommands.ftList();
2716                return executeAndTransform(cmd, Transformer.FILE_TRANSFER /* FileTransfer::new */);
2717        }
2718
2719        /**
2720         * Displays detailed configuration information about the server instance including
2721         * uptime, number of virtual servers online, traffic information, etc.
2722         *
2723         * @return information about the host
2724         *
2725         * @throws TS3CommandFailedException
2726         *              if the execution of a command fails
2727         * @querycommands 1
2728         */
2729        public CommandFuture<HostInfo> getHostInfo() {
2730                final Command cmd = ServerCommands.hostInfo();
2731                return executeAndTransformFirst(cmd, Transformer.HOST_INFO /* HostInfo::new */);
2732        }
2733
2734        /**
2735         * Gets a list of all icon files on this virtual server.
2736         *
2737         * @return a list of all icons
2738         */
2739        public CommandFuture<List<IconFile>> getIconList() {
2740                final CommandFuture<List<IconFile>> future = new CommandFuture<>();
2741
2742                getFileList("/icons/", 0).onSuccess(new CommandFuture.SuccessListener<List<FileListEntry>>() {
2743                        @Override
2744                        public void handleSuccess(List<FileListEntry> result) {
2745                                List<IconFile> icons = new ArrayList<>(result.size());
2746                                for (FileListEntry file : result) {
2747                                        if (file.isDirectory() || file.isStillUploading()) continue;
2748                                        icons.add(new IconFile(file.getMap()));
2749                                }
2750                                future.set(icons);
2751                        }
2752                }).forwardFailure(future);
2753
2754                return future;
2755        }
2756
2757        /**
2758         * Displays the server instance configuration including database revision number,
2759         * the file transfer port, default group IDs, etc.
2760         *
2761         * @return information about the TeamSpeak server instance.
2762         *
2763         * @throws TS3CommandFailedException
2764         *              if the execution of a command fails
2765         * @querycommands 1
2766         */
2767        public CommandFuture<InstanceInfo> getInstanceInfo() {
2768                final Command cmd = ServerCommands.instanceInfo();
2769                return executeAndTransformFirst(cmd, Transformer.INSTANCE_INFO /* InstanceInfo::new */);
2770        }
2771
2772        /**
2773         * Fetches the specified amount of log entries from the server log.
2774         *
2775         * @param lines
2776         *              the amount of log entries to fetch, in the range between 1 and 100.
2777         *              Returns 100 entries if the argument is not in range
2778         *
2779         * @return a list of the latest log entries
2780         *
2781         * @throws TS3CommandFailedException
2782         *              if the execution of a command fails
2783         * @querycommands 1
2784         */
2785        public CommandFuture<List<String>> getInstanceLogEntries(int lines) {
2786                final Command cmd = ServerCommands.logView(lines, true);
2787                return executeAndReturnLogLines(cmd);
2788        }
2789
2790        /**
2791         * Fetches the last 100 log entries from the server log.
2792         *
2793         * @return a list of up to 100 log entries
2794         *
2795         * @throws TS3CommandFailedException
2796         *              if the execution of a command fails
2797         * @querycommands 1
2798         */
2799        public CommandFuture<List<String>> getInstanceLogEntries() {
2800                return getInstanceLogEntries(100);
2801        }
2802
2803        /**
2804         * Reads the message body of a message. This will not set the read flag, though.
2805         *
2806         * @param messageId
2807         *              the ID of the message to be read
2808         *
2809         * @return the body of the message with the specified ID or {@code null} if there was no message with that ID
2810         *
2811         * @throws TS3CommandFailedException
2812         *              if the execution of a command fails
2813         * @querycommands 1
2814         * @see Message#getId()
2815         * @see #setMessageRead(int)
2816         */
2817        public CommandFuture<String> getOfflineMessage(int messageId) {
2818                final Command cmd = MessageCommands.messageGet(messageId);
2819                return executeAndReturnStringProperty(cmd, "message");
2820        }
2821
2822        /**
2823         * Reads the message body of a message. This will not set the read flag, though.
2824         *
2825         * @param message
2826         *              the message to be read
2827         *
2828         * @return the body of the message with the specified ID or {@code null} if there was no message with that ID
2829         *
2830         * @throws TS3CommandFailedException
2831         *              if the execution of a command fails
2832         * @querycommands 1
2833         * @see Message#getId()
2834         * @see #setMessageRead(Message)
2835         */
2836        public CommandFuture<String> getOfflineMessage(Message message) {
2837                return getOfflineMessage(message.getId());
2838        }
2839
2840        /**
2841         * Gets a list of all offline messages for the server query.
2842         * The returned messages lack their message body, though.
2843         * To read the actual message, use {@link #getOfflineMessage(int)} or {@link #getOfflineMessage(Message)}.
2844         *
2845         * @return a list of all offline messages this server query has received
2846         *
2847         * @throws TS3CommandFailedException
2848         *              if the execution of a command fails
2849         * @querycommands 1
2850         */
2851        public CommandFuture<List<Message>> getOfflineMessages() {
2852                final Command cmd = MessageCommands.messageList();
2853                return executeAndTransform(cmd, Transformer.MESSAGE /* Message::new */);
2854        }
2855
2856        /**
2857         * Displays detailed information about all assignments of the permission specified
2858         * with {@code permName}. The output includes the type and the ID of the client,
2859         * channel or group associated with the permission.
2860         *
2861         * @param permName
2862         *              the name of the permission
2863         *
2864         * @return a list of permission assignments
2865         *
2866         * @throws TS3CommandFailedException
2867         *              if the execution of a command fails
2868         * @querycommands 1
2869         * @see #getPermissionOverview(int, int)
2870         */
2871        public CommandFuture<List<PermissionAssignment>> getPermissionAssignments(String permName) {
2872                final Command cmd = PermissionCommands.permFind(permName);
2873                final CommandFuture<List<PermissionAssignment>> future = new CommandFuture<>();
2874
2875                executeAndTransform(cmd, Transformer.PERMISSION_ASSIGNMENT /* PermissionAssignment::new */)
2876                                .forwardSuccess(future)
2877                                .onFailure(transformError(future, 2562, Collections.<PermissionAssignment>emptyList()));
2878
2879                return future;
2880        }
2881
2882        /**
2883         * Gets the ID of the permission specified by {@code permName}.
2884         * <p>
2885         * Note that the use of numeric permission IDs is deprecated
2886         * and that this API only uses the string variant of the IDs.
2887         * </p>
2888         *
2889         * @param permName
2890         *              the name of the permission
2891         *
2892         * @return the numeric ID of the specified permission
2893         *
2894         * @throws TS3CommandFailedException
2895         *              if the execution of a command fails
2896         * @querycommands 1
2897         */
2898        public CommandFuture<Integer> getPermissionIdByName(String permName) {
2899                final Command cmd = PermissionCommands.permIdGetByName(permName);
2900                return executeAndReturnIntProperty(cmd, "permid");
2901        }
2902
2903        /**
2904         * Gets the IDs of the permissions specified by {@code permNames}.
2905         * <p>
2906         * Note that the use of numeric permission IDs is deprecated
2907         * and that this API only uses the string variant of the IDs.
2908         * </p>
2909         *
2910         * @param permNames
2911         *              the names of the permissions
2912         *
2913         * @return the numeric IDs of the specified permission
2914         *
2915         * @throws IllegalArgumentException
2916         *              if {@code permNames} is {@code null}
2917         * @throws TS3CommandFailedException
2918         *              if the execution of a command fails
2919         * @querycommands 1
2920         */
2921        public CommandFuture<int[]> getPermissionIdsByName(String... permNames) {
2922                final Command cmd = PermissionCommands.permIdGetByName(permNames);
2923                final CommandFuture<int[]> future = new CommandFuture<>();
2924
2925                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
2926                        @Override
2927                        public void handleSuccess(DefaultArrayResponse result) {
2928                                final List<Wrapper> responses = result.getResponses();
2929                                int[] ids = new int[responses.size()];
2930                                int i = 0;
2931                                for (final Wrapper response : responses) {
2932                                        ids[i++] = response.getInt("permid");
2933                                }
2934                                future.set(ids);
2935                        }
2936                }).forwardFailure(future);
2937
2938                query.doCommandAsync(cmd);
2939                return future;
2940        }
2941
2942        /**
2943         * Gets a list of all assigned permissions for a client in a specified channel.
2944         * If you do not care about channel permissions, set {@code channelId} to {@code 0}.
2945         *
2946         * @param channelId
2947         *              the ID of the channel
2948         * @param clientDBId
2949         *              the database ID of the client to create the overview for
2950         *
2951         * @return a list of all permission assignments for the client in the specified channel
2952         *
2953         * @throws TS3CommandFailedException
2954         *              if the execution of a command fails
2955         * @querycommands 1
2956         * @see Channel#getId()
2957         * @see Client#getDatabaseId()
2958         */
2959        public CommandFuture<List<PermissionAssignment>> getPermissionOverview(int channelId, int clientDBId) {
2960                final Command cmd = PermissionCommands.permOverview(channelId, clientDBId);
2961                return executeAndTransform(cmd, Transformer.PERMISSION_ASSIGNMENT /* PermissionAssignment::new */);
2962        }
2963
2964        /**
2965         * Displays a list of all permissions, including ID, name and description.
2966         *
2967         * @return a list of all permissions
2968         *
2969         * @throws TS3CommandFailedException
2970         *              if the execution of a command fails
2971         * @querycommands 1
2972         */
2973        public CommandFuture<List<PermissionInfo>> getPermissions() {
2974                final Command cmd = PermissionCommands.permissionList();
2975                return executeAndTransform(cmd, Transformer.PERMISSION_INFO /* PermissionInfo::new */);
2976        }
2977
2978        /**
2979         * Displays the current value of the specified permission for this server query instance.
2980         *
2981         * @param permName
2982         *              the name of the permission
2983         *
2984         * @return the permission value, usually ranging from 0 to 100
2985         *
2986         * @throws TS3CommandFailedException
2987         *              if the execution of a command fails
2988         * @querycommands 1
2989         */
2990        public CommandFuture<Integer> getPermissionValue(String permName) {
2991                final Command cmd = PermissionCommands.permGet(permName);
2992                return executeAndReturnIntProperty(cmd, "permvalue");
2993        }
2994
2995        /**
2996         * Displays the current values of the specified permissions for this server query instance.
2997         *
2998         * @param permNames
2999         *              the names of the permissions
3000         *
3001         * @return the permission values, usually ranging from 0 to 100
3002         *
3003         * @throws IllegalArgumentException
3004         *              if {@code permNames} is {@code null}
3005         * @throws TS3CommandFailedException
3006         *              if the execution of a command fails
3007         * @querycommands 1
3008         */
3009        public CommandFuture<int[]> getPermissionValues(String... permNames) {
3010                final Command cmd = PermissionCommands.permGet(permNames);
3011                final CommandFuture<int[]> future = new CommandFuture<>();
3012
3013                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
3014                        @Override
3015                        public void handleSuccess(DefaultArrayResponse result) {
3016                                final List<Wrapper> responses = result.getResponses();
3017                                int[] values = new int[responses.size()];
3018                                int i = 0;
3019                                for (final Wrapper response : responses) {
3020                                        values[i++] = response.getInt("permvalue");
3021                                }
3022                                future.set(values);
3023                        }
3024                }).forwardFailure(future);
3025
3026                query.doCommandAsync(cmd);
3027                return future;
3028        }
3029
3030        /**
3031         * Gets a list of all available tokens to join channel or server groups,
3032         * including their type and group IDs.
3033         *
3034         * @return a list of all generated, but still unclaimed privilege keys
3035         *
3036         * @throws TS3CommandFailedException
3037         *              if the execution of a command fails
3038         * @querycommands 1
3039         * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String)
3040         * @see #usePrivilegeKey(String)
3041         */
3042        public CommandFuture<List<PrivilegeKey>> getPrivilegeKeys() {
3043                final Command cmd = PrivilegeKeyCommands.privilegeKeyList();
3044                return executeAndTransform(cmd, Transformer.PRIVILEGE_KEY /* PrivilegeKey::new */);
3045        }
3046
3047        /**
3048         * Gets a list of all clients in the specified server group.
3049         *
3050         * @param serverGroupId
3051         *              the ID of the server group for which the clients should be looked up
3052         *
3053         * @return a list of all clients in the server group
3054         *
3055         * @throws TS3CommandFailedException
3056         *              if the execution of a command fails
3057         * @querycommands 1
3058         */
3059        public CommandFuture<List<ServerGroupClient>> getServerGroupClients(int serverGroupId) {
3060                final Command cmd = ServerGroupCommands.serverGroupClientList(serverGroupId);
3061                return executeAndTransform(cmd, Transformer.SERVER_GROUP_CLIENT /* ServerGroupClient::new */);
3062        }
3063
3064        /**
3065         * Gets a list of all clients in the specified server group.
3066         *
3067         * @param serverGroup
3068         *              the server group for which the clients should be looked up
3069         *
3070         * @return a list of all clients in the server group
3071         *
3072         * @throws TS3CommandFailedException
3073         *              if the execution of a command fails
3074         * @querycommands 1
3075         */
3076        public CommandFuture<List<ServerGroupClient>> getServerGroupClients(ServerGroup serverGroup) {
3077                return getServerGroupClients(serverGroup.getId());
3078        }
3079
3080        /**
3081         * Gets a list of all permissions assigned to the specified server group.
3082         *
3083         * @param serverGroupId
3084         *              the ID of the server group for which the permissions should be looked up
3085         *
3086         * @return a list of all permissions assigned to the server group
3087         *
3088         * @throws TS3CommandFailedException
3089         *              if the execution of a command fails
3090         * @querycommands 1
3091         * @see ServerGroup#getId()
3092         * @see #getServerGroupPermissions(ServerGroup)
3093         */
3094        public CommandFuture<List<Permission>> getServerGroupPermissions(int serverGroupId) {
3095                final Command cmd = PermissionCommands.serverGroupPermList(serverGroupId);
3096                return executeAndTransform(cmd, Transformer.PERMISSION /* Permission::new */);
3097        }
3098
3099        /**
3100         * Gets a list of all permissions assigned to the specified server group.
3101         *
3102         * @param serverGroup
3103         *              the server group for which the permissions should be looked up
3104         *
3105         * @return a list of all permissions assigned to the server group
3106         *
3107         * @throws TS3CommandFailedException
3108         *              if the execution of a command fails
3109         * @querycommands 1
3110         */
3111        public CommandFuture<List<Permission>> getServerGroupPermissions(ServerGroup serverGroup) {
3112                return getServerGroupPermissions(serverGroup.getId());
3113        }
3114
3115        /**
3116         * Gets a list of all server groups on the virtual server.
3117         * <p>
3118         * Depending on your permissions, the output may also contain
3119         * global server query groups and template groups.
3120         * </p>
3121         *
3122         * @return a list of all server groups
3123         *
3124         * @throws TS3CommandFailedException
3125         *              if the execution of a command fails
3126         * @querycommands 1
3127         */
3128        public CommandFuture<List<ServerGroup>> getServerGroups() {
3129                final Command cmd = ServerGroupCommands.serverGroupList();
3130                return executeAndTransform(cmd, Transformer.SERVER_GROUP /* ServerGroup::new */);
3131        }
3132
3133        /**
3134         * Gets a list of all server groups set for a client.
3135         *
3136         * @param clientDatabaseId
3137         *              the database ID of the client for which the server groups should be looked up
3138         *
3139         * @return a list of all server groups set for the client
3140         *
3141         * @throws TS3CommandFailedException
3142         *              if the execution of a command fails
3143         * @querycommands 2
3144         * @see Client#getDatabaseId()
3145         * @see #getServerGroupsByClient(Client)
3146         */
3147        public CommandFuture<List<ServerGroup>> getServerGroupsByClientId(int clientDatabaseId) {
3148                final Command cmd = ServerGroupCommands.serverGroupsByClientId(clientDatabaseId);
3149                final CommandFuture<List<ServerGroup>> future = new CommandFuture<>();
3150
3151                getServerGroups().onSuccess(new CommandFuture.SuccessListener<List<ServerGroup>>() {
3152                        @Override
3153                        public void handleSuccess(final List<ServerGroup> allServerGroups) {
3154                                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
3155                                        @Override
3156                                        public void handleSuccess(DefaultArrayResponse result) {
3157                                                final List<Wrapper> responses = result.getResponses();
3158                                                final List<ServerGroup> list = new ArrayList<>(responses.size());
3159
3160                                                for (final Wrapper response : responses) {
3161                                                        for (final ServerGroup s : allServerGroups) {
3162                                                                if (s.getId() == response.getInt("sgid")) {
3163                                                                        list.add(s);
3164                                                                }
3165                                                        }
3166                                                }
3167                                                future.set(list);
3168                                        }
3169                                }).forwardFailure(future);
3170
3171                                query.doCommandAsync(cmd);
3172                        }
3173                }).forwardFailure(future);
3174                return future;
3175        }
3176
3177        /**
3178         * Gets a list of all server groups set for a client.
3179         *
3180         * @param client
3181         *              the client for which the server groups should be looked up
3182         *
3183         * @return a list of all server group set for the client
3184         *
3185         * @throws TS3CommandFailedException
3186         *              if the execution of a command fails
3187         * @querycommands 2
3188         * @see #getServerGroupsByClientId(int)
3189         */
3190        public CommandFuture<List<ServerGroup>> getServerGroupsByClient(Client client) {
3191                return getServerGroupsByClientId(client.getDatabaseId());
3192        }
3193
3194        /**
3195         * Gets the ID of a virtual server by its port.
3196         *
3197         * @param port
3198         *              the port of a virtual server
3199         *
3200         * @return the ID of the virtual server
3201         *
3202         * @throws TS3CommandFailedException
3203         *              if the execution of a command fails
3204         * @querycommands 1
3205         * @see VirtualServer#getPort()
3206         * @see VirtualServer#getId()
3207         */
3208        public CommandFuture<Integer> getServerIdByPort(int port) {
3209                final Command cmd = VirtualServerCommands.serverIdGetByPort(port);
3210                return executeAndReturnIntProperty(cmd, "server_id");
3211        }
3212
3213        /**
3214         * Gets detailed information about the virtual server the server query is currently in.
3215         *
3216         * @return information about the current virtual server
3217         *
3218         * @throws TS3CommandFailedException
3219         *              if the execution of a command fails
3220         * @querycommands 1
3221         */
3222        public CommandFuture<VirtualServerInfo> getServerInfo() {
3223                final Command cmd = VirtualServerCommands.serverInfo();
3224                return executeAndTransformFirst(cmd, Transformer.VIRTUAL_SERVER_INFO /* VirtualServerInfo::new */);
3225        }
3226
3227        /**
3228         * Gets the version, build number and platform of the TeamSpeak3 server.
3229         *
3230         * @return the version information of the server
3231         *
3232         * @throws TS3CommandFailedException
3233         *              if the execution of a command fails
3234         * @querycommands 1
3235         */
3236        public CommandFuture<Version> getVersion() {
3237                final Command cmd = ServerCommands.version();
3238                return executeAndTransformFirst(cmd, Transformer.VERSION /* Version::new */);
3239        }
3240
3241        /**
3242         * Gets a list of all virtual servers including their ID, status, number of clients online, etc.
3243         *
3244         * @return a list of all virtual servers
3245         *
3246         * @throws TS3CommandFailedException
3247         *              if the execution of a command fails
3248         * @querycommands 1
3249         */
3250        public CommandFuture<List<VirtualServer>> getVirtualServers() {
3251                final Command cmd = VirtualServerCommands.serverList();
3252                return executeAndTransform(cmd, Transformer.VIRTUAL_SERVER /* VirtualServer::new */);
3253        }
3254
3255        /**
3256         * Fetches the specified amount of log entries from the currently selected virtual server.
3257         * If no virtual server is selected, the entries will be read from the server log instead.
3258         *
3259         * @param lines
3260         *              the amount of log entries to fetch, in the range between 1 and 100.
3261         *              Returns 100 entries if the argument is not in range
3262         *
3263         * @return a list of the latest log entries
3264         *
3265         * @throws TS3CommandFailedException
3266         *              if the execution of a command fails
3267         * @querycommands 1
3268         */
3269        public CommandFuture<List<String>> getVirtualServerLogEntries(int lines) {
3270                final Command cmd = ServerCommands.logView(lines, false);
3271                return executeAndReturnLogLines(cmd);
3272        }
3273
3274        /**
3275         * Fetches the last 100 log entries from the currently selected virtual server.
3276         * If no virtual server is selected, the entries will be read from the server log instead.
3277         *
3278         * @return a list of up to 100 log entries
3279         *
3280         * @throws TS3CommandFailedException
3281         *              if the execution of a command fails
3282         * @querycommands 1
3283         */
3284        public CommandFuture<List<String>> getVirtualServerLogEntries() {
3285                return getVirtualServerLogEntries(100);
3286        }
3287
3288        /**
3289         * Checks whether the client with the specified client ID is online.
3290         * <p>
3291         * Please note that there is no guarantee that the client will still be
3292         * online by the time the next command is executed.
3293         * </p>
3294         *
3295         * @param clientId
3296         *              the ID of the client
3297         *
3298         * @return {@code true} if the client is online, {@code false} otherwise
3299         *
3300         * @querycommands 1
3301         * @see #getClientInfo(int)
3302         */
3303        public CommandFuture<Boolean> isClientOnline(int clientId) {
3304                final Command cmd = ClientCommands.clientInfo(clientId);
3305                final CommandFuture<Boolean> future = new CommandFuture<>();
3306
3307                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
3308                        @Override
3309                        public void handleSuccess(DefaultArrayResponse result) {
3310                                future.set(true);
3311                        }
3312                }).onFailure(transformError(future, 512, false));
3313
3314                query.doCommandAsync(cmd);
3315                return future;
3316        }
3317
3318        /**
3319         * Checks whether the client with the specified unique identifier is online.
3320         * <p>
3321         * Please note that there is no guarantee that the client will still be
3322         * online by the time the next command is executed.
3323         * </p>
3324         *
3325         * @param clientUId
3326         *              the unique ID of the client
3327         *
3328         * @return {@code true} if the client is online, {@code false} otherwise
3329         *
3330         * @querycommands 1
3331         * @see #getClientByUId(String)
3332         */
3333        public CommandFuture<Boolean> isClientOnline(String clientUId) {
3334                final Command cmd = ClientCommands.clientGetIds(clientUId);
3335                final CommandFuture<Boolean> future = new CommandFuture<>();
3336
3337                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
3338                        @Override
3339                        public void handleSuccess(DefaultArrayResponse result) {
3340                                future.set(!result.getResponses().isEmpty());
3341                        }
3342                }).forwardFailure(future);
3343
3344                query.doCommandAsync(cmd);
3345                return future;
3346        }
3347
3348        /**
3349         * Kicks one or more clients from their current channels.
3350         * This will move the kicked clients into the default channel and
3351         * won't do anything if the clients are already in the default channel.
3352         *
3353         * @param clientIds
3354         *              the IDs of the clients to kick
3355         *
3356         * @return a future to track the progress of this command
3357         *
3358         * @throws TS3CommandFailedException
3359         *              if the execution of a command fails
3360         * @querycommands 1
3361         * @see #kickClientFromChannel(Client...)
3362         * @see #kickClientFromChannel(String, int...)
3363         */
3364        public CommandFuture<Void> kickClientFromChannel(int... clientIds) {
3365                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, null, clientIds);
3366        }
3367
3368        /**
3369         * Kicks one or more clients from their current channels.
3370         * This will move the kicked clients into the default channel and
3371         * won't do anything if the clients are already in the default channel.
3372         *
3373         * @param clients
3374         *              the clients to kick
3375         *
3376         * @return a future to track the progress of this command
3377         *
3378         * @throws TS3CommandFailedException
3379         *              if the execution of a command fails
3380         * @querycommands 1
3381         * @see #kickClientFromChannel(int...)
3382         * @see #kickClientFromChannel(String, Client...)
3383         */
3384        public CommandFuture<Void> kickClientFromChannel(Client... clients) {
3385                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, null, clients);
3386        }
3387
3388        /**
3389         * Kicks one or more clients from their current channels for the specified reason.
3390         * This will move the kicked clients into the default channel and
3391         * won't do anything if the clients are already in the default channel.
3392         *
3393         * @param message
3394         *              the reason message to display to the clients
3395         * @param clientIds
3396         *              the IDs of the clients to kick
3397         *
3398         * @return a future to track the progress of this command
3399         *
3400         * @throws TS3CommandFailedException
3401         *              if the execution of a command fails
3402         * @querycommands 1
3403         * @see Client#getId()
3404         * @see #kickClientFromChannel(int...)
3405         * @see #kickClientFromChannel(String, Client...)
3406         */
3407        public CommandFuture<Void> kickClientFromChannel(String message, int... clientIds) {
3408                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, message, clientIds);
3409        }
3410
3411        /**
3412         * Kicks one or more clients from their current channels for the specified reason.
3413         * This will move the kicked clients into the default channel and
3414         * won't do anything if the clients are already in the default channel.
3415         *
3416         * @param message
3417         *              the reason message to display to the clients
3418         * @param clients
3419         *              the clients to kick
3420         *
3421         * @return a future to track the progress of this command
3422         *
3423         * @throws TS3CommandFailedException
3424         *              if the execution of a command fails
3425         * @querycommands 1
3426         * @see #kickClientFromChannel(Client...)
3427         * @see #kickClientFromChannel(String, int...)
3428         */
3429        public CommandFuture<Void> kickClientFromChannel(String message, Client... clients) {
3430                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, message, clients);
3431        }
3432
3433        /**
3434         * Kicks one or more clients from the server.
3435         *
3436         * @param clientIds
3437         *              the IDs of the clients to kick
3438         *
3439         * @return a future to track the progress of this command
3440         *
3441         * @throws TS3CommandFailedException
3442         *              if the execution of a command fails
3443         * @querycommands 1
3444         * @see Client#getId()
3445         * @see #kickClientFromServer(Client...)
3446         * @see #kickClientFromServer(String, int...)
3447         */
3448        public CommandFuture<Void> kickClientFromServer(int... clientIds) {
3449                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, null, clientIds);
3450        }
3451
3452        /**
3453         * Kicks one or more clients from the server.
3454         *
3455         * @param clients
3456         *              the clients to kick
3457         *
3458         * @return a future to track the progress of this command
3459         *
3460         * @throws TS3CommandFailedException
3461         *              if the execution of a command fails
3462         * @querycommands 1
3463         * @see #kickClientFromServer(int...)
3464         * @see #kickClientFromServer(String, Client...)
3465         */
3466        public CommandFuture<Void> kickClientFromServer(Client... clients) {
3467                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, null, clients);
3468        }
3469
3470        /**
3471         * Kicks one or more clients from the server for the specified reason.
3472         *
3473         * @param message
3474         *              the reason message to display to the clients
3475         * @param clientIds
3476         *              the IDs of the clients to kick
3477         *
3478         * @return a future to track the progress of this command
3479         *
3480         * @throws TS3CommandFailedException
3481         *              if the execution of a command fails
3482         * @querycommands 1
3483         * @see Client#getId()
3484         * @see #kickClientFromServer(int...)
3485         * @see #kickClientFromServer(String, Client...)
3486         */
3487        public CommandFuture<Void> kickClientFromServer(String message, int... clientIds) {
3488                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, message, clientIds);
3489        }
3490
3491        /**
3492         * Kicks one or more clients from the server for the specified reason.
3493         *
3494         * @param message
3495         *              the reason message to display to the clients
3496         * @param clients
3497         *              the clients to kick
3498         *
3499         * @return a future to track the progress of this command
3500         *
3501         * @throws TS3CommandFailedException
3502         *              if the execution of a command fails
3503         * @querycommands 1
3504         * @see #kickClientFromServer(Client...)
3505         * @see #kickClientFromServer(String, int...)
3506         */
3507        public CommandFuture<Void> kickClientFromServer(String message, Client... clients) {
3508                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, message, clients);
3509        }
3510
3511        /**
3512         * Kicks a list of clients from either the channel or the server for a given reason.
3513         *
3514         * @param reason
3515         *              where to kick the clients from
3516         * @param message
3517         *              the reason message to display to the clients
3518         * @param clients
3519         *              the clients to kick
3520         *
3521         * @return a future to track the progress of this command
3522         *
3523         * @throws TS3CommandFailedException
3524         *              if the execution of a command fails
3525         * @querycommands 1
3526         */
3527        private CommandFuture<Void> kickClients(ReasonIdentifier reason, String message, Client... clients) {
3528                int[] clientIds = new int[clients.length];
3529                for (int i = 0; i < clients.length; ++i) {
3530                        clientIds[i] = clients[i].getId();
3531                }
3532                return kickClients(reason, message, clientIds);
3533        }
3534
3535        /**
3536         * Kicks a list of clients from either the channel or the server for a given reason.
3537         *
3538         * @param reason
3539         *              where to kick the clients from
3540         * @param message
3541         *              the reason message to display to the clients
3542         * @param clientIds
3543         *              the IDs of the clients to kick
3544         *
3545         * @return a future to track the progress of this command
3546         *
3547         * @throws TS3CommandFailedException
3548         *              if the execution of a command fails
3549         * @querycommands 1
3550         * @see Client#getId()
3551         */
3552        private CommandFuture<Void> kickClients(ReasonIdentifier reason, String message, int... clientIds) {
3553                final Command cmd = ClientCommands.clientKick(reason, message, clientIds);
3554                return executeAndReturnError(cmd);
3555        }
3556
3557        /**
3558         * Logs the server query in using the specified username and password.
3559         * <p>
3560         * Note that you can also set the login in the {@link TS3Config},
3561         * so that you will be logged in right after the connection is established.
3562         * </p>
3563         *
3564         * @param username
3565         *              the username of the server query
3566         * @param password
3567         *              the password to use
3568         *
3569         * @return a future to track the progress of this command
3570         *
3571         * @throws TS3CommandFailedException
3572         *              if the execution of a command fails
3573         * @querycommands 1
3574         * @see #logout()
3575         */
3576        public CommandFuture<Void> login(String username, String password) {
3577                final Command cmd = QueryCommands.logIn(username, password);
3578                return executeAndReturnError(cmd);
3579        }
3580
3581        /**
3582         * Logs the server query out and deselects the current virtual server.
3583         *
3584         * @return a future to track the progress of this command
3585         *
3586         * @throws TS3CommandFailedException
3587         *              if the execution of a command fails
3588         * @querycommands 1
3589         * @see #login(String, String)
3590         */
3591        public CommandFuture<Void> logout() {
3592                final Command cmd = QueryCommands.logOut();
3593                return executeAndReturnError(cmd);
3594        }
3595
3596        /**
3597         * Moves a channel to a new parent channel specified by its ID.
3598         * To move a channel to root level, set {@code channelTargetId} to {@code 0}.
3599         * <p>
3600         * This will move the channel right below the specified parent channel, above all other child channels.
3601         * This command will fail if the channel already has the specified target channel as the parent channel.
3602         * </p>
3603         *
3604         * @param channelId
3605         *              the channel to move
3606         * @param channelTargetId
3607         *              the new parent channel for the specified channel
3608         *
3609         * @return a future to track the progress of this command
3610         *
3611         * @throws TS3CommandFailedException
3612         *              if the execution of a command fails
3613         * @querycommands 1
3614         * @see Channel#getId()
3615         * @see #moveChannel(int, int, int)
3616         */
3617        public CommandFuture<Void> moveChannel(int channelId, int channelTargetId) {
3618                return moveChannel(channelId, channelTargetId, 0);
3619        }
3620
3621        /**
3622         * Moves a channel to a new parent channel specified by its ID.
3623         * To move a channel to root level, set {@code channelTargetId} to {@code 0}.
3624         * <p>
3625         * The channel will be ordered below the channel with the ID specified by {@code order}.
3626         * To move the channel right below the parent channel, set {@code order} to {@code 0}.
3627         * Also note that a channel cannot be re-ordered without also changing its parent channel.
3628         * </p>
3629         *
3630         * @param channelId
3631         *              the channel to move
3632         * @param channelTargetId
3633         *              the new parent channel for the specified channel
3634         * @param order
3635         *              the channel to sort the specified channel below
3636         *
3637         * @return a future to track the progress of this command
3638         *
3639         * @throws TS3CommandFailedException
3640         *              if the execution of a command fails
3641         * @querycommands 1
3642         * @see Channel#getId()
3643         * @see #moveChannel(int, int)
3644         */
3645        public CommandFuture<Void> moveChannel(int channelId, int channelTargetId, int order) {
3646                final Command cmd = ChannelCommands.channelMove(channelId, channelTargetId, order);
3647                return executeAndReturnError(cmd);
3648        }
3649
3650        /**
3651         * Moves a client into a channel.
3652         * <p>
3653         * Consider using {@link #moveClients(int[], int)}
3654         * for moving multiple clients.
3655         * </p>
3656         *
3657         * @param clientId
3658         *              the ID of the client to move
3659         * @param channelId
3660         *              the ID of the channel to move the client into
3661         *
3662         * @return a future to track the progress of this command
3663         *
3664         * @throws TS3CommandFailedException
3665         *              if the execution of a command fails
3666         * @querycommands 1
3667         * @see Client#getId()
3668         * @see Channel#getId()
3669         */
3670        public CommandFuture<Void> moveClient(int clientId, int channelId) {
3671                return moveClient(clientId, channelId, null);
3672        }
3673
3674        /**
3675         * Moves multiple clients into a channel.
3676         * Immediately returns {@code true} for an empty client ID array.
3677         * <p>
3678         * Use this method instead of {@link #moveClient(int, int)} for moving
3679         * several clients as this will only send 1 command to the server and thus complete faster.
3680         * </p>
3681         *
3682         * @param clientIds
3683         *              the IDs of the clients to move, cannot be {@code null}
3684         * @param channelId
3685         *              the ID of the channel to move the clients into
3686         *
3687         * @return a future to track the progress of this command
3688         *
3689         * @throws IllegalArgumentException
3690         *              if {@code clientIds} is {@code null}
3691         * @throws TS3CommandFailedException
3692         *              if the execution of a command fails
3693         * @querycommands 1
3694         * @see Client#getId()
3695         * @see Channel#getId()
3696         */
3697        public CommandFuture<Void> moveClients(int[] clientIds, int channelId) {
3698                return moveClients(clientIds, channelId, null);
3699        }
3700
3701        /**
3702         * Moves a client into a channel.
3703         * <p>
3704         * Consider using {@link #moveClients(Client[], ChannelBase)}
3705         * for moving multiple clients.
3706         * </p>
3707         *
3708         * @param client
3709         *              the client to move, cannot be {@code null}
3710         * @param channel
3711         *              the channel to move the client into, cannot be {@code null}
3712         *
3713         * @return a future to track the progress of this command
3714         *
3715         * @throws IllegalArgumentException
3716         *              if {@code client} or {@code channel} is {@code null}
3717         * @throws TS3CommandFailedException
3718         *              if the execution of a command fails
3719         * @querycommands 1
3720         */
3721        public CommandFuture<Void> moveClient(Client client, ChannelBase channel) {
3722                return moveClient(client, channel, null);
3723        }
3724
3725        /**
3726         * Moves multiple clients into a channel.
3727         * Immediately returns {@code true} for an empty client array.
3728         * <p>
3729         * Use this method instead of {@link #moveClient(Client, ChannelBase)} for moving
3730         * several clients as this will only send 1 command to the server and thus complete faster.
3731         * </p>
3732         *
3733         * @param clients
3734         *              the clients to move, cannot be {@code null}
3735         * @param channel
3736         *              the channel to move the clients into, cannot be {@code null}
3737         *
3738         * @return a future to track the progress of this command
3739         *
3740         * @throws IllegalArgumentException
3741         *              if {@code clients} or {@code channel} is {@code null}
3742         * @throws TS3CommandFailedException
3743         *              if the execution of a command fails
3744         * @querycommands 1
3745         */
3746        public CommandFuture<Void> moveClients(Client[] clients, ChannelBase channel) {
3747                return moveClients(clients, channel, null);
3748        }
3749
3750        /**
3751         * Moves a client into a channel using the specified password.
3752         *
3753         * @param clientId
3754         *              the ID of the client to move
3755         * @param channelId
3756         *              the ID of the channel to move the client into
3757         * @param channelPassword
3758         *              the password of the channel, can be {@code null}
3759         *
3760         * @return a future to track the progress of this command
3761         *
3762         * @throws TS3CommandFailedException
3763         *              if the execution of a command fails
3764         * @querycommands 1
3765         * @see Client#getId()
3766         * @see Channel#getId()
3767         */
3768        public CommandFuture<Void> moveClient(int clientId, int channelId, String channelPassword) {
3769                final Command cmd = ClientCommands.clientMove(clientId, channelId, channelPassword);
3770                return executeAndReturnError(cmd);
3771        }
3772
3773        /**
3774         * Moves multiple clients into a channel using the specified password.
3775         * Immediately returns {@code true} for an empty client ID array.
3776         * <p>
3777         * Use this method instead of {@link #moveClient(int, int, String)} for moving
3778         * several clients as this will only send 1 command to the server and thus complete faster.
3779         * </p>
3780         *
3781         * @param clientIds
3782         *              the IDs of the clients to move, cannot be {@code null}
3783         * @param channelId
3784         *              the ID of the channel to move the clients into
3785         * @param channelPassword
3786         *              the password of the channel, can be {@code null}
3787         *
3788         * @return a future to track the progress of this command
3789         *
3790         * @throws IllegalArgumentException
3791         *              if {@code clientIds} is {@code null}
3792         * @throws TS3CommandFailedException
3793         *              if the execution of a command fails
3794         * @querycommands 1
3795         * @see Client#getId()
3796         * @see Channel#getId()
3797         */
3798        public CommandFuture<Void> moveClients(int[] clientIds, int channelId, String channelPassword) {
3799                if (clientIds == null) throw new IllegalArgumentException("Client ID array was null");
3800                if (clientIds.length == 0) return CommandFuture.immediate(null); // Success
3801
3802                final Command cmd = ClientCommands.clientMove(clientIds, channelId, channelPassword);
3803                return executeAndReturnError(cmd);
3804        }
3805
3806        /**
3807         * Moves a client into a channel using the specified password.
3808         * <p>
3809         * Consider using {@link #moveClients(Client[], ChannelBase, String)}
3810         * for moving multiple clients.
3811         * </p>
3812         *
3813         * @param client
3814         *              the client to move, cannot be {@code null}
3815         * @param channel
3816         *              the channel to move the client into, cannot be {@code null}
3817         * @param channelPassword
3818         *              the password of the channel, can be {@code null}
3819         *
3820         * @return a future to track the progress of this command
3821         *
3822         * @throws IllegalArgumentException
3823         *              if {@code client} or {@code channel} is {@code null}
3824         * @throws TS3CommandFailedException
3825         *              if the execution of a command fails
3826         * @querycommands 1
3827         */
3828        public CommandFuture<Void> moveClient(Client client, ChannelBase channel, String channelPassword) {
3829                if (client == null) throw new IllegalArgumentException("Client cannot be null");
3830                if (channel == null) throw new IllegalArgumentException("Channel cannot be null");
3831
3832                return moveClient(client.getId(), channel.getId(), channelPassword);
3833        }
3834
3835        /**
3836         * Moves multiple clients into a channel using the specified password.
3837         * Immediately returns {@code true} for an empty client array.
3838         * <p>
3839         * Use this method instead of {@link #moveClient(Client, ChannelBase, String)} for moving
3840         * several clients as this will only send 1 command to the server and thus complete faster.
3841         * </p>
3842         *
3843         * @param clients
3844         *              the clients to move, cannot be {@code null}
3845         * @param channel
3846         *              the channel to move the clients into, cannot be {@code null}
3847         * @param channelPassword
3848         *              the password of the channel, can be {@code null}
3849         *
3850         * @return a future to track the progress of this command
3851         *
3852         * @throws IllegalArgumentException
3853         *              if {@code clients} or {@code channel} is {@code null}
3854         * @throws TS3CommandFailedException
3855         *              if the execution of a command fails
3856         * @querycommands 1
3857         */
3858        public CommandFuture<Void> moveClients(Client[] clients, ChannelBase channel, String channelPassword) {
3859                if (clients == null) throw new IllegalArgumentException("Client array cannot be null");
3860                if (channel == null) throw new IllegalArgumentException("Channel cannot be null");
3861
3862                int[] clientIds = new int[clients.length];
3863                for (int i = 0; i < clients.length; i++) {
3864                        Client client = clients[i];
3865                        clientIds[i] = client.getId();
3866                }
3867                return moveClients(clientIds, channel.getId(), channelPassword);
3868        }
3869
3870        /**
3871         * Moves and renames a file on the file repository within the same channel.
3872         *
3873         * @param oldPath
3874         *              the current path to the file
3875         * @param newPath
3876         *              the desired new path
3877         * @param channelId
3878         *              the ID of the channel the file resides in
3879         *
3880         * @return a future to track the progress of this command
3881         *
3882         * @throws TS3CommandFailedException
3883         *              if the execution of a command fails
3884         * @querycommands 1
3885         * @see FileInfo#getPath()
3886         * @see Channel#getId()
3887         * @see #moveFile(String, String, int, int) moveFile to a different channel
3888         */
3889        public CommandFuture<Void> moveFile(String oldPath, String newPath, int channelId) {
3890                return moveFile(oldPath, newPath, channelId, null);
3891        }
3892
3893        /**
3894         * Renames a file on the file repository and moves it to a new path in a different channel.
3895         *
3896         * @param oldPath
3897         *              the current path to the file
3898         * @param newPath
3899         *              the desired new path
3900         * @param oldChannelId
3901         *              the ID of the channel the file currently resides in
3902         * @param newChannelId
3903         *              the ID of the channel the file should be moved to
3904         *
3905         * @return a future to track the progress of this command
3906         *
3907         * @throws TS3CommandFailedException
3908         *              if the execution of a command fails
3909         * @querycommands 1
3910         * @see FileInfo#getPath()
3911         * @see Channel#getId()
3912         * @see #moveFile(String, String, int) moveFile within the same channel
3913         */
3914        public CommandFuture<Void> moveFile(String oldPath, String newPath, int oldChannelId, int newChannelId) {
3915                return moveFile(oldPath, newPath, oldChannelId, null, newChannelId, null);
3916        }
3917
3918        /**
3919         * Moves and renames a file on the file repository within the same channel.
3920         *
3921         * @param oldPath
3922         *              the current path to the file
3923         * @param newPath
3924         *              the desired new path
3925         * @param channelId
3926         *              the ID of the channel the file resides in
3927         * @param channelPassword
3928         *              the password of the channel
3929         *
3930         * @return a future to track the progress of this command
3931         *
3932         * @throws TS3CommandFailedException
3933         *              if the execution of a command fails
3934         * @querycommands 1
3935         * @see FileInfo#getPath()
3936         * @see Channel#getId()
3937         * @see #moveFile(String, String, int, String, int, String) moveFile to a different channel
3938         */
3939        public CommandFuture<Void> moveFile(String oldPath, String newPath, int channelId, String channelPassword) {
3940                final Command cmd = FileCommands.ftRenameFile(oldPath, newPath, channelId, channelPassword);
3941                return executeAndReturnError(cmd);
3942        }
3943
3944        /**
3945         * Renames a file on the file repository and moves it to a new path in a different channel.
3946         *
3947         * @param oldPath
3948         *              the current path to the file
3949         * @param newPath
3950         *              the desired new path
3951         * @param oldChannelId
3952         *              the ID of the channel the file currently resides in
3953         * @param oldPassword
3954         *              the password of the current channel
3955         * @param newChannelId
3956         *              the ID of the channel the file should be moved to
3957         * @param newPassword
3958         *              the password of the new channel
3959         *
3960         * @return a future to track the progress of this command
3961         *
3962         * @throws TS3CommandFailedException
3963         *              if the execution of a command fails
3964         * @querycommands 1
3965         * @see FileInfo#getPath()
3966         * @see Channel#getId()
3967         * @see #moveFile(String, String, int, String) moveFile within the same channel
3968         */
3969        public CommandFuture<Void> moveFile(String oldPath, String newPath, int oldChannelId, String oldPassword, int newChannelId, String newPassword) {
3970                final Command cmd = FileCommands.ftRenameFile(oldPath, newPath, oldChannelId, oldPassword, newChannelId, newPassword);
3971                return executeAndReturnError(cmd);
3972        }
3973
3974        /**
3975         * Moves the server query into a channel.
3976         *
3977         * @param channelId
3978         *              the ID of the channel to move the server query into
3979         *
3980         * @return a future to track the progress of this command
3981         *
3982         * @throws TS3CommandFailedException
3983         *              if the execution of a command fails
3984         * @querycommands 1
3985         * @see Channel#getId()
3986         */
3987        public CommandFuture<Void> moveQuery(int channelId) {
3988                return moveClient(0, channelId, null);
3989        }
3990
3991        /**
3992         * Moves the server query into a channel.
3993         *
3994         * @param channel
3995         *              the channel to move the server query into, cannot be {@code null}
3996         *
3997         * @return a future to track the progress of this command
3998         *
3999         * @throws IllegalArgumentException
4000         *              if {@code channel} is {@code null}
4001         * @throws TS3CommandFailedException
4002         *              if the execution of a command fails
4003         * @querycommands 1
4004         */
4005        public CommandFuture<Void> moveQuery(ChannelBase channel) {
4006                if (channel == null) throw new IllegalArgumentException("Channel cannot be null");
4007
4008                return moveClient(0, channel.getId(), null);
4009        }
4010
4011        /**
4012         * Moves the server query into a channel using the specified password.
4013         *
4014         * @param channelId
4015         *              the ID of the channel to move the client into
4016         * @param channelPassword
4017         *              the password of the channel, can be {@code null}
4018         *
4019         * @return a future to track the progress of this command
4020         *
4021         * @throws TS3CommandFailedException
4022         *              if the execution of a command fails
4023         * @querycommands 1
4024         * @see Channel#getId()
4025         */
4026        public CommandFuture<Void> moveQuery(int channelId, String channelPassword) {
4027                return moveClient(0, channelId, channelPassword);
4028        }
4029
4030        /**
4031         * Moves the server query into a channel using the specified password.
4032         *
4033         * @param channel
4034         *              the channel to move the client into, cannot be {@code null}
4035         * @param channelPassword
4036         *              the password of the channel, can be {@code null}
4037         *
4038         * @return a future to track the progress of this command
4039         *
4040         * @throws IllegalArgumentException
4041         *              if {@code channel} is {@code null}
4042         * @throws TS3CommandFailedException
4043         *              if the execution of a command fails
4044         * @querycommands 1
4045         */
4046        public CommandFuture<Void> moveQuery(ChannelBase channel, String channelPassword) {
4047                if (channel == null) throw new IllegalArgumentException("Channel cannot be null");
4048
4049                return moveClient(0, channel.getId(), channelPassword);
4050        }
4051
4052        /**
4053         * Pokes the client with the specified client ID.
4054         * This opens up a small popup window for the client containing your message and plays a sound.
4055         * The displayed message will be formatted like this: <br>
4056         * {@code hh:mm:ss - "Your Nickname" poked you: <your message in green color>}
4057         * <p>
4058         * The displayed message length is limited to 100 UTF-8 bytes.
4059         * If a client has already received a poke message, all subsequent pokes will simply add a line
4060         * to the already opened popup window and will still play a sound.
4061         * </p>
4062         *
4063         * @param clientId
4064         *              the ID of the client to poke
4065         * @param message
4066         *              the message to send, may contain BB codes
4067         *
4068         * @return a future to track the progress of this command
4069         *
4070         * @throws TS3CommandFailedException
4071         *              if the execution of a command fails
4072         * @querycommands 1
4073         * @see Client#getId()
4074         */
4075        public CommandFuture<Void> pokeClient(int clientId, String message) {
4076                final Command cmd = ClientCommands.clientPoke(clientId, message);
4077                return executeAndReturnError(cmd);
4078        }
4079
4080        /**
4081         * Terminates the connection with the TeamSpeak3 server.
4082         * <p>
4083         * This command should never be executed by a user of this API,
4084         * as it leaves the query in an undefined state. To terminate
4085         * a connection regularly, use {@link TS3Query#exit()}.
4086         * </p>
4087         *
4088         * @throws TS3CommandFailedException
4089         *              if the execution of a command fails
4090         * @querycommands 1
4091         */
4092        CommandFuture<Void> quit() {
4093                final Command cmd = QueryCommands.quit();
4094                return executeAndReturnError(cmd);
4095        }
4096
4097        /**
4098         * Registers the server query to receive notifications about all server events.
4099         * <p>
4100         * This means that the following actions will trigger event notifications:
4101         * </p>
4102         * <ul>
4103         * <li>A client joins the server or disconnects from it</li>
4104         * <li>A client switches channels</li>
4105         * <li>A client sends a server message</li>
4106         * <li>A client sends a channel message <b>in the channel the query is in</b></li>
4107         * <li>A client sends a private message to <b>the server query</b></li>
4108         * <li>A client uses a privilege key</li>
4109         * </ul>
4110         * <p>
4111         * The limitations to when the query receives notifications about chat events cannot be circumvented.
4112         * </p>
4113         * To be able to process these events in your application, register an event listener.
4114         *
4115         * @return whether all commands succeeded or not
4116         *
4117         * @throws TS3CommandFailedException
4118         *              if the execution of a command fails
4119         * @querycommands 6
4120         * @see #addTS3Listeners(TS3Listener...)
4121         */
4122        public CommandFuture<Void> registerAllEvents() {
4123                final CommandFuture<Void> future = new CommandFuture<>();
4124                final Collection<CommandFuture<Void>> eventFutures = new ArrayList<>(5);
4125
4126                eventFutures.add(registerEvent(TS3EventType.SERVER));
4127                eventFutures.add(registerEvent(TS3EventType.TEXT_SERVER));
4128                eventFutures.add(registerEvent(TS3EventType.CHANNEL, 0));
4129                eventFutures.add(registerEvent(TS3EventType.TEXT_CHANNEL, 0));
4130                eventFutures.add(registerEvent(TS3EventType.TEXT_PRIVATE));
4131                eventFutures.add(registerEvent(TS3EventType.PRIVILEGE_KEY_USED));
4132
4133                CommandFuture.ofAll(eventFutures).onSuccess(new CommandFuture.SuccessListener<List<Void>>() {
4134                        @Override
4135                        public void handleSuccess(List<Void> ignored) {
4136                                future.set(null); // Mark as successful
4137                        }
4138                }).forwardFailure(future);
4139                return future;
4140        }
4141
4142        /**
4143         * Registers the server query to receive notifications about a given event type.
4144         * <p>
4145         * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel.
4146         * If used with {@link TS3EventType#CHANNEL}, this will listen to <b>all</b> channel events.
4147         * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}.
4148         * </p>
4149         *
4150         * @param eventType
4151         *              the event type to be notified about
4152         *
4153         * @return a future to track the progress of this command
4154         *
4155         * @throws TS3CommandFailedException
4156         *              if the execution of a command fails
4157         * @querycommands 1
4158         * @see #addTS3Listeners(TS3Listener...)
4159         * @see #registerEvent(TS3EventType, int)
4160         * @see #registerAllEvents()
4161         */
4162        public CommandFuture<Void> registerEvent(TS3EventType eventType) {
4163                if (eventType == TS3EventType.CHANNEL || eventType == TS3EventType.TEXT_CHANNEL) {
4164                        return registerEvent(eventType, 0);
4165                }
4166                return registerEvent(eventType, -1);
4167        }
4168
4169        /**
4170         * Registers the server query to receive notifications about a given event type.
4171         *
4172         * @param eventType
4173         *              the event type to be notified about
4174         * @param channelId
4175         *              the ID of the channel to listen to, will be ignored if set to {@code -1}.
4176         *              Can be set to {@code 0} for {@link TS3EventType#CHANNEL} to receive notifications about all channel switches.
4177         *
4178         * @return a future to track the progress of this command
4179         *
4180         * @throws TS3CommandFailedException
4181         *              if the execution of a command fails
4182         * @querycommands 1
4183         * @see Channel#getId()
4184         * @see #addTS3Listeners(TS3Listener...)
4185         * @see #registerAllEvents()
4186         */
4187        public CommandFuture<Void> registerEvent(TS3EventType eventType, int channelId) {
4188                final Command cmd = QueryCommands.serverNotifyRegister(eventType, channelId);
4189                return executeAndReturnError(cmd);
4190        }
4191
4192        /**
4193         * Registers the server query to receive notifications about multiple given event types.
4194         * <p>
4195         * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel.
4196         * If used with {@link TS3EventType#CHANNEL}, this will listen to <b>all</b> channel events.
4197         * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}.
4198         * </p>
4199         *
4200         * @param eventTypes
4201         *              the event types to be notified about
4202         *
4203         * @return a future to track the progress of this command
4204         *
4205         * @throws TS3CommandFailedException
4206         *              if the execution of a command fails
4207         * @querycommands n, one command per TS3EventType
4208         * @see #addTS3Listeners(TS3Listener...)
4209         * @see #registerEvent(TS3EventType, int)
4210         * @see #registerAllEvents()
4211         */
4212        public CommandFuture<Void> registerEvents(TS3EventType... eventTypes) {
4213                if (eventTypes.length == 0) return CommandFuture.immediate(null); // Success
4214
4215                final Collection<CommandFuture<Void>> registerFutures = new ArrayList<>(eventTypes.length);
4216                for (final TS3EventType type : eventTypes) {
4217                        registerFutures.add(registerEvent(type));
4218                }
4219
4220                final CommandFuture<Void> future = new CommandFuture<>();
4221                CommandFuture.ofAll(registerFutures).onSuccess(new CommandFuture.SuccessListener<List<Void>>() {
4222                        @Override
4223                        public void handleSuccess(List<Void> ignored) {
4224                                future.set(null); // Mark as successful
4225                        }
4226                }).forwardFailure(future);
4227                return future;
4228        }
4229
4230        /**
4231         * Removes the client specified by its database ID from the specified server group.
4232         *
4233         * @param serverGroupId
4234         *              the ID of the server group
4235         * @param clientDatabaseId
4236         *              the database ID of the client
4237         *
4238         * @return a future to track the progress of this command
4239         *
4240         * @throws TS3CommandFailedException
4241         *              if the execution of a command fails
4242         * @querycommands 1
4243         * @see ServerGroup#getId()
4244         * @see Client#getDatabaseId()
4245         * @see #removeClientFromServerGroup(ServerGroup, Client)
4246         */
4247        public CommandFuture<Void> removeClientFromServerGroup(int serverGroupId, int clientDatabaseId) {
4248                final Command cmd = ServerGroupCommands.serverGroupDelClient(serverGroupId, clientDatabaseId);
4249                return executeAndReturnError(cmd);
4250        }
4251
4252        /**
4253         * Removes the specified client from the specified server group.
4254         *
4255         * @param serverGroup
4256         *              the server group to remove the client from
4257         * @param client
4258         *              the client to remove from the server group
4259         *
4260         * @return a future to track the progress of this command
4261         *
4262         * @throws TS3CommandFailedException
4263         *              if the execution of a command fails
4264         * @querycommands 1
4265         * @see #removeClientFromServerGroup(int, int)
4266         */
4267        public CommandFuture<Void> removeClientFromServerGroup(ServerGroup serverGroup, Client client) {
4268                return removeClientFromServerGroup(serverGroup.getId(), client.getDatabaseId());
4269        }
4270
4271        /**
4272         * Removes one or more {@link TS3Listener}s to the event manager of the query.
4273         * <p>
4274         * If a listener was not actually registered, it will be ignored and no exception will be thrown.
4275         * </p>
4276         *
4277         * @param listeners
4278         *              one or more listeners to remove
4279         *
4280         * @see #addTS3Listeners(TS3Listener...)
4281         * @see TS3Listener
4282         * @see TS3EventType
4283         */
4284        public void removeTS3Listeners(TS3Listener... listeners) {
4285                query.getEventManager().removeListeners(listeners);
4286        }
4287
4288        /**
4289         * Renames the channel group with the specified ID.
4290         *
4291         * @param channelGroupId
4292         *              the ID of the channel group to rename
4293         * @param name
4294         *              the new name for the channel group
4295         *
4296         * @return a future to track the progress of this command
4297         *
4298         * @throws TS3CommandFailedException
4299         *              if the execution of a command fails
4300         * @querycommands 1
4301         * @see ChannelGroup#getId()
4302         * @see #renameChannelGroup(ChannelGroup, String)
4303         */
4304        public CommandFuture<Void> renameChannelGroup(int channelGroupId, String name) {
4305                final Command cmd = ChannelGroupCommands.channelGroupRename(channelGroupId, name);
4306                return executeAndReturnError(cmd);
4307        }
4308
4309        /**
4310         * Renames the specified channel group.
4311         *
4312         * @param channelGroup
4313         *              the channel group to rename
4314         * @param name
4315         *              the new name for the channel group
4316         *
4317         * @return a future to track the progress of this command
4318         *
4319         * @throws TS3CommandFailedException
4320         *              if the execution of a command fails
4321         * @querycommands 1
4322         * @see #renameChannelGroup(int, String)
4323         */
4324        public CommandFuture<Void> renameChannelGroup(ChannelGroup channelGroup, String name) {
4325                return renameChannelGroup(channelGroup.getId(), name);
4326        }
4327
4328        /**
4329         * Renames the server group with the specified ID.
4330         *
4331         * @param serverGroupId
4332         *              the ID of the server group to rename
4333         * @param name
4334         *              the new name for the server group
4335         *
4336         * @return a future to track the progress of this command
4337         *
4338         * @throws TS3CommandFailedException
4339         *              if the execution of a command fails
4340         * @querycommands 1
4341         * @see ServerGroup#getId()
4342         * @see #renameServerGroup(ServerGroup, String)
4343         */
4344        public CommandFuture<Void> renameServerGroup(int serverGroupId, String name) {
4345                final Command cmd = ServerGroupCommands.serverGroupRename(serverGroupId, name);
4346                return executeAndReturnError(cmd);
4347        }
4348
4349        /**
4350         * Renames the specified server group.
4351         *
4352         * @param serverGroup
4353         *              the server group to rename
4354         * @param name
4355         *              the new name for the server group
4356         *
4357         * @return a future to track the progress of this command
4358         *
4359         * @throws TS3CommandFailedException
4360         *              if the execution of a command fails
4361         * @querycommands 1
4362         * @see #renameServerGroup(int, String)
4363         */
4364        public CommandFuture<Void> renameServerGroup(ServerGroup serverGroup, String name) {
4365                return renameChannelGroup(serverGroup.getId(), name);
4366        }
4367
4368        /**
4369         * Resets all permissions and deletes all server / channel groups. Use carefully.
4370         *
4371         * @return a token for a new administrator account
4372         *
4373         * @throws TS3CommandFailedException
4374         *              if the execution of a command fails
4375         * @querycommands 1
4376         */
4377        public CommandFuture<String> resetPermissions() {
4378                final Command cmd = PermissionCommands.permReset();
4379                return executeAndReturnStringProperty(cmd, "token");
4380        }
4381
4382        /**
4383         * Moves the server query into the virtual server with the specified ID.
4384         *
4385         * @param id
4386         *              the ID of the virtual server
4387         *
4388         * @return a future to track the progress of this command
4389         *
4390         * @throws TS3CommandFailedException
4391         *              if the execution of a command fails
4392         * @querycommands 1
4393         * @see VirtualServer#getId()
4394         * @see #selectVirtualServerByPort(int)
4395         * @see #selectVirtualServer(VirtualServer)
4396         */
4397        public CommandFuture<Void> selectVirtualServerById(int id) {
4398                final Command cmd = QueryCommands.useId(id);
4399                return executeAndReturnError(cmd);
4400        }
4401
4402        /**
4403         * Moves the server query into the virtual server with the specified voice port.
4404         *
4405         * @param port
4406         *              the voice port of the virtual server
4407         *
4408         * @return a future to track the progress of this command
4409         *
4410         * @throws TS3CommandFailedException
4411         *              if the execution of a command fails
4412         * @querycommands 1
4413         * @see VirtualServer#getPort()
4414         * @see #selectVirtualServerById(int)
4415         * @see #selectVirtualServer(VirtualServer)
4416         */
4417        public CommandFuture<Void> selectVirtualServerByPort(int port) {
4418                final Command cmd = QueryCommands.usePort(port);
4419                return executeAndReturnError(cmd);
4420        }
4421
4422        /**
4423         * Moves the server query into the specified virtual server.
4424         *
4425         * @param server
4426         *              the virtual server to move into
4427         *
4428         * @return a future to track the progress of this command
4429         *
4430         * @throws TS3CommandFailedException
4431         *              if the execution of a command fails
4432         * @querycommands 1
4433         * @see #selectVirtualServerById(int)
4434         * @see #selectVirtualServerByPort(int)
4435         */
4436        public CommandFuture<Void> selectVirtualServer(VirtualServer server) {
4437                return selectVirtualServerById(server.getId());
4438        }
4439
4440        /**
4441         * Sends an offline message to the client with the given unique identifier.
4442         * <p>
4443         * The message subject's length is limited to 200 UTF-8 bytes and BB codes in it will be ignored.
4444         * The message body's length is limited to 4096 UTF-8 bytes and accepts BB codes
4445         * </p>
4446         *
4447         * @param clientUId
4448         *              the unique identifier of the client to send the message to
4449         * @param subject
4450         *              the subject for the message, may not contain BB codes
4451         * @param message
4452         *              the actual message body, may contain BB codes
4453         *
4454         * @return a future to track the progress of this command
4455         *
4456         * @throws TS3CommandFailedException
4457         *              if the execution of a command fails
4458         * @querycommands 1
4459         * @see Client#getUniqueIdentifier()
4460         * @see Message
4461         */
4462        public CommandFuture<Void> sendOfflineMessage(String clientUId, String subject, String message) {
4463                final Command cmd = MessageCommands.messageAdd(clientUId, subject, message);
4464                return executeAndReturnError(cmd);
4465        }
4466
4467        /**
4468         * Sends a text message either to the whole virtual server, a channel or specific client.
4469         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
4470         * <p>
4471         * To send a message to all virtual servers, use {@link #broadcast(String)}.
4472         * To send an offline message, use {@link #sendOfflineMessage(String, String, String)}.
4473         * </p>
4474         *
4475         * @param targetMode
4476         *              where the message should be sent to
4477         * @param targetId
4478         *              the client ID of the recipient of this message. This value is ignored unless {@code targetMode} is {@code CLIENT}
4479         * @param message
4480         *              the text message to send
4481         *
4482         * @return a future to track the progress of this command
4483         *
4484         * @throws TS3CommandFailedException
4485         *              if the execution of a command fails
4486         * @querycommands 1
4487         * @see Client#getId()
4488         */
4489        public CommandFuture<Void> sendTextMessage(TextMessageTargetMode targetMode, int targetId, String message) {
4490                final Command cmd = ClientCommands.sendTextMessage(targetMode.getIndex(), targetId, message);
4491                return executeAndReturnError(cmd);
4492        }
4493
4494        /**
4495         * Sends a text message to the channel with the specified ID.
4496         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
4497         * <p>
4498         * This will move the client into the channel with the specified channel ID,
4499         * <b>but will not move it back to the original channel!</b>
4500         * </p>
4501         *
4502         * @param channelId
4503         *              the ID of the channel to which the message should be sent to
4504         * @param message
4505         *              the text message to send
4506         *
4507         * @return a future to track the progress of this command
4508         *
4509         * @throws TS3CommandFailedException
4510         *              if the execution of a command fails
4511         * @querycommands 1
4512         * @see #sendChannelMessage(String)
4513         * @see Channel#getId()
4514         */
4515        public CommandFuture<Void> sendChannelMessage(int channelId, final String message) {
4516                final CommandFuture<Void> future = new CommandFuture<>();
4517
4518                moveQuery(channelId).onSuccess(new CommandFuture.SuccessListener<Void>() {
4519                        @Override
4520                        public void handleSuccess(Void ignored) {
4521                                sendTextMessage(TextMessageTargetMode.CHANNEL, 0, message).forwardResult(future);
4522                        }
4523                }).forwardFailure(future);
4524
4525                return future;
4526        }
4527
4528        /**
4529         * Sends a text message to the channel the server query is currently in.
4530         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
4531         *
4532         * @param message
4533         *              the text message to send
4534         *
4535         * @return a future to track the progress of this command
4536         *
4537         * @throws TS3CommandFailedException
4538         *              if the execution of a command fails
4539         * @querycommands 1
4540         */
4541        public CommandFuture<Void> sendChannelMessage(String message) {
4542                return sendTextMessage(TextMessageTargetMode.CHANNEL, 0, message);
4543        }
4544
4545        /**
4546         * Sends a text message to the virtual server with the specified ID.
4547         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
4548         * <p>
4549         * This will move the client to the virtual server with the specified server ID,
4550         * <b>but will not move it back to the original virtual server!</b>
4551         * </p>
4552         *
4553         * @param serverId
4554         *              the ID of the virtual server to which the message should be sent to
4555         * @param message
4556         *              the text message to send
4557         *
4558         * @return a future to track the progress of this command
4559         *
4560         * @throws TS3CommandFailedException
4561         *              if the execution of a command fails
4562         * @querycommands 1
4563         * @see #sendServerMessage(String)
4564         * @see VirtualServer#getId()
4565         */
4566        public CommandFuture<Void> sendServerMessage(int serverId, final String message) {
4567                final CommandFuture<Void> future = new CommandFuture<>();
4568
4569                selectVirtualServerById(serverId).onSuccess(new CommandFuture.SuccessListener<Void>() {
4570                        @Override
4571                        public void handleSuccess(Void ignored) {
4572                                sendTextMessage(TextMessageTargetMode.SERVER, 0, message).forwardResult(future);
4573                        }
4574                }).forwardFailure(future);
4575
4576                return future;
4577        }
4578
4579        /**
4580         * Sends a text message to the virtual server the server query is currently in.
4581         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
4582         *
4583         * @param message
4584         *              the text message to send
4585         *
4586         * @return a future to track the progress of this command
4587         *
4588         * @throws TS3CommandFailedException
4589         *              if the execution of a command fails
4590         * @querycommands 1
4591         */
4592        public CommandFuture<Void> sendServerMessage(String message) {
4593                return sendTextMessage(TextMessageTargetMode.SERVER, 0, message);
4594        }
4595
4596        /**
4597         * Sends a private message to the client with the specified client ID.
4598         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
4599         *
4600         * @param clientId
4601         *              the ID of the client to send the message to
4602         * @param message
4603         *              the text message to send
4604         *
4605         * @return a future to track the progress of this command
4606         *
4607         * @throws TS3CommandFailedException
4608         *              if the execution of a command fails
4609         * @querycommands 1
4610         * @see Client#getId()
4611         */
4612        public CommandFuture<Void> sendPrivateMessage(int clientId, String message) {
4613                return sendTextMessage(TextMessageTargetMode.CLIENT, clientId, message);
4614        }
4615
4616        /**
4617         * Sets a channel group for a client in a specific channel.
4618         *
4619         * @param groupId
4620         *              the ID of the group the client should join
4621         * @param channelId
4622         *              the ID of the channel where the channel group should be assigned
4623         * @param clientDBId
4624         *              the database ID of the client for which the channel group should be set
4625         *
4626         * @return a future to track the progress of this command
4627         *
4628         * @throws TS3CommandFailedException
4629         *              if the execution of a command fails
4630         * @querycommands 1
4631         * @see ChannelGroup#getId()
4632         * @see Channel#getId()
4633         * @see Client#getDatabaseId()
4634         */
4635        public CommandFuture<Void> setClientChannelGroup(int groupId, int channelId, int clientDBId) {
4636                final Command cmd = ChannelGroupCommands.setClientChannelGroup(groupId, channelId, clientDBId);
4637                return executeAndReturnError(cmd);
4638        }
4639
4640        /**
4641         * Sets the read flag to true for a given message. This will not delete the message.
4642         *
4643         * @param messageId
4644         *              the ID of the message for which the read flag should be set
4645         *
4646         * @return a future to track the progress of this command
4647         *
4648         * @throws TS3CommandFailedException
4649         *              if the execution of a command fails
4650         * @querycommands 1
4651         * @see #setMessageReadFlag(int, boolean)
4652         */
4653        public CommandFuture<Void> setMessageRead(int messageId) {
4654                return setMessageReadFlag(messageId, true);
4655        }
4656
4657        /**
4658         * Sets the read flag to true for a given message. This will not delete the message.
4659         *
4660         * @param message
4661         *              the message for which the read flag should be set
4662         *
4663         * @return a future to track the progress of this command
4664         *
4665         * @throws TS3CommandFailedException
4666         *              if the execution of a command fails
4667         * @querycommands 1
4668         * @see #setMessageRead(int)
4669         * @see #setMessageReadFlag(Message, boolean)
4670         * @see #deleteOfflineMessage(int)
4671         */
4672        public CommandFuture<Void> setMessageRead(Message message) {
4673                return setMessageReadFlag(message.getId(), true);
4674        }
4675
4676        /**
4677         * Sets the read flag for a given message. This will not delete the message.
4678         *
4679         * @param messageId
4680         *              the ID of the message for which the read flag should be set
4681         * @param read
4682         *              the boolean value to which the read flag should be set
4683         *
4684         * @return a future to track the progress of this command
4685         *
4686         * @throws TS3CommandFailedException
4687         *              if the execution of a command fails
4688         * @querycommands 1
4689         * @see #setMessageRead(int)
4690         * @see #setMessageReadFlag(Message, boolean)
4691         * @see #deleteOfflineMessage(int)
4692         */
4693        public CommandFuture<Void> setMessageReadFlag(int messageId, boolean read) {
4694                final Command cmd = MessageCommands.messageUpdateFlag(messageId, read);
4695                return executeAndReturnError(cmd);
4696        }
4697
4698        /**
4699         * Sets the read flag for a given message. This will not delete the message.
4700         *
4701         * @param message
4702         *              the message for which the read flag should be set
4703         * @param read
4704         *              the boolean value to which the read flag should be set
4705         *
4706         * @return a future to track the progress of this command
4707         *
4708         * @throws TS3CommandFailedException
4709         *              if the execution of a command fails
4710         * @querycommands 1
4711         * @see #setMessageRead(Message)
4712         * @see #setMessageReadFlag(int, boolean)
4713         * @see #deleteOfflineMessage(int)
4714         */
4715        public CommandFuture<Void> setMessageReadFlag(Message message, boolean read) {
4716                return setMessageReadFlag(message.getId(), read);
4717        }
4718
4719        /**
4720         * Sets the nickname of the server query client.
4721         * The nickname must be between 3 and 30 UTF-8 bytes long and BB codes will be ignored.
4722         *
4723         * @param nickname
4724         *              the new nickname, may not contain any BB codes and may not be {@code null}
4725         *
4726         * @return a future to track the progress of this command
4727         *
4728         * @throws TS3CommandFailedException
4729         *              if the execution of a command fails
4730         * @querycommands 1
4731         * @see #updateClient(Map)
4732         */
4733        public CommandFuture<Void> setNickname(String nickname) {
4734                final Map<ClientProperty, String> options = Collections.singletonMap(ClientProperty.CLIENT_NICKNAME, nickname);
4735                return updateClient(options);
4736        }
4737
4738        /**
4739         * Starts the virtual server with the specified ID.
4740         *
4741         * @param serverId
4742         *              the ID of the virtual server
4743         *
4744         * @return a future to track the progress of this command
4745         *
4746         * @throws TS3CommandFailedException
4747         *              if the execution of a command fails
4748         * @querycommands 1
4749         */
4750        public CommandFuture<Void> startServer(int serverId) {
4751                final Command cmd = VirtualServerCommands.serverStart(serverId);
4752                return executeAndReturnError(cmd);
4753        }
4754
4755        /**
4756         * Starts the specified virtual server.
4757         *
4758         * @param virtualServer
4759         *              the virtual server to start
4760         *
4761         * @return a future to track the progress of this command
4762         *
4763         * @throws TS3CommandFailedException
4764         *              if the execution of a command fails
4765         * @querycommands 1
4766         */
4767        public CommandFuture<Void> startServer(VirtualServer virtualServer) {
4768                return startServer(virtualServer.getId());
4769        }
4770
4771        /**
4772         * Stops the virtual server with the specified ID.
4773         *
4774         * @param serverId
4775         *              the ID of the virtual server
4776         *
4777         * @return a future to track the progress of this command
4778         *
4779         * @throws TS3CommandFailedException
4780         *              if the execution of a command fails
4781         * @querycommands 1
4782         */
4783        public CommandFuture<Void> stopServer(int serverId) {
4784                final Command cmd = VirtualServerCommands.serverStop(serverId);
4785                return executeAndReturnError(cmd);
4786        }
4787
4788        /**
4789         * Stops the specified virtual server.
4790         *
4791         * @param virtualServer
4792         *              the virtual server to stop
4793         *
4794         * @return a future to track the progress of this command
4795         *
4796         * @throws TS3CommandFailedException
4797         *              if the execution of a command fails
4798         * @querycommands 1
4799         */
4800        public CommandFuture<Void> stopServer(VirtualServer virtualServer) {
4801                return stopServer(virtualServer.getId());
4802        }
4803
4804        /**
4805         * Stops the entire TeamSpeak 3 Server instance by shutting down the process.
4806         * <p>
4807         * To have permission to use this command, you need to use the server query admin login.
4808         * </p>
4809         *
4810         * @return a future to track the progress of this command
4811         *
4812         * @throws TS3CommandFailedException
4813         *              if the execution of a command fails
4814         * @querycommands 1
4815         */
4816        public CommandFuture<Void> stopServerProcess() {
4817                final Command cmd = ServerCommands.serverProcessStop();
4818                return executeAndReturnError(cmd);
4819        }
4820
4821        /**
4822         * Unregisters the server query from receiving any event notifications.
4823         *
4824         * @return a future to track the progress of this command
4825         *
4826         * @throws TS3CommandFailedException
4827         *              if the execution of a command fails
4828         * @querycommands 1
4829         */
4830        public CommandFuture<Void> unregisterAllEvents() {
4831                final Command cmd = QueryCommands.serverNotifyUnregister();
4832                return executeAndReturnError(cmd);
4833        }
4834
4835        /**
4836         * Updates several client properties for this server query instance.
4837         *
4838         * @param options
4839         *              the map of properties to update
4840         *
4841         * @return a future to track the progress of this command
4842         *
4843         * @throws TS3CommandFailedException
4844         *              if the execution of a command fails
4845         * @querycommands 1
4846         * @see #editClient(int, Map)
4847         */
4848        public CommandFuture<Void> updateClient(Map<ClientProperty, String> options) {
4849                final Command cmd = ClientCommands.clientUpdate(options);
4850                return executeAndReturnError(cmd);
4851        }
4852
4853        /**
4854         * Changes a single client property for this server query instance.
4855         * <p>
4856         * Note that one can set many properties at once with the overloaded method that
4857         * takes a map of client properties and strings.
4858         * </p>
4859         *
4860         * @param property
4861         *              the client property to modify, make sure it is editable
4862         * @param value
4863         *              the new value of the property
4864         *
4865         * @return a future to track the progress of this command
4866         *
4867         * @throws TS3CommandFailedException
4868         *              if the execution of a command fails
4869         * @querycommands 1
4870         * @see #updateClient(Map)
4871         * @see #editClient(int, Map)
4872         */
4873        public CommandFuture<Void> updateClient(ClientProperty property, String value) {
4874                return updateClient(Collections.singletonMap(property, value));
4875        }
4876
4877        /**
4878         * Generates new login credentials for the currently connected server query instance, using the given name.
4879         * <p>
4880         * <b>This will remove the current login credentials!</b> You won't be logged out, but after disconnecting,
4881         * the old credentials will no longer work. Make sure to not lock yourselves out!
4882         * </p>
4883         *
4884         * @param loginName
4885         *              the name for the server query login
4886         *
4887         * @return the generated password for the server query login
4888         *
4889         * @throws TS3CommandFailedException
4890         *              if the execution of a command fails
4891         * @querycommands 1
4892         */
4893        public CommandFuture<String> updateServerQueryLogin(String loginName) {
4894                final Command cmd = ClientCommands.clientSetServerQueryLogin(loginName);
4895                return executeAndReturnStringProperty(cmd, "client_login_password");
4896        }
4897
4898        /**
4899         * Uploads a file to the file repository at a given path and channel
4900         * by reading {@code dataLength} bytes from an open {@link InputStream}.
4901         * <p>
4902         * It is the user's responsibility to ensure that the given {@code InputStream} is
4903         * open and that {@code dataLength} bytes can eventually be read from it. The user is
4904         * also responsible for closing the stream once the upload has finished.
4905         * </p><p>
4906         * Note that this method will not read the entire file to memory and can thus
4907         * upload arbitrarily sized files to the file repository.
4908         * </p>
4909         *
4910         * @param dataIn
4911         *              a stream that contains the data that should be uploaded
4912         * @param dataLength
4913         *              how many bytes should be read from the stream
4914         * @param filePath
4915         *              the path the file should have after being uploaded
4916         * @param overwrite
4917         *              if {@code false}, fails if there's already a file at {@code filePath}
4918         * @param channelId
4919         *              the ID of the channel to upload the file to
4920         *
4921         * @return a future to track the progress of this command
4922         *
4923         * @throws TS3CommandFailedException
4924         *              if the execution of a command fails
4925         * @throws TS3FileTransferFailedException
4926         *              if the file transfer fails for any reason
4927         * @querycommands 1
4928         * @see FileInfo#getPath()
4929         * @see Channel#getId()
4930         * @see #uploadFileDirect(byte[], String, boolean, int, String)
4931         */
4932        public CommandFuture<Void> uploadFile(InputStream dataIn, long dataLength, String filePath, boolean overwrite, int channelId) {
4933                return uploadFile(dataIn, dataLength, filePath, overwrite, channelId, null);
4934        }
4935
4936        /**
4937         * Uploads a file to the file repository at a given path and channel
4938         * by reading {@code dataLength} bytes from an open {@link InputStream}.
4939         * <p>
4940         * It is the user's responsibility to ensure that the given {@code InputStream} is
4941         * open and that {@code dataLength} bytes can eventually be read from it. The user is
4942         * also responsible for closing the stream once the upload has finished.
4943         * </p><p>
4944         * Note that this method will not read the entire file to memory and can thus
4945         * upload arbitrarily sized files to the file repository.
4946         * </p>
4947         *
4948         * @param dataIn
4949         *              a stream that contains the data that should be uploaded
4950         * @param dataLength
4951         *              how many bytes should be read from the stream
4952         * @param filePath
4953         *              the path the file should have after being uploaded
4954         * @param overwrite
4955         *              if {@code false}, fails if there's already a file at {@code filePath}
4956         * @param channelId
4957         *              the ID of the channel to upload the file to
4958         * @param channelPassword
4959         *              that channel's password
4960         *
4961         * @return a future to track the progress of this command
4962         *
4963         * @throws TS3CommandFailedException
4964         *              if the execution of a command fails
4965         * @throws TS3FileTransferFailedException
4966         *              if the file transfer fails for any reason
4967         * @querycommands 1
4968         * @see FileInfo#getPath()
4969         * @see Channel#getId()
4970         * @see #uploadFileDirect(byte[], String, boolean, int, String)
4971         */
4972        public CommandFuture<Void> uploadFile(final InputStream dataIn, final long dataLength, String filePath, boolean overwrite, int channelId, String channelPassword) {
4973                final FileTransferHelper helper = query.getFileTransferHelper();
4974                final int transferId = helper.getClientTransferId();
4975                final Command cmd = FileCommands.ftInitUpload(transferId, filePath, channelId, channelPassword, dataLength, overwrite);
4976                final CommandFuture<Void> future = new CommandFuture<>();
4977
4978                cmd.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
4979                        @Override
4980                        public void handleSuccess(DefaultArrayResponse result) {
4981                                FileTransferParameters params = new FileTransferParameters(result.getFirstResponse().getMap());
4982                                QueryError error = params.getQueryError();
4983                                if (!error.isSuccessful()) {
4984                                        future.fail(new TS3CommandFailedException(error));
4985                                        return;
4986                                }
4987
4988                                try {
4989                                        query.getFileTransferHelper().uploadFile(dataIn, dataLength, params);
4990                                } catch (IOException e) {
4991                                        future.fail(new TS3FileTransferFailedException("Upload failed", e));
4992                                        return;
4993                                }
4994                                future.set(null); // Mark as successful
4995                        }
4996                }).forwardFailure(future);
4997
4998                query.doCommandAsync(cmd);
4999                return future;
5000        }
5001
5002        /**
5003         * Uploads a file that is already stored in memory to the file repository
5004         * at a given path and channel.
5005         *
5006         * @param data
5007         *              the file's data as a byte array
5008         * @param filePath
5009         *              the path the file should have after being uploaded
5010         * @param overwrite
5011         *              if {@code false}, fails if there's already a file at {@code filePath}
5012         * @param channelId
5013         *              the ID of the channel to upload the file to
5014         *
5015         * @return a future to track the progress of this command
5016         *
5017         * @throws TS3CommandFailedException
5018         *              if the execution of a command fails
5019         * @throws TS3FileTransferFailedException
5020         *              if the file transfer fails for any reason
5021         * @querycommands 1
5022         * @see FileInfo#getPath()
5023         * @see Channel#getId()
5024         * @see #uploadFile(InputStream, long, String, boolean, int)
5025         */
5026        public CommandFuture<Void> uploadFileDirect(byte[] data, String filePath, boolean overwrite, int channelId) {
5027                return uploadFileDirect(data, filePath, overwrite, channelId, null);
5028        }
5029
5030        /**
5031         * Uploads a file that is already stored in memory to the file repository
5032         * at a given path and channel.
5033         *
5034         * @param data
5035         *              the file's data as a byte array
5036         * @param filePath
5037         *              the path the file should have after being uploaded
5038         * @param overwrite
5039         *              if {@code false}, fails if there's already a file at {@code filePath}
5040         * @param channelId
5041         *              the ID of the channel to upload the file to
5042         * @param channelPassword
5043         *              that channel's password
5044         *
5045         * @return a future to track the progress of this command
5046         *
5047         * @throws TS3CommandFailedException
5048         *              if the execution of a command fails
5049         * @throws TS3FileTransferFailedException
5050         *              if the file transfer fails for any reason
5051         * @querycommands 1
5052         * @see FileInfo#getPath()
5053         * @see Channel#getId()
5054         * @see #uploadFile(InputStream, long, String, boolean, int, String)
5055         */
5056        public CommandFuture<Void> uploadFileDirect(byte[] data, String filePath, boolean overwrite, int channelId, String channelPassword) {
5057                return uploadFile(new ByteArrayInputStream(data), data.length, filePath, overwrite, channelId, channelPassword);
5058        }
5059
5060        /**
5061         * Uploads an icon to the icon directory in the file repository
5062         * by reading {@code dataLength} bytes from an open {@link InputStream}.
5063         * <p>
5064         * It is the user's responsibility to ensure that the given {@code InputStream} is
5065         * open and that {@code dataLength} bytes can eventually be read from it. The user is
5066         * also responsible for closing the stream once the upload has finished.
5067         * </p><p>
5068         * Note that unlike the file upload methods, this <strong>will read the entire file to memory</strong>.
5069         * This is because the CRC32 hash must be calculated before the icon can be uploaded.
5070         * That means that all icon files must be less than 2<sup>31</sup>-1 bytes in size.
5071         * </p>
5072         * Uploads  that is already stored in memory to the icon directory
5073         * in the file repository. If this icon has already been uploaded or
5074         * if a hash collision occurs (CRC32), this command will fail.
5075         *
5076         * @param dataIn
5077         *              a stream that contains the data that should be uploaded
5078         * @param dataLength
5079         *              how many bytes should be read from the stream
5080         *
5081         * @return the ID of the uploaded icon
5082         *
5083         * @throws TS3CommandFailedException
5084         *              if the execution of a command fails
5085         * @throws TS3FileTransferFailedException
5086         *              if the file transfer fails for any reason
5087         * @querycommands 1
5088         * @see IconFile#getIconId()
5089         * @see #uploadIconDirect(byte[])
5090         * @see #downloadIcon(OutputStream, long)
5091         */
5092        public CommandFuture<Long> uploadIcon(InputStream dataIn, long dataLength) {
5093                final FileTransferHelper helper = query.getFileTransferHelper();
5094                final byte[] data;
5095                try {
5096                        data = helper.readFully(dataIn, dataLength);
5097                } catch (IOException e) {
5098                        throw new TS3FileTransferFailedException("Reading stream failed", e);
5099                }
5100                return uploadIconDirect(data);
5101        }
5102
5103        /**
5104         * Uploads an icon that is already stored in memory to the icon directory
5105         * in the file repository. If this icon has already been uploaded or
5106         * if a CRC32 hash collision occurs, this command will fail.
5107         *
5108         * @param data
5109         *              the icon's data as a byte array
5110         *
5111         * @return the ID of the uploaded icon
5112         *
5113         * @throws TS3CommandFailedException
5114         *              if the execution of a command fails
5115         * @throws TS3FileTransferFailedException
5116         *              if the file transfer fails for any reason
5117         * @querycommands 1
5118         * @see IconFile#getIconId()
5119         * @see #uploadIcon(InputStream, long)
5120         * @see #downloadIconDirect(long)
5121         */
5122        public CommandFuture<Long> uploadIconDirect(byte[] data) {
5123                final FileTransferHelper helper = query.getFileTransferHelper();
5124                final CommandFuture<Long> future = new CommandFuture<>();
5125
5126                final long iconId;
5127                iconId = helper.getIconId(data);
5128
5129                final String path = "/icon_" + iconId;
5130                uploadFileDirect(data, path, false, 0).onSuccess(new CommandFuture.SuccessListener<Void>() {
5131                        @Override
5132                        public void handleSuccess(Void ignored) {
5133                                future.set(iconId);
5134                        }
5135                }).onFailure(transformError(future, 2050, iconId));
5136
5137                return future;
5138        }
5139
5140        /**
5141         * Uses an existing privilege key to join a server or channel group.
5142         *
5143         * @param token
5144         *              the privilege key to use
5145         *
5146         * @return a future to track the progress of this command
5147         *
5148         * @throws TS3CommandFailedException
5149         *              if the execution of a command fails
5150         * @querycommands 1
5151         * @see PrivilegeKey
5152         * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String)
5153         * @see #usePrivilegeKey(PrivilegeKey)
5154         */
5155        public CommandFuture<Void> usePrivilegeKey(String token) {
5156                final Command cmd = PrivilegeKeyCommands.privilegeKeyUse(token);
5157                return executeAndReturnError(cmd);
5158        }
5159
5160        /**
5161         * Uses an existing privilege key to join a server or channel group.
5162         *
5163         * @param privilegeKey
5164         *              the privilege key to use
5165         *
5166         * @return a future to track the progress of this command
5167         *
5168         * @throws TS3CommandFailedException
5169         *              if the execution of a command fails
5170         * @querycommands 1
5171         * @see PrivilegeKey
5172         * @see #addPrivilegeKey(PrivilegeKeyType, int, int, String)
5173         * @see #usePrivilegeKey(String)
5174         */
5175        public CommandFuture<Void> usePrivilegeKey(PrivilegeKey privilegeKey) {
5176                return usePrivilegeKey(privilegeKey.getToken());
5177        }
5178
5179        /**
5180         * Gets information about the current server query instance.
5181         *
5182         * @return information about the server query instance
5183         *
5184         * @throws TS3CommandFailedException
5185         *              if the execution of a command fails
5186         * @querycommands 1
5187         * @see #getClientInfo(int)
5188         */
5189        public CommandFuture<ServerQueryInfo> whoAmI() {
5190                final Command cmd = QueryCommands.whoAmI();
5191                return executeAndTransformFirst(cmd, Transformer.SERVER_QUERY_INFO /* ServerQueryInfo::new */);
5192        }
5193
5194        /**
5195         * Checks whether a given {@link TS3Exception} is a {@link TS3CommandFailedException} with the
5196         * specified error ID.
5197         *
5198         * @param exception
5199         *              the exception to check
5200         * @param errorId
5201         *              the error ID to match
5202         *
5203         * @return whether {@code exception} is a {@code TS3CommandFailedException} with error ID {@code errorId}.
5204         */
5205        private static boolean isQueryError(TS3Exception exception, int errorId) {
5206                if (exception instanceof TS3CommandFailedException) {
5207                        TS3CommandFailedException cfe = (TS3CommandFailedException) exception;
5208                        return (cfe.getError().getId() == errorId);
5209                } else {
5210                        return false;
5211                }
5212        }
5213
5214        /**
5215         * Creates a {@code FailureListener} that checks whether the caught exception is
5216         * a {@code TS3CommandFailedException} with error ID {@code errorId}.
5217         * <p>
5218         * If so, the listener makes {@code future} succeed by setting its result value to an empty
5219         * list with element type {@code T}. Else, the caught exception is forwarded to {@code future}.
5220         * </p>
5221         *
5222         * @param future the future to forward the result to
5223         * @param errorId the error ID to catch
5224         * @param replacement the value to
5225         * @param <T> the type of {@code replacement} and element type of {@code future}
5226         * @return a {@code FailureListener} with the described properties
5227         */
5228        private static <T> CommandFuture.FailureListener transformError(final CommandFuture<T> future, final int errorId, final T replacement) {
5229                return new CommandFuture.FailureListener() {
5230                        @Override
5231                        public void handleFailure(TS3Exception exception) {
5232                                if (isQueryError(exception, errorId)) {
5233                                        future.set(replacement);
5234                                } else {
5235                                        future.fail(exception);
5236                                }
5237                        }
5238                };
5239        }
5240
5241        /**
5242         * Executes a command and sets the returned future to true if the command succeeded.
5243         *
5244         * @param command
5245         *              the command to execute
5246         *
5247         * @return a future to track the progress of this command
5248         */
5249        private CommandFuture<Void> executeAndReturnError(final Command command) {
5250                final CommandFuture<Void> future = new CommandFuture<>();
5251                command.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
5252                        @Override
5253                        public void handleSuccess(DefaultArrayResponse result) {
5254                                future.set(null); // Mark as successful
5255                        }
5256                }).forwardFailure(future);
5257
5258                query.doCommandAsync(command);
5259                return future;
5260        }
5261
5262        /**
5263         * Executes a command, checking for failure and returning a single
5264         * {@code String} property from the first response map.
5265         *
5266         * @param command
5267         *              the command to execute
5268         * @param property
5269         *              the name of the property to return
5270         *
5271         * @return the value of the specified {@code String} property
5272         */
5273        private CommandFuture<String> executeAndReturnStringProperty(final Command command, final String property) {
5274                final CommandFuture<String> future = new CommandFuture<>();
5275                command.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
5276                        @Override
5277                        public void handleSuccess(DefaultArrayResponse result) {
5278                                future.set(result.getFirstResponse().get(property));
5279                        }
5280                }).forwardFailure(future);
5281
5282                query.doCommandAsync(command);
5283                return future;
5284        }
5285
5286        /**
5287         * Executes a command and returns a single {@code Integer} property from the first response map.
5288         *
5289         * @param command
5290         *              the command to execute
5291         * @param property
5292         *              the name of the property to return
5293         *
5294         * @return the value of the specified {@code Integer} property
5295         */
5296        private CommandFuture<Integer> executeAndReturnIntProperty(final Command command, final String property) {
5297                final CommandFuture<Integer> future = new CommandFuture<>();
5298                command.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
5299                        @Override
5300                        public void handleSuccess(DefaultArrayResponse result) {
5301                                future.set(result.getFirstResponse().getInt(property));
5302                        }
5303                }).forwardFailure(future);
5304
5305                query.doCommandAsync(command);
5306                return future;
5307        }
5308
5309        /**
5310         * Executes a command and returns the log lines contained in the response.
5311         *
5312         * @param command
5313         *              the command to execute
5314         *
5315         * @return the lines from the log file
5316         */
5317        private CommandFuture<List<String>> executeAndReturnLogLines(final Command command) {
5318                final CommandFuture<List<String>> future = new CommandFuture<>();
5319                command.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
5320                        @Override
5321                        public void handleSuccess(DefaultArrayResponse result) {
5322                                List<Wrapper> responses = result.getResponses();
5323                                List<String> lines = new ArrayList<>(responses.size());
5324
5325                                for (Wrapper r : result.getResponses()) {
5326                                        lines.add(r.getMap().get("l"));
5327                                }
5328
5329                                future.set(lines);
5330                        }
5331                }).forwardFailure(future);
5332
5333                query.doCommandAsync(command);
5334                return future;
5335        }
5336
5337        /**
5338         * Executes a command, checks for failure and transforms the first
5339         * response map by invoking {@code transformer}.
5340         *
5341         * @param command
5342         *              the command to execute
5343         * @param transformer
5344         *              the function that creates a new wrapper of type {@code T}
5345         * @param <T>
5346         *              the wrapper class the map should be wrapped with
5347         *
5348         * @return a wrapped version of the first response map
5349         */
5350        private <T extends Wrapper> CommandFuture<T> executeAndTransformFirst(final Command command, final Transformer<T> transformer) {
5351                final CommandFuture<T> future = new CommandFuture<>();
5352                command.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
5353                        @Override
5354                        public void handleSuccess(DefaultArrayResponse result) {
5355                                Wrapper firstResponse = result.getFirstResponse();
5356                                T transformed = transformer.apply(firstResponse.getMap());
5357                                future.set(transformed);
5358                        }
5359                }).forwardFailure(future);
5360
5361                query.doCommandAsync(command);
5362                return future;
5363        }
5364
5365        /**
5366         * Executes a command, checks for failure and transforms all
5367         * response maps by invoking {@code transformer} on each one.
5368         *
5369         * @param command
5370         *              the command to execute
5371         * @param transformer
5372         *              the function that creates the new wrappers of type {@code T}
5373         * @param <T>
5374         *              the wrapper class the maps should be wrapped with
5375         *
5376         * @return a list of wrapped response maps
5377         */
5378        private <T extends Wrapper> CommandFuture<List<T>> executeAndTransform(final Command command, final Transformer<T> transformer) {
5379                final CommandFuture<List<T>> future = new CommandFuture<>();
5380
5381                command.getFuture().onSuccess(new CommandFuture.SuccessListener<DefaultArrayResponse>() {
5382                        @Override
5383                        public void handleSuccess(DefaultArrayResponse result) {
5384                                List<Wrapper> response = result.getResponses();
5385                                List<T> transformed = new ArrayList<>(response.size());
5386                                for (Wrapper wrapper : response) {
5387                                        transformed.add(transformer.apply(wrapper.getMap()));
5388                                }
5389
5390                                future.set(transformed);
5391                        }
5392                }).forwardFailure(future);
5393
5394                query.doCommandAsync(command);
5395                return future;
5396        }
5397
5398        /**
5399         * This interface directly corresponds to {@code java.util.function.Function<Map<String, String>, T>}
5400         * and only exists because we're still using Java 7 instead of Java 8.
5401         * <p>
5402         * All uses of this interface can be replaced by a method reference to the constructor of the wrapper class
5403         * if and when we switch to using Java 8.
5404         * </p>
5405         *
5406         * @param <T>
5407         *              the type of the wrapper class to transform maps to
5408         */
5409        private interface Transformer<T extends Wrapper> /* extends Function<Map<String, String>, T> */ {
5410
5411                Transformer<Ban> BAN = new ReflectiveTransformer<>(Ban.class);
5412                Transformer<Binding> BINDING = new ReflectiveTransformer<>(Binding.class);
5413                Transformer<Channel> CHANNEL = new ReflectiveTransformer<>(Channel.class);
5414                Transformer<ChannelGroup> CHANNEL_GROUP = new ReflectiveTransformer<>(ChannelGroup.class);
5415                Transformer<ChannelGroupClient> CHANNEL_GROUP_CLIENT = new ReflectiveTransformer<>(ChannelGroupClient.class);
5416                Transformer<Client> CLIENT = new ReflectiveTransformer<>(Client.class);
5417                Transformer<Complaint> COMPLAINT = new ReflectiveTransformer<>(Complaint.class);
5418                Transformer<ConnectionInfo> CONNECTION_INFO = new ReflectiveTransformer<>(ConnectionInfo.class);
5419                Transformer<CreatedVirtualServer> CREATED_VIRTUAL_SERVER = new ReflectiveTransformer<>(CreatedVirtualServer.class);
5420                Transformer<DatabaseClient> DATABASE_CLIENT = new ReflectiveTransformer<>(DatabaseClient.class);
5421                Transformer<DatabaseClientInfo> DATABASE_CLIENT_INFO = new ReflectiveTransformer<>(DatabaseClientInfo.class);
5422                Transformer<FileInfo> FILE_INFO = new ReflectiveTransformer<>(FileInfo.class);
5423                Transformer<FileListEntry> FILE_LIST_ENTRY = new ReflectiveTransformer<>(FileListEntry.class);
5424                Transformer<FileTransfer> FILE_TRANSFER = new ReflectiveTransformer<>(FileTransfer.class);
5425                Transformer<HostInfo> HOST_INFO = new ReflectiveTransformer<>(HostInfo.class);
5426                Transformer<InstanceInfo> INSTANCE_INFO = new ReflectiveTransformer<>(InstanceInfo.class);
5427                Transformer<Message> MESSAGE = new ReflectiveTransformer<>(Message.class);
5428                Transformer<Permission> PERMISSION = new ReflectiveTransformer<>(Permission.class);
5429                Transformer<PermissionAssignment> PERMISSION_ASSIGNMENT = new ReflectiveTransformer<>(PermissionAssignment.class);
5430                Transformer<PermissionInfo> PERMISSION_INFO = new ReflectiveTransformer<>(PermissionInfo.class);
5431                Transformer<PrivilegeKey> PRIVILEGE_KEY = new ReflectiveTransformer<>(PrivilegeKey.class);
5432                Transformer<ServerGroup> SERVER_GROUP = new ReflectiveTransformer<>(ServerGroup.class);
5433                Transformer<ServerGroupClient> SERVER_GROUP_CLIENT = new ReflectiveTransformer<>(ServerGroupClient.class);
5434                Transformer<ServerQueryInfo> SERVER_QUERY_INFO = new ReflectiveTransformer<>(ServerQueryInfo.class);
5435                Transformer<Version> VERSION = new ReflectiveTransformer<>(Version.class);
5436                Transformer<VirtualServer> VIRTUAL_SERVER = new ReflectiveTransformer<>(VirtualServer.class);
5437                Transformer<VirtualServerInfo> VIRTUAL_SERVER_INFO = new ReflectiveTransformer<>(VirtualServerInfo.class);
5438
5439                T apply(Map<String, String> map);
5440        }
5441
5442        /**
5443         * This implementation of Transformer uses MethodHandles because creating an anonymous class
5444         * for each of the about 30 instances of Transformer would significantly increase the class loading time
5445         * and would also necessitate around 150 more lines of boilerplate code.
5446         *
5447         * @param <T>
5448         *              the type of the wrapper class to transform maps to
5449         */
5450        private static class ReflectiveTransformer<T extends Wrapper> implements Transformer<T> {
5451
5452                private final Class<T> wrapperClass;
5453                private final MethodHandle constructor;
5454
5455                public ReflectiveTransformer(Class<T> wrapperClass) {
5456                        this.wrapperClass = wrapperClass;
5457
5458                        try {
5459                                MethodType type = MethodType.methodType(void.class, Map.class);
5460                                constructor = MethodHandles.publicLookup()
5461                                                .in(wrapperClass)
5462                                                .findConstructor(wrapperClass, type);
5463                        } catch (NoSuchMethodException | IllegalAccessException e) {
5464                                throw new Error("Missing public constructor of wrapper class " + wrapperClass.getSimpleName(), e);
5465                        }
5466                }
5467
5468                @Override
5469                public T apply(Map<String, String> map) {
5470                        try {
5471                                return wrapperClass.cast(constructor.invoke(map));
5472                        } catch (Throwable t) {
5473                                throw new Error("Method handle error", t);
5474                        }
5475                }
5476        }
5477}