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.wrapper.*;
034import com.github.theholywaffle.teamspeak3.commands.*;
035
036import java.util.ArrayList;
037import java.util.Collection;
038import java.util.Collections;
039import java.util.List;
040import java.util.Map;
041import java.util.concurrent.TimeUnit;
042import java.util.regex.Pattern;
043
044/**
045 * Asynchronous version of {@link TS3Api} to interact with the {@link TS3Query}.
046 * <p>
047 * This class is used to easily interact with a {@link TS3Query}. It constructs commands,
048 * sends them to the TeamSpeak3 server, processes the response and returns the result.
049 * </p><p>
050 * All methods in this class are asynchronous (so they won't block) and
051 * will return a {@link CommandFuture} of the corresponding return type in {@link TS3Api}.
052 * If a command fails, no exception will be thrown directly. It will however be rethrown in
053 * {@link CommandFuture#get()} and {@link CommandFuture#get(long, TimeUnit)}.
054 * Usually, the thrown exception is a {@link TS3CommandFailedException}, which will get you
055 * access to the {@link QueryError} from which more information about the error can be obtained.
056 * </p><p>
057 * Also note that while these methods are asynchronous, the commands will still be sent through a
058 * synchronous command pipeline. That means if an asynchronous method is called immediately
059 * followed by a synchronous method, the synchronous method will first have to wait until the
060 * asynchronous method completed until it its command is sent.
061 * </p><p>
062 * You won't be able to execute most commands while you're not logged in due to missing permissions.
063 * Make sure to either pass your login credentials to the {@link TS3Config} object when
064 * creating the {@code TS3Query} or to call {@link #login(String, String)} to log in.
065 * </p><p>
066 * After that, most commands also require you to select a {@linkplain VirtualServer virtual server}.
067 * To do so, call either {@link #selectVirtualServerByPort(int)} or {@link #selectVirtualServerById(int)}.
068 * </p>
069 *
070 * @see TS3Api The synchronous version of the API
071 */
072public class TS3ApiAsync {
073
074        /**
075         * The TS3 query to which this API sends its commands.
076         */
077        private final TS3Query query;
078
079        /**
080         * Creates a new asynchronous API object for the given {@code TS3Query}.
081         * <p>
082         * <b>Usually, this constructor should not be called.</b> Use {@link TS3Query#getAsyncApi()} instead.
083         * </p>
084         *
085         * @param query
086         *              the TS3Query to call
087         */
088        public TS3ApiAsync(TS3Query query) {
089                this.query = query;
090        }
091
092        /**
093         * Adds a new ban entry. At least one of the parameters {@code ip}, {@code name} or {@code uid} needs to be not null.
094         * Returns the ID of the newly created ban.
095         *
096         * @param ip
097         *              a RegEx pattern to match a client's IP against, can be null
098         * @param name
099         *              a RegEx pattern to match a client's name against, can be null
100         * @param uid
101         *              the unique identifier of a client, can be null
102         * @param timeInSeconds
103         *              the duration of the ban in seconds. 0 equals a permanent ban
104         * @param reason
105         *              the reason for the ban, can be null
106         *
107         * @return the ID of the newly created ban entry
108         *
109         * @querycommands 1
110         * @see Pattern RegEx Pattern
111         * @see Client#getId()
112         * @see Client#getUniqueIdentifier()
113         * @see ClientInfo#getIp()
114         */
115        public CommandFuture<Integer> addBan(String ip, String name, String uid, long timeInSeconds, String reason) {
116                if (ip == null && name == null && uid == null) {
117                        throw new IllegalArgumentException("Either IP, Name or UID must be set");
118                }
119
120                final CBanAdd add = new CBanAdd(ip, name, uid, timeInSeconds, reason);
121                return executeAndReturnIntProperty(add, "banid");
122        }
123
124        /**
125         * Adds a specified permission to a client in a specific channel.
126         *
127         * @param channelId
128         *              the ID of the channel wherein the permission should be granted
129         * @param clientDBId
130         *              the database ID of the client to add a permission to
131         * @param permName
132         *              the name of the permission to grant
133         * @param permValue
134         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
135         *
136         * @return whether the command succeeded or not
137         *
138         * @querycommands 1
139         * @see Channel#getId()
140         * @see Client#getDatabaseId()
141         * @see Permission
142         */
143        public CommandFuture<Boolean> addChannelClientPermission(int channelId, int clientDBId, String permName, int permValue) {
144                final CChannelClientAddPerm add = new CChannelClientAddPerm(channelId, clientDBId, permName, permValue);
145                return executeAndReturnError(add);
146        }
147
148        /**
149         * Creates a new channel group for clients using a given name and returns its ID.
150         * <p>
151         * To create channel group templates or ones for server queries,
152         * use {@link #addChannelGroup(String, PermissionGroupDatabaseType)}.
153         * </p>
154         *
155         * @param name
156         *              the name of the new channel group
157         *
158         * @return the ID of the newly created channel group
159         *
160         * @querycommands 1
161         * @see ChannelGroup
162         */
163        public CommandFuture<Integer> addChannelGroup(String name) {
164                return addChannelGroup(name, null);
165        }
166
167        /**
168         * Creates a new channel group using a given name and returns its ID.
169         *
170         * @param name
171         *              the name of the new channel group
172         * @param type
173         *              the desired type of channel group
174         *
175         * @return the ID of the newly created channel group
176         *
177         * @querycommands 1
178         * @see ChannelGroup
179         */
180        public CommandFuture<Integer> addChannelGroup(String name, PermissionGroupDatabaseType type) {
181                final CChannelGroupAdd add = new CChannelGroupAdd(name, type);
182                return executeAndReturnIntProperty(add, "cgid");
183        }
184
185        /**
186         * Adds a specified permission to a channel group.
187         *
188         * @param groupId
189         *              the ID of the channel group to grant the permission
190         * @param permName
191         *              the name of the permission to be granted
192         * @param permValue
193         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
194         *
195         * @return whether the command succeeded or not
196         *
197         * @querycommands 1
198         * @see ChannelGroup#getId()
199         * @see Permission
200         */
201        public CommandFuture<Boolean> addChannelGroupPermission(int groupId, String permName, int permValue) {
202                final CChannelGroupAddPerm add = new CChannelGroupAddPerm(groupId, permName, permValue);
203                return executeAndReturnError(add);
204        }
205
206        /**
207         * Adds a specified permission to a channel.
208         *
209         * @param channelId
210         *              the ID of the channel wherein the permission should be granted
211         * @param permName
212         *              the name of the permission to grant
213         * @param permValue
214         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
215         *
216         * @return whether the command succeeded or not
217         *
218         * @querycommands 1
219         * @see Channel#getId()
220         * @see Permission
221         */
222        public CommandFuture<Boolean> addChannelPermission(int channelId, String permName, int permValue) {
223                final CChannelAddPerm perm = new CChannelAddPerm(channelId, permName, permValue);
224                return executeAndReturnError(perm);
225        }
226
227        /**
228         * Adds a specified permission to a channel.
229         *
230         * @param clientDBId
231         *              the database ID of the client to grant the permission
232         * @param permName
233         *              the name of the permission to grant
234         * @param value
235         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
236         * @param skipped
237         *              if set to {@code true}, the permission will not be overridden by channel permissions
238         *
239         * @return whether the command succeeded or not
240         *
241         * @querycommands 1
242         * @see Client#getDatabaseId()
243         * @see Permission
244         */
245        public CommandFuture<Boolean> addClientPermission(int clientDBId, String permName, int value, boolean skipped) {
246                final CClientAddPerm add = new CClientAddPerm(clientDBId, permName, value, skipped);
247                return executeAndReturnError(add);
248        }
249
250        /**
251         * Adds a client to the specified server group.
252         * <p>
253         * Please note that a client cannot be added to default groups or template groups.
254         * </p>
255         *
256         * @param groupId
257         *              the ID of the server group to add the client to
258         * @param clientDatabaseId
259         *              the database ID of the client to add
260         *
261         * @return whether the command succeeded or not
262         *
263         * @querycommands 1
264         * @see ServerGroup#getId()
265         * @see Client#getDatabaseId()
266         */
267        public CommandFuture<Boolean> addClientToServerGroup(int groupId, int clientDatabaseId) {
268                final CServerGroupAddClient add = new CServerGroupAddClient(groupId, clientDatabaseId);
269                return executeAndReturnError(add);
270        }
271
272        /**
273         * Submits a complaint about the specified client.
274         * The length of the message is limited to 200 UTF-8 bytes and BB codes in it will be ignored.
275         *
276         * @param clientDBId
277         *              the database ID of the client
278         * @param message
279         *              the message of the complaint, may not contain BB codes
280         *
281         * @return whether the command succeeded or not
282         *
283         * @querycommands 1
284         * @see Client#getDatabaseId()
285         * @see Complaint#getMessage()
286         */
287        public CommandFuture<Boolean> addComplaint(int clientDBId, String message) {
288                final CComplainAdd add = new CComplainAdd(clientDBId, message);
289                return executeAndReturnError(add);
290        }
291
292        /**
293         * Adds a specified permission to all server groups of the type specified by {@code type} on all virtual servers.
294         *
295         * @param type
296         *              the kind of server group this permission should be added to
297         * @param permName
298         *              the name of the permission to be granted
299         * @param value
300         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
301         * @param negated
302         *              if set to true, the lowest permission value will be selected instead of the highest
303         * @param skipped
304         *              if set to true, this permission will not be overridden by client of channel permissions
305         *
306         * @return whether the command succeeded or not
307         *
308         * @querycommands 1
309         * @see ServerGroupType
310         * @see Permission
311         */
312        public CommandFuture<Boolean> addPermissionToAllServerGroups(ServerGroupType type, String permName, int value, boolean negated, boolean skipped) {
313                final CServerGroupAutoAddPerm add = new CServerGroupAutoAddPerm(type, permName, value, negated, skipped);
314                return executeAndReturnError(add);
315        }
316
317        /**
318         * Create a new privilege key that allows one client to join a server or channel group.
319         * <ul>
320         * <li>If {@code type} is set to {@linkplain TokenType#SERVER_GROUP SERVER_GROUP},
321         * {@code groupId} is used as a server group ID and {@code channelId} is ignored.</li>
322         * <li>If {@code type} is set to {@linkplain TokenType#CHANNEL_GROUP CHANNEL_GROUP},
323         * {@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>
324         * </ul>
325         *
326         * @param type
327         *              the type of token that should be created
328         * @param groupId
329         *              the ID of the server or channel group
330         * @param channelId
331         *              the ID of the channel, in case the token is channel group token
332         * @param description
333         *              the description for the token, can be null
334         *
335         * @return the created token for a client to use
336         *
337         * @querycommands 1
338         * @see TokenType
339         * @see #addPrivilegeKeyServerGroup(int, String)
340         * @see #addPrivilegeKeyChannelGroup(int, int, String)
341         */
342        public CommandFuture<String> addPrivilegeKey(TokenType type, int groupId, int channelId, String description) {
343                final CPrivilegeKeyAdd add = new CPrivilegeKeyAdd(type, groupId, channelId, description);
344                return executeAndReturnStringProperty(add, "token");
345        }
346
347        /**
348         * Creates a new privilege key for a channel group.
349         *
350         * @param channelGroupId
351         *              the ID of the channel group
352         * @param channelId
353         *              the ID of the channel in which the channel group should be set
354         * @param description
355         *              the description for the token, can be null
356         *
357         * @return the created token for a client to use
358         *
359         * @querycommands 1
360         * @see ChannelGroup#getId()
361         * @see Channel#getId()
362         * @see #addPrivilegeKey(TokenType, int, int, String)
363         * @see #addPrivilegeKeyServerGroup(int, String)
364         */
365        public CommandFuture<String> addPrivilegeKeyChannelGroup(int channelGroupId, int channelId, String description) {
366                return addPrivilegeKey(TokenType.CHANNEL_GROUP, channelGroupId, channelId, description);
367        }
368
369        /**
370         * Creates a new privilege key for a server group.
371         *
372         * @param serverGroupId
373         *              the ID of the server group
374         * @param description
375         *              the description for the token, can be null
376         *
377         * @return the created token for a client to use
378         *
379         * @querycommands 1
380         * @see ServerGroup#getId()
381         * @see #addPrivilegeKey(TokenType, int, int, String)
382         * @see #addPrivilegeKeyChannelGroup(int, int, String)
383         */
384        public CommandFuture<String> addPrivilegeKeyServerGroup(int serverGroupId, String description) {
385                return addPrivilegeKey(TokenType.SERVER_GROUP, serverGroupId, 0, description);
386        }
387
388        /**
389         * Creates a new server group for clients using a given name and returns its ID.
390         * <p>
391         * To create server group templates or ones for server queries,
392         * use {@link #addServerGroup(String, PermissionGroupDatabaseType)}.
393         * </p>
394         *
395         * @param name
396         *              the name of the new server group
397         *
398         * @return the ID of the newly created server group
399         *
400         * @querycommands 1
401         * @see ServerGroup
402         */
403        public CommandFuture<Integer> addServerGroup(String name) {
404                return addServerGroup(name, PermissionGroupDatabaseType.REGULAR);
405        }
406
407        /**
408         * Creates a new server group using a given name and returns its ID.
409         *
410         * @param name
411         *              the name of the new server group
412         * @param type
413         *              the desired type of server group
414         *
415         * @return the ID of the newly created server group
416         *
417         * @querycommands 1
418         * @see ServerGroup
419         * @see PermissionGroupDatabaseType
420         */
421        public CommandFuture<Integer> addServerGroup(String name, PermissionGroupDatabaseType type) {
422                final CServerGroupAdd add = new CServerGroupAdd(name, type);
423                return executeAndReturnIntProperty(add, "sgid");
424        }
425
426        /**
427         * Adds a specified permission to a server group.
428         *
429         * @param groupId
430         *              the ID of the channel group to which the permission should be added
431         * @param permName
432         *              the name of the permission to add
433         * @param value
434         *              the numeric value of the permission (or for boolean permissions: 1 = true, 0 = false)
435         * @param negated
436         *              if set to true, the lowest permission value will be selected instead of the highest
437         * @param skipped
438         *              if set to true, this permission will not be overridden by client of channel permissions
439         *
440         * @return whether the command succeeded or not
441         *
442         * @querycommands 1
443         * @see ServerGroup#getId()
444         * @see Permission
445         */
446        public CommandFuture<Boolean> addServerGroupPermission(int groupId, String permName, int value, boolean negated, boolean skipped) {
447                final CServerGroupAddPerm add = new CServerGroupAddPerm(groupId, permName, value, negated, skipped);
448                return executeAndReturnError(add);
449        }
450
451        /**
452         * Adds one or more {@link TS3Listener}s to the event manager of the query.
453         * These listeners will be notified when the TS3 server fires an event.
454         * <p>
455         * Note that for the TS3 server to fire events, you must first also register
456         * the event types you want to listen to.
457         * </p>
458         *
459         * @param listeners
460         *              one or more listeners to register
461         *
462         * @see #registerAllEvents()
463         * @see #registerEvent(TS3EventType, int)
464         * @see TS3Listener
465         * @see TS3EventType
466         */
467        public void addTS3Listeners(TS3Listener... listeners) {
468                query.getEventManager().addListeners(listeners);
469        }
470
471        /**
472         * Bans a client with a given client ID for a given time.
473         * <p>
474         * Please note that this will create two separate ban rules,
475         * one for the targeted client's IP address and their unique identifier.
476         * </p>
477         *
478         * @param clientId
479         *              the ID of the client
480         * @param timeInSeconds
481         *              the duration of the ban in seconds. 0 equals a permanent ban
482         *
483         * @return an array containing the IDs of the first and the second ban entry
484         *
485         * @querycommands 1
486         * @see Client#getId()
487         * @see #addBan(String, String, String, long, String)
488         */
489        public CommandFuture<Integer[]> banClient(int clientId, long timeInSeconds) {
490                return banClient(clientId, timeInSeconds, null);
491        }
492
493        /**
494         * Bans a client with a given client ID for a given time for the specified reason.
495         * <p>
496         * Please note that this will create two separate ban rules,
497         * one for the targeted client's IP address and their unique identifier.
498         * </p>
499         *
500         * @param clientId
501         *              the ID of the client
502         * @param timeInSeconds
503         *              the duration of the ban in seconds. 0 equals a permanent ban
504         * @param reason
505         *              the reason for the ban, can be null
506         *
507         * @return an array containing the IDs of the first and the second ban entry
508         *
509         * @querycommands 1
510         * @see Client#getId()
511         * @see #addBan(String, String, String, long, String)
512         */
513        public CommandFuture<Integer[]> banClient(int clientId, long timeInSeconds, String reason) {
514                final CBanClient client = new CBanClient(clientId, timeInSeconds, reason);
515                final CommandFuture<Integer[]> future = new CommandFuture<>();
516
517                query.doCommandAsync(client, new Callback() {
518                        @Override
519                        public void handle() {
520                                if (hasFailed(client, future)) return;
521
522                                final List<Wrapper> response = client.getResponse();
523                                final int banId1 = response.get(0).getInt("banid");
524                                final int banId2 = response.get(1).getInt("banid");
525                                future.set(new Integer[] {banId1, banId2});
526                        }
527                });
528                return future;
529        }
530
531        /**
532         * Bans a client with a given client ID permanently for the specified reason.
533         * <p>
534         * Please note that this will create two separate ban rules,
535         * one for the targeted client's IP address and their unique identifier.
536         * </p>
537         *
538         * @param clientId
539         *              the ID of the client
540         * @param reason
541         *              the reason for the ban, can be null
542         *
543         * @return an array containing the IDs of the first and the second ban entry
544         *
545         * @querycommands 1
546         * @see Client#getId()
547         * @see #addBan(String, String, String, long, String)
548         */
549        public CommandFuture<Integer[]> banClient(int clientId, String reason) {
550                return banClient(clientId, 0, reason);
551        }
552
553        /**
554         * Sends a text message to all clients on all virtual servers.
555         * These messages will appear to clients in the tab for server messages.
556         *
557         * @param message
558         *              the message to be sent
559         *
560         * @return whether the command succeeded or not
561         *
562         * @querycommands 1
563         */
564        public CommandFuture<Boolean> broadcast(String message) {
565                final CGM broadcast = new CGM(message);
566                return executeAndReturnError(broadcast);
567        }
568
569        /**
570         * Creates a copy of the channel group specified by {@code sourceGroupId},
571         * overwriting any other channel group specified by {@code targetGroupId}.
572         * <p>
573         * The parameter {@code type} can be used to create server query and template groups.
574         * </p>
575         *
576         * @param sourceGroupId
577         *              the ID of the channel group to copy
578         * @param targetGroupId
579         *              the ID of another channel group to overwrite
580         * @param type
581         *              the desired type of channel group
582         *
583         * @return whether the command succeeded or not
584         *
585         * @querycommands 1
586         * @see ChannelGroup#getId()
587         */
588        public CommandFuture<Boolean> copyChannelGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) {
589                if (targetGroupId <= 0) {
590                        throw new IllegalArgumentException("To create a new channel group, use the method with a String argument");
591                }
592
593                final CChannelGroupCopy copy = new CChannelGroupCopy(sourceGroupId, targetGroupId, type);
594                return executeAndReturnError(copy);
595        }
596
597        /**
598         * Creates a copy of the channel group specified by {@code sourceGroupId} with a given name
599         * and returns the ID of the newly created channel group.
600         *
601         * @param sourceGroupId
602         *              the ID of the channel group to copy
603         * @param targetName
604         *              the name for the copy of the channel group
605         * @param type
606         *              the desired type of channel group
607         *
608         * @return the ID of the newly created channel group
609         *
610         * @querycommands 1
611         * @see ChannelGroup#getId()
612         */
613        public CommandFuture<Integer> copyChannelGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) {
614                final CChannelGroupCopy copy = new CChannelGroupCopy(sourceGroupId, targetName, type);
615                return executeAndReturnIntProperty(copy, "cgid");
616        }
617
618        /**
619         * Creates a copy of the server group specified by {@code sourceGroupId},
620         * overwriting another server group specified by {@code targetGroupId}.
621         * <p>
622         * The parameter {@code type} can be used to create server query and template groups.
623         * </p>
624         *
625         * @param sourceGroupId
626         *              the ID of the server group to copy
627         * @param targetGroupId
628         *              the ID of another server group to overwrite
629         * @param type
630         *              the desired type of server group
631         *
632         * @return whether the command succeeded or not
633         *
634         * @querycommands 1
635         * @see ServerGroup#getId()
636         */
637        public CommandFuture<Integer> copyServerGroup(int sourceGroupId, int targetGroupId, PermissionGroupDatabaseType type) {
638                if (targetGroupId <= 0) {
639                        throw new IllegalArgumentException("To create a new server group, use the method with a String argument");
640                }
641
642                final CServerGroupCopy copy = new CServerGroupCopy(sourceGroupId, targetGroupId, type);
643                return executeAndReturnIntProperty(copy, "sgid");
644        }
645
646        /**
647         * Creates a copy of the server group specified by {@code sourceGroupId} with a given name
648         * and returns the ID of the newly created server group.
649         *
650         * @param sourceGroupId
651         *              the ID of the server group to copy
652         * @param targetName
653         *              the name for the copy of the server group
654         * @param type
655         *              the desired type of server group
656         *
657         * @return the ID of the newly created server group
658         *
659         * @querycommands 1
660         * @see ServerGroup#getId()
661         */
662        public CommandFuture<Integer> copyServerGroup(int sourceGroupId, String targetName, PermissionGroupDatabaseType type) {
663                final CServerGroupCopy copy = new CServerGroupCopy(sourceGroupId, targetName, type);
664                return executeAndReturnIntProperty(copy, "sgid");
665        }
666
667        /**
668         * Creates a new channel with a given name using the given properties and returns its ID.
669         *
670         * @param name
671         *              the name for the new channel
672         * @param options
673         *              a map of options that should be set for the channel
674         *
675         * @return the ID of the newly created channel
676         *
677         * @querycommands 1
678         * @see Channel
679         */
680        public CommandFuture<Integer> createChannel(String name, Map<ChannelProperty, String> options) {
681                final CChannelCreate create = new CChannelCreate(name, options);
682                return executeAndReturnIntProperty(create, "cid");
683        }
684
685        /**
686         * Creates a new virtual server with the given name and returns an object containing the ID of the newly
687         * created virtual server, the default server admin token and the virtual server's voice port. Usually,
688         * the virtual server is also automatically started. This can be turned off on the TS3 server, though.
689         * <p>
690         * If {@link VirtualServerProperty#VIRTUALSERVER_PORT} is not specified in the virtual server properties,
691         * the server will test for the first unused UDP port.
692         * </p><p>
693         * Please also note that creating virtual servers usually requires the server query admin account
694         * and that there is a limit to how many virtual servers can be created, which is dependent on your license.
695         * Unlicensed TS3 server instances are limited to 1 virtual server with up to 32 client slots.
696         * </p>
697         *
698         * @param name
699         *              the name for the new virtual server
700         * @param options
701         *              a map of options that should be set for the virtual server
702         *
703         * @return information about the newly created virtual server
704         *
705         * @querycommands 1
706         * @see VirtualServer
707         */
708        public CommandFuture<CreatedVirtualServer> createServer(String name, Map<VirtualServerProperty, String> options) {
709                final CServerCreate create = new CServerCreate(name, options);
710                final CommandFuture<CreatedVirtualServer> future = new CommandFuture<>();
711
712                query.doCommandAsync(create, new Callback() {
713                        @Override
714                        public void handle() {
715                                if (hasFailed(create, future)) return;
716                                future.set(new CreatedVirtualServer(create.getFirstResponse().getMap()));
717                        }
718                });
719                return future;
720        }
721
722        /**
723         * Creates a {@link Snapshot} of the selected virtual server containing all settings,
724         * groups and known client identities. The data from a server snapshot can be
725         * used to restore a virtual servers configuration.
726         *
727         * @return a snapshot of the virtual server
728         *
729         * @querycommands 1
730         * @see #deployServerSnapshot(Snapshot)
731         */
732        public CommandFuture<Snapshot> createServerSnapshot() {
733                final CServerSnapshotCreate create = new CServerSnapshotCreate();
734                final CommandFuture<Snapshot> future = new CommandFuture<>();
735
736                query.doCommandAsync(create, new Callback() {
737                        @Override
738                        public void handle() {
739                                if (hasFailed(create, future)) return;
740                                future.set(new Snapshot(create.getRaw()));
741                        }
742                });
743                return future;
744        }
745
746        /**
747         * Deletes all active ban rules from the server. Use with caution.
748         *
749         * @return whether the command succeeded or not
750         *
751         * @querycommands 1
752         */
753        public CommandFuture<Boolean> deleteAllBans() {
754                final CBanDelAll del = new CBanDelAll();
755                return executeAndReturnError(del);
756        }
757
758        /**
759         * Deletes all complaints about the client with specified database ID from the server.
760         *
761         * @param clientDBId
762         *              the database ID of the client
763         *
764         * @return whether the command succeeded or not
765         *
766         * @querycommands 1
767         * @see Client#getDatabaseId()
768         * @see Complaint
769         */
770        public CommandFuture<Boolean> deleteAllComplaints(int clientDBId) {
771                final CComplainDelAll del = new CComplainDelAll(clientDBId);
772                return executeAndReturnError(del);
773        }
774
775        /**
776         * Deletes the ban rule with the specified ID from the server.
777         *
778         * @param banId
779         *              the ID of the ban to delete
780         *
781         * @return whether the command succeeded or not
782         *
783         * @querycommands 1
784         * @see Ban#getId()
785         */
786        public CommandFuture<Boolean> deleteBan(int banId) {
787                final CBanDel del = new CBanDel(banId);
788                return executeAndReturnError(del);
789        }
790
791        /**
792         * Deletes an existing channel specified by its ID, kicking all clients out of the channel.
793         *
794         * @param channelId
795         *              the ID of the channel to delete
796         *
797         * @return whether the command succeeded or not
798         *
799         * @querycommands 1
800         * @see Channel#getId()
801         * @see #deleteChannel(int, boolean)
802         * @see #kickClientFromChannel(String, int...)
803         */
804        public CommandFuture<Boolean> deleteChannel(int channelId) {
805                return deleteChannel(channelId, true);
806        }
807
808        /**
809         * Deletes an existing channel with a given ID.
810         * If {@code force} is true, the channel will be deleted even if there are clients within,
811         * else the command will fail in this situation.
812         *
813         * @param channelId
814         *              the ID of the channel to delete
815         * @param force
816         *              whether clients should be kicked out of the channel
817         *
818         * @return whether the command succeeded or not
819         *
820         * @querycommands 1
821         * @see Channel#getId()
822         * @see #kickClientFromChannel(String, int...)
823         */
824        public CommandFuture<Boolean> deleteChannel(int channelId, boolean force) {
825                final CChannelDelete del = new CChannelDelete(channelId, force);
826                return executeAndReturnError(del);
827        }
828
829        /**
830         * Removes a specified permission from a client in a specific channel.
831         *
832         * @param channelId
833         *              the ID of the channel wherein the permission should be removed
834         * @param clientDBId
835         *              the database ID of the client
836         * @param permName
837         *              the name of the permission to revoke
838         *
839         * @return whether the command succeeded or not
840         *
841         * @querycommands 1
842         * @see Channel#getId()
843         * @see Client#getDatabaseId()
844         * @see Permission#getName()
845         */
846        public CommandFuture<Boolean> deleteChannelClientPermission(int channelId, int clientDBId, String permName) {
847                final CChannelClientDelPerm del = new CChannelClientDelPerm(channelId, clientDBId, permName);
848                return executeAndReturnError(del);
849        }
850
851        /**
852         * Removes the channel group with the given ID.
853         *
854         * @param groupId
855         *              the ID of the channel group
856         *
857         * @return whether the command succeeded or not
858         *
859         * @querycommands 1
860         * @see ChannelGroup#getId()
861         */
862        public CommandFuture<Boolean> deleteChannelGroup(int groupId) {
863                return deleteChannelGroup(groupId, true);
864        }
865
866        /**
867         * Removes the channel group with the given ID.
868         * If {@code force} is true, the channel group will be deleted even if it still contains clients,
869         * else the command will fail in this situation.
870         *
871         * @param groupId
872         *              the ID of the channel group
873         * @param force
874         *              whether the channel group should be deleted even if it still contains clients
875         *
876         * @return whether the command succeeded or not
877         *
878         * @querycommands 1
879         * @see ChannelGroup#getId()
880         */
881        public CommandFuture<Boolean> deleteChannelGroup(int groupId, boolean force) {
882                final CChannelGroupDel del = new CChannelGroupDel(groupId, force);
883                return executeAndReturnError(del);
884        }
885
886        /**
887         * Removes a permission from the channel group with the given ID.
888         *
889         * @param groupId
890         *              the ID of the channel group
891         * @param permName
892         *              the name of the permission to revoke
893         *
894         * @return whether the command succeeded or not
895         *
896         * @querycommands 1
897         * @see ChannelGroup#getId()
898         * @see Permission#getName()
899         */
900        public CommandFuture<Boolean> deleteChannelGroupPermission(int groupId, String permName) {
901                final CChannelGroupDelPerm del = new CChannelGroupDelPerm(groupId, permName);
902                return executeAndReturnError(del);
903        }
904
905        /**
906         * Removes a permission from the channel with the given ID.
907         *
908         * @param channelId
909         *              the ID of the channel
910         * @param permName
911         *              the name of the permission to revoke
912         *
913         * @return whether the command succeeded or not
914         *
915         * @querycommands 1
916         * @see Channel#getId()
917         * @see Permission#getName()
918         */
919        public CommandFuture<Boolean> deleteChannelPermission(int channelId, String permName) {
920                final CChannelDelPerm del = new CChannelDelPerm(channelId, permName);
921                return executeAndReturnError(del);
922        }
923
924        /**
925         * Removes a permission from a client.
926         *
927         * @param clientDBId
928         *              the database ID of the client
929         * @param permName
930         *              the name of the permission to revoke
931         *
932         * @return whether the command succeeded or not
933         *
934         * @querycommands 1
935         * @see Client#getDatabaseId()
936         * @see Permission#getName()
937         */
938        public CommandFuture<Boolean> deleteClientPermission(int clientDBId, String permName) {
939                final CClientDelPerm del = new CClientDelPerm(clientDBId, permName);
940                return executeAndReturnError(del);
941        }
942
943        /**
944         * Deletes the complaint about the client with database ID {@code targetClientDBId} submitted by
945         * the client with database ID {@code fromClientDBId} from the server.
946         *
947         * @param targetClientDBId
948         *              the database ID of the client the complaint is about
949         * @param fromClientDBId
950         *              the database ID of the client who added the complaint
951         *
952         * @return whether the command succeeded or not
953         *
954         * @querycommands 1
955         * @see Complaint
956         * @see Client#getDatabaseId()
957         */
958        public CommandFuture<Boolean> deleteComplaint(int targetClientDBId, int fromClientDBId) {
959                final CComplainDel del = new CComplainDel(targetClientDBId, fromClientDBId);
960                return executeAndReturnError(del);
961        }
962
963        /**
964         * Removes all stored database information about the specified client.
965         * Please note that this data is also automatically removed after a configured time (usually 90 days).
966         * <p>
967         * See {@link DatabaseClientInfo} for a list of stored information about a client.
968         * </p>
969         *
970         * @param clientDBId
971         *              the database ID of the client
972         *
973         * @return whether the command succeeded or not
974         *
975         * @querycommands 1
976         * @see Client#getDatabaseId()
977         * @see #getDatabaseClientInfo(int)
978         * @see DatabaseClientInfo
979         */
980        public CommandFuture<Boolean> deleteDatabaseClientProperties(int clientDBId) {
981                final CClientDBDelete del = new CClientDBDelete(clientDBId);
982                return executeAndReturnError(del);
983        }
984
985        /**
986         * Deletes the offline message with the specified ID.
987         *
988         * @param messageId
989         *              the ID of the offline message to delete
990         *
991         * @return whether the command succeeded or not
992         *
993         * @querycommands 1
994         * @see Message#getId()
995         */
996        public CommandFuture<Boolean> deleteOfflineMessage(int messageId) {
997                final CMessageDel del = new CMessageDel(messageId);
998                return executeAndReturnError(del);
999        }
1000
1001        /**
1002         * Removes a specified permission from all server groups of the type specified by {@code type} on all virtual servers.
1003         *
1004         * @param type
1005         *              the kind of server group this permission should be removed from
1006         * @param permName
1007         *              the name of the permission to remove
1008         *
1009         * @return whether the command succeeded or not
1010         *
1011         * @querycommands 1
1012         * @see ServerGroupType
1013         * @see Permission#getName()
1014         */
1015        public CommandFuture<Boolean> deletePermissionFromAllServerGroups(ServerGroupType type, String permName) {
1016                final CServerGroupAutoDelPerm del = new CServerGroupAutoDelPerm(type, permName);
1017                return executeAndReturnError(del);
1018        }
1019
1020        /**
1021         * Deletes the privilege key with the given token.
1022         *
1023         * @param token
1024         *              the token of the privilege key
1025         *
1026         * @return whether the command succeeded or not
1027         *
1028         * @querycommands 1
1029         * @see PrivilegeKey
1030         */
1031        public CommandFuture<Boolean> deletePrivilegeKey(String token) {
1032                final CPrivilegeKeyDelete del = new CPrivilegeKeyDelete(token);
1033                return executeAndReturnError(del);
1034        }
1035
1036        /**
1037         * Deletes the virtual server with the specified ID.
1038         * <p>
1039         * Only stopped virtual servers can be deleted.
1040         * </p>
1041         *
1042         * @param serverId
1043         *              the ID of the virtual server
1044         *
1045         * @return whether the command succeeded or not
1046         *
1047         * @querycommands 1
1048         * @see VirtualServer#getId()
1049         * @see #stopServer(int)
1050         */
1051        public CommandFuture<Boolean> deleteServer(int serverId) {
1052                final CServerDelete delete = new CServerDelete(serverId);
1053                return executeAndReturnError(delete);
1054        }
1055
1056        /**
1057         * Deletes the server group with the specified ID, even if the server group still contains clients.
1058         *
1059         * @param groupId
1060         *              the ID of the server group
1061         *
1062         * @return whether the command succeeded or not
1063         *
1064         * @querycommands 1
1065         * @see ServerGroup#getId()
1066         */
1067        public CommandFuture<Boolean> deleteServerGroup(int groupId) {
1068                return deleteServerGroup(groupId, true);
1069        }
1070
1071        /**
1072         * Deletes a server group with the specified ID.
1073         * <p>
1074         * If {@code force} is true, the server group will be deleted even if it contains clients,
1075         * else the command will fail in this situation.
1076         * </p>
1077         *
1078         * @param groupId
1079         *              the ID of the server group
1080         * @param force
1081         *              whether the server group should be deleted if it still contains clients
1082         *
1083         * @return whether the command succeeded or not
1084         *
1085         * @querycommands 1
1086         * @see ServerGroup#getId()
1087         */
1088        public CommandFuture<Boolean> deleteServerGroup(int groupId, boolean force) {
1089                final CServerGroupDel del = new CServerGroupDel(groupId, force);
1090                return executeAndReturnError(del);
1091        }
1092
1093        /**
1094         * Removes a permission from the server group with the given ID.
1095         *
1096         * @param groupId
1097         *              the ID of the server group
1098         * @param permName
1099         *              the name of the permission to revoke
1100         *
1101         * @return whether the command succeeded or not
1102         *
1103         * @querycommands 1
1104         * @see ServerGroup#getId()
1105         * @see Permission#getName()
1106         */
1107        public CommandFuture<Boolean> deleteServerGroupPermission(int groupId, String permName) {
1108                final CServerGroupDelPerm del = new CServerGroupDelPerm(groupId, permName);
1109                return executeAndReturnError(del);
1110        }
1111
1112        /**
1113         * Restores the selected virtual servers configuration using the data from a
1114         * previously created server snapshot.
1115         *
1116         * @param snapshot
1117         *              the snapshot to restore
1118         *
1119         * @return whether the command succeeded or not
1120         *
1121         * @querycommands 1
1122         * @see #createServerSnapshot()
1123         */
1124        public CommandFuture<Boolean> deployServerSnapshot(Snapshot snapshot) {
1125                return deployServerSnapshot(snapshot.get());
1126        }
1127
1128        /**
1129         * Restores the configuration of the selected virtual server using the data from a
1130         * previously created server snapshot.
1131         *
1132         * @param snapshot
1133         *              the snapshot to restore
1134         *
1135         * @return whether the command succeeded or not
1136         *
1137         * @querycommands 1
1138         * @see #createServerSnapshot()
1139         */
1140        public CommandFuture<Boolean> deployServerSnapshot(String snapshot) {
1141                final CServerSnapshotDeploy deploy = new CServerSnapshotDeploy(snapshot);
1142                return executeAndReturnError(deploy);
1143        }
1144
1145        /**
1146         * Changes a channel's configuration using the given properties.
1147         *
1148         * @param channelId
1149         *              the ID of the channel to edit
1150         * @param options
1151         *              the map of properties to modify
1152         *
1153         * @return whether the command succeeded or not
1154         *
1155         * @querycommands 1
1156         * @see Channel#getId()
1157         */
1158        public CommandFuture<Boolean> editChannel(int channelId, Map<ChannelProperty, String> options) {
1159                final CChannelEdit edit = new CChannelEdit(channelId, options);
1160                return executeAndReturnError(edit);
1161        }
1162
1163        /**
1164         * Changes a client's configuration using given properties.
1165         * <p>
1166         * Only {@link ClientProperty#CLIENT_DESCRIPTION} can be changed for other clients.
1167         * To update the current client's properties, use {@link #updateClient(Map)}.
1168         * </p>
1169         *
1170         * @param clientId
1171         *              the ID of the client to edit
1172         * @param options
1173         *              the map of properties to modify
1174         *
1175         * @return whether the command succeeded or not
1176         *
1177         * @querycommands 1
1178         * @see Client#getId()
1179         * @see #updateClient(Map)
1180         */
1181        public CommandFuture<Boolean> editClient(int clientId, Map<ClientProperty, String> options) {
1182                final CClientEdit edit = new CClientEdit(clientId, options);
1183                return executeAndReturnError(edit);
1184        }
1185
1186        /**
1187         * Changes a client's database settings using given properties.
1188         *
1189         * @param clientDBId
1190         *              the database ID of the client to edit
1191         * @param options
1192         *              the map of properties to modify
1193         *
1194         * @return whether the command succeeded or not
1195         *
1196         * @querycommands 1
1197         * @see DatabaseClientInfo
1198         * @see Client#getDatabaseId()
1199         */
1200        public CommandFuture<Boolean> editDatabaseClient(int clientDBId, Map<ClientProperty, String> options) {
1201                final CClientDBEdit edit = new CClientDBEdit(clientDBId, options);
1202                return executeAndReturnError(edit);
1203        }
1204
1205        /**
1206         * Changes the server instance configuration using given properties.
1207         * If the given property is not changeable, {@code IllegalArgumentException} will be thrown.
1208         *
1209         * @param property
1210         *              the property to edit, must be changeable
1211         * @param value
1212         *              the new value for the edit
1213         *
1214         * @return whether the command succeeded or not
1215         *
1216         * @throws IllegalArgumentException
1217         *              if {@code property} is not changeable
1218         * @querycommands 1
1219         * @see ServerInstanceProperty#isChangeable()
1220         */
1221        public CommandFuture<Boolean> editInstance(ServerInstanceProperty property, String value) {
1222                if (!property.isChangeable()) {
1223                        throw new IllegalArgumentException("Property is not changeable");
1224                }
1225
1226                final CInstanceEdit edit = new CInstanceEdit(property, value);
1227                return executeAndReturnError(edit);
1228        }
1229
1230        /**
1231         * Changes the configuration of the selected virtual server using given properties.
1232         *
1233         * @param options
1234         *              the map of properties to edit
1235         *
1236         * @return whether the command succeeded or not
1237         *
1238         * @querycommands 1
1239         * @see VirtualServerProperty
1240         */
1241        public CommandFuture<Boolean> editServer(Map<VirtualServerProperty, String> options) {
1242                final CServerEdit edit = new CServerEdit(options);
1243                return executeAndReturnError(edit);
1244        }
1245
1246        /**
1247         * Gets a list of all bans on the selected virtual server.
1248         *
1249         * @return a list of all bans on the virtual server
1250         *
1251         * @querycommands 1
1252         * @see Ban
1253         */
1254        public CommandFuture<List<Ban>> getBans() {
1255                final CBanList list = new CBanList();
1256                final CommandFuture<List<Ban>> future = new CommandFuture<>();
1257
1258                query.doCommandAsync(list, new Callback() {
1259                        @Override
1260                        public void handle() {
1261                                if (hasFailed(list, future)) return;
1262
1263                                final List<Wrapper> responses = list.getResponse();
1264                                final List<Ban> bans = new ArrayList<>(responses.size());
1265
1266                                for (final Wrapper response : responses) {
1267                                        bans.add(new Ban(response.getMap()));
1268                                }
1269                                future.set(bans);
1270                        }
1271                });
1272                return future;
1273        }
1274
1275        /**
1276         * Gets a list of IP addresses used by the server instance.
1277         *
1278         * @return the list of bound IP addresses
1279         *
1280         * @querycommands 1
1281         * @see Binding
1282         */
1283        public CommandFuture<List<Binding>> getBindings() {
1284                final CBindingList list = new CBindingList();
1285                final CommandFuture<List<Binding>> future = new CommandFuture<>();
1286
1287                query.doCommandAsync(list, new Callback() {
1288                        @Override
1289                        public void handle() {
1290                                if (hasFailed(list, future)) return;
1291
1292                                final List<Wrapper> responses = list.getResponse();
1293                                final List<Binding> bindings = new ArrayList<>(responses.size());
1294
1295                                for (final Wrapper response : responses) {
1296                                        bindings.add(new Binding(response.getMap()));
1297                                }
1298                                future.set(bindings);
1299                        }
1300                });
1301                return future;
1302        }
1303
1304        /**
1305         * Finds and returns the channel matching the given name exactly.
1306         *
1307         * @param name
1308         *              the name of the channel
1309         * @param ignoreCase
1310         *              whether the case of the name should be ignored
1311         *
1312         * @return the found channel or {@code null} if no channel was found
1313         *
1314         * @querycommands 1
1315         * @see Channel
1316         * @see #getChannelsByName(String)
1317         */
1318        public CommandFuture<Channel> getChannelByNameExact(String name, final boolean ignoreCase) {
1319                final CommandFuture<Channel> future = new CommandFuture<>();
1320                final String caseName = ignoreCase ? name.toLowerCase() : name;
1321
1322                getChannels().onSuccess(new CommandFuture.SuccessListener<List<Channel>>() {
1323                        @Override
1324                        public void handleSuccess(final List<Channel> allChannels) {
1325                                for (final Channel c : allChannels) {
1326                                        final String channelName = ignoreCase ? c.getName().toLowerCase() : c.getName();
1327                                        if (caseName.equals(channelName)) {
1328                                                future.set(c);
1329                                                return;
1330                                        }
1331                                }
1332                                future.set(null); // Not found
1333                        }
1334                }).forwardFailure(future);
1335                return future;
1336        }
1337
1338        /**
1339         * Gets a list of channels whose names contain the given search string.
1340         *
1341         * @param name
1342         *              the name to search
1343         *
1344         * @return a list of all channels with names matching the search pattern
1345         *
1346         * @querycommands 2
1347         * @see Channel
1348         * @see #getChannelByNameExact(String, boolean)
1349         */
1350        public CommandFuture<List<Channel>> getChannelsByName(String name) {
1351                final CChannelFind find = new CChannelFind(name);
1352                final CommandFuture<List<Channel>> future = new CommandFuture<>();
1353
1354                getChannels().onSuccess(new CommandFuture.SuccessListener<List<Channel>>() {
1355                        @Override
1356                        public void handleSuccess(final List<Channel> allChannels) {
1357                                query.doCommandAsync(find, new Callback() {
1358                                        @Override
1359                                        public void handle() {
1360                                                if (hasFailed(find, future)) return;
1361
1362                                                final List<Wrapper> responses = find.getResponse();
1363                                                final List<Channel> channels = new ArrayList<>(responses.size());
1364
1365                                                for (final Wrapper response : responses) {
1366                                                        final int channelId = response.getInt("cid");
1367                                                        for (final Channel c : allChannels) {
1368                                                                if (c.getId() == channelId) {
1369                                                                        channels.add(c);
1370                                                                        break;
1371                                                                }
1372                                                        }
1373                                                }
1374                                                future.set(channels);
1375                                        }
1376                                });
1377                        }
1378                }).forwardFailure(future);
1379                return future;
1380        }
1381
1382        /**
1383         * Displays a list of permissions defined for a client in a specific channel.
1384         *
1385         * @param channelId
1386         *              the ID of the channel
1387         * @param clientDBId
1388         *              the database ID of the client
1389         *
1390         * @return a list of permissions for the user in the specified channel
1391         *
1392         * @querycommands 1
1393         * @see Channel#getId()
1394         * @see Client#getDatabaseId()
1395         * @see Permission
1396         */
1397        public CommandFuture<List<Permission>> getChannelClientPermissions(int channelId, int clientDBId) {
1398                final CChannelClientPermList list = new CChannelClientPermList(channelId, clientDBId);
1399                final CommandFuture<List<Permission>> future = new CommandFuture<>();
1400
1401                query.doCommandAsync(list, new Callback() {
1402                        @Override
1403                        public void handle() {
1404                                if (hasFailed(list, future)) return;
1405
1406                                final List<Wrapper> responses = list.getResponse();
1407                                final List<Permission> permissions = new ArrayList<>(responses.size());
1408
1409                                for (final Wrapper response : responses) {
1410                                        permissions.add(new Permission(response.getMap()));
1411                                }
1412                                future.set(permissions);
1413                        }
1414                });
1415                return future;
1416        }
1417
1418        /**
1419         * Gets all client / channel ID combinations currently assigned to channel groups.
1420         * All three parameters are optional and can be turned off by setting it to {@code -1}.
1421         *
1422         * @param channelId
1423         *              restricts the search to the channel with a specified ID. Set to {@code -1} to ignore.
1424         * @param clientDBId
1425         *              restricts the search to the client with a specified database ID. Set to {@code -1} to ignore.
1426         * @param groupId
1427         *              restricts the search to the channel group with the specified ID. Set to {@code -1} to ignore.
1428         *
1429         * @return a list of combinations of channel ID, client database ID and channel group ID
1430         *
1431         * @querycommands 1
1432         * @see Channel#getId()
1433         * @see Client#getDatabaseId()
1434         * @see ChannelGroup#getId()
1435         * @see ChannelGroupClient
1436         */
1437        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClients(int channelId, int clientDBId, int groupId) {
1438                final CChannelGroupClientList list = new CChannelGroupClientList(channelId, clientDBId, groupId);
1439                final CommandFuture<List<ChannelGroupClient>> future = new CommandFuture<>();
1440
1441                query.doCommandAsync(list, new Callback() {
1442                        @Override
1443                        public void handle() {
1444                                if (hasFailed(list, future)) return;
1445
1446                                final List<Wrapper> responses = list.getResponse();
1447                                final List<ChannelGroupClient> clients = new ArrayList<>(responses.size());
1448
1449                                for (final Wrapper response : responses) {
1450                                        clients.add(new ChannelGroupClient(response.getMap()));
1451                                }
1452                                future.set(clients);
1453                        }
1454                });
1455                return future;
1456        }
1457
1458        /**
1459         * Gets all client / channel ID combinations currently assigned to the specified channel group.
1460         *
1461         * @param groupId
1462         *              the ID of the channel group whose client / channel assignments should be returned.
1463         *
1464         * @return a list of combinations of channel ID, client database ID and channel group ID
1465         *
1466         * @querycommands 1
1467         * @see ChannelGroup#getId()
1468         * @see ChannelGroupClient
1469         * @see #getChannelGroupClients(int, int, int)
1470         */
1471        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClientsByChannelGroupId(int groupId) {
1472                return getChannelGroupClients(-1, -1, groupId);
1473        }
1474
1475        /**
1476         * Gets all channel group assignments in the specified channel.
1477         *
1478         * @param channelId
1479         *              the ID of the channel whose channel group assignments should be returned.
1480         *
1481         * @return a list of combinations of channel ID, client database ID and channel group ID
1482         *
1483         * @querycommands 1
1484         * @see Channel#getId()
1485         * @see ChannelGroupClient
1486         * @see #getChannelGroupClients(int, int, int)
1487         */
1488        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClientsByChannelId(int channelId) {
1489                return getChannelGroupClients(channelId, -1, -1);
1490        }
1491
1492        /**
1493         * Gets all channel group assignments for the specified client.
1494         *
1495         * @param clientDBId
1496         *              the database ID of the client whose channel group
1497         *
1498         * @return a list of combinations of channel ID, client database ID and channel group ID
1499         *
1500         * @querycommands 1
1501         * @see Client#getDatabaseId()
1502         * @see ChannelGroupClient
1503         * @see #getChannelGroupClients(int, int, int)
1504         */
1505        public CommandFuture<List<ChannelGroupClient>> getChannelGroupClientsByClientDBId(int clientDBId) {
1506                return getChannelGroupClients(-1, clientDBId, -1);
1507        }
1508
1509        /**
1510         * Gets a list of all permissions assigned to the specified channel group.
1511         *
1512         * @param groupId
1513         *              the ID of the channel group.
1514         *
1515         * @return a list of permissions assigned to the channel group
1516         *
1517         * @querycommands 1
1518         * @see ChannelGroup#getId()
1519         * @see Permission
1520         */
1521        public CommandFuture<List<Permission>> getChannelGroupPermissions(int groupId) {
1522                final CChannelGroupPermList list = new CChannelGroupPermList(groupId);
1523                final CommandFuture<List<Permission>> future = new CommandFuture<>();
1524
1525                query.doCommandAsync(list, new Callback() {
1526                        @Override
1527                        public void handle() {
1528                                if (hasFailed(list, future)) return;
1529
1530                                final List<Wrapper> responses = list.getResponse();
1531                                final List<Permission> permissions = new ArrayList<>(responses.size());
1532
1533                                for (final Wrapper response : responses) {
1534                                        permissions.add(new Permission(response.getMap()));
1535                                }
1536                                future.set(permissions);
1537                        }
1538                });
1539                return future;
1540        }
1541
1542        /**
1543         * Gets a list of all channel groups on the selected virtual server.
1544         *
1545         * @return a list of all channel groups on the virtual server
1546         *
1547         * @querycommands 1
1548         * @see ChannelGroup
1549         */
1550        public CommandFuture<List<ChannelGroup>> getChannelGroups() {
1551                final CChannelGroupList list = new CChannelGroupList();
1552                final CommandFuture<List<ChannelGroup>> future = new CommandFuture<>();
1553
1554                query.doCommandAsync(list, new Callback() {
1555                        @Override
1556                        public void handle() {
1557                                if (hasFailed(list, future)) return;
1558
1559                                final List<Wrapper> responses = list.getResponse();
1560                                final List<ChannelGroup> groups = new ArrayList<>(responses.size());
1561
1562                                for (final Wrapper response : responses) {
1563                                        groups.add(new ChannelGroup(response.getMap()));
1564                                }
1565                                future.set(groups);
1566                        }
1567                });
1568                return future;
1569        }
1570
1571        /**
1572         * Gets detailed configuration information about the channel specified channel.
1573         *
1574         * @param channelId
1575         *              the ID of the channel
1576         *
1577         * @return information about the channel
1578         *
1579         * @querycommands 1
1580         * @see Channel#getId()
1581         * @see ChannelInfo
1582         */
1583        public CommandFuture<ChannelInfo> getChannelInfo(final int channelId) {
1584                final CChannelInfo info = new CChannelInfo(channelId);
1585                final CommandFuture<ChannelInfo> future = new CommandFuture<>();
1586
1587                query.doCommandAsync(info, new Callback() {
1588                        @Override
1589                        public void handle() {
1590                                if (hasFailed(info, future)) return;
1591                                future.set(new ChannelInfo(channelId, info.getFirstResponse().getMap()));
1592                        }
1593                });
1594                return future;
1595        }
1596
1597        /**
1598         * Gets a list of all permissions assigned to the specified channel.
1599         *
1600         * @param channelId
1601         *              the ID of the channel
1602         *
1603         * @return a list of all permissions assigned to the channel
1604         *
1605         * @querycommands 1
1606         * @see Channel#getId()
1607         * @see Permission
1608         */
1609        public CommandFuture<List<Permission>> getChannelPermissions(int channelId) {
1610                final CChannelPermList list = new CChannelPermList(channelId);
1611                final CommandFuture<List<Permission>> future = new CommandFuture<>();
1612
1613                query.doCommandAsync(list, new Callback() {
1614                        @Override
1615                        public void handle() {
1616                                if (hasFailed(list, future)) return;
1617
1618                                final List<Wrapper> responses = list.getResponse();
1619                                final List<Permission> permissions = new ArrayList<>(responses.size());
1620
1621                                for (final Wrapper response : responses) {
1622                                        permissions.add(new Permission(response.getMap()));
1623                                }
1624                                future.set(permissions);
1625                        }
1626                });
1627                return future;
1628        }
1629
1630        /**
1631         * Gets a list of all channels on the selected virtual server.
1632         *
1633         * @return a list of all channels on the virtual server
1634         *
1635         * @querycommands 1
1636         * @see Channel
1637         */
1638        public CommandFuture<List<Channel>> getChannels() {
1639                final CChannelList list = new CChannelList();
1640                final CommandFuture<List<Channel>> future = new CommandFuture<>();
1641
1642                query.doCommandAsync(list, new Callback() {
1643                        @Override
1644                        public void handle() {
1645                                if (hasFailed(list, future)) return;
1646
1647                                final List<Wrapper> responses = list.getResponse();
1648                                final List<Channel> channels = new ArrayList<>(responses.size());
1649
1650                                for (final Wrapper response : responses) {
1651                                        channels.add(new Channel(response.getMap()));
1652                                }
1653                                future.set(channels);
1654                        }
1655                });
1656                return future;
1657        }
1658
1659        /**
1660         * Finds and returns the client whose nickname matches the given name exactly.
1661         *
1662         * @param name
1663         *              the name of the client
1664         * @param ignoreCase
1665         *              whether the case of the name should be ignored
1666         *
1667         * @return the found client or {@code null} if no client was found
1668         *
1669         * @querycommands 1
1670         * @see Client
1671         * @see #getClientsByName(String)
1672         */
1673        public CommandFuture<Client> getClientByNameExact(String name, final boolean ignoreCase) {
1674                final CommandFuture<Client> future = new CommandFuture<>();
1675                final String caseName = ignoreCase ? name.toLowerCase() : name;
1676
1677                getClients().onSuccess(new CommandFuture.SuccessListener<List<Client>>() {
1678                        @Override
1679                        public void handleSuccess(final List<Client> allClients) {
1680                                for (final Client c : allClients) {
1681                                        final String clientName = ignoreCase ? c.getNickname().toLowerCase() : c.getNickname();
1682                                        if (caseName.equals(clientName)) {
1683                                                future.set(c);
1684                                                return;
1685                                        }
1686                                }
1687                                future.set(null); // Not found
1688                        }
1689                }).forwardFailure(future);
1690                return future;
1691        }
1692
1693        /**
1694         * Gets a list of clients whose nicknames contain the given search string.
1695         *
1696         * @param name
1697         *              the name to search
1698         *
1699         * @return a list of all clients with nicknames matching the search pattern
1700         *
1701         * @querycommands 2
1702         * @see Client
1703         * @see #getClientByNameExact(String, boolean)
1704         */
1705        public CommandFuture<List<Client>> getClientsByName(String name) {
1706                final CClientFind find = new CClientFind(name);
1707                final CommandFuture<List<Client>> future = new CommandFuture<>();
1708
1709                getClients().onSuccess(new CommandFuture.SuccessListener<List<Client>>() {
1710                        @Override
1711                        public void handleSuccess(final List<Client> allClients) {
1712                                query.doCommandAsync(find, new Callback() {
1713                                        @Override
1714                                        public void handle() {
1715                                                if (hasFailed(find, future)) return;
1716
1717                                                final List<Wrapper> responses = find.getResponse();
1718                                                final List<Client> clients = new ArrayList<>(responses.size());
1719
1720                                                for (final Wrapper response : responses) {
1721                                                        for (final Client c : allClients) {
1722                                                                if (c.getId() == response.getInt("clid")) {
1723                                                                        clients.add(c);
1724                                                                        break;
1725                                                                }
1726                                                        }
1727                                                }
1728                                                future.set(clients);
1729                                        }
1730                                });
1731                        }
1732                }).forwardFailure(future);
1733                return future;
1734        }
1735
1736        /**
1737         * Gets information about the client with the specified unique identifier.
1738         *
1739         * @param clientUId
1740         *              the unique identifier of the client
1741         *
1742         * @return the client or {@code null} if no client was found
1743         *
1744         * @querycommands 2
1745         * @see Client#getUniqueIdentifier()
1746         * @see ClientInfo
1747         */
1748        public CommandFuture<ClientInfo> getClientByUId(String clientUId) {
1749                final CClientGetIds get = new CClientGetIds(clientUId);
1750                final CommandFuture<ClientInfo> future = new CommandFuture<>();
1751
1752                query.doCommandAsync(get, new Callback() {
1753                        @Override
1754                        public void handle() {
1755                                if (hasFailed(get, future)) return;
1756
1757                                getClientInfo(get.getFirstResponse().getInt("clid")).forwardResult(future);
1758                        }
1759                });
1760                return future;
1761        }
1762
1763        /**
1764         * Gets information about the client with the specified client ID.
1765         *
1766         * @param clientId
1767         *              the client ID of the client
1768         *
1769         * @return the client or {@code null} if no client was found
1770         *
1771         * @querycommands 1
1772         * @see Client#getId()
1773         * @see ClientInfo
1774         */
1775        public CommandFuture<ClientInfo> getClientInfo(final int clientId) {
1776                final CClientInfo info = new CClientInfo(clientId);
1777                final CommandFuture<ClientInfo> future = new CommandFuture<>();
1778
1779                query.doCommandAsync(info, new Callback() {
1780                        @Override
1781                        public void handle() {
1782                                if (hasFailed(info, future)) return;
1783                                future.set(new ClientInfo(clientId, info.getFirstResponse().getMap()));
1784                        }
1785                });
1786                return future;
1787        }
1788
1789        /**
1790         * Gets a list of all permissions assigned to the specified client.
1791         *
1792         * @param clientDBId
1793         *              the database ID of the client
1794         *
1795         * @return a list of all permissions assigned to the client
1796         *
1797         * @querycommands 1
1798         * @see Client#getDatabaseId()
1799         * @see Permission
1800         */
1801        public CommandFuture<List<Permission>> getClientPermissions(int clientDBId) {
1802                final CClientPermList list = new CClientPermList(clientDBId);
1803                final CommandFuture<List<Permission>> future = new CommandFuture<>();
1804
1805                query.doCommandAsync(list, new Callback() {
1806                        @Override
1807                        public void handle() {
1808                                if (hasFailed(list, future)) return;
1809
1810                                final List<Wrapper> responses = list.getResponse();
1811                                final List<Permission> permissions = new ArrayList<>(responses.size());
1812
1813                                for (final Wrapper response : responses) {
1814                                        permissions.add(new Permission(response.getMap()));
1815                                }
1816                                future.set(permissions);
1817                        }
1818                });
1819                return future;
1820        }
1821
1822        /**
1823         * Gets a list of all clients on the selected virtual server.
1824         *
1825         * @return a list of all clients on the virtual server
1826         *
1827         * @querycommands 1
1828         * @see Client
1829         */
1830        public CommandFuture<List<Client>> getClients() {
1831                final CClientList list = new CClientList();
1832                final CommandFuture<List<Client>> future = new CommandFuture<>();
1833
1834                query.doCommandAsync(list, new Callback() {
1835                        @Override
1836                        public void handle() {
1837                                if (hasFailed(list, future)) return;
1838
1839                                final List<Wrapper> responses = list.getResponse();
1840                                final List<Client> clients = new ArrayList<>(responses.size());
1841
1842                                for (final Wrapper response : responses) {
1843                                        clients.add(new Client(response.getMap()));
1844                                }
1845                                future.set(clients);
1846                        }
1847                });
1848                return future;
1849        }
1850
1851        /**
1852         * Gets a list of all complaints on the selected virtual server.
1853         *
1854         * @return a list of all complaints on the virtual server
1855         *
1856         * @querycommands 1
1857         * @see Complaint
1858         * @see #getComplaints(int)
1859         */
1860        public CommandFuture<List<Complaint>> getComplaints() {
1861                return getComplaints(-1);
1862        }
1863
1864        /**
1865         * Gets a list of all complaints about the specified client.
1866         *
1867         * @param clientDBId
1868         *              the database ID of the client
1869         *
1870         * @return a list of all complaints about the specified client
1871         *
1872         * @querycommands 1
1873         * @see Client#getDatabaseId()
1874         * @see Complaint
1875         */
1876        public CommandFuture<List<Complaint>> getComplaints(int clientDBId) {
1877                final CComplainList list = new CComplainList(clientDBId);
1878                final CommandFuture<List<Complaint>> future = new CommandFuture<>();
1879
1880                query.doCommandAsync(list, new Callback() {
1881                        @Override
1882                        public void handle() {
1883                                if (hasFailed(list, future)) return;
1884
1885                                final List<Wrapper> responses = list.getResponse();
1886                                final List<Complaint> complaints = new ArrayList<>(responses.size());
1887
1888                                for (final Wrapper response : responses) {
1889                                        complaints.add(new Complaint(response.getMap()));
1890                                }
1891                                future.set(complaints);
1892                        }
1893                });
1894                return future;
1895        }
1896
1897        /**
1898         * Gets detailed connection information about the selected virtual server.
1899         *
1900         * @return connection information about the selected virtual server
1901         *
1902         * @querycommands 1
1903         * @see ConnectionInfo
1904         * @see #getServerInfo()
1905         */
1906        public CommandFuture<ConnectionInfo> getConnectionInfo() {
1907                final CServerRequestConnectionInfo info = new CServerRequestConnectionInfo();
1908                final CommandFuture<ConnectionInfo> future = new CommandFuture<>();
1909
1910                query.doCommandAsync(info, new Callback() {
1911                        @Override
1912                        public void handle() {
1913                                if (hasFailed(info, future)) return;
1914                                future.set(new ConnectionInfo(info.getFirstResponse().getMap()));
1915                        }
1916                });
1917                return future;
1918        }
1919
1920        /**
1921         * Gets all clients in the database whose last nickname matches the specified name <b>exactly</b>.
1922         *
1923         * @param name
1924         *              the nickname for the clients to match
1925         *
1926         * @return a list of all clients with a matching nickname
1927         *
1928         * @querycommands 1 + n,
1929         * where n is the amount of database clients with a matching nickname
1930         * @see Client#getNickname()
1931         */
1932        public CommandFuture<List<DatabaseClientInfo>> getDatabaseClientsByName(String name) {
1933                final CClientDBFind find = new CClientDBFind(name, false);
1934                final CommandFuture<List<DatabaseClientInfo>> future = new CommandFuture<>();
1935
1936                query.doCommandAsync(find, new Callback() {
1937                        @Override
1938                        public void handle() {
1939                                if (hasFailed(find, future)) return;
1940
1941                                final List<Wrapper> responses = find.getResponse();
1942                                final Collection<CommandFuture<DatabaseClientInfo>> infoFutures = new ArrayList<>(responses.size());
1943                                for (Wrapper response : responses) {
1944                                        final int databaseId = response.getInt("cldbid");
1945                                        infoFutures.add(getDatabaseClientInfo(databaseId));
1946                                }
1947
1948                                CommandFuture.ofAll(infoFutures).forwardResult(future);
1949                        }
1950                });
1951                return future;
1952        }
1953
1954        /**
1955         * Gets information about the client with the specified unique identifier in the server database.
1956         *
1957         * @param clientUId
1958         *              the unique identifier of the client
1959         *
1960         * @return the database client or {@code null} if no client was found
1961         *
1962         * @querycommands 2
1963         * @see Client#getUniqueIdentifier()
1964         * @see DatabaseClientInfo
1965         */
1966        public CommandFuture<DatabaseClientInfo> getDatabaseClientByUId(String clientUId) {
1967                final CClientGetDBIdFromUId get = new CClientGetDBIdFromUId(clientUId);
1968                final CommandFuture<DatabaseClientInfo> future = new CommandFuture<>();
1969
1970                query.doCommandAsync(get, new Callback() {
1971                        @Override
1972                        public void handle() {
1973                                if (hasFailed(get, future)) return;
1974
1975                                getDatabaseClientInfo(get.getFirstResponse().getInt("cldbid")).forwardResult(future);
1976                        }
1977                });
1978                return future;
1979        }
1980
1981        /**
1982         * Gets information about the client with the specified database ID in the server database.
1983         *
1984         * @param clientDBId
1985         *              the database ID of the client
1986         *
1987         * @return the database client or {@code null} if no client was found
1988         *
1989         * @querycommands 1
1990         * @see Client#getDatabaseId()
1991         * @see DatabaseClientInfo
1992         */
1993        public CommandFuture<DatabaseClientInfo> getDatabaseClientInfo(int clientDBId) {
1994                final CClientDBInfo info = new CClientDBInfo(clientDBId);
1995                final CommandFuture<DatabaseClientInfo> future = new CommandFuture<>();
1996
1997                query.doCommandAsync(info, new Callback() {
1998                        @Override
1999                        public void handle() {
2000                                if (hasFailed(info, future)) return;
2001                                future.set(new DatabaseClientInfo(info.getFirstResponse().getMap()));
2002                        }
2003                });
2004                return future;
2005        }
2006
2007        /**
2008         * Gets information about all clients in the server database.
2009         * <p>
2010         * As this method uses internal commands which can only return 200 clients at once,
2011         * this method can take quite some time to execute.
2012         * </p><p>
2013         * Also keep in mind that the client database can easily accumulate several thousand entries.
2014         * </p>
2015         *
2016         * @return a {@link List} of all database clients
2017         *
2018         * @querycommands 1 + n,
2019         * where n = Math.ceil([amount of database clients] / 200)
2020         * @see DatabaseClient
2021         */
2022        public CommandFuture<List<DatabaseClient>> getDatabaseClients() {
2023                final CClientDBList countList = new CClientDBList(0, 1, true);
2024                final CommandFuture<List<DatabaseClient>> future = new CommandFuture<>();
2025
2026                query.doCommandAsync(countList, new Callback() {
2027                        @Override
2028                        public void handle() {
2029                                if (hasFailed(countList, future)) return;
2030
2031                                final int count = countList.getFirstResponse().getInt("count");
2032                                final int futuresCount = ((count - 1) / 200) + 1;
2033                                final Collection<CommandFuture<List<DatabaseClient>>> futures = new ArrayList<>(futuresCount);
2034                                for (int i = 0; i < count; i += 200) {
2035                                        futures.add(getDatabaseClients(i, 200));
2036                                }
2037
2038                                CommandFuture.ofAll(futures).onSuccess(new CommandFuture.SuccessListener<List<List<DatabaseClient>>>() {
2039                                        @Override
2040                                        public void handleSuccess(List<List<DatabaseClient>> result) {
2041                                                int total = 0;
2042                                                for (List<DatabaseClient> list : result) {
2043                                                        total += list.size();
2044                                                }
2045
2046                                                final List<DatabaseClient> combination = new ArrayList<>(total);
2047                                                for (List<DatabaseClient> list : result) {
2048                                                        combination.addAll(list);
2049                                                }
2050                                                future.set(combination);
2051                                        }
2052                                }).forwardFailure(future);
2053                        }
2054                });
2055                return future;
2056        }
2057
2058        /**
2059         * Gets information about a set number of clients in the server database, starting at {@code offset}.
2060         *
2061         * @param offset
2062         *              the index of the first database client to be returned.
2063         *              Note that this is <b>not</b> a database ID, but an arbitrary, 0-based index.
2064         * @param count
2065         *              the number of database clients that should be returned.
2066         *              Any integer greater than 200 might cause problems with the connection
2067         *
2068         * @return a {@link List} of database clients
2069         *
2070         * @querycommands 1
2071         * @see DatabaseClient
2072         */
2073        public CommandFuture<List<DatabaseClient>> getDatabaseClients(final int offset, final int count) {
2074                final CClientDBList list = new CClientDBList(offset, count, false);
2075                final CommandFuture<List<DatabaseClient>> future = new CommandFuture<>();
2076
2077                query.doCommandAsync(list, new Callback() {
2078                        @Override
2079                        public void handle() {
2080                                if (hasFailed(list, future)) return;
2081
2082                                final List<DatabaseClient> clients = new ArrayList<>(count);
2083                                for (final Wrapper response : list.getResponse()) {
2084                                        clients.add(new DatabaseClient(response.getMap()));
2085                                }
2086                                future.set(clients);
2087                        }
2088                });
2089                return future;
2090        }
2091
2092        /**
2093         * Displays detailed configuration information about the server instance including
2094         * uptime, number of virtual servers online, traffic information, etc.
2095         *
2096         * @return information about the host
2097         *
2098         * @querycommands 1
2099         */
2100        public CommandFuture<HostInfo> getHostInfo() {
2101                final CHostInfo info = new CHostInfo();
2102                final CommandFuture<HostInfo> future = new CommandFuture<>();
2103
2104                query.doCommandAsync(info, new Callback() {
2105                        @Override
2106                        public void handle() {
2107                                if (hasFailed(info, future)) return;
2108                                future.set(new HostInfo(info.getFirstResponse().getMap()));
2109                        }
2110                });
2111                return future;
2112        }
2113
2114        /**
2115         * Displays the server instance configuration including database revision number,
2116         * the file transfer port, default group IDs, etc.
2117         *
2118         * @return information about the TeamSpeak server instance.
2119         *
2120         * @querycommands 1
2121         */
2122        public CommandFuture<InstanceInfo> getInstanceInfo() {
2123                final CInstanceInfo info = new CInstanceInfo();
2124                final CommandFuture<InstanceInfo> future = new CommandFuture<>();
2125
2126                query.doCommandAsync(info, new Callback() {
2127                        @Override
2128                        public void handle() {
2129                                if (hasFailed(info, future)) return;
2130                                future.set(new InstanceInfo(info.getFirstResponse().getMap()));
2131                        }
2132                });
2133                return future;
2134        }
2135
2136        /**
2137         * Reads the message body of a message. This will not set the read flag, though.
2138         *
2139         * @param messageId
2140         *              the ID of the message to be read
2141         *
2142         * @return the body of the message with the specified ID or {@code null} if there was no message with that ID
2143         *
2144         * @querycommands 1
2145         * @see Message#getId()
2146         * @see #setMessageRead(int)
2147         */
2148        public CommandFuture<String> getOfflineMessage(int messageId) {
2149                final CMessageGet get = new CMessageGet(messageId);
2150                return executeAndReturnStringProperty(get, "message");
2151        }
2152
2153        /**
2154         * Reads the message body of a message. This will not set the read flag, though.
2155         *
2156         * @param message
2157         *              the message to be read
2158         *
2159         * @return the body of the message with the specified ID or {@code null} if there was no message with that ID
2160         *
2161         * @querycommands 1
2162         * @see Message#getId()
2163         * @see #setMessageRead(Message)
2164         */
2165        public CommandFuture<String> getOfflineMessage(Message message) {
2166                return getOfflineMessage(message.getId());
2167        }
2168
2169        /**
2170         * Gets a list of all offline messages for the server query.
2171         * The returned messages lack their message body, though.
2172         * To read the actual message, use {@link #getOfflineMessage(int)} or {@link #getOfflineMessage(Message)}.
2173         *
2174         * @return a list of all offline messages this server query has received
2175         *
2176         * @querycommands 1
2177         */
2178        public CommandFuture<List<Message>> getOfflineMessages() {
2179                final CMessageList list = new CMessageList();
2180                final CommandFuture<List<Message>> future = new CommandFuture<>();
2181
2182                query.doCommandAsync(list, new Callback() {
2183                        @Override
2184                        public void handle() {
2185                                if (hasFailed(list, future)) return;
2186
2187                                final List<Wrapper> responses = list.getResponse();
2188                                final List<Message> msg = new ArrayList<>(responses.size());
2189
2190                                for (final Wrapper response : responses) {
2191                                        msg.add(new Message(response.getMap()));
2192                                }
2193                                future.set(msg);
2194                        }
2195                });
2196                return future;
2197        }
2198
2199        /**
2200         * Displays detailed information about all assignments of the permission specified
2201         * with {@code permName}. The output includes the type and the ID of the client,
2202         * channel or group associated with the permission.
2203         *
2204         * @param permName
2205         *              the name of the permission
2206         *
2207         * @return a list of permission assignments
2208         *
2209         * @querycommands 1
2210         * @see #getPermissionOverview(int, int)
2211         */
2212        public CommandFuture<List<AdvancedPermission>> getPermissionAssignments(String permName) {
2213                final CPermFind find = new CPermFind(permName);
2214                final CommandFuture<List<AdvancedPermission>> future = new CommandFuture<>();
2215
2216                query.doCommandAsync(find, new Callback() {
2217                        @Override
2218                        public void handle() {
2219                                if (hasFailed(find, future)) return;
2220
2221                                final List<Wrapper> responses = find.getResponse();
2222                                final List<AdvancedPermission> assignments = new ArrayList<>(responses.size());
2223
2224                                for (final Wrapper response : responses) {
2225                                        assignments.add(new AdvancedPermission(response.getMap()));
2226                                }
2227                                future.set(assignments);
2228                        }
2229                });
2230                return future;
2231        }
2232
2233        /**
2234         * Gets the ID of the permission specified by {@code permName}.
2235         * <p>
2236         * Note that the use of numeric permission IDs is deprecated
2237         * and that this API only uses the string variant of the IDs.
2238         * </p>
2239         *
2240         * @param permName
2241         *              the name of the permission
2242         *
2243         * @return the numeric ID of the specified permission
2244         *
2245         * @querycommands 1
2246         */
2247        public CommandFuture<Integer> getPermissionIdByName(String permName) {
2248                final CPermIdGetByName get = new CPermIdGetByName(permName);
2249                return executeAndReturnIntProperty(get, "permid");
2250        }
2251
2252        /**
2253         * Gets a list of all assigned permissions for a client in a specified channel.
2254         * If you do not care about channel permissions, set {@code channelId} to {@code -1}.
2255         *
2256         * @param channelId
2257         *              the ID of the channel
2258         * @param clientDBId
2259         *              the database ID of the client to create the overview for
2260         *
2261         * @return a list of all permission assignments for the client in the specified channel
2262         *
2263         * @querycommands 1
2264         * @see Channel#getId()
2265         * @see Client#getDatabaseId()
2266         */
2267        public CommandFuture<List<AdvancedPermission>> getPermissionOverview(int channelId, int clientDBId) {
2268                final CPermOverview overview = new CPermOverview(channelId, clientDBId);
2269                final CommandFuture<List<AdvancedPermission>> future = new CommandFuture<>();
2270
2271                query.doCommandAsync(overview, new Callback() {
2272                        @Override
2273                        public void handle() {
2274                                if (hasFailed(overview, future)) return;
2275
2276                                final List<Wrapper> responses = overview.getResponse();
2277                                final List<AdvancedPermission> permissions = new ArrayList<>(responses.size());
2278
2279                                for (final Wrapper response : responses) {
2280                                        permissions.add(new AdvancedPermission(response.getMap()));
2281                                }
2282                                future.set(permissions);
2283                        }
2284                });
2285                return future;
2286        }
2287
2288        /**
2289         * Displays a list of all permissions, including ID, name and description.
2290         *
2291         * @return a list of all permissions
2292         *
2293         * @querycommands 1
2294         */
2295        public CommandFuture<List<PermissionInfo>> getPermissions() {
2296                final CPermissionList list = new CPermissionList();
2297                final CommandFuture<List<PermissionInfo>> future = new CommandFuture<>();
2298
2299                query.doCommandAsync(list, new Callback() {
2300                        @Override
2301                        public void handle() {
2302                                if (hasFailed(list, future)) return;
2303
2304                                final List<Wrapper> responses = list.getResponse();
2305                                final List<PermissionInfo> permissions = new ArrayList<>(responses.size());
2306
2307                                for (final Wrapper response : responses) {
2308                                        permissions.add(new PermissionInfo(response.getMap()));
2309                                }
2310                                future.set(permissions);
2311                        }
2312                });
2313                return future;
2314        }
2315
2316        /**
2317         * Displays the current value of the specified permission for this server query instance.
2318         *
2319         * @param permName
2320         *              the name of the permission
2321         *
2322         * @return the permission value, usually ranging from 0 to 100
2323         *
2324         * @querycommands 1
2325         */
2326        public CommandFuture<Integer> getPermissionValue(String permName) {
2327                final CPermGet get = new CPermGet(permName);
2328                return executeAndReturnIntProperty(get, "permvalue");
2329        }
2330
2331        /**
2332         * Gets a list of all available tokens to join channel or server groups,
2333         * including their type and group IDs.
2334         *
2335         * @return a list of all generated, but still unclaimed privilege keys
2336         *
2337         * @querycommands 1
2338         * @see #addPrivilegeKey(TokenType, int, int, String)
2339         * @see #usePrivilegeKey(String)
2340         */
2341        public CommandFuture<List<PrivilegeKey>> getPrivilegeKeys() {
2342                final CPrivilegeKeyList list = new CPrivilegeKeyList();
2343                final CommandFuture<List<PrivilegeKey>> future = new CommandFuture<>();
2344
2345                query.doCommandAsync(list, new Callback() {
2346                        @Override
2347                        public void handle() {
2348                                if (hasFailed(list, future)) return;
2349
2350                                final List<Wrapper> responses = list.getResponse();
2351                                final List<PrivilegeKey> keys = new ArrayList<>(responses.size());
2352
2353                                for (final Wrapper response : responses) {
2354                                        keys.add(new PrivilegeKey(response.getMap()));
2355                                }
2356                                future.set(keys);
2357                        }
2358                });
2359                return future;
2360        }
2361
2362        /**
2363         * Gets a list of all clients in the specified server group.
2364         *
2365         * @param serverGroupId
2366         *              the ID of the server group for which the clients should be looked up
2367         *
2368         * @return a list of all clients in the server group
2369         *
2370         * @querycommands 1
2371         */
2372        public CommandFuture<List<ServerGroupClient>> getServerGroupClients(int serverGroupId) {
2373                final CServerGroupClientList list = new CServerGroupClientList(serverGroupId);
2374                final CommandFuture<List<ServerGroupClient>> future = new CommandFuture<>();
2375
2376                query.doCommandAsync(list, new Callback() {
2377                        @Override
2378                        public void handle() {
2379                                if (hasFailed(list, future)) return;
2380
2381                                final List<Wrapper> responses = list.getResponse();
2382                                final List<ServerGroupClient> clients = new ArrayList<>(responses.size());
2383
2384                                for (final Wrapper response : responses) {
2385                                        clients.add(new ServerGroupClient(response.getMap()));
2386                                }
2387                                future.set(clients);
2388                        }
2389                });
2390                return future;
2391        }
2392
2393        /**
2394         * Gets a list of all clients in the specified server group.
2395         *
2396         * @param serverGroup
2397         *              the server group for which the clients should be looked up
2398         *
2399         * @return a list of all clients in the server group
2400         *
2401         * @querycommands 1
2402         */
2403        public CommandFuture<List<ServerGroupClient>> getServerGroupClients(ServerGroup serverGroup) {
2404                return getServerGroupClients(serverGroup.getId());
2405        }
2406
2407        /**
2408         * Gets a list of all permissions assigned to the specified server group.
2409         *
2410         * @param serverGroupId
2411         *              the ID of the server group for which the permissions should be looked up
2412         *
2413         * @return a list of all permissions assigned to the server group
2414         *
2415         * @querycommands 1
2416         * @see ServerGroup#getId()
2417         * @see #getServerGroupPermissions(ServerGroup)
2418         */
2419        public CommandFuture<List<Permission>> getServerGroupPermissions(int serverGroupId) {
2420                final CServerGroupPermList list = new CServerGroupPermList(serverGroupId);
2421                final CommandFuture<List<Permission>> future = new CommandFuture<>();
2422
2423                query.doCommandAsync(list, new Callback() {
2424                        @Override
2425                        public void handle() {
2426                                if (hasFailed(list, future)) return;
2427
2428                                final List<Wrapper> responses = list.getResponse();
2429                                final List<Permission> permissions = new ArrayList<>(responses.size());
2430
2431                                for (final Wrapper response : responses) {
2432                                        permissions.add(new Permission(response.getMap()));
2433                                }
2434                                future.set(permissions);
2435                        }
2436                });
2437                return future;
2438        }
2439
2440        /**
2441         * Gets a list of all permissions assigned to the specified server group.
2442         *
2443         * @param serverGroup
2444         *              the server group for which the permissions should be looked up
2445         *
2446         * @return a list of all permissions assigned to the server group
2447         *
2448         * @querycommands 1
2449         */
2450        public CommandFuture<List<Permission>> getServerGroupPermissions(ServerGroup serverGroup) {
2451                return getServerGroupPermissions(serverGroup.getId());
2452        }
2453
2454        /**
2455         * Gets a list of all server groups on the virtual server.
2456         * <p>
2457         * Depending on your permissions, the output may also contain
2458         * global server query groups and template groups.
2459         * </p>
2460         *
2461         * @return a list of all server groups
2462         *
2463         * @querycommands 1
2464         */
2465        public CommandFuture<List<ServerGroup>> getServerGroups() {
2466                final CServerGroupList list = new CServerGroupList();
2467                final CommandFuture<List<ServerGroup>> future = new CommandFuture<>();
2468
2469                query.doCommandAsync(list, new Callback() {
2470                        @Override
2471                        public void handle() {
2472                                if (hasFailed(list, future)) return;
2473
2474                                final List<Wrapper> responses = list.getResponse();
2475                                final List<ServerGroup> groups = new ArrayList<>(responses.size());
2476
2477                                for (final Wrapper response : responses) {
2478                                        groups.add(new ServerGroup(response.getMap()));
2479                                }
2480                                future.set(groups);
2481                        }
2482                });
2483                return future;
2484        }
2485
2486        /**
2487         * Gets a list of all server groups set for a client.
2488         *
2489         * @param clientDatabaseId
2490         *              the database ID of the client for which the server groups should be looked up
2491         *
2492         * @return a list of all server groups set for the client
2493         *
2494         * @querycommands 2
2495         * @see Client#getDatabaseId()
2496         * @see #getServerGroupsByClient(Client)
2497         */
2498        public CommandFuture<List<ServerGroup>> getServerGroupsByClientId(int clientDatabaseId) {
2499                final CServerGroupsByClientId client = new CServerGroupsByClientId(clientDatabaseId);
2500                final CommandFuture<List<ServerGroup>> future = new CommandFuture<>();
2501
2502                getServerGroups().onSuccess(new CommandFuture.SuccessListener<List<ServerGroup>>() {
2503                        @Override
2504                        public void handleSuccess(final List<ServerGroup> allServerGroups) {
2505                                query.doCommandAsync(client, new Callback() {
2506                                        @Override
2507                                        public void handle() {
2508                                                if (hasFailed(client, future)) return;
2509
2510                                                final List<Wrapper> responses = client.getResponse();
2511                                                final List<ServerGroup> list = new ArrayList<>(responses.size());
2512
2513                                                for (final Wrapper response : responses) {
2514                                                        for (final ServerGroup s : allServerGroups) {
2515                                                                if (s.getId() == response.getInt("sgid")) {
2516                                                                        list.add(s);
2517                                                                }
2518                                                        }
2519                                                }
2520                                                future.set(list);
2521                                        }
2522                                });
2523                        }
2524                }).forwardFailure(future);
2525                return future;
2526        }
2527
2528        /**
2529         * Gets a list of all server groups set for a client.
2530         *
2531         * @param client
2532         *              the client for which the server groups should be looked up
2533         *
2534         * @return a list of all server group set for the client
2535         *
2536         * @querycommands 2
2537         * @see #getServerGroupsByClientId(int)
2538         */
2539        public CommandFuture<List<ServerGroup>> getServerGroupsByClient(Client client) {
2540                return getServerGroupsByClientId(client.getDatabaseId());
2541        }
2542
2543        /**
2544         * Gets the ID of a virtual server by its port.
2545         *
2546         * @param port
2547         *              the port of a virtual server
2548         *
2549         * @return the ID of the virtual server
2550         *
2551         * @querycommands 1
2552         * @see VirtualServer#getPort()
2553         * @see VirtualServer#getId()
2554         */
2555        public CommandFuture<Integer> getServerIdByPort(int port) {
2556                final CServerIdGetByPort s = new CServerIdGetByPort(port);
2557                return executeAndReturnIntProperty(s, "server_id");
2558        }
2559
2560        /**
2561         * Gets detailed information about the virtual server the server query is currently in.
2562         *
2563         * @return information about the current virtual server
2564         *
2565         * @querycommands 1
2566         */
2567        public CommandFuture<VirtualServerInfo> getServerInfo() {
2568                final CServerInfo info = new CServerInfo();
2569                final CommandFuture<VirtualServerInfo> future = new CommandFuture<>();
2570
2571                query.doCommandAsync(info, new Callback() {
2572                        @Override
2573                        public void handle() {
2574                                if (hasFailed(info, future)) return;
2575                                future.set(new VirtualServerInfo(info.getFirstResponse().getMap()));
2576                        }
2577                });
2578                return future;
2579        }
2580
2581        /**
2582         * Gets the version, build number and platform of the TeamSpeak3 server.
2583         *
2584         * @return the version information of the server
2585         *
2586         * @querycommands 1
2587         */
2588        public CommandFuture<Version> getVersion() {
2589                final CVersion version = new CVersion();
2590                final CommandFuture<Version> future = new CommandFuture<>();
2591
2592                query.doCommandAsync(version, new Callback() {
2593                        @Override
2594                        public void handle() {
2595                                if (hasFailed(version, future)) return;
2596                                future.set(new Version(version.getFirstResponse().getMap()));
2597                        }
2598                });
2599                return future;
2600        }
2601
2602        /**
2603         * Gets a list of all virtual servers including their ID, status, number of clients online, etc.
2604         *
2605         * @return a list of all virtual servers
2606         *
2607         * @querycommands 1
2608         */
2609        public CommandFuture<List<VirtualServer>> getVirtualServers() {
2610                final CServerList serverList = new CServerList();
2611                final CommandFuture<List<VirtualServer>> future = new CommandFuture<>();
2612
2613                query.doCommandAsync(serverList, new Callback() {
2614                        @Override
2615                        public void handle() {
2616                                if (hasFailed(serverList, future)) return;
2617
2618                                final List<Wrapper> responses = serverList.getResponse();
2619                                final List<VirtualServer> servers = new ArrayList<>(responses.size());
2620
2621                                for (final Wrapper response : responses) {
2622                                        servers.add((new VirtualServer(response.getMap())));
2623                                }
2624                                future.set(servers);
2625                        }
2626                });
2627                return future;
2628        }
2629
2630        /**
2631         * Kicks one or more clients from their current channels.
2632         * This will move the kicked clients into the default channel and
2633         * won't do anything if the clients are already in the default channel.
2634         *
2635         * @param clientIds
2636         *              the IDs of the clients to kick
2637         *
2638         * @return whether the command succeeded or not
2639         *
2640         * @querycommands 1
2641         * @see #kickClientFromChannel(Client...)
2642         * @see #kickClientFromChannel(String, int...)
2643         */
2644        public CommandFuture<Boolean> kickClientFromChannel(int... clientIds) {
2645                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, null, clientIds);
2646        }
2647
2648        /**
2649         * Kicks one or more clients from their current channels.
2650         * This will move the kicked clients into the default channel and
2651         * won't do anything if the clients are already in the default channel.
2652         *
2653         * @param clients
2654         *              the clients to kick
2655         *
2656         * @return whether the command succeeded or not
2657         *
2658         * @querycommands 1
2659         * @see #kickClientFromChannel(int...)
2660         * @see #kickClientFromChannel(String, Client...)
2661         */
2662        public CommandFuture<Boolean> kickClientFromChannel(Client... clients) {
2663                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, null, clients);
2664        }
2665
2666        /**
2667         * Kicks one or more clients from their current channels for the specified reason.
2668         * This will move the kicked clients into the default channel and
2669         * won't do anything if the clients are already in the default channel.
2670         *
2671         * @param message
2672         *              the reason message to display to the clients
2673         * @param clientIds
2674         *              the IDs of the clients to kick
2675         *
2676         * @return whether the command succeeded or not
2677         *
2678         * @querycommands 1
2679         * @see Client#getId()
2680         * @see #kickClientFromChannel(int...)
2681         * @see #kickClientFromChannel(String, Client...)
2682         */
2683        public CommandFuture<Boolean> kickClientFromChannel(String message, int... clientIds) {
2684                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, message, clientIds);
2685        }
2686
2687        /**
2688         * Kicks one or more clients from their current channels for the specified reason.
2689         * This will move the kicked clients into the default channel and
2690         * won't do anything if the clients are already in the default channel.
2691         *
2692         * @param message
2693         *              the reason message to display to the clients
2694         * @param clients
2695         *              the clients to kick
2696         *
2697         * @return whether the command succeeded or not
2698         *
2699         * @querycommands 1
2700         * @see #kickClientFromChannel(Client...)
2701         * @see #kickClientFromChannel(String, int...)
2702         */
2703        public CommandFuture<Boolean> kickClientFromChannel(String message, Client... clients) {
2704                return kickClients(ReasonIdentifier.REASON_KICK_CHANNEL, message, clients);
2705        }
2706
2707        /**
2708         * Kicks one or more clients from the server.
2709         *
2710         * @param clientIds
2711         *              the IDs of the clients to kick
2712         *
2713         * @return whether the command succeeded or not
2714         *
2715         * @querycommands 1
2716         * @see Client#getId()
2717         * @see #kickClientFromServer(Client...)
2718         * @see #kickClientFromServer(String, int...)
2719         */
2720        public CommandFuture<Boolean> kickClientFromServer(int... clientIds) {
2721                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, null, clientIds);
2722        }
2723
2724        /**
2725         * Kicks one or more clients from the server.
2726         *
2727         * @param clients
2728         *              the clients to kick
2729         *
2730         * @return whether the command succeeded or not
2731         *
2732         * @querycommands 1
2733         * @see #kickClientFromServer(int...)
2734         * @see #kickClientFromServer(String, Client...)
2735         */
2736        public CommandFuture<Boolean> kickClientFromServer(Client... clients) {
2737                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, null, clients);
2738        }
2739
2740        /**
2741         * Kicks one or more clients from the server for the specified reason.
2742         *
2743         * @param message
2744         *              the reason message to display to the clients
2745         * @param clientIds
2746         *              the IDs of the clients to kick
2747         *
2748         * @return whether the command succeeded or not
2749         *
2750         * @querycommands 1
2751         * @see Client#getId()
2752         * @see #kickClientFromServer(int...)
2753         * @see #kickClientFromServer(String, Client...)
2754         */
2755        public CommandFuture<Boolean> kickClientFromServer(String message, int... clientIds) {
2756                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, message, clientIds);
2757        }
2758
2759        /**
2760         * Kicks one or more clients from the server for the specified reason.
2761         *
2762         * @param message
2763         *              the reason message to display to the clients
2764         * @param clients
2765         *              the clients to kick
2766         *
2767         * @return whether the command succeeded or not
2768         *
2769         * @querycommands 1
2770         * @see #kickClientFromServer(Client...)
2771         * @see #kickClientFromServer(String, int...)
2772         */
2773        public CommandFuture<Boolean> kickClientFromServer(String message, Client... clients) {
2774                return kickClients(ReasonIdentifier.REASON_KICK_SERVER, message, clients);
2775        }
2776
2777        /**
2778         * Kicks a list of clients from either the channel or the server for a given reason.
2779         *
2780         * @param reason
2781         *              where to kick the clients from
2782         * @param message
2783         *              the reason message to display to the clients
2784         * @param clients
2785         *              the clients to kick
2786         *
2787         * @return whether the command succeeded or not
2788         *
2789         * @querycommands 1
2790         */
2791        private CommandFuture<Boolean> kickClients(ReasonIdentifier reason, String message, Client... clients) {
2792                int[] clientIds = new int[clients.length];
2793                for (int i = 0; i < clients.length; ++i) {
2794                        clientIds[i] = clients[i].getId();
2795                }
2796                return kickClients(reason, message, clientIds);
2797        }
2798
2799        /**
2800         * Kicks a list of clients from either the channel or the server for a given reason.
2801         *
2802         * @param reason
2803         *              where to kick the clients from
2804         * @param message
2805         *              the reason message to display to the clients
2806         * @param clientIds
2807         *              the IDs of the clients to kick
2808         *
2809         * @return whether the command succeeded or not
2810         *
2811         * @querycommands 1
2812         * @see Client#getId()
2813         */
2814        private CommandFuture<Boolean> kickClients(ReasonIdentifier reason, String message, int... clientIds) {
2815                final CClientKick kick = new CClientKick(reason, message, clientIds);
2816                return executeAndReturnError(kick);
2817        }
2818
2819        /**
2820         * Logs the server query in using the specified username and password.
2821         * <p>
2822         * Note that you can also set the login in the {@link TS3Config},
2823         * so that you will be logged in right after the connection is established.
2824         * </p>
2825         *
2826         * @param username
2827         *              the username of the server query
2828         * @param password
2829         *              the password to use
2830         *
2831         * @return whether the command succeeded or not
2832         *
2833         * @querycommands 1
2834         * @see TS3Config#setLoginCredentials(String, String)
2835         * @see #logout()
2836         */
2837        public CommandFuture<Boolean> login(String username, String password) {
2838                final CLogin login = new CLogin(username, password);
2839                return executeAndReturnError(login);
2840        }
2841
2842        /**
2843         * Logs the server query out and deselects the current virtual server.
2844         *
2845         * @return whether the command succeeded or not
2846         *
2847         * @querycommands 1
2848         * @see #login(String, String)
2849         */
2850        public CommandFuture<Boolean> logout() {
2851                final CLogout logout = new CLogout();
2852                return executeAndReturnError(logout);
2853        }
2854
2855        /**
2856         * Moves a channel to a new parent channel specified by its ID.
2857         * To move a channel to root level, set {@code channelTargetId} to {@code 0}.
2858         * <p>
2859         * This will move the channel right below the specified parent channel, above all other child channels.
2860         * This command will fail if the channel already has the specified target channel as the parent channel.
2861         * </p>
2862         *
2863         * @param channelId
2864         *              the channel to move
2865         * @param channelTargetId
2866         *              the new parent channel for the specified channel
2867         *
2868         * @return whether the command succeeded or not
2869         *
2870         * @querycommands 1
2871         * @see Channel#getId()
2872         * @see #moveChannel(int, int, int)
2873         */
2874        public CommandFuture<Boolean> moveChannel(int channelId, int channelTargetId) {
2875                return moveChannel(channelId, channelTargetId, 0);
2876        }
2877
2878        /**
2879         * Moves a channel to a new parent channel specified by its ID.
2880         * To move a channel to root level, set {@code channelTargetId} to {@code 0}.
2881         * <p>
2882         * The channel will be ordered below the channel with the ID specified by {@code order}.
2883         * To move the channel right below the parent channel, set {@code order} to {@code 0}.
2884         * Also note that a channel cannot be re-ordered without also changing its parent channel.
2885         * </p>
2886         *
2887         * @param channelId
2888         *              the channel to move
2889         * @param channelTargetId
2890         *              the new parent channel for the specified channel
2891         * @param order
2892         *              the channel to sort the specified channel below
2893         *
2894         * @return whether the command succeeded or not
2895         *
2896         * @querycommands 1
2897         * @see Channel#getId()
2898         * @see #moveChannel(int, int)
2899         */
2900        public CommandFuture<Boolean> moveChannel(int channelId, int channelTargetId, int order) {
2901                final CChannelMove move = new CChannelMove(channelId, channelTargetId, order);
2902                return executeAndReturnError(move);
2903        }
2904
2905        /**
2906         * Moves the server query into the specified channel.
2907         *
2908         * @param channelId
2909         *              the ID of the channel to move the client into
2910         *
2911         * @return whether the command succeeded or not
2912         *
2913         * @querycommands 1
2914         * @see Channel#getId()
2915         */
2916        public CommandFuture<Boolean> moveClient(int channelId) {
2917                return moveClient(channelId, null);
2918        }
2919
2920        /**
2921         * Moves the server query into the specified channel.
2922         *
2923         * @param channel
2924         *              the channel to move the client into
2925         *
2926         * @return whether the command succeeded or not
2927         *
2928         * @querycommands 1
2929         */
2930        public CommandFuture<Boolean> moveClient(ChannelBase channel) {
2931                return moveClient(channel.getId(), null);
2932        }
2933
2934        /**
2935         * Moves the server query into the specified channel using the specified password.
2936         *
2937         * @param channelId
2938         *              the ID of the channel to move the client into
2939         * @param channelPassword
2940         *              the password of the channel
2941         *
2942         * @return whether the command succeeded or not
2943         *
2944         * @querycommands 1
2945         * @see Channel#getId()
2946         */
2947        public CommandFuture<Boolean> moveClient(int channelId, String channelPassword) {
2948                return moveClient(0, channelId, channelPassword);
2949        }
2950
2951        /**
2952         * Moves the server query into the specified channel using the specified password.
2953         *
2954         * @param channel
2955         *              the channel to move the client into
2956         * @param channelPassword
2957         *              the password of the channel
2958         *
2959         * @return whether the command succeeded or not
2960         *
2961         * @querycommands 1
2962         */
2963        public CommandFuture<Boolean> moveClient(ChannelBase channel, String channelPassword) {
2964                return moveClient(0, channel.getId(), channelPassword);
2965        }
2966
2967        /**
2968         * Moves a client into the specified channel.
2969         *
2970         * @param clientId
2971         *              the ID of the client to move
2972         * @param channelId
2973         *              the ID of the channel to move the client into
2974         *
2975         * @return whether the command succeeded or not
2976         *
2977         * @querycommands 1
2978         * @see Client#getId()
2979         * @see Channel#getId()
2980         */
2981        public CommandFuture<Boolean> moveClient(int clientId, int channelId) {
2982                return moveClient(clientId, channelId, null);
2983        }
2984
2985        /**
2986         * Moves a client into the specified channel.
2987         *
2988         * @param client
2989         *              the client to move
2990         * @param channel
2991         *              the channel to move the client into
2992         *
2993         * @return whether the command succeeded or not
2994         *
2995         * @querycommands 1
2996         */
2997        public CommandFuture<Boolean> moveClient(Client client, ChannelBase channel) {
2998                return moveClient(client.getId(), channel.getId(), null);
2999        }
3000
3001        /**
3002         * Moves a client into the specified channel using the specified password.
3003         *
3004         * @param clientId
3005         *              the ID of the client to move
3006         * @param channelId
3007         *              the ID of the channel to move the client into
3008         * @param channelPassword
3009         *              the password of the channel
3010         *
3011         * @return whether the command succeeded or not
3012         *
3013         * @querycommands 1
3014         * @see Client#getId()
3015         * @see Channel#getId()
3016         */
3017        public CommandFuture<Boolean> moveClient(int clientId, int channelId, String channelPassword) {
3018                final CClientMove move = new CClientMove(clientId, channelId, channelPassword);
3019                return executeAndReturnError(move);
3020        }
3021
3022        /**
3023         * Moves a client into the specified channel using the specified password.
3024         *
3025         * @param client
3026         *              the client to move
3027         * @param channel
3028         *              the channel to move the client into
3029         * @param channelPassword
3030         *              the password of the channel
3031         *
3032         * @return whether the command succeeded or not
3033         *
3034         * @querycommands 1
3035         */
3036        public CommandFuture<Boolean> moveClient(Client client, ChannelBase channel, String channelPassword) {
3037                return moveClient(client.getId(), channel.getId(), channelPassword);
3038        }
3039
3040        /**
3041         * Pokes the client with the specified client ID.
3042         * This opens up a small popup window for the client containing your message and plays a sound.
3043         * The displayed message will be formatted like this: <br>
3044         * {@code hh:mm:ss - "Your Nickname" poked you: <your message in green color>}
3045         * <p>
3046         * The displayed message length is limited to 100 UTF-8 bytes.
3047         * If a client has already received a poke message, all subsequent pokes will simply add a line
3048         * to the already opened popup window and will still play a sound.
3049         * </p>
3050         *
3051         * @param clientId
3052         *              the ID of the client to poke
3053         * @param message
3054         *              the message to send, may contain BB codes
3055         *
3056         * @return whether the command succeeded or not
3057         *
3058         * @querycommands 1
3059         * @see Client#getId()
3060         */
3061        public CommandFuture<Boolean> pokeClient(int clientId, String message) {
3062                final CClientPoke poke = new CClientPoke(clientId, message);
3063                return executeAndReturnError(poke);
3064        }
3065
3066        /**
3067         * Terminates the connection with the TeamSpeak3 server.
3068         * This command should never be executed manually.
3069         *
3070         * @return whether the command succeeded or not
3071         *
3072         * @querycommands 1
3073         * @deprecated This command leaves the query in an undefined state,
3074         * where the connection is closed without the socket being closed and no more command can be executed.
3075         * To terminate a connection, use {@link TS3Query#exit()}.
3076         */
3077        @Deprecated
3078        public CommandFuture<Boolean> quit() {
3079                final CQuit quit = new CQuit();
3080                return executeAndReturnError(quit);
3081        }
3082
3083        /**
3084         * Registers the server query to receive notifications about all server events.
3085         * <p>
3086         * This means that the following actions will trigger event notifications:
3087         * </p>
3088         * <ul>
3089         * <li>A client joins the server or disconnects from it</li>
3090         * <li>A client switches channels</li>
3091         * <li>A client sends a server message</li>
3092         * <li>A client sends a channel message <b>in the channel the query is in</b></li>
3093         * <li>A client sends <b>the server query</b> a private message or a response to a private message</li>
3094         * </ul>
3095         * <p>
3096         * The limitations to when the query receives notifications about chat events cannot be circumvented.
3097         * </p>
3098         * To be able to process these events in your application, register an event listener.
3099         *
3100         * @return whether all commands succeeded or not
3101         *
3102         * @querycommands 5
3103         * @see #addTS3Listeners(TS3Listener...)
3104         */
3105        public CommandFuture<Boolean> registerAllEvents() {
3106                final CommandFuture<Boolean> future = new CommandFuture<>();
3107                final Collection<CommandFuture<Boolean>> eventFutures = new ArrayList<>(5);
3108
3109                eventFutures.add(registerEvent(TS3EventType.SERVER));
3110                eventFutures.add(registerEvent(TS3EventType.TEXT_SERVER));
3111                eventFutures.add(registerEvent(TS3EventType.CHANNEL, 0));
3112                eventFutures.add(registerEvent(TS3EventType.TEXT_CHANNEL, 0));
3113                eventFutures.add(registerEvent(TS3EventType.TEXT_PRIVATE));
3114
3115                CommandFuture.ofAll(eventFutures).onSuccess(new CommandFuture.SuccessListener<List<Boolean>>() {
3116                        @Override
3117                        public void handleSuccess(List<Boolean> result) {
3118                                future.set(true);
3119                        }
3120                }).forwardFailure(future);
3121                return future;
3122        }
3123
3124        /**
3125         * Registers the server query to receive notifications about a given event type.
3126         * <p>
3127         * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel.
3128         * If used with {@link TS3EventType#CHANNEL}, this will listen to <b>all</b> channel events.
3129         * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}.
3130         * </p>
3131         *
3132         * @param eventType
3133         *              the event type to be notified about
3134         *
3135         * @return whether the command succeeded or not
3136         *
3137         * @querycommands 1
3138         * @see #addTS3Listeners(TS3Listener...)
3139         * @see #registerEvent(TS3EventType, int)
3140         * @see #registerAllEvents()
3141         */
3142        public CommandFuture<Boolean> registerEvent(TS3EventType eventType) {
3143                if (eventType == TS3EventType.CHANNEL || eventType == TS3EventType.TEXT_CHANNEL) {
3144                        return registerEvent(eventType, 0);
3145                }
3146                return registerEvent(eventType, -1);
3147        }
3148
3149        /**
3150         * Registers the server query to receive notifications about a given event type.
3151         *
3152         * @param eventType
3153         *              the event type to be notified about
3154         * @param channelId
3155         *              the ID of the channel to listen to, will be ignored if set to {@code -1}.
3156         *              Can be set to {@code 0} for {@link TS3EventType#CHANNEL} to receive notifications about all channel switches.
3157         *
3158         * @return whether the command succeeded or not
3159         *
3160         * @querycommands 1
3161         * @see Channel#getId()
3162         * @see #addTS3Listeners(TS3Listener...)
3163         * @see #registerAllEvents()
3164         */
3165        public CommandFuture<Boolean> registerEvent(TS3EventType eventType, int channelId) {
3166                final CServerNotifyRegister register = new CServerNotifyRegister(eventType, channelId);
3167                return executeAndReturnError(register);
3168        }
3169
3170        /**
3171         * Registers the server query to receive notifications about multiple given event types.
3172         * <p>
3173         * If used with {@link TS3EventType#TEXT_CHANNEL}, this will listen to chat events in the current channel.
3174         * If used with {@link TS3EventType#CHANNEL}, this will listen to <b>all</b> channel events.
3175         * To specify a different channel for channel events, use {@link #registerEvent(TS3EventType, int)}.
3176         * </p>
3177         *
3178         * @param eventTypes
3179         *              the event types to be notified about
3180         *
3181         * @return whether the command succeeded or not
3182         *
3183         * @querycommands n, one command per TS3EventType
3184         * @see #addTS3Listeners(TS3Listener...)
3185         * @see #registerEvent(TS3EventType, int)
3186         * @see #registerAllEvents()
3187         */
3188        public CommandFuture<Boolean> registerEvents(TS3EventType... eventTypes) {
3189                final CommandFuture<Boolean> future = new CommandFuture<>();
3190                if (eventTypes.length == 0) {
3191                        future.set(true);
3192                        return future;
3193                }
3194
3195                final Collection<CommandFuture<Boolean>> registerFutures = new ArrayList<>(eventTypes.length);
3196                for (final TS3EventType type : eventTypes) {
3197                        registerFutures.add(registerEvent(type));
3198                }
3199
3200                CommandFuture.ofAll(registerFutures).onSuccess(new CommandFuture.SuccessListener<List<Boolean>>() {
3201                        @Override
3202                        public void handleSuccess(List<Boolean> result) {
3203                                future.set(true);
3204                        }
3205                }).forwardFailure(future);
3206                return future;
3207        }
3208
3209        /**
3210         * Removes the client specified by its database ID from the specified server group.
3211         *
3212         * @param serverGroupId
3213         *              the ID of the server group
3214         * @param clientDatabaseId
3215         *              the database ID of the client
3216         *
3217         * @return whether the command succeeded or not
3218         *
3219         * @querycommands 1
3220         * @see ServerGroup#getId()
3221         * @see Client#getDatabaseId()
3222         * @see #removeClientFromServerGroup(ServerGroup, Client)
3223         */
3224        public CommandFuture<Boolean> removeClientFromServerGroup(int serverGroupId, int clientDatabaseId) {
3225                final CServerGroupDelClient del = new CServerGroupDelClient(serverGroupId, clientDatabaseId);
3226                return executeAndReturnError(del);
3227        }
3228
3229        /**
3230         * Removes the specified client from the specified server group.
3231         *
3232         * @param serverGroup
3233         *              the server group to remove the client from
3234         * @param client
3235         *              the client to remove from the server group
3236         *
3237         * @return whether the command succeeded or not
3238         *
3239         * @querycommands 1
3240         * @see #removeClientFromServerGroup(int, int)
3241         */
3242        public CommandFuture<Boolean> removeClientFromServerGroup(ServerGroup serverGroup, Client client) {
3243                return removeClientFromServerGroup(serverGroup.getId(), client.getDatabaseId());
3244        }
3245
3246        /**
3247         * Removes one or more {@link TS3Listener}s to the event manager of the query.
3248         * <p>
3249         * If a listener was not actually registered, it will be ignored and no exception will be thrown.
3250         * </p>
3251         *
3252         * @param listeners
3253         *              one or more listeners to remove
3254         *
3255         * @see #addTS3Listeners(TS3Listener...)
3256         * @see TS3Listener
3257         * @see TS3EventType
3258         */
3259        public void removeTS3Listeners(TS3Listener... listeners) {
3260                query.getEventManager().removeListeners(listeners);
3261        }
3262
3263        /**
3264         * Renames the channel group with the specified ID.
3265         *
3266         * @param channelGroupId
3267         *              the ID of the channel group to rename
3268         * @param name
3269         *              the new name for the channel group
3270         *
3271         * @return whether the command succeeded or not
3272         *
3273         * @querycommands 1
3274         * @see ChannelGroup#getId()
3275         * @see #renameChannelGroup(ChannelGroup, String)
3276         */
3277        public CommandFuture<Boolean> renameChannelGroup(int channelGroupId, String name) {
3278                final CChannelGroupRename rename = new CChannelGroupRename(channelGroupId, name);
3279                return executeAndReturnError(rename);
3280        }
3281
3282        /**
3283         * Renames the specified channel group.
3284         *
3285         * @param channelGroup
3286         *              the channel group to rename
3287         * @param name
3288         *              the new name for the channel group
3289         *
3290         * @return whether the command succeeded or not
3291         *
3292         * @querycommands 1
3293         * @see #renameChannelGroup(int, String)
3294         */
3295        public CommandFuture<Boolean> renameChannelGroup(ChannelGroup channelGroup, String name) {
3296                return renameChannelGroup(channelGroup.getId(), name);
3297        }
3298
3299        /**
3300         * Renames the server group with the specified ID.
3301         *
3302         * @param serverGroupId
3303         *              the ID of the server group to rename
3304         * @param name
3305         *              the new name for the server group
3306         *
3307         * @return whether the command succeeded or not
3308         *
3309         * @querycommands 1
3310         * @see ServerGroup#getId()
3311         * @see #renameServerGroup(ServerGroup, String)
3312         */
3313        public CommandFuture<Boolean> renameServerGroup(int serverGroupId, String name) {
3314                final CServerGroupRename rename = new CServerGroupRename(serverGroupId, name);
3315                return executeAndReturnError(rename);
3316        }
3317
3318        /**
3319         * Renames the specified server group.
3320         *
3321         * @param serverGroup
3322         *              the server group to rename
3323         * @param name
3324         *              the new name for the server group
3325         *
3326         * @return whether the command succeeded or not
3327         *
3328         * @querycommands 1
3329         * @see #renameServerGroup(int, String)
3330         */
3331        public CommandFuture<Boolean> renameServerGroup(ServerGroup serverGroup, String name) {
3332                return renameChannelGroup(serverGroup.getId(), name);
3333        }
3334
3335        /**
3336         * Resets all permissions and deletes all server / channel groups. Use carefully.
3337         *
3338         * @return a token for a new administrator account
3339         *
3340         * @querycommands 1
3341         */
3342        public CommandFuture<String> resetPermissions() {
3343                final CPermReset reset = new CPermReset();
3344                return executeAndReturnStringProperty(reset, "token");
3345        }
3346
3347        /**
3348         * Moves the server query into the virtual server with the specified ID.
3349         *
3350         * @param id
3351         *              the ID of the virtual server
3352         *
3353         * @return whether the command succeeded or not
3354         *
3355         * @querycommands 1
3356         * @see VirtualServer#getId()
3357         * @see #selectVirtualServerByPort(int)
3358         * @see #selectVirtualServer(VirtualServer)
3359         */
3360        public CommandFuture<Boolean> selectVirtualServerById(int id) {
3361                final CUse use = new CUse(id, -1);
3362                return executeAndReturnError(use);
3363        }
3364
3365        /**
3366         * Moves the server query into the virtual server with the specified voice port.
3367         *
3368         * @param port
3369         *              the voice port of the virtual server
3370         *
3371         * @return whether the command succeeded or not
3372         *
3373         * @querycommands 1
3374         * @see VirtualServer#getPort()
3375         * @see #selectVirtualServerById(int)
3376         * @see #selectVirtualServer(VirtualServer)
3377         */
3378        public CommandFuture<Boolean> selectVirtualServerByPort(int port) {
3379                final CUse use = new CUse(-1, port);
3380                return executeAndReturnError(use);
3381        }
3382
3383        /**
3384         * Moves the server query into the specified virtual server.
3385         *
3386         * @param server
3387         *              the virtual server to move into
3388         *
3389         * @return whether the command succeeded or not
3390         *
3391         * @querycommands 1
3392         * @see #selectVirtualServerById(int)
3393         * @see #selectVirtualServerByPort(int)
3394         */
3395        public CommandFuture<Boolean> selectVirtualServer(VirtualServer server) {
3396                return selectVirtualServerById(server.getId());
3397        }
3398
3399        /**
3400         * Sends an offline message to the client with the given unique identifier.
3401         * <p>
3402         * The message subject's length is limited to 200 UTF-8 bytes and BB codes in it will be ignored.
3403         * The message body's length is limited to 4096 UTF-8 bytes and accepts BB codes
3404         * </p>
3405         *
3406         * @param clientUId
3407         *              the unique identifier of the client to send the message to
3408         * @param subject
3409         *              the subject for the message, may not contain BB codes
3410         * @param message
3411         *              the actual message body, may contain BB codes
3412         *
3413         * @return whether the command succeeded or not
3414         *
3415         * @querycommands 1
3416         * @see Client#getUniqueIdentifier()
3417         * @see Message
3418         */
3419        public CommandFuture<Boolean> sendOfflineMessage(String clientUId, String subject, String message) {
3420                final CMessageAdd add = new CMessageAdd(clientUId, subject, message);
3421                return executeAndReturnError(add);
3422        }
3423
3424        /**
3425         * Sends a text message either to the whole virtual server, a channel or specific client.
3426         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
3427         * <p>
3428         * To send a message to all virtual servers, use {@link #broadcast(String)}.
3429         * To send an offline message, use {@link #sendOfflineMessage(String, String, String)}.
3430         * </p>
3431         *
3432         * @param targetMode
3433         *              where the message should be sent to
3434         * @param targetId
3435         *              the client ID of the recipient of this message. This value is ignored unless {@code targetMode} is {@code CLIENT}
3436         * @param message
3437         *              the text message to send
3438         *
3439         * @return whether the command succeeded or not
3440         *
3441         * @querycommands 1
3442         * @see Client#getId()
3443         */
3444        public CommandFuture<Boolean> sendTextMessage(TextMessageTargetMode targetMode, int targetId, String message) {
3445                final CSendTextMessage msg = new CSendTextMessage(targetMode.getIndex(), targetId, message);
3446                return executeAndReturnError(msg);
3447        }
3448
3449        /**
3450         * Sends a text message to the channel with the specified ID.
3451         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
3452         * <p>
3453         * This will move the client into the channel with the specified channel ID,
3454         * <b>but will not move it back to the original channel!</b>
3455         * </p>
3456         *
3457         * @param channelId
3458         *              the ID of the channel to which the message should be sent to
3459         * @param message
3460         *              the text message to send
3461         *
3462         * @return whether the command succeeded or not
3463         *
3464         * @querycommands 1
3465         * @see #sendChannelMessage(String)
3466         * @see Channel#getId()
3467         */
3468        public CommandFuture<Boolean> sendChannelMessage(int channelId, final String message) {
3469                final CommandFuture<Boolean> future = new CommandFuture<>();
3470
3471                moveClient(channelId).onSuccess(new CommandFuture.SuccessListener<Boolean>() {
3472                        @Override
3473                        public void handleSuccess(Boolean result) {
3474                                sendTextMessage(TextMessageTargetMode.CHANNEL, 0, message).forwardResult(future);
3475                        }
3476                }).forwardFailure(future);
3477
3478                return future;
3479        }
3480
3481        /**
3482         * Sends a text message to the channel the server query is currently in.
3483         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
3484         *
3485         * @param message
3486         *              the text message to send
3487         *
3488         * @return whether the command succeeded or not
3489         *
3490         * @querycommands 1
3491         */
3492        public CommandFuture<Boolean> sendChannelMessage(String message) {
3493                return sendTextMessage(TextMessageTargetMode.CHANNEL, 0, message);
3494        }
3495
3496        /**
3497         * Sends a text message to the virtual server with the specified ID.
3498         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
3499         * <p>
3500         * This will move the client to the virtual server with the specified server ID,
3501         * <b>but will not move it back to the original virtual server!</b>
3502         * </p>
3503         *
3504         * @param serverId
3505         *              the ID of the virtual server to which the message should be sent to
3506         * @param message
3507         *              the text message to send
3508         *
3509         * @return whether the command succeeded or not
3510         *
3511         * @querycommands 1
3512         * @see #sendServerMessage(String)
3513         * @see VirtualServer#getId()
3514         */
3515        public CommandFuture<Boolean> sendServerMessage(int serverId, final String message) {
3516                final CommandFuture<Boolean> future = new CommandFuture<>();
3517
3518                selectVirtualServerById(serverId).onSuccess(new CommandFuture.SuccessListener<Boolean>() {
3519                        @Override
3520                        public void handleSuccess(Boolean result) {
3521                                sendTextMessage(TextMessageTargetMode.SERVER, 0, message).forwardResult(future);
3522                        }
3523                }).forwardFailure(future);
3524
3525                return future;
3526        }
3527
3528        /**
3529         * Sends a text message to the virtual server the server query is currently in.
3530         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
3531         *
3532         * @param message
3533         *              the text message to send
3534         *
3535         * @return whether the command succeeded or not
3536         *
3537         * @querycommands 1
3538         */
3539        public CommandFuture<Boolean> sendServerMessage(String message) {
3540                return sendTextMessage(TextMessageTargetMode.SERVER, 0, message);
3541        }
3542
3543        /**
3544         * Sends a private message to the client with the specified client ID.
3545         * Your message may contain BB codes, but its length is limited to 1024 UTF-8 bytes.
3546         *
3547         * @param clientId
3548         *              the ID of the client to send the message to
3549         * @param message
3550         *              the text message to send
3551         *
3552         * @return whether the command succeeded or not
3553         *
3554         * @querycommands 1
3555         * @see Client#getId()
3556         */
3557        public CommandFuture<Boolean> sendPrivateMessage(int clientId, String message) {
3558                return sendTextMessage(TextMessageTargetMode.CLIENT, clientId, message);
3559        }
3560
3561        /**
3562         * Sets a channel group for a client in a specific channel.
3563         *
3564         * @param groupId
3565         *              the ID of the group the client should join
3566         * @param channelId
3567         *              the ID of the channel where the channel group should be assigned
3568         * @param clientDBId
3569         *              the database ID of the client for which the channel group should be set
3570         *
3571         * @return whether the command succeeded or not
3572         *
3573         * @querycommands 1
3574         * @see ChannelGroup#getId()
3575         * @see Channel#getId()
3576         * @see Client#getDatabaseId()
3577         */
3578        public CommandFuture<Boolean> setClientChannelGroup(int groupId, int channelId, int clientDBId) {
3579                final CSetClientChannelGroup group = new CSetClientChannelGroup(groupId, channelId, clientDBId);
3580                return executeAndReturnError(group);
3581        }
3582
3583        /**
3584         * Sets the read flag to true for a given message. This will not delete the message.
3585         *
3586         * @param messageId
3587         *              the ID of the message for which the read flag should be set
3588         *
3589         * @return whether the command succeeded or not
3590         *
3591         * @querycommands 1
3592         * @see #setMessageReadFlag(int, boolean)
3593         */
3594        public CommandFuture<Boolean> setMessageRead(int messageId) {
3595                return setMessageReadFlag(messageId, true);
3596        }
3597
3598        /**
3599         * Sets the read flag to true for a given message. This will not delete the message.
3600         *
3601         * @param message
3602         *              the message for which the read flag should be set
3603         *
3604         * @return whether the command succeeded or not
3605         *
3606         * @querycommands 1
3607         * @see #setMessageRead(int)
3608         * @see #setMessageReadFlag(Message, boolean)
3609         * @see #deleteOfflineMessage(int)
3610         */
3611        public CommandFuture<Boolean> setMessageRead(Message message) {
3612                return setMessageReadFlag(message.getId(), true);
3613        }
3614
3615        /**
3616         * Sets the read flag for a given message. This will not delete the message.
3617         *
3618         * @param messageId
3619         *              the ID of the message for which the read flag should be set
3620         * @param read
3621         *              the boolean value to which the read flag should be set
3622         *
3623         * @return whether the command succeeded or not
3624         *
3625         * @querycommands 1
3626         * @see #setMessageRead(int)
3627         * @see #setMessageReadFlag(Message, boolean)
3628         * @see #deleteOfflineMessage(int)
3629         */
3630        public CommandFuture<Boolean> setMessageReadFlag(int messageId, boolean read) {
3631                final CMessageUpdateFlag flag = new CMessageUpdateFlag(messageId, read);
3632                return executeAndReturnError(flag);
3633        }
3634
3635        /**
3636         * Sets the read flag for a given message. This will not delete the message.
3637         *
3638         * @param message
3639         *              the message for which the read flag should be set
3640         * @param read
3641         *              the boolean value to which the read flag should be set
3642         *
3643         * @return whether the command succeeded or not
3644         *
3645         * @querycommands 1
3646         * @see #setMessageRead(Message)
3647         * @see #setMessageReadFlag(int, boolean)
3648         * @see #deleteOfflineMessage(int)
3649         */
3650        public CommandFuture<Boolean> setMessageReadFlag(Message message, boolean read) {
3651                return setMessageReadFlag(message.getId(), read);
3652        }
3653
3654        /**
3655         * Sets the nickname of the server query client.
3656         * The nickname must be between 3 and 30 UTF-8 bytes long and BB codes will be ignored.
3657         *
3658         * @param nickname
3659         *              the new nickname, may not contain any BB codes and may not be {@code null}
3660         *
3661         * @return whether the command succeeded or not
3662         *
3663         * @querycommands 1
3664         * @see #updateClient(Map)
3665         */
3666        public CommandFuture<Boolean> setNickname(String nickname) {
3667                final Map<ClientProperty, String> options = Collections.singletonMap(ClientProperty.CLIENT_NICKNAME, nickname);
3668                return updateClient(options);
3669        }
3670
3671        /**
3672         * Starts the virtual server with the specified ID.
3673         *
3674         * @param serverId
3675         *              the ID of the virtual server
3676         *
3677         * @return whether the command succeeded or not
3678         *
3679         * @querycommands 1
3680         */
3681        public CommandFuture<Boolean> startServer(int serverId) {
3682                final CServerStart start = new CServerStart(serverId);
3683                return executeAndReturnError(start);
3684        }
3685
3686        /**
3687         * Starts the specified virtual server.
3688         *
3689         * @param virtualServer
3690         *              the virtual server to start
3691         *
3692         * @return whether the command succeeded or not
3693         *
3694         * @querycommands 1
3695         */
3696        public CommandFuture<Boolean> startServer(VirtualServer virtualServer) {
3697                return startServer(virtualServer.getId());
3698        }
3699
3700        /**
3701         * Stops the virtual server with the specified ID.
3702         *
3703         * @param serverId
3704         *              the ID of the virtual server
3705         *
3706         * @return whether the command succeeded or not
3707         *
3708         * @querycommands 1
3709         */
3710        public CommandFuture<Boolean> stopServer(int serverId) {
3711                final CServerStop stop = new CServerStop(serverId);
3712                return executeAndReturnError(stop);
3713        }
3714
3715        /**
3716         * Stops the specified virtual server.
3717         *
3718         * @param virtualServer
3719         *              the virtual server to stop
3720         *
3721         * @return whether the command succeeded or not
3722         *
3723         * @querycommands 1
3724         */
3725        public CommandFuture<Boolean> stopServer(VirtualServer virtualServer) {
3726                return stopServer(virtualServer.getId());
3727        }
3728
3729        /**
3730         * Stops the entire TeamSpeak 3 Server instance by shutting down the process.
3731         * <p>
3732         * To have permission to use this command, you need to use the server query admin login.
3733         * </p>
3734         *
3735         * @return whether the command succeeded or not
3736         *
3737         * @querycommands 1
3738         */
3739        public CommandFuture<Boolean> stopServerProcess() {
3740                final CServerProcessStop stop = new CServerProcessStop();
3741                return executeAndReturnError(stop);
3742        }
3743
3744        /**
3745         * Unregisters the server query from receiving any event notifications.
3746         *
3747         * @return whether the command succeeded or not
3748         *
3749         * @querycommands 1
3750         */
3751        public CommandFuture<Boolean> unregisterAllEvents() {
3752                final CServerNotifyUnregister unr = new CServerNotifyUnregister();
3753                return executeAndReturnError(unr);
3754        }
3755
3756        /**
3757         * Updates several client properties for this server query instance.
3758         *
3759         * @param options
3760         *              the map of properties to update
3761         *
3762         * @return whether the command succeeded or not
3763         *
3764         * @querycommands 1
3765         * @see #editClient(int, Map)
3766         */
3767        public CommandFuture<Boolean> updateClient(Map<ClientProperty, String> options) {
3768                final CClientUpdate update = new CClientUpdate(options);
3769                return executeAndReturnError(update);
3770        }
3771
3772        /**
3773         * Generates new login credentials for the currently connected server query instance, using the given name.
3774         * <p>
3775         * <b>This will remove the current login credentials!</b> You won't be logged out, but after disconnecting,
3776         * the old credentials will no longer work. Make sure to not lock yourselves out!
3777         * </p>
3778         *
3779         * @param loginName
3780         *              the name for the server query login
3781         *
3782         * @return the generated password for the server query login
3783         *
3784         * @querycommands 1
3785         */
3786        public CommandFuture<String> updateServerQueryLogin(String loginName) {
3787                final CClientSetServerQueryLogin login = new CClientSetServerQueryLogin(loginName);
3788                return executeAndReturnStringProperty(login, "client_login_password");
3789        }
3790
3791        /**
3792         * Uses an existing privilege key to join a server or channel group.
3793         *
3794         * @param token
3795         *              the privilege key to use
3796         *
3797         * @return whether the command succeeded or not
3798         *
3799         * @querycommands 1
3800         * @see PrivilegeKey
3801         * @see #addPrivilegeKey(TokenType, int, int, String)
3802         * @see #usePrivilegeKey(PrivilegeKey)
3803         */
3804        public CommandFuture<Boolean> usePrivilegeKey(String token) {
3805                final CPrivilegeKeyUse use = new CPrivilegeKeyUse(token);
3806                return executeAndReturnError(use);
3807        }
3808
3809        /**
3810         * Uses an existing privilege key to join a server or channel group.
3811         *
3812         * @param privilegeKey
3813         *              the privilege key to use
3814         *
3815         * @return whether the command succeeded or not
3816         *
3817         * @querycommands 1
3818         * @see PrivilegeKey
3819         * @see #addPrivilegeKey(TokenType, int, int, String)
3820         * @see #usePrivilegeKey(String)
3821         */
3822        public CommandFuture<Boolean> usePrivilegeKey(PrivilegeKey privilegeKey) {
3823                return usePrivilegeKey(privilegeKey.getToken());
3824        }
3825
3826        /**
3827         * Gets information about the current server query instance.
3828         *
3829         * @return information about the server query instance
3830         *
3831         * @querycommands 1
3832         * @see #getClientInfo(int)
3833         */
3834        public CommandFuture<ServerQueryInfo> whoAmI() {
3835                final CWhoAmI whoAmI = new CWhoAmI();
3836                final CommandFuture<ServerQueryInfo> future = new CommandFuture<>();
3837
3838                query.doCommandAsync(whoAmI, new Callback() {
3839                        @Override
3840                        public void handle() {
3841                                if (hasFailed(whoAmI, future)) return;
3842                                future.set(new ServerQueryInfo(whoAmI.getFirstResponse().getMap()));
3843                        }
3844                });
3845                return future;
3846        }
3847
3848        /**
3849         * Executes a command, checking for failure and returning true if the command succeeded.
3850         *
3851         * @param command
3852         *              the command to execute
3853         *
3854         * @return whether the command succeeded or not
3855         */
3856        private CommandFuture<Boolean> executeAndReturnError(final Command command) {
3857                final CommandFuture<Boolean> future = new CommandFuture<>();
3858
3859                query.doCommandAsync(command, new Callback() {
3860                        @Override
3861                        public void handle() {
3862                                if (hasFailed(command, future)) return;
3863                                future.set(true);
3864                        }
3865                });
3866                return future;
3867        }
3868
3869        /**
3870         * Executes a command, checking for failure and returning a single
3871         * {@code String} property from the first response map.
3872         *
3873         * @param command
3874         *              the command to execute
3875         * @param property
3876         *              the name of the property to return
3877         *
3878         * @return the value of the specified {@code String} property
3879         */
3880        private CommandFuture<String> executeAndReturnStringProperty(final Command command, final String property) {
3881                final CommandFuture<String> future = new CommandFuture<>();
3882
3883                query.doCommandAsync(command, new Callback() {
3884                        @Override
3885                        public void handle() {
3886                                if (hasFailed(command, future)) return;
3887                                future.set(command.getFirstResponse().get(property));
3888                        }
3889                });
3890                return future;
3891        }
3892
3893        /**
3894         * Executes a command, checking for failure and returning a single
3895         * {@code Integer} property from the first response map.
3896         *
3897         * @param command
3898         *              the command to execute
3899         * @param property
3900         *              the name of the property to return
3901         *
3902         * @return the value of the specified {@code Integer} property
3903         */
3904        private CommandFuture<Integer> executeAndReturnIntProperty(final Command command, final String property) {
3905                final CommandFuture<Integer> future = new CommandFuture<>();
3906
3907                query.doCommandAsync(command, new Callback() {
3908                        @Override
3909                        public void handle() {
3910                                if (hasFailed(command, future)) return;
3911                                future.set(command.getFirstResponse().getInt(property));
3912                        }
3913                });
3914                return future;
3915        }
3916
3917        /**
3918         * If a command has failed (i.e. the error ID is not 0),
3919         * the future will be marked as failed and true will be returned.
3920         *
3921         * @param command
3922         *              the command to be checked for failure
3923         * @param future
3924         *              the future to be notified in case of a failure
3925         *
3926         * @return true if the command has failed
3927         *
3928         * @see Command
3929         */
3930        private boolean hasFailed(Command command, CommandFuture<?> future) {
3931                final QueryError error = command.getError();
3932                if (error.isSuccessful()) return false;
3933
3934                future.fail(error);
3935                return true;
3936        }
3937}