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