<?PHP
/**
 *                         ts3admin.class.php
 *                         ------------------                    
 *   begin                : 18. December 2009
 *   copyright            : (C) 2009-2013 Par0noid Solutions
 *   email                : contact@ts3admin.info
 *   version              : 0.7.0.0
 *   last modified        : 28. January 2014
 *
 *
 *  This file is a powerful library for querying TeamSpeak3 servers.
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/** 
 * The ts3admin.class.php is a powerful library that offers functions to communicate with Teamspeak 3 Servers from your website!
 * 
 * You can do everything, your creativity knows no bounds!
 * That library is faster than all other librarys because its optimized to find the shortest way to your information.
 * No unneeded PHP 5 OOP Stuff, just the basics!
 * There are a lot of professional developers and some big companys using my library.
 * The best thing is that you can use it for free under the terms of the GNU General Public License v3.
 * Take a look on the project website where you can find code examples, a manual and some other stuff.
 * 
 * @author      Par0noid Solutions <contact@ts3admin.info>
 * @version     0.7.0.0
 * @copyright   Copyright (c) 2009-2014, Stefan Z.
 * @package		ts3admin
 * @link        http://ts3admin.info
 */

class ts3admin {

//*******************************************************************************************	
//****************************************** Vars *******************************************
//*******************************************************************************************

/**
  * runtime is an private handle and configuration storage
  *
  * @author     Par0noid Solutions
  * @access		private
  */
	private $runtime = array('socket' => '', 'selected' => false, 'host' => '', 'queryport' => '10011', 'timeout' => 2, 'debug' => array(), 'fileSocket' => '');


//*******************************************************************************************	
//************************************ Public Functions *************************************
//******************************************************************************************

/**
  * banAddByIp
  *
  * Adds a new ban rule on the selected virtual server.
  *
  *	<b>Output:</b>
  * <code>
  * Array
  * {
  *  [banid] => 109
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$ip			clientIp
  * @param		integer	$time		bantime in seconds (0=unlimited)
  * @param		string	$banreason	Banreason [optional]
  * @return     array banId
  */
	function banAddByIp($ip, $time, $banreason = NULL) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(!empty($banreason)) { $msg = ' banreason='.$this->escapeText($banreason); } else { $msg = NULL; }

		return $this->getData('array', 'banadd ip='.$ip.' time='.$time.$msg);
	}

/**
  * banAddByUid
  *
  *	Adds a new ban rule on the selected virtual server.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [banid] => 110
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$uid		clientUniqueId
  * @param		integer	$time		bantime in seconds (0=unlimited)
  * @param		string	$banreason	Banreason [optional]
  * @return     array banId
  */
	function banAddByUid($uid, $time, $banreason = NULL) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(!empty($banreason)) { $msg = ' banreason='.$this->escapeText($banreason); } else { $msg = NULL; }
		
		return $this->getData('array', 'banadd uid='.$uid.' time='.$time.$msg);
	}

/**
  * banAddByName
  *
  *	Adds a new ban rule on the selected virtual server.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [banid] => 111
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$name		clientName
  * @param		integer	$time		bantime in seconds (0=unlimited)
  * @param		string	$banreason	Banreason [optional]
  * @return     array banId
  */
	function banAddByName($name, $time, $banreason = NULL) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(!empty($banreason)) { $msg = ' banreason='.$this->escapeText($banreason); } else { $msg = NULL; }
										
		return $this->getData('array', 'banadd name='.$this->escapeText($name).' time='.$time.$msg);
	}

/**
  * banClient
  * 
  * Bans the client specified with ID clid from the server. Please note that this will create two separate ban rules for the targeted clients IP address and his unique identifier.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [1] => 129
  *  [2] => 130
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $clid		clientId
  * @param		integer $time		bantime in seconds (0=unlimited)
  * @param		string	$banreason	Banreason [optional]
  * @return     array banIds
  */
	function banClient($clid, $time, $banreason = NULL) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(!empty($banreason)) { $msg = ' banreason='.$this->escapeText($banreason); } else { $msg = ''; }
		
		$result = $this->getData('plain', 'banclient clid='.$clid.' time='.$time.$msg);
		
		if($result['success']) {
			return $this->generateOutput(true, $result['errors'], $this->splitBanIds($result['data']));
		}else{
			return $this->generateOutput(false, $result['errors'], false);
		}
	}

/**
  * banDelete
  * 
  * Deletes the ban rule with ID banid from the server.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $banID	banID
  * @return     boolean success
  */
	function banDelete($banID) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'bandel banid='.$banID);
	}

/**
  * banDeleteAll
  * 
  * Deletes all active ban rules from the server.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @return     boolean success
  */
	function banDeleteAll() {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'bandelall');
	}

/**
  * banList
  * 
  * Displays a list of active bans on the selected virtual server.
  * 
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [banid] => 131
  *  [ip] => 1.2.3.4
  *  [name] => eugen
  *  [uid] => IYAntAcZHgVC7s3n3DNWmuJB/aM=
  *  [created] => 1286660391
  *  [duration] => 0
  *  [invokername] => Par0noid
  *  [invokercldbid] => 2086
  *  [invokeruid] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [reason] => insult
  *  [enforcements] => 0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @return     array banlist
  */
	function banList() {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }		
		return $this->getData('multi', 'banlist');
	}

/**
  * bindingList
  * 
  * Displays a list of IP addresses used by the server instance on multi-homed machines.
  *
  * <b>Output:</b><br>
  * <code>
  * Array
  * {
  *  [ip] => 0.0.0.0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @return     array bindingList
  */
	function bindingList() {
		return $this->getData('multi', 'bindinglist');
	}

/**
  * channelAddPerm
  * 
  * Adds a set of specified permissions to a channel. Multiple permissions can be added by providing the two parameters of each permission. A permission can be specified by permid or permsid.
  * 
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions['permissionID'] = 'permissionValue';
  * //or you could use Permission Name
  * $permissions['permissionName'] = 'permissionValue';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$cid			channelId
  * @param		array	$permissions	permissions
  * @return     boolean success
  */
	function channelAddPerm($cid, $permissions) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(count($permissions) > 0) {
			//Permissions given
			
			//Errorcollector
			$errors = array();
			
			//Split Permissions to prevent query from overload
			$permissions = array_chunk($permissions, 50, true);
			
			//Action for each splitted part of permission
			foreach($permissions as $permission_part)
			{
				//Create command_string for each command that we could use implode later
				$command_string = array();
				
				foreach($permission_part as $key => $value)
				{
					$command_string[] = (is_numeric($key) ? "permid=" : "permsid=").$this->escapeText($key).' permvalue='.$value;
				}
				
				$result = $this->getData('boolean', 'channeladdperm cid='.$cid.' '.implode('|', $command_string));
				
				if(!$result['success'])
				{
					foreach($result['errors'] as $error)
					{
						$errors[] = $error;
					}
				}
			}
			
			if(count($errors) == 0)
			{
				return $this->generateOutput(true, array(), true);
			}else{
				return $this->generateOutput(false, $errors, false);
			}
			
		}else{
			// No permissions given
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * channelClientAddPerm
  * 
  * Adds a set of specified permissions to a client in a specific channel. Multiple permissions can be added by providing the three parameters of each permission. A permission can be specified by permid or permsid.
  * 
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions['permissionID'] = 'permissionValue';
  * //or you could use Permission Name
  * $permissions['permissionName'] = 'permissionValue';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cid			channelID
  * @param		integer		$cldbid			clientDBID
  * @param		array		$permissions	permissions
  * @return     boolean success
  */
	function channelClientAddPerm($cid, $cldbid, $permissions) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(count($permissions) > 0) {
			//Permissions given
				
			//Errorcollector
			$errors = array();
				
			//Split Permissions to prevent query from overload
			$permissions = array_chunk($permissions, 50, true);
				
			//Action for each splitted part of permission
			foreach($permissions as $permission_part)
			{
				//Create command_string for each command that we could use implode later
				$command_string = array();
		
				foreach($permission_part as $key => $value)
				{
					$command_string[] = (is_numeric($key) ? "permid=" : "permsid=").$this->escapeText($key).' permvalue='.$value;
				}
		
				$result = $this->getData('boolean', 'channelclientaddperm cid='.$cid.' cldbid='.$cldbid.' '.implode('|', $command_string));
		
				if(!$result['success'])
				{
					foreach($result['errors'] as $error)
					{
						$errors[] = $error;
					}
				}
			}
				
			if(count($errors) == 0)
			{
				return $this->generateOutput(true, array(), true);
			}else{
				return $this->generateOutput(false, $errors, false);
			}
				
		}else{
			// No permissions given
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * channelClientDelPerm
  * 
  * Removes a set of specified permissions from a client in a specific channel. Multiple permissions can be removed at once. A permission can be specified by permid or permsid.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions[] = 'permissionID';
  * $permissions[] = 'permissionName';
  * //or
  * $permissions = array('permissionID', 'permissionName', 'permissionID');
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cid				channelID
  * @param		integer		$cldbid				clientDBID
  * @param		array		$permissions		permissions
  * @return     boolean success
  */
	function channelClientDelPerm($cid, $cldbid, $permissions) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		$permissionArray = array();
		
		if(count($permissions) > 0) {
			foreach($permissions AS $value) {
				$permissionArray[] = is_numeric($value) ? 'permid='.$value : 'permsid='.$value;
			}
			return $this->getData('boolean', 'channelclientdelperm cid='.$cid.' cldbid='.$cldbid.' '.implode('|', $permissionArray));
		}else{
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * channelClientPermList
  * 
  * Displays a list of permissions defined for a client in a specific channel.
  *
  * <b>Output:</b><br>
  * <code>
  * Array
  * {
  *  [cid] => 250 (only in first result)
  *  [cldbid] => 2086 (only in first result)
  *  [permid] => 12876 (if permsid = false)
  *  [permsid] => b_client_info_view (if permsid = true)
  *  [permvalue] => 1
  *  [permnegated] => 0
  *  [permskip] => 0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cid		channelID
  * @param		integer		$cldbid		clientDBID
  * @param		boolean		$permsid	displays permissionName instead of permissionID
  * @return     array	channelclientpermlist
  */
	function channelClientPermList($cid, $cldbid, $permsid = false) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }		
		return $this->getData('multi', 'channelclientpermlist cid='.$cid.' cldbid='.$cldbid.($permsid ? ' -permsid' : ''));
	}

/**
  * channelCreate
  * 
  * Creates a new channel using the given properties and displays its ID. Note that this command accepts multiple properties which means that you're able to specifiy all settings of the new channel at once.
  * 
  * <b style="color:red">Hint:</b> don't forget to set channel_flag_semi_permanent = 1 or channel_flag_permanent = 1
  * 
  * <b style="color:red">Hint:</b> you'll get an error if you want to create a channel without channel_name
  * 
  * <b>Input-Array like this:</b>
  * <code>
  * $data = array();
  * 
  * $data['setting'] = 'value';
  * $data['setting'] = 'value';
  * </code>
  * 
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cid] => 257
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		array $data properties
  * @return     array channelInfo
  */
	function channelCreate($data) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		$propertiesString = '';
		
		foreach($data as $key => $value) {
			$propertiesString .= ' '.$key.'='.$this->escapeText($value);
		}
		
		return $this->getData('array', 'channelcreate '.$propertiesString);
	}

/**
  * channelDelete
  * 
  * Deletes an existing channel by ID. If force is set to 1, the channel will be deleted even if there are clients within. The clients will be kicked to the default channel with an appropriate reason message.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $cid channelID
  * @param		integer $force {1|0} (default: 1)
  * @return     boolean success
  */
	function channelDelete($cid, $force = 1) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'channeldelete cid='.$cid.' force='.$force);
	}

/**
  * channelDelPerm
  * 
  * Removes a set of specified permissions from a channel. Multiple permissions can be removed at once. A permission can be specified by permid or permsid.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions[] = 'permissionID';
  * //or you could use
  * $permissions[] = 'permissionName';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cid				channelID
  * @param		array		$permissions		permissions
  * @return     boolean	success
  */
	function channelDelPerm($cid, $permissions) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		$permissionArray = array();
		
		if(count($permissions) > 0) {
			foreach($permissions AS $value) {
				$permissionArray[] = (is_numeric($value) ? 'permid=' : 'permsid=').$value;
			}
			return $this->getData('boolean', 'channeldelperm cid='.$cid.' '.implode('|', $permissionArray));
		}else{
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * channelEdit
  * 
  * Changes a channels configuration using given properties. Note that this command accepts multiple properties which means that you're able to change all settings of the channel specified with cid at once.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $data = array();
  *	
  * $data['setting'] = 'value';
  * $data['setting'] = 'value';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$cid	$channelID
  * @param		array	$data	edited settings
  * @return     boolean success
  */
	function channelEdit($cid, $data) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		$settingsString = '';
		
		foreach($data as $key => $value) {
			$settingsString .= ' '.$key.'='.$this->escapeText($value);
		}

		return $this->getData('boolean', 'channeledit cid='.$cid.$settingsString);
	}

/**
  * channelFind
  * 
  * displays a list of channels matching a given name pattern.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cid] => 2
  *  [channel_name] => Lobby
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$pattern	channelName
  * @return     array channelList 
  */
	function channelFind($pattern) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('multi', 'channelfind pattern='.$this->escapeText($pattern));
	}

/**
  * channelGroupAdd
  * 
  * Creates a new channel group using a given name and displays its ID. The optional type parameter can be used to create ServerQuery groups and template groups.
  *
  * <b>groupDbTypes:</b>
  *	<ol start="0">
  *		<li>template group (used for new virtual servers)</li>
  *		<li>regular group (used for regular clients)</li>
  *		<li>global query group (used for ServerQuery clients)</li>
  *	</ol>
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cgid] => 86
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$name	groupName
  * @param		integer	$type   groupDbType [optional] (default: 1)
  * @return     boolean success
  */
	function channelGroupAdd($name, $type = 1) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'channelgroupadd name='.$this->escapeText($name).' type='.$type);
	}

/**
  * channelGroupAddPerm
  * 
  * Adds a set of specified permissions to a channel group. Multiple permissions can be added by providing the two parameters of each permission. A permission can be specified by permid or permsid.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions['permissionID'] = 'permissionValue';
  * //or you could use:
  * $permissions['permissionName'] = 'permissionValue';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cgid			channelGroupID
  * @param		array		$permissions	permissions
  * @return     boolean success
  */
	function channelGroupAddPerm($cgid, $permissions) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(count($permissions) > 0) {
			//Permissions given
		
			//Errorcollector
			$errors = array();
		
			//Split Permissions to prevent query from overload
			$permissions = array_chunk($permissions, 50, true);
		
			//Action for each splitted part of permission
			foreach($permissions as $permission_part)
			{
				//Create command_string for each command that we could use implode later
				$command_string = array();
		
				foreach($permission_part as $key => $value)
				{
					$command_string[] = (is_numeric($key) ? "permid=" : "permsid=").$this->escapeText($key).' permvalue='.$value;
				}
		
				$result = $this->getData('boolean', 'channelgroupaddperm cgid='.$cgid.' '.implode('|', $command_string));
		
				if(!$result['success'])
				{
					foreach($result['errors'] as $error)
					{
						$errors[] = $error;
					}
				}
			}
		
			if(count($errors) == 0) {
				return $this->generateOutput(true, array(), true);
			}else{
				return $this->generateOutput(false, $errors, false);
			}
		
		}else{
			// No permissions given
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * channelGroupClientList
  * 
  * Displays all the client and/or channel IDs currently assigned to channel groups. All three parameters are optional so you're free to choose the most suitable combination for your requirement
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cid] => 2
  *  [cldbid] => 9
  *  [cgid] => 9
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $cid		channelID [optional]
  * @param		integer $cldbid		clientDBID [optional]
  * @param		integer $cgid		channelGroupID [optional]
  * @return     array channelGroupClientList
  */
	function channelGroupClientList($cid = NULL, $cldbid = NULL, $cgid = NULL) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		return $this->getData('multi', 'channelgroupclientlist'.(!empty($cid) ? ' cid='.$cid : '').(!empty($cldbid) ? ' cldbid='.$cldbid : '').(!empty($cgid) ? ' cgid='.$cgid : ''));
	}

/**
  * channelGroupCopy
  * 
  * Creates a copy of the channel group specified with scgid. If tcgid is set to 0, the server will create a new group. To overwrite an existing group, simply set tcgid to the ID of a designated target group. If a target group is set, the name parameter will be ignored. The type parameter can be used to create ServerQuery groups and template groups.
  *
  * <b>groupDbTypes:</b>
  *	<ol start="0">
  *		<li>template group (used for new virtual servers)</li>
  *		<li>regular group (used for regular clients)</li>
  *		<li>global query group (used for ServerQuery clients)</li>
  *	</ol>
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cgid] => 86
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$scgid	sourceChannelGroupID
  * @param		integer	$tcgid	targetChannelGroupID 
  * @param		integer $name	groupName
  * @param		integer	$type	groupDbType
  * @return     array groupId
  */
	function channelGroupCopy($scgid, $tcgid, $name, $type = 1) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'channelgroupcopy scgid='.$scgid.' tcgid='.$tcgid.' name='.$this->escapeText($name).' type='.$type);
	}

/**
  * channelGroupDelete
  * 
  * Deletes a channel group by ID. If force is set to 1, the channel group will be deleted even if there are clients within.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $cgid	channelGroupID
  * @param		integer $force	forces deleting channelGroup (default: 1)
  * @return     boolean success
  */
	function channelGroupDelete($cgid, $force = 1) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'channelgroupdel cgid='.$cgid.' force='.$force);
	}

/**
  * channelGroupDelPerm
  * 
  * Removes a set of specified permissions from the channel group. Multiple permissions can be removed at once. A permission can be specified by permid or permsid.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions[] = 'permissionID';
  * $permissions[] = 'permissionName';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cgid				channelGroupID
  * @param		array		$permissions		permissions
  * @return     boolean success
  */
	function channelGroupDelPerm($cgid, $permissions) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		$permissionArray = array();
		
		if(count($permissions) > 0) {
			foreach($permissions AS $value) {
				$permissionArray[] = (is_numeric($value) ? 'permid=' : 'permsid=').$value;
			}
			return $this->getData('boolean', 'channelgroupdelperm cgid='.$cgid.' '.implode('|', $permissionArray));
		}else{
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * channelGroupList
  * 
  * Displays a list of channel groups available on the selected virtual server.
  * 
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cgid] => 3
  *  [name] => Testname
  *  [type] => 0
  *  [iconid] => 100
  *  [savedb] => 1
  *  [sortid] => 0
  *  [namemode] => 0
  *  [n_modifyp] => 75
  *  [n_member_addp] => 50
  *  [n_member_removep] => 50
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @return     array channelGroupList
  */
	function channelGroupList() {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		return $this->getData('multi', 'channelgrouplist');
	}

/**
  * channelGroupPermList
  * 
  * Displays a list of permissions assigned to the channel group specified with cgid.
  * If the permsid option is specified, the output will contain the permission names instead of the internal IDs.
  * 
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [permid] => 8471 (displayed if permsid is false)
  *  [permsid] => i_channel_create_modify_with_codec_latency_factor_min (displayed if permsid is true)
  *  [permvalue] => 1
  *  [permnegated] => 0
  *  [permskip] => 0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cgid		channelGroupID
  * @param		boolean		$permsid	permsid
  * @return		array	channelGroupPermlist
  */
  	function channelGroupPermList($cgid, $permsid = false) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }		
		return $this->getData('multi', 'channelgrouppermlist cgid='.$cgid.($permsid ? ' -permsid' : ''));
	}

/**
  * channelGroupRename
  * 
  * Changes the name of a specified channel group.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $cgid groupID
  * @param		integer $name groupName
  * @return     boolean success
  */
	function channelGroupRename($cgid, $name) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'channelgrouprename cgid='.$cgid.' name='.$this->escapeText($name));
	}

/**
  * channelInfo
  *
  *	Displays detailed configuration information about a channel including ID, topic, description, etc.

  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [pid] => 0
  *  [channel_name] => Test
  *  [channel_topic] => 
  *  [channel_description] => 
  *  [channel_password] => cc97Pm4oOYq0J9fXDAgiWv/qScQ=
  *  [channel_codec] => 2
  *  [channel_codec_quality] => 7
  *  [channel_maxclients] => -1
  *  [channel_maxfamilyclients] => -1
  *  [channel_order] => 1
  *  [channel_flag_permanent] => 1
  *  [channel_flag_semi_permanent] => 0
  *  [channel_flag_default] => 0
  *  [channel_flag_password] => 0
  *  [channel_codec_latency_factor] => 1
  *  [channel_codec_is_unencrypted] => 1
  *  [channel_flag_maxclients_unlimited] => 1
  *  [channel_flag_maxfamilyclients_unlimited] => 0
  *  [channel_flag_maxfamilyclients_inherited] => 1
  *  [channel_filepath] => files\\virtualserver_1\\channel_2
  *  [channel_needed_talk_power] => 0
  *  [channel_forced_silence] => 0
  *  [channel_name_phonetic] => 
  *  [channel_icon_id] => 0
  *  [seconds_empty] => 61 (If it's a temporary channel with a channel delete delay)
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $cid channelID
  * @return     array channelInfo
  */
	function channelInfo($cid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'channelinfo cid='.$cid);
	}

/**
  * channelList
  * 
  * Displays a list of channels created on a virtual server including their ID, order, name, etc. The output can be modified using several command options.
  *
  * <br><b>Possible parameters:</b> [-topic] [-flags] [-voice] [-limits] [-icon]<br><br>
  *
  * <b>Output: (without parameters)</b>
  * <code>
  * Array
  * {
  *  [cid] => 2
  *  [pid] => 0
  *  [channel_order] => 1
  *  [channel_name] => Test
  *  [total_clients] => 0
  *  [channel_needed_subscribe_power] => 0
  * }
  * </code><br>
  * <b>Output: (from parameters)</b>
  * <code>
  * Array
  * {
  *  [-topic] => [channel_topic] => Default Channel has no topic
  *  [-flags] => [channel_flag_default] => 1
  *  [-flags] => [channel_flag_password] => 0
  *  [-flags] => [channel_flag_permanent] => 1
  *  [-flags] => [channel_flag_semi_permanent] => 0
  *  [-voice] => [channel_codec] => 2
  *  [-voice] => [channel_codec_quality] => 7
  *  [-voice] => [channel_needed_talk_power] => 0
  *  [-limits] => [total_clients_family] => 1
  *  [-limits] => [channel_maxclients] => -1
  *  [-limits] => [channel_maxfamilyclients] => -1
  *  [-icon] => [channel_icon_id] => 0
  * }
  * </code><br>
  * <b>Usage:</b>
  * <code>
  * $ts3->channelList(); //No parameters
  * $ts3->channelList("-flags"); //Single parameter
  * $ts3->channelList("-topic -flags -voice -limits -icon"); //Multiple parameters / all
  * </code><br>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string		$params		additional parameters [optional]
  * @return		array	channelList
  */
	function channelList($params = null) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		if(!empty($params)) { $params = ' '.$params; }
		
		return $this->getData('multi', 'channellist'.$params);
	}

/**
  * channelMove
  * 
  * Moves a channel to a new parent channel with the ID cpid. If order is specified, the channel will be sorted right under the channel with the specified ID. If order is set to 0, the channel will be sorted right below the new parent.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $cid	channelID
  * @param		integer $cpid	channelParentID
  * @param		integer $order	channelSortOrder
  * @return     boolean success
  */
	function channelMove($cid, $cpid, $order = null) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }		
		return $this->getData('boolean', 'channelmove cid='.$cid.' cpid='.$cpid.($order != null ? ' order='.$order : ''));
	}

/**
  * channelPermList
  * 
  * Displays a list of permissions defined for a channel.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cid] => 2 (only in first result)
  *  [permid] => 8471 (if permsid = false)
  *  [permsid] => i_channel_needed_delete_power (if permsid = true)
  *  [permvalue] => 1
  *  [permnegated] => 0
  *  [permskip] => 0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cid		channelID
  * @param		boolean		$permsid	displays permissionName instead of permissionID [optional]
  * @return     array channelpermlist
  */
	function channelPermList($cid, $permsid = false) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }	
		return $this->getData('multi', 'channelpermlist cid='.$cid.($permsid ? ' -permsid' : ''));
	}

/**
  * clientAddPerm
  * 
  * Adds a set of specified permissions to a client. Multiple permissions can be added by providing the three parameters of each permission. A permission can be specified by permid or permsid.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions['permissionID'] = array('permissionValue', 'permskip');
  * //or you could use Permission Name
  * $permissions['permissionName'] = array('permissionValue', 'permskip');
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$cldbid			clientDBID
  * @param		array	$permissions	permissions
  * @return     boolean success
  */
	function clientAddPerm($cldbid, $permissions) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(count($permissions) > 0) {
			//Permissions given
				
			//Errorcollector
			$errors = array();
				
			//Split Permissions to prevent query from overload
			$permissions = array_chunk($permissions, 50, true);
				
			//Action for each splitted part of permission
			foreach($permissions as $permission_part)
			{
				//Create command_string for each command that we could use implode later
				$command_string = array();
		
				foreach($permission_part as $key => $value)
				{
					$command_string[] = (is_numeric($key) ? "permid=" : "permsid=").$this->escapeText($key).' permvalue='.$this->escapeText($value[0]).' permskip='.$this->escapeText($value[1]);
				}
		
				$result = $this->getData('boolean', 'clientaddperm cldbid='.$cldbid.' '.implode('|', $command_string));
		
				if(!$result['success'])
				{
					foreach($result['errors'] as $error)
					{
						$errors[] = $error;
					}
				}
			}
				
			if(count($errors) == 0)
			{
				return $this->generateOutput(true, array(), true);
			}else{
				return $this->generateOutput(false, $errors, false);
			}
		}else{
			// No permissions given
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * clientDbDelete
  * 
  * Deletes a clients properties from the database.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $cldbid	clientDBID
  * @return     boolean success
  */
	function clientDbDelete($cldbid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'clientdbdelete cldbid='.$cldbid);
	}

/**
  * clientDbEdit
  * 
  * Changes a clients settings using given properties.
  *
  * <b>Input-Array like this:</b><br>
  * <br><code>
  * $data = array();
  * 
  * $data['property'] = 'value';
  * $data['property'] = 'value';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cldbid		clientDBID
  * @param		array		$data	 	clientProperties
  * @return     boolean success
  */
	function clientDbEdit($cldbid, $data) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		$settingsString = '';
		
		foreach($data as $key => $value) {
			$settingsString .= ' '.$key.'='.$this->escapeText($value);
		}
		
		return $this->getData('boolean', 'clientdbedit cldbid='.$cldbid.$settingsString);
	}

/**
  * clientDbFind
  * 
  * Displays a list of client database IDs matching a given pattern. You can either search for a clients last known nickname or his unique identity by using the -uid option.
  *
  * <b>Output:</b><br>
  * <code>
  * Array
  * {
  *  [cldbid] => 2
  * }
  *	</code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$pattern	clientName
  * @param		boolean	$uid		set true to add -uid param [optional]
  * @return     array clientList 
  */
	function clientDbFind($pattern, $uid = false) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('multi', 'clientdbfind pattern='.$this->escapeText($pattern).($uid ? ' -uid' : ''));
	}

/**
  * clientDbInfo
  *
  * Displays detailed database information about a client including unique ID, creation date, etc.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [client_unique_identifier] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [client_nickname] => par0noid
  *  [client_database_id] => 2
  *  [client_created] => 1361027850
  *  [client_lastconnected] => 1361027850
  *  [client_totalconnections] => 1
  *  [client_flag_avatar] => 
  *  [client_description] => 
  *  [client_month_bytes_uploaded] => 0
  *  [client_month_bytes_downloaded] => 0
  *  [client_total_bytes_uploaded] => 0
  *  [client_total_bytes_downloaded] => 0
  *  [client_icon_id] => 0
  *  [client_base64HashClientUID] => jneilbgomklpfnkjclkoggokfdmdlhnbbpmdpagh
  *  [client_lastip] => 127.0.0.1
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cldbid		clientDBID
  * @return     array	clientDbInfo
  */
	function clientDbInfo($cldbid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'clientdbinfo cldbid='.$cldbid);
	}

/**
  * clientDbList
  * 
  * Displays a list of client identities known by the server including their database ID, last nickname, etc.
  *
  * <br><b>Possible params:</b> [start={offset}] [duration={limit}] [-count]<br><br>
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [count] => 1 (if count parameter is set)
  *  [cldbid] => 2
  *  [client_unique_identifier] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [client_nickname] => par0noid
  *  [client_created] => 1361027850
  *  [client_lastconnected] => 1361027850
  *  [client_totalconnections] => 1
  *  [client_description] => 
  *  [client_lastip] => 127.0.0.1
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$start		offset [optional] (Default: 0)
  * @param		integer	$duration	limit [optional] (Default: -1)
  * @param		boolean	$count		set true to add -count param [optional]
  * @return     array clientdblist
  */
	function clientDbList($start = 0, $duration = -1, $count = false) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('multi', 'clientdblist start='.$start.' duration='.$duration.($count ? ' -count' : ''));
	}

/**
  * clientDelPerm
  * 
  * Removes a set of specified permissions from a client. Multiple permissions can be removed at once. A permission can be specified by permid or permsid.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $permissions = array();
  * $permissions['permissionID'] = 'permissionValue';
  * //or you could use Permission Name
  * $permissions['permissionName'] = 'permissionValue';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer		$cldbid				clientDBID
  * @param		array		$permissionIds		permissionIDs
  * @return     boolean success
  */
	function clientDelPerm($cldbid, $permissionIds) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		$permissionArray = array();
		
		if(count($permissionIds) > 0) {
			foreach($permissionIds AS $value) {
				$permissionArray[] = (is_numeric($value) ? 'permid=' : 'permsid=').$value;
			}
			return $this->getData('boolean', 'clientdelperm cldbid='.$cldbid.' '.implode('|', $permissionArray));
		}else{
			$this->addDebugLog('no permissions given');
			return $this->generateOutput(false, array('Error: no permissions given'), false);
		}
	}

/**
  * clientEdit
  * 
  * Changes a clients settings using given properties.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $data = array();
  *	
  * $data['property'] = 'value';
  * $data['property'] = 'value';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$clid 			clientID
  * @param		array	$data			clientProperties
  * @return     boolean success
  */
	function clientEdit($clid, $data) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		$settingsString = '';
		
		foreach($data as $key => $value) {
			$settingsString .= ' '.$key.'='.$this->escapeText($value);
		}
		
		return $this->getData('boolean', 'clientedit clid='.$clid.$settingsString);
	}

/**
  * clientFind
  * 
  * Displays a list of clients matching a given name pattern.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [clid] => 18
  *  [client_nickname] => par0noid
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$pattern	clientName
  * @return     array clienList
  */
	function clientFind($pattern) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('multi', 'clientfind pattern='.$this->escapeText($pattern));
	}

/**
  * clientGetDbIdFromUid
  * 
  * Displays the database ID matching the unique identifier specified by cluid.
  *
  *	<b>Output:</b>
  * <code>
  * Array
  * {
  *  [cluid] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [cldbid] => 2
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$cluid	clientUID
  * @return     array clientInfo
  */
	function clientGetDbIdFromUid($cluid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'clientgetdbidfromuid cluid='.$cluid);
	}

/**
  * clientGetIds
  * 
  * Displays all client IDs matching the unique identifier specified by cluid.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cluid] => nUixbdf/XakrrmsdffO30R/D8Gc=
  *  [clid] => 7
  *  [name] => Par0noid
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$cluid	clientUID
  * @return     array clientList 
  */
	function clientGetIds($cluid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('multi', 'clientgetids cluid='.$cluid);
	}

/**
  * clientGetNameFromDbid
  * 
  * Displays the unique identifier and nickname matching the database ID specified by cldbid.
  *
  *	<b>Output:</b>
  * <code>
  * Array
  * {
  *  [cluid] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [cldbid] => 2
  *  [name] => Par0noid
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$cldbid	clientDBID
  * @return     array clientInfo
  */
	function clientGetNameFromDbid($cldbid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'clientgetnamefromdbid cldbid='.$cldbid);
	}
	
/**
  * clientGetNameFromUid
  * 
  * Displays the database ID and nickname matching the unique identifier specified by cluid.
  *
  *	<b>Output:</b>
  * <code>
  * Array
  * {
  *  [cluid] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [cldbid] => 2
  *  [name] => Par0noid
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$cluid	clientUID
  * @return     array clientInfo
  */
	function clientGetNameFromUid($cluid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'clientgetnamefromuid cluid='.$cluid);
	}

/**
  * clientInfo
  * 
  * Displays detailed configuration information about a client including unique ID, nickname, client version, etc.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cid] => 2
  *  [client_idle_time] => 4445369
  *  [client_unique_identifier] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [client_nickname] => par0noid
  *  [client_version] => 3.0.9.2 [Build: 1351504843]
  *  [client_platform] => Windows
  *  [client_input_muted] => 1
  *  [client_output_muted] => 1
  *  [client_outputonly_muted] => 0
  *  [client_input_hardware] => 1
  *  [client_output_hardware] => 1
  *  [client_default_channel] => 
  *  [client_meta_data] => 
  *  [client_is_recording] => 0
  *  [client_login_name] => 
  *  [client_database_id] => 2
  *  [client_channel_group_id] => 5
  *  [client_servergroups] => 6
  *  [client_created] => 1361027850
  *  [client_lastconnected] => 1361027850
  *  [client_totalconnections] => 1
  *  [client_away] => 0
  *  [client_away_message] => 
  *  [client_type] => 0
  *  [client_flag_avatar] => 
  *  [client_talk_power] => 75
  *  [client_talk_request] => 0
  *  [client_talk_request_msg] => 
  *  [client_description] => 
  *  [client_is_talker] => 0
  *  [client_month_bytes_uploaded] => 0
  *  [client_month_bytes_downloaded] => 0
  *  [client_total_bytes_uploaded] => 0
  *  [client_total_bytes_downloaded] => 0
  *  [client_is_priority_speaker] => 0
  *  [client_nickname_phonetic] => 
  *  [client_needed_serverquery_view_power] => 75
  *  [client_default_token] => 
  *  [client_icon_id] => 0
  *  [client_is_channel_commander] => 0
  *  [client_country] => 
  *  [client_channel_group_inherited_channel_id] => 2
  *  [client_base64HashClientUID] => jneilbgomklpfnkjclkoggokfdmdlhnbbpmdpagh
  *  [connection_filetransfer_bandwidth_sent] => 0
  *  [connection_filetransfer_bandwidth_received] => 0
  *  [connection_packets_sent_total] => 12130
  *  [connection_bytes_sent_total] => 542353
  *  [connection_packets_received_total] => 12681
  *  [connection_bytes_received_total] => 592935
  *  [connection_bandwidth_sent_last_second_total] => 82
  *  [connection_bandwidth_sent_last_minute_total] => 92
  *  [connection_bandwidth_received_last_second_total] => 84
  *  [connection_bandwidth_received_last_minute_total] => 88
  *  [connection_connected_time] => 5908749
  *  [connection_client_ip] => 127.0.0.1
  * } 
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$clid	clientID
  * @return     array	clientInformation
  */
	function clientInfo($clid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'clientinfo clid='.$clid);
	}

/**
  * clientKick
  * 
  * Kicks one or more clients specified with clid from their currently joined channel or from the server, depending on reasonid. The reasonmsg parameter specifies a text message sent to the kicked clients. This parameter is optional and may only have a maximum of 40 characters.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $clid		clientID
  * @param		string	$kickMode	kickMode (server or channel) (Default: servera)
  * @param		string	$kickmsg 	kick reason [optional]
  * @return     boolean success
  */
	function clientKick($clid, $kickMode = "server", $kickmsg = "") {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(in_array($kickMode, array('server', 'channel'))) {
		
			if($kickMode == 'server') { $from = '5'; }
			if($kickMode == 'channel') { $from = '4'; }
			
			if(!empty($kickmsg)) { $msg = ' reasonmsg='.$this->escapeText($kickmsg); } else{ $msg = ''; }
			
			return $this->getData('boolean', 'clientkick clid='.$clid.' reasonid='.$from.$msg);
		}else{
			$this->addDebugLog('invalid kickMode');
			return $this->generateOutput(false, array('Error: invalid kickMode'), false);
		}
	}

/**
  * clientList
  * 
  * Displays a list of clients online on a virtual server including their ID, nickname, status flags, etc. The output can be modified using several command options. Please note that the output will only contain clients which are currently in channels you're able to subscribe to.
  *
  * <br><b>Possible params:</b> [-uid] [-away] [-voice] [-times] [-groups] [-info] [-icon] [-country] [-ip] [-badges]<br><br>
  *
  * <b>Output: (without parameters)</b>
  * <code>
  * Array
  * {
  *  [clid] => 1
  *  [cid] => 1
  *  [client_database_id] => 2
  *  [client_nickname] => Par0noid
  *  [client_type] => 0
  *  [-uid] => [client_unique_identifier] => nUixbsq/XakrrmbqU8O30R/D8Gc=
  *  [-away] => [client_away] => 0
  *  [-away] => [client_away_message] => 
  *  [-voice] => [client_flag_talking] => 0
  *  [-voice] => [client_input_muted] => 0
  *  [-voice] => [client_output_muted] => 0
  *  [-voice] => [client_input_hardware] => 0
  *  [-voice] => [client_output_hardware] => 0
  *  [-voice] => [client_talk_power] => 0
  *  [-voice] => [client_is_talker] => 0
  *  [-voice] => [client_is_priority_speaker] => 0
  *  [-voice] => [client_is_recording] => 0
  *  [-voice] => [client_is_channel_commander] => 0
  *  [-times] => [client_idle_time] => 1714
  *  [-times] => [client_created] => 1361027850
  *  [-times] => [client_lastconnected] => 1361042955
  *  [-groups] => [client_servergroups] => 6,7
  *  [-groups] => [client_channel_group_id] => 8
  *  [-groups] => [client_channel_group_inherited_channel_id] => 1
  *  [-info] => [client_version] => 3.0.9.2 [Build: 1351504843]
  *  [-info] => [client_platform] => Windows
  *  [-icon] => [client_icon_id] => 0
  *  [-country] => [client_country] => 
  *  [-ip] => [connection_client_ip] => 127.0.0.1
  *  [-badges] => [client_badges] => Overwolf=0
  * }
  * </code><br>
  * <b>Usage:</b>
  * <code>
  * $ts3->clientList(); //No parameters
  * $ts3->clientList("-uid"); //Single parameter
  * $ts3->clientList("-uid -away -voice -times -groups -info -country -icon -ip -badges"); //Multiple parameters
  * </code><br>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$params	additional parameters [optional]
  * @return     array clientList 
  */
	function clientList($params = null) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if(!empty($params)) { $params = ' '.$params; }
		
		return $this->getData('multi', 'clientlist'.$params);
	}

/**
  * clientMove
  * 
  * Moves one or more clients specified with clid to the channel with ID cid. If the target channel has a password, it needs to be specified with cpw. If the channel has no password, the parameter can be omitted.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $clid	clientID
  * @param		integer $cid	channelID
  * @param		string	$cpw	channelPassword [optional]
  * @return     boolean success
  */
	function clientMove($clid, $cid, $cpw = null) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'clientmove clid='.$clid.' cid='.$cid.(!empty($cpw) ? ' cpw='.$this->escapeText($cpw) : ''));
	}

/**
  * clientPermList
  * 
  * Displays a list of permissions defined for a client.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [permid] => 20654 //with permsid = false
  *  [permsid] => b_client_ignore_bans //with permsid = true
  *  [permvalue] => 1
  *  [permnegated] => 0
  *  [permskip] => 0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		intege		$cldbid 	clientDBID
  * @param		boolean		$permsid	set true to add -permsid param [optional]
  * @return     array clientPermList
  */
	function clientPermList($cldbid, $permsid = false) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }		
		return $this->getData('multi', 'clientpermlist cldbid='.$cldbid.($permsid ? ' -permsid' : ''));
	}

/**
  * clientPoke
  * 
  * Sends a poke message to the client specified with clid.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $clid	clientID
  * @param		string 	$msg 	pokeMessage
  * @return     boolean success
  */
	function clientPoke($clid, $msg) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'clientpoke clid='.$clid.' msg='.$this->escapeText($msg));
	}

/**
  * clientSetServerQueryLogin
  * 
  * Updates your own ServerQuery login credentials using a specified username. The password will be auto-generated.
  * 
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [client_login_password] => +r\/TQqvR
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$username	username
  * @return     array userInfomation
  */
	function clientSetServerQueryLogin($username) {
		return $this->getData('array', 'clientsetserverquerylogin client_login_name='.$this->escapeText($username));
	}

/**
  * clientUpdate
  * 
  * Change your ServerQuery clients settings using given properties.
  * 
  * <b>Input-Array like this:</b>
  * <code>
  * $data = array();
  * $data['property'] = 'value';
  * $data['property'] = 'value';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		array	$data	clientProperties
  * @return     boolean success
  */
	function clientUpdate($data) {
		$settingsString = '';
		
		foreach($data as $key => $value) {
			$settingsString .= ' '.$key.'='.$this->escapeText($value);
		}
		
		return $this->getData('boolean', 'clientupdate '.$settingsString);
	}

/**
  * complainAdd
  *
  * Submits a complaint about the client with database ID tcldbid to the server.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $tcldbid	targetClientDBID
  * @param		string	$msg		complainMessage
  * @return     boolean success
  */
	function complainAdd($tcldbid, $msg) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'complainadd tcldbid='.$tcldbid.' message='.$this->escapeText($msg));
	}

/**
  * complainDelete
  * 
  * Deletes the complaint about the client with ID tcldbid submitted by the client with ID fcldbid from the server.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $tcldbid targetClientDBID
  * @param		integer $fcldbid fromClientDBID
  * @return     boolean success
  */
	function complainDelete($tcldbid, $fcldbid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'complaindel tcldbid='.$tcldbid.' fcldbid='.$fcldbid);
	}

/**
  * complainDeleteAll
  * 
  * Deletes all complaints about the client with database ID tcldbid from the server.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer $tcldbid targetClientDBID
  * @return     boolean success
  */
	function complainDeleteAll($tcldbid) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'complaindelall tcldbid='.$tcldbid);
	}

/**
  * complainList
  * 
  * Displays a list of complaints on the selected virtual server. If tcldbid is specified, only complaints about the targeted client will be shown.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [tcldbid] => 2
  *  [tname] => par0noid
  *  [fcldbid] => 1
  *  [fname] => serveradmin from 127.0.0.1:6814
  *  [message] => Steals crayons
  *  [timestamp] => 1361044090
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string $tcldbid	targetClientDBID [optional]
  * @return     array complainList
  */
	function complainList($tcldbid = null) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		if(!empty($tcldbid)) { $tcldbid = ' tcldbid='.$tcldbid; }
		return $this->getData('multi', 'complainlist'.$tcldbid);
	}

/**
  * execOwnCommand
  * 
  * executes a command that isn't defined in class and returns data like your propose
  * 
  * <b>Modes:</b>
  * <ul>
  * 	<li><b>0:</b> execute -> return boolean</li>
  * 	<li><b>1:</b> execute -> return normal array</li>
  * 	<li><b>2:</b> execute -> return multidimensional array</li>
  * 	<li><b>3:</b> execute -> return plaintext serverquery</li>
  * </ul>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$mode		executionMode
  * @param		string	$command	command
  * @return     mixed result
  */
	function execOwnCommand($mode, $command) {
		if($mode == '0') {
			return $this->getData('boolean', $command);
		}
		if($mode == '1') {
			return $this->getData('array', $command);
		}
		if($mode == '2') {
			return $this->getData('multi', $command);
		}
		if($mode == '3') {
			return $this->getData('plain', $command);
		}
	}

/**
  * ftCreateDir
  * 
  * Creates new directory in a channels file repository.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$cid		channelId
  * @param		string	$cpw		channelPassword (leave blank if not needed)
  * @param		string	$dirname	dirPath
  * @return     boolean success
  */
	function ftCreateDir($cid, $cpw = null, $dirname) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('boolean', 'ftcreatedir cid='.$cid.' cpw='.$this->escapeText($cpw).' dirname='.$this->escapeText($dirname));
	}

/**
  * ftDeleteFile
  * 
  * Deletes one or more files stored in a channels file repository.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $files = array();
  *	
  * $files[] = '/pic1.jpg';
  * $files[] = '/dokumente/test.txt';
  * $files[] = '/dokumente';
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$cid	channelID
  * @param		string	$cpw	channelPassword (leave blank if not needed)
  * @param		array	$files	files
  * @return     boolean success
  */
	function ftDeleteFile($cid, $cpw = '', $files) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		$fileArray = array();
		
		if(count($files) > 0) {
			foreach($files AS $file) {
				$fileArray[] = 'name='.$this->escapeText($file);
			}
			return $this->getData('boolean', 'ftdeletefile cid='.$cid.' cpw='.$this->escapeText($cpw).' '.implode('|', $fileArray));
		}else{
			$this->addDebugLog('no files given');
			return $this->generateOutput(false, array('Error: no files given'), false);
		}
	}

/**
  * ftDownloadFile
  * 
  * Ddownloads a file and returns its contents
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		array	$data	return of ftInitDownload
  * @return     array downloadedFile
  */
	function ftDownloadFile($data) {
  		$this->runtime['fileSocket'] = @fsockopen($this->runtime['host'], $data['data']['port'], $errnum, $errstr, $this->runtime['timeout']);
  		if($this->runtime['fileSocket']) {
  			$this->ftSendKey($data['data']['ftkey']);
  			$content = $this->ftRead($data['data']['size']);
  			@fclose($this->runtime['fileSocket']);
  			$this->runtime['fileSocket'] = '';
  			return $content;
  		}else{
  			$this->addDebugLog('fileSocket returns '.$errnum. ' | '.$errstr);
  			return $this->generateOutput(false, array('Error in fileSocket: '.$errnum. ' | '.$errstr), false);
  		}
	}
	
/**
  * ftGetFileInfo
  * 
  * Displays detailed information about one or more specified files stored in a channels file repository.
  *
  * <b>Input-Array like this:</b>
  * <code>
  * $files = array();
  *	
  * $files[] = '/pic1.jpg';
  * $files[] = '/dokumente/test.txt';
  * $files[] = '/dokumente';
  * </code><br>
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cid] => 231
  *  [name] => /dfsdfsdf.txt
  *  [size] => 1412
  *  [datetime] => 1286634258
  * }
  * </code>
  * 
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$cid	channelID
  * @param		string	$cpw	channelPassword (leave blank if not needed)
  * @param		array	$files	files
  * @return     boolean success
  */
	function ftGetFileInfo($cid, $cpw = '', $files) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		$fileArray = array();
		
		if(count($files) > 0) {
			foreach($files AS $file) {
				$fileArray[] = 'name='.$this->escapeText($file);
			}
			return $this->getData('multi', 'ftgetfileinfo cid='.$cid.' cpw='.$this->escapeText($cpw).' '.implode('|', $fileArray));
		}else{
			$this->addDebugLog('no files given');
			return $this->generateOutput(false, array('Error: no files given'), false);
		}
	}

/**
  * ftGetFileList
  *
  * Displays a list of files and directories stored in the specified channels file repository.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [cid] => 231
  *  [path] => /
  *  [name] => Documents
  *  [size] => 0
  *  [datetime] => 1286633633
  *  [type] => 0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$cid	channelID
  * @param		string	$cpw	channelPassword (leave blank if not needed)
  * @param		string	$path	filePath
  * @return     array	fileList
  */
	function ftGetFileList($cid, $cpw = '', $path) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('multi', 'ftgetfilelist cid='.$cid.' cpw='.$this->escapeText($cpw).' path='.$this->escapeText($path));
	}
	
/**
  * ftInitDownload
  * 
  * Initializes a file transfer download. clientftfid is an arbitrary ID to identify the file transfer on client-side. On success, the server generates a new ftkey which is required to start downloading the file through TeamSpeak 3's file transfer interface.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [clientftfid] => 89
  *  [serverftfid] => 3
  *  [ftkey] => jSzWiRmFGdZnoJzW7BSDYJRUWB2WAUhb
  *  [port] => 30033
  *  [size] => 94
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$name			filePath
  * @param		string	$cid			channelID
  * @param		string	$cpw			channelPassword (leave blank if not needed)
  * @param		integer	$seekpos		seekpos (default = 0) [optional]
  * @return     array	initDownloadFileInfo
  */	
	function ftInitDownload($name, $cid, $cpw = '', $seekpos = 0) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('array', 'ftinitdownload clientftfid='.rand(1,99).' name='.$this->escapeText($name).' cid='.$cid.' cpw='.$this->escapeText($cpw).' seekpos='.$seekpos);
	}

/**
  * ftInitUpload
  * 
  * Initializes a file transfer upload. clientftfid is an arbitrary ID to identify the file transfer on client-side. On success, the server generates a new ftkey which is required to start uploading the file through TeamSpeak 3's file transfer interface.
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [clientftfid] => 84
  *  [serverftfid] => 41
  *  [ftkey] => HCnXpunOdAorqj3dGqfiuLszX18O0PHP
  *  [port] => 30033
  *  [seekpos] => 0
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		string	$filename	filePath
  * @param		string	$cid		channelID
  * @param		integer	$size		fileSize in bytes
  * @param		string	$cpw		channelPassword (leave blank if not needed)
  * @param		boolean	$overwrite	overwrite	[optional] (default = 0)
  * @param		boolean	$resume		resume		[optional] (default = 0)
  * @return     array	initUploadFileInfo
  */	
	function ftInitUpload($filename, $cid, $size, $cpw = '', $overwrite = false, $resume = false) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		
		if($overwrite) { $overwrite = ' overwrite=1'; }else{ $overwrite = ' overwrite=0'; }
		if($resume) { $resume = ' resume=1'; }else{ $resume = ' resume=0'; }
		
		return $this->getData('array', 'ftinitupload clientftfid='.rand(1,99).' name='.$this->escapeText($filename).' cid='.$cid.' cpw='.$this->escapeText($cpw).' size='.($size + 1).$overwrite.$resume);
	}
	
/**
  * ftList
  * 
  * Displays a list of running file transfers on the selected virtual server. The output contains the path to which a file is uploaded to, the current transfer rate in bytes per second, etc
  *
  * <b>Output:</b>
  * <code>
  * Array
  * {
  *  [clid] => 1
  *  [cldbid] => 2019
  *  [path] => files/virtualserver_11/channel_231
  *  [name] => 1285412348878.png
  *  [size] => 1161281
  *  [sizedone] => 275888
  *  [clientftfid] => 15
  *  [serverftfid] => 52
  *  [sender] => 0
  *  [status] => 1
  *  [current_speed] => 101037.4453
  *  [average_speed] => 101037.4453
  *  [runtime] => 2163
  * }
  * </code>
  *
  * @author     Par0noid Solutions
  * @access		public
  * @return     array	fileTransferList
  */
	function ftList() {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		return $this->getData('multi', 'ftlist');
	}

/**
  * ftRenameFile
  * 
  * Renames a file in a channels file repository. If the two parameters tcid and tcpw are specified, the file will be moved into another channels file repository.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$cid		channelID
  * @param		string	$cpw		channelPassword (leave blank if not needed)
  * @param		string	$oldname	oldFilePath
  * @param		string	$newname	newFilePath
  * @param		string  $tcid		targetChannelID [optional]
  * @param		string  $tcpw		targetChannelPassword [optional]
  * @return     boolean success
  */
	function ftRenameFile($cid, $cpw = null, $oldname, $newname, $tcid = null,  $tcpw = null) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }
		$newTarget = ($tcid != null ? ' tcid='.$tcid.' '.$tcpw : '');
		return $this->getData('boolean', 'ftrenamefile cid='.$cid.' cpw='.$cpw.' oldname='.$this->escapeText($oldname).' newname='.$this->escapeText($newname).$newTarget);
	}

/**
  * ftStop
  * 
  * Stops the running file transfer with server-side ID serverftfid.
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		integer	$serverftfid	serverFileTransferID
  * @param		boolean	$delete			delete incomplete file [optional] (default: true) 
  * @return     boolean success
  */
	function ftStop($serverftfid, $delete = true) {
		if(!$this->runtime['selected']) { return $this->checkSelected(); }		
		return $this->getData('boolean', 'ftstop serverftfid='.$serverftfid.' delete='.($delete ? '1' : '0'));
	}

/**
  * ftUploadFile
  * 
  * Uploads a file to server
  * To check if upload was successful, you have to search for this file in fileList after
  *
  * @author     Par0noid Solutions
  * @access		public
  * @param		array	$data			return of ftInitUpload
  * @param		string	$uploadData		data which should be uploaded
  * @return     array response
  */
?>