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