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