Вход Регистрация
Файл: core/classes/WebIcqPro.class.php
Строк: 2170
<?php
include(dirname(__FILE__) . '/rtf.class.php');
/**
 * http://wip.asminog.com/forum/ - disscus this package here.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * See http://www.gnu.org/copyleft/lesser.html
 * 
 * @author Sergey Akudovich
 * @package WebIcqPro
 *
 */

class WebIcqPro_LV {
    const 
VERSION '1.4.8b';
    
/**
     * Last error message
     *
     * @var string
     */
    
public $error;

    protected function 
packLV($value$length 'n')
    {
        return 
pack($lengthstrlen($value)).$value;
    }

    protected function 
unpackLV(&$data$length 2)
    {
        if (
strlen($data)>=$length)
        {
            
$result unpack('nsize'$data);
            
$result['data'] = substr($data$length $result['size']);
            
$data substr($data$result['size']+$length);
            return 
$result['data'];
        }
        
$this->error 'unpackLV brocken LV';
        return 
false;
    }
}

/**
 * Layer for TLV (Type-Length-Value) data format 
 * @access private
 */
class WebIcqPro_TLV extends WebIcqPro_LV {

    
/**
     * Pack data to TLV
     * 
     * Use this method to create TLVs. available formats 'c', 'n', 'N', 'a*', 'H*'
     *
     * @access private
     * @param integer $type
     * @param string $value
     * @param string $format
     * @return string binary
     */
    
protected function packTLV($type$value=''$format 'a*')
    {
        if (
in_array($format, array('c''n''N''a*''H*')))
        {
            
$len strlen($value);
            switch (
$format) {
                case 
'c':
                    
$len 1;
                    break;
                case 
'n':
                    
$len 2;
                    break;
                case 
'N':
                    
$len 4;
                    break;
                case 
'H*':
                    
$len strlen($value)/2;
                    break;
            }
            return 
pack('nn'.$format$type$len$value);
        }
        
$this->error 'Warning: packTLV unknown data format: '.$format;
        return 
false;
    }

    
/**
     * Unpack data from TLV
     * 
     * Use this method to extract TLV binary data
     *
     * @access private
     * @param string $data
     * @return string
     */
    
protected function unpackTLV(& $data)
    {
        if (
strlen($data)>=4)
        {
            
$result unpack('ntype/nsize'$data);
            
$result['data'] = substr($data$result['size']);
            
$data substr($data$result['size']+4);
            return 
$result;
        }
        
$this->error 'Error: unpackTLV brocken TLV';
        return 
false;
    }

    
/**
     * Unpuck data from set of TLVs
     * 
     * Use this method to extract a set of TLVs binary data to an associated array where array indexes are TLV type and value TLV data
     *
     * @access private
     * @param string $data
     * @return array
     */
    
protected function splitTLVsToArray($data$fixqip false)
    {
        
$tlv = array();
        while (
strlen($data) > 0)
        {
            
$tlv_data $this->unpackTLV($data);
            if (
$tlv_data)
            {
                
$tlv[$tlv_data['type']] = $tlv_data['data'];
            }
            elseif(
$fixqip)
            {
                if(isset(
$tlv[0x0101]))
                {
                    
$tlv[0x0101] = substr($tlv[0x0101], , -1);
                }
                return 
$tlv;
            }
            else
            {
                return 
false;
            }
        }
        return 
$tlv;
    }
}

/**
 * Layer for SNAC data format
 * @access private
 */
class WebIcqPro_SNAC extends WebIcqPro_TLV {

    
/**
     * Request counter
     *
     * This variable store request id for SNAC
     * 
     * @access private
     * @var integer
     */
    
private $request_id;

    
/**
     * Capability for message
     *
     * This variable store current method of capability for ibcm
     * 
     * @access private
     * @var string
     */
    
private $ibcm_capabilities;

    
/**
     * Message type
     * 
     * This variable store current type of ibcm
     *
     * @access private
     * @var string
     */
    
private $ibcm_type;

    
/**
     * Message Encoding
     * 
     * This variable store current encoding of message
     *
     * @access private
     * @var string
     */
    
private $ibcm_encoding;

    
/**
     * Client type.
     * 
     * This variable used to store current client name.
     *
     * @access private
     * @var string
     */
    
protected $agent;

    
/**
     * Array of known SNACs
     * 
     * Full list of supported SNACs. All added handlers should be added here.
     *
     * @access private
     * @var array
     */
    
protected $snac_names = array(
    
0x01 => array( // Generic service controls
    
0x02 => 'ClientReady',
    
0x03 => 'ServerFamilies',
    
0x06 => 'ClientRates',
    
0x07 => 'ServerRates',
    
0x08 => 'ClientRatesAck',
    
0x0E => 'ClientRequestSelfInfo',
    
0x0F => 'ServerResponseSelfInfo',
    
0x11 => 'ClientIdleTime',
    
0x17 => 'ClientFamiliesVersions',
    
0x18 => 'ServerFamiliesVersions',
    
0x1E => 'ClientStatus',
    
'version' => 0x04
    
),
    
0x02 => array( // Location services
    
0x02 => 'ClientLocationRights',
    
0x03 => 'ServerLocationRights',
    
0x04 => 'ClientLocationInfo',
    
'version' => 0x01
    
),
    
0x03 => array( // Buddy List management service
    
0x01 => 'OscarError',
    
0x02 => 'ClientBuddylistRights',
    
0x03 => 'ServerBuddylistRights',
    
0x04 => 'ClientBuddylistAdd',
    
0x05 => 'ClientBuddylistDelete',
    
0x0B => 'ServerUserOnline',
    
0x0C => 'ServerUserOffline',
    
'version' => 0x01
    
),
    
0x04 => array( // ICBM (messages) service
    
0x01 => 'OscarError',
    
0x02 => 'ClientSetIBCMParams',
    
0x04 => 'ClientIBCMRights',
    
0x05 => 'ServerIBCMRights',
    
0x06 => 'ClientIBCM',
    
0x07 => 'ServerIBCM',
    
0x0B => 'ClientIBCMAck',
    
0x0C => 'ServerIBCMAck',
    
'version' => 0x01
    
),
    
0x09 => array( // Privacy management service
    
0x02 => 'ClientPrivicyRights',
    
0x03 => 'ServerPrivicyRights',
    
'version' => 0x01
    
),
    
0x13 => array( // Server Side Information (SSI) service
    
0x01 => 'ServerSSIError',
    
0x02 => 'ClientSSIRights',
    
0x03 => 'ServerSSIRights',
    
0x04 => 'ClientSSI',
    
0x05 => 'ClientSSICheckout',
    
0x06 => 'ServerSSI',
    
0x07 => 'ClientSSIActivate',
    
0x08 => 'ClientSSIAdd',
    
0x0A => 'ClientSSIDelete',
    
0x0E => 'ServerSSIAck',
    
0x0F => 'ServerSSIModificationDate',
    
0x11 => 'ClientSSIEditStart',
    
0x12 => 'ClientSSIEditEnd',
    
0x18 => 'ClientSSIAuthRequest',
    
0x19 => 'ServerSSIAuthRequest',
    
0x1a => 'ClientSSIAuthResponse',
    
0x1b => 'ServerSSIAuthResponse',
    
0x1c => 'ServerSSIYouAdded',
    
'version' => 0x04
    
),
    
0x15 => array( // ICQ specific extensions service
    
0x02 => 'ClientMetaData',
    
0x03 => 'ServerMetaData',
    
'version' => 0x01
    
),
    
0x17 => array( // Authorization/registration service
    
0x02 => 'ClientMd5Login',
    
0x03 => 'ServerMd5LoginReply',
    
0x06 => 'ClientMd5Request',
    
0x07 => 'ServerMd5Response',
    
'version' => 0x01
    
)
    );

    public 
$debug false;

    protected 
$protocol_version 11;
    protected 
$capability_flag '03000000';

    private 
$rates;
    protected 
$rates_groups;

    private 
$login_errors = array(
    
0x0001 => 'Invalid nick or password',
    
0x0002 => 'Service temporarily unavailable',
    
0x0003 => 'All other errors',
    
0x0004 => 'Incorrect nick or password, re-enter',
    
0x0005 => 'Mismatch nick or password, re-enter',
    
0x0006 => 'Internal client error (bad input to authorizer)',
    
0x0007 => 'Invalid account',
    
0x0008 => 'Deleted account',
    
0x0009 => 'Expired account',
    
0x000A => 'No access to database',
    
0x000B => 'No access to resolver',
    
0x000C => 'Invalid database fields',
    
0x000D => 'Bad database status',
    
0x000E => 'Bad resolver status',
    
0x000F => 'Internal error',
    
0x0010 => 'Service temporarily offline',
    
0x0011 => 'Suspended account',
    
0x0012 => 'DB send error',
    
0x0013 => 'DB link error',
    
0x0014 => 'Reservation map error',
    
0x0015 => 'Reservation link error',
    
0x0016 => 'The users num connected from this IP has reached the maximum',
    
0x0017 => 'The users num connected from this IP has reached the maximum (reservation)',
    
0x0018 => 'Rate limit exceeded (reservation). Please try to reconnect in a few minutes',
    
0x0019 => 'User too heavily warned',
    
0x001A => 'Reservation timeout',
    
0x001B => 'You are using an older version of ICQ. Upgrade required',
    
0x001C => 'You are using an older version of ICQ. Upgrade recommended',
    
0x001D => 'Rate limit exceeded. Please try to reconnect in a few minutes',
    
0x001E => 'Can`t register on the ICQ network. Reconnect in a few minutes',
    
0x0020 => 'Invalid SecurID',
    
0x0022 => 'Account suspended because of your age (age < 13)'
    
);

    private 
$oscar_errors = array(
    
0x01 => 'Invalid SNAC header.',
    
0x02 => 'Server rate limit exceeded',
    
0x03 => 'Client rate limit exceeded',
    
0x04 => 'Recipient is not logged in',
    
0x05 => 'Requested service unavailable',
    
0x06 => 'Requested service not defined',
    
0x07 => 'You sent obsolete SNAC',
    
0x08 => 'Not supported by server',
    
0x09 => 'Not supported by client',
    
0x0A => 'Refused by client',
    
0x0B => 'Reply too big',
    
0x0C => 'Responses lost',
    
0x0D => 'Request denied',
    
0x0E => 'Incorrect SNAC format',
    
0x0F => 'Insufficient rights',
    
0x10 => 'In local permit/deny (recipient blocked)',
    
0x11 => 'Sender too evil',
    
0x12 => 'Receiver too evil',
    
0x13 => 'User temporarily unavailable',
    
0x14 => 'No match',
    
0x15 => 'List overflow',
    
0x16 => 'Request ambiguous',
    
0x17 => 'Server queue full',
    
0x18 => 'Not while on AOL',
    );
    private 
$oscar_buddy_errors = array(
    
0x0000 => 'No errors (success)',
    
0x0002 => 'Item you want to modify not found in list',
    
0x0003 => 'Item you want to add allready exists',
    
0x000A => 'Error adding item (invalid id, allready in list, invalid data)',
    
0x000C => 'Can't add itemLimit for this type of items exceeded',
    0x000D => '
Trying to add ICQ contact to an AIM list',
    0x000E => '
Can't add this contact because it requires authorization'
    
);
    protected 
$substatuses = array(
    
'STATUS_WEBAWARE'   => 0x0001,
    
'STATUS_SHOWIP'     => 0x0002,
    
'STATUS_BIRTHDAY'   => 0x0008,
    
'STATUS_WEBFRONT'   => 0x0020,
    
'STATUS_DCDISABLED' => 0x0100,
    
'STATUS_DCAUTH'     => 0x1000,
    
'STATUS_DCCONT'     => 0x2000
    
);

    protected 
$statuses = array(
    
'STATUS_ONLINE'     => 0x0000,
    
'STATUS_AWAY'       => 0x0001,
    
'STATUS_DND'        => 0x0002,
    
'STATUS_DND2'       => 0x0013,
    
'STATUS_NA'         => 0x0004,
    
'STATUS_NA2'        => 0x0005,
    
'STATUS_OCCUPIED'   => 0x0010,
    
'STATUS_OCCUPIED2'  => 0x0011,
    
'STATUS_FREE4CHAT'  => 0x0020,
    
'STATUS_INVISIBLE'  => 0x0100,
     
'STATUS_EVIL'       => 0x3000,
     
'STATUS_DEPRESSION' => 0x4000,
     
'STATUS_ATHOME'     => 0x5000,
     
'STATUS_ATWORK'     => 0x6000,
     
'STATUS_LUNCH'        => 0x2001,
     
'STATUS_OFFLINE'    => 0xFFFF
    
);

    protected 
$status_message '';

    protected 
$capabilities = array(
    
'0138ca7b769a491588f213fc00979ea8',
    
'67361515612d4c078f3dbde6408ea041',
    
'1a093c6cd7fd4ec59d51a6474e34f5a0',
    
'b2ec8f167c6f451bbd79dc58497888b9',
    
'094613494C7F11D18222444553540000',
    
'0946134E4C7F11D18222444553540000',
    
'094613434C7F11D18222444553540000',
    
'563FC8090B6F41BD9F79422609DFA2F3'

//    '094600004C7F11D18222444553540000', //  Avatar
    //'0946134D4C7F11D18222444553540000', //  Setting this lets AIM users receive messages from ICQ users, and ICQ users receive messages from AIM users. It also lets ICQ users show up in buddy lists for AIM users, and AIM users show up in buddy lists for ICQ users. And ICQ privacy/invisibility acts like AIM privacy, in that if you add a user to your deny list, you will not be able to see them as online (previous you could still see them, but they couldn't see you.
    //'094613444C7F11D18222444553540000', //  Something called "route finder". Currently used only by ICQ2K clients.
    //'094613494C7F11D18222444553540000', //    Client supports channel 2 extended, TLV(0x2711) based messages. Currently used only by ICQ clients. ICQ clients and clones use this GUID as message format sign.
    //'1A093C6CD7FD4EC59D51A6474E34F5A0', //  Xtraz
//    '0946134E4C7F11D18222444553540000', //    Client supports UTF-8 messages. This capability currently used by AIM service and AIM clients.
//    '97B12751243C4334AD22D6ABF73F1492', //  Client supports RTF messages. This capability currently used by ICQ service and ICQ clients.
//    '563FC8090B6f41BD9F79422609DFA2F3', //    Typing Notifications
    
);

    protected 
$user_agent_capability = array(
    
'miranda'   => '4D6972616E64614D0004000200030700',
    
'sim'       => '53494D20636C69656E74202000090402',
    
'trillian'  => '97B12751243C4334AD22D6ABF73F1409',
    
'licq'      => '4c69637120636c69656e742030303030',
    
'kopete'    => '4b6f7065746520494351202030303030',
    
'micq'      => '6d49435120A920522e4b2e2030303030',
    
'andrq'     => '265251696e7369646530303030303030',
    
'randq'     => '522651696e7369646530303030303030',
    
'mchat'     => '6d436861742069637120303030303030',
    
'jimm'      => '4a696d6d203030303030303030303030',
    
'macicq'    => 'dd16f20284e611d490db00104b9b4b7d',
    
'icqlite'   => '178C2D9BDAA545BB8DDBF3BDBD53A10A',
    
'icq'        => '178c2d9bdaa545bb8ddbf3bdbd53a10a',
    
//        'qip'       => '563FC8090B6F41514950203230303561',
    //        'qippda'    => '563FC8090B6F41514950202020202021',
    //        'qipmobile' => '563FC8090B6F41514950202020202022',
    //        'anastasia' => '44E5BFCEB096E547BD65EFD6A37E3602',
    //        'icq2001'   => '2e7a6475fadf4dc8886fea3595fdb6df',
    //        'icq2002'   => '10cf40d14c7f11d18222444553540000',
    //        'IcqJs7     => '6963716A202020202020202020202020',
    //        'IcqJs7sec  => '6963716A2053656375726520494D2020',
    //        'TrilCrypt  => 'f2e7c7f4fead4dfbb23536798bdf0000',
    //        'SimOld     => '97b12751243c4334ad22d6abf73f1400',
    //        'Im2        => '74EDC33644DF485B8B1C671A1F86099F',
    //        'Is2001     => '2e7a6475fadf4dc8886fea3595fdb6df',
    //        'Is2002     => '10cf40d14c7f11d18222444553540000',
    //        'Comm20012  => 'a0e93f374c7f11d18222444553540000',
    //        'StrIcq     => 'a0e93f374fe9d311bcd20004ac96dd96',
    //        'AimIcon    => '094613464c7f11d18222444553540000',
    //        'AimDirect  => '094613454c7f11d18222444553540000',
    //        'AimChat    => '748F2420628711D18222444553540000',
    //        'Uim        => 'A7E40A96B3A0479AB845C9E467C56B1F',
    //        'Rambler    => '7E11B778A3534926A80244735208C42A',
    //        'Abv        => '00E7E0DFA9D04Fe19162C8909A132A1B',
    //        'Netvigator => '4C6B90A33D2D480E89D62E4B2C10D99F',
    //        'tZers      => 'b2ec8f167c6f451bbd79dc58497888b9',
    //        'HtmlMsgs   => '0138ca7b769a491588f213fc00979ea8',
    //        'SimpLite   => '53494D5053494D5053494D5053494D50',
    //        'SimpPro    => '53494D505F50524F53494D505F50524F',
    
'webicqpro' => '57656249637150726f00010405000062'
    
);

    private 
$message_capabilities = array(
    
'TLV2711'     => '094613494c7f11d18222444553540000',
    
'REVERSE_REQ' => '094613444c7f11d18222444553540000',
    
'OSCAR_FT'    => '094613434c7f11d18222444553540000',
    
'OSCAR_FT'    => '094613434c7f11d18222444553540000',
    
'MESSAGE'     => '00000000000000000000000000000000',
    
'PLUGIN'      => ARRAY(
    
'MESSAGE'       => 'be6b73050fc2104fa6de4db1e3564b0e',
    
'STATUSMSGEXT'  => '811a18bc0e6c1847a5916f18dcc76f1a',
    
'FILE'          => 'f02d12d93091d3118dd700104b06462e',
    
'WEBURL'        => '371c5872e987d411a4c100d0b759b1d9',
    
'CONTACTS'      => '2a0e7d467676d411bce60004ac961ea6',
    
'GREETING_CARD' => '01e53b482ae4d111b679006097e1e294',
    
'CHAT'          => 'bff720b2378ed411bd280004ac96d905',
    
'SMS_MESSAGE'   => '0e28f60011e7d311bcf30004ac969dc2',
    
'XTRAZ_SCRIPT'  => '3b60b3efd82a6c45a4e09c5a5e67e865'
    
)
    );


    
/**
     * Set of - X Statuses.
     * @thanks �_� ���� (http://intrigue.ru/forum/index.php?action=profile;u=190)
     * 
     * @var array
     */
    
protected $x_statuses = array(
    
"journal"             => '0072d9084ad143dd91996f026966026f',
    
"angry"               => '01d8d7eeac3b492aa58dd3d877e66b92',
    
"ppc"                 => '101117c9a3b040f981ac49e159fbd5d4',
    
"cinema"              => '107a9a1812324da4b6cd0879db780f09',
    
"phone"               => '1292e5501b644f66b206b29af378e48d',
    
"browsing"            => '12d07e3ef885489e8e97a72a6551e58d',
    
"mobile"              => '160c60bbdd4443f39140050f00e6c009',
    
"wc"                  => '16f5b76fa9d240358cc5c084703c98fa',
    
"coffee"              => '1b78ae31fa0b4d3893d1997eeeafb218',
    
"sick"                => '1f7a4071bf3b4e60bc324c5787b04cf1',
    
"picnic"              => '2ce0e4e57c6443709c3a7a1ce878a7dc',
    
"thinking"            => '3fb0bd36af3b4a609eefcf190f6a5a7e',
    
"smoking"             => '3fb0bd36af3b4a609eefcf190f6a5a7f',
    
"business"            => '488e14898aca4a0882aa77ce7a165208',
    
"duck"                => '5a581ea1e580430ca06f612298b7e4c7',
    
"studying"            => '609d52f8a29a49a6b2a02524c5e9d260',
    
"?"                   => '631436ff3f8a40d0a5cb7b66e051b364',
    
"typing"              => '634f6bd8add24aa1aab9115bc26d05a1',
    
"shopping"            => '63627337a03f49ff80e5f709cde0a4ee',
    
"music"               => '61bee0dd8bdd475d8dee5f4baacf19a7',
    
"zzz"                 => '6443c6af22604517b58cd7df8e290352',
    
"fun"                 => '6f4930984f7c4affa27634a03bceaea7',
    
"sleeping"            => '785e8c4840d34c65886f04cf3f3f43df',
    
"tv"                  => '80537de2a4674a76b3546dfd075f5ec6',
    
"tired"               => '83c9b78e77e74378b2c5fb6cfcc35bec',
    
"beer"                => '8c50dbae81ed4786acca16cc3213c7b7',
    
"surfing"             => 'a6ed557e6bf744d4a5d4d2e7d95ce81f',
    
"pro7"                => 'b70867f538254327a1ffcf4cc1939797',
    
"working"             => 'ba74db3e9e24434b87b62f6b8dfee50f',
    
"love2"               => 'cd5643a2c94c4724b52cdc0124a1d0cd',
    
"gaming"              => 'd4a611d08f014ec09223c5b6bec6ccf0',
    
"google"              => 'd4e2b0ba334e4fa598d0117dbf4d3cc8',
    
"love"                => 'ddcf0ea971954048a9c6413206d6f280',
    
"party"               => 'e601e41c33734bd1bc06811d6c323d81',
    
"sex"                 => 'e601e41c33734bd1bc06811d6c323d82',
    
"meeting"             => 'f18ab52edc57491d99dc6444502457af',
    
"eating"              => 'f8e8d7b282c4414290f810c6ce0a89a6',
    );

    protected 
$contact_list = array();
    protected 
$contact_list_groups = array('all_childs_ids' => array(0));
    
    protected 
$encodings = array(
        
'ASCII' => 0x00,
        
'UNICODE' => 0x02,
        
'LATIN_1' => 0x03
    
);

    
/**
     * Set default values
     *
     * @return WebIcqPro_SNAC
     */
    
protected function __construct()
    {
        
$this->request_id 0;
        
$this->setMessageType();
        
$this->setMessageCapabilities();
        
$this->setUserAgent();
    }


    private function 
beautifyBinaryLog($data)
    {
        
$return chunk_split(chunk_split(strtoupper(bin2hex($data)), 2' '), 24' ');
        
$return str_split($return50);
        
$search = array("r""n""t");
        
$replace = array(".""."".");
        foreach (
$return as $key => $line)
        {
            
$return[$key] = str_pad($line50) . str_replace($search$replacesubstr($data0+16*$key16));
        }
        return  
implode("rn"$return);
    }

    private function 
dump($str$file 'dump')
    {
        if (
$this->debug && $file) {
            
$f fopen($file'a');
            
fwrite($f$str);
            
fclose($f);
        }
        else if (!
$file)
        {
            echo 
$str;
        }
    }

    public function 
log($msg$data=''$tolog false)
    {
        
$msg .= "n".trim($this->beautifyBinaryLog($data))."nn";
        if (
$tolog)
        {
            
$this->dump($msgfalse);
        }
        else
        {
            
$this->dump($msg);
        }
    }

    private function 
parseSnac($snac)
    {
        if (
strlen($snac) > 10)
        {
            
$return unpack('ntype/nsubtype/nflag/Nrequest_id'$snac);
            
$return['data'] = substr($snac10);
            return 
$return;
        }
        
$this->error 'Error: Broken SNAC can`t parse';
        return 
false;
    }

    protected function 
analizeSnac($snac)
    {
        
$snac $this->parseSnac($snac);
        if (
$snac)
        {
            if (isset(
$this->snac_names[$snac['type']][$snac['subtype']]))
            {
                if (
method_exists($this$this->snac_names[$snac['type']][$snac['subtype']]))
                {
                    
$snac['callback'] = $this->snac_names[$snac['type']][$snac['subtype']];
                }
            }
        }
        return 
$snac;
    }

    
/**
     * Return SNAC header
     *
     * @param ineger $type
     * @param integer $subtype
     * @param integer $flag
     * @return string binary
     */
    
private function __header($type$subtype$flag 0)
    {
        return 
pack('nnnN'$type$subtype$flag, ++$this->request_id);
    }

    
/**
     * Pack ready CNAC
     *
     * @return string binary
     */
    
protected function ClientReady($args)
    {
        return 
$this->__header(0x010x02).pack('n*'
        
0x00220x00010x01100x164F
        
0x00010x00040x01100x164F
        
0x00130x00040x01100x164F
        
0x00020x00010x01100x164F
        
0x00030x00010x01100x164F
        
0x00150x00010x01100x164F
        
0x00040x00010x01100x164F
        
0x00060x00010x01100x164F
        
0x00090x00010x01100x164F
        
0x000A0x00010x01100x164F
        
0x000B0x00010x01100x164F);
    }

    protected function 
ServerFamilies($data)
    {
        
$families unpack('n*'$data);
        foreach (
$this->snac_names as $family => $value)
        {
            if (!
in_array($family$families))
            {
                unset(
$this->snac_names[$family]);
            }
        }
        return 
true;
    }

    protected function 
ClientRates($args)
    {
        return 
$this->__header(0x010x06);
    }

    protected function 
ServerRates($data)
    {
        
$classes unpack('n'$data);
        
$data substr($data2);
        for (
$i 0$i $classes$i++)
        {
            if (
strlen($data)>=35)
            {
                
$rate unpack('nrate/Nwindow/Nclear/Nalert/Nlimit/Ndisconect/Ncurrent/Nmax/Ntime/cstate'$data);
                
$this->rates[array_shift($rate)] = $rate;
                
$data substr($data35);
            }
            else
            {
                
$this->error 'Notice: Can`t get rates from server';
                return 
false;
            }
        }
        while (
strlen($data) >= 4)
        {
            
$group unpack('nclass/nsise');
            
$data substr($data4);
            if (
strlen($data) >= 4*$group['size'])
            {
                
$this->rates_groups[$group['class']] = unpack(substr(str_repeat('N/'$group['size']), -1), $data);
                
$data substr($data4*$group['size']);
            }
            else
            {
                
$this->error 'Notice: Can`t get rates groups from server';
                return 
false;
            }
        }
        if (
strlen($data))
        {
            
$this->error 'Notice: Can`t get rates/groups from server';
            return 
false;
        }
        return 
true;
    }

    protected function 
ClientRatesAck($args)
    {
        if (
is_array($this->rates_groups) && count($this->rates_groups))
        {
            
$snac $this->__header(0x010x08);
            foreach (
$this->rates_groups as $group => $falilies)
            {
                
$snac .= pack('n'$group);
            }
            return 
$snac;
        }
        
$this->error 'Error: Can`t create SNAC rates groups empty';
        return 
false;
    }

    protected function 
ClientRequestSelfInfo($args)
    {
        return 
$this->__header(0x010x0E);
    }

    
/**
     * Requested online info response
     *
     * @todo implement this functionality
     * @param string $data
     * @return boolean
     */
    
protected function ServerResponseSelfInfo($data)
    {
        return 
true;
    }

    protected function 
ClientIdleTime($args)
    {
        return 
$this->__header(0x010x11).pack('N'1);
    }

    protected function 
ClientFamiliesVersions($args)
    {
        
$snac $this->__header(0x010x17);
        foreach (
$this->snac_names as $fname => $family)
        {
            
$snac .= pack('n2'$fname$family['version']);
        }
        return 
$snac;
    }

    protected function 
ServerFamiliesVersions($data)
    {
        
$families unpack('n*'$data);
        while (
count($families)>1)
        {
            
$falily array_shift($families);
            
$version array_shift($families);
            
$versions[$falily] = $version;
        }

        foreach (
$this->snac_names as $fname => $family)
        {
            if (!isset(
$versions[$fname])) // todo: need to do something with families lower versions // || $versions[$fname] > $family['version']
            
{
                unset(
$this->snac_names[$fname]);
            }
        }
        return 
true;
    }

    protected function 
ClientStatus($args)
    {
        
extract($args);
        
$snac $this->__header(0x010x1E);
        
$snac .= $this->packTLV(0x06, ($this->substatuses[$substatus]<<16) + $this->statuses[$status], 'N');
        
$snac .= $this->packTLV(0x0Cpack('NNcnNNNNNNn'0x000x000x00$this->protocol_version0x000x000x030x000x000x000x00));
        return 
$snac;
    }


    protected function 
ClientLocationRights($args)
    {
        return 
$this->__header(0x020x02);
    }

    
/**
     * @todo 
     */
    
protected function ServerLocationRights($data)
    {
        return 
true;
    }

    protected function 
ClientLocationInfo($args)
    {
        return 
$this->__header(0x020x04).$this->packTLV(0x05implode($this->capabilities).$this->user_agent_capability[$this->agent], 'H*');
    }

    protected function 
OscarError($data)
    {
        
$error unpack('ncode'$data);
        if (isset(
$this->oscar_errors[$error['code']])) {
            
$error['error'] = $this->oscar_errors[$error['code']];
        }
        return 
$this->createResponse("error"$error);
    }

    protected function 
ClientBuddylistRights($args)
    {
        return 
$this->__header(0x030x02);
    }

    
/**
     * @todo 
     */
    
protected function ServerBuddylistRights($data)
    {
        return 
true;
    }

    
/**
     * Add contact to list
     *
     * @deprecated 
     * @param array $args
     * @return string binary
     */
    
protected function ClientBuddyListAdd($args)
    {
        
extract($args);
        
$data '';
        if(
is_array($uins)) {
            foreach (
$uins as $id) {
                
$data .= pack('c'strlen($id)).$id;
                
$this->contact_list[$id] = array();
            }
        }
        else {
            
$data .= pack('c'strlen($uins)).$uins;
            
$this->contact_list[$uins] = array();
        }
        return 
$this->__header(0x030x04).$data;
    }

    
/**
     * Delete contact from list
     *
     * @deprecated 
     * @param array $args
     * @return string binary
     */
    
protected function ClientBuddyListDelete($args)
    {
        
extract($args);
        
$data '';
        if(
is_array($uins)) {
            foreach (
$uins as $uin) {
                
$data .= pack('c'strlen($uin)).$uin;
                unset(
$this->contact_list[$uin]);
            }
        }
        else {
            
$data .= pack('c'strlen($uins)).$uins;
            unset(
$this->contact_list[$uins]);
        }
        return 
$this->__header(0x030x05).$data;
    }

    protected function 
ServerUserOnline($data)
    {
        
$response $this->createResponse('useronline');
        
$info unpack('clength'$data);
        
$uin substr($data1$info['length']);
        
$response['uin'] = $uin;
        
$data substr($data, ($info['length']+1));
        if (!isset(
$this->contact_list[$uin]))
        {
            return 
false;
            
// We dont need it!!!
            //$this->contact_list[$uin] = array();
        
}
        else
        {
            if(isset(
$this->contact_list[$uin]['status'])) 
            {
                
$response['old_status'] = $this->contact_list[$uin]['status'];
            }
            unset(
$this->contact_list[$uin]['ip']);
            unset(
$this->contact_list[$uin]['online_time']);
            unset(
$this->contact_list[$uin]['signon_time']);
            unset(
$this->contact_list[$uin]['member_since']);
            unset(
$this->contact_list[$uin]['substatus']);
            unset(
$this->contact_list[$uin]['status']);
        }
        
$info unpack('nwarning_level/nsize'$data);
        
$data substr($data4);
        for (
$i 0$i $info['size']; $i++)
        {
            
$tlv $this->unpackTLV($data);
            switch (
$tlv['type']) {
                case 
0x000A:
                    
$this->contact_list[$uin]['ip'] = long2ip($tlv['data']);
                    break;
                case 
0x000F:
                    
$this->contact_list[$uin]['online_time'] = $tlv['data'];
                    break;
                case 
0x0003:
                    
$this->contact_list[$uin]['signon_time'] = $tlv['data'];
                    break;
                case 
0x0005:
                    
$this->contact_list[$uin]['member_since'] = $tlv['data'];
                    break;
                case 
0x0006:
                    
$status unpack('nsubstatus/nstatus'$tlv['data']);
                    
$this->contact_list[$uin]['substatus'] = array_search($status['substatus'], $this->substatuses);
                    
$this->contact_list[$uin]['status'] = array_search($status['status'], $this->statuses);
                    
$response['status'] = $this->contact_list[$uin]['status'];
                    break;
                case 
0x0001// todo: user class
                
case 0x000C// todo: dc info
                
case 0x000D// todo: user capabilities
                
case 0x0011// todo: time updated
                
case 0x0019// todo: new style capabilities
                
case 0x001D// todo: user icon id & hash
                
break;
            }
        }
        return 
$response;
    }

    protected function 
ServerUserOffline($data)
    {
        return 
$this->ServerUserOnline($data);
    }

    protected function 
ClientIBCMRights()
    {
        return 
$this->__header(0x040x04);
    }

    
/**
     * @todo 
     */
    
protected function ServerIBCMRights($data)
    {
      
$this->writeFlap('ClientSetIBCMParams', array());
        return 
true;
    }

    protected function 
ClientSetIBCMParams($args)
    {
      
extract($args);
      if(!isset(
$channel)) $channel 0x00;
      return 
$this->__header(0x040x02).pack('nNnnnN'$channel0x030x1f400x03e70X03e70x00);
  }

    
/**
     * Pack message to SNAC
     *
     * @param string $uin
     * @param string $message
     * @return string binary
     */
    
protected function ClientIBCM($args)
    {
        
extract($args);
        
$uin_size     strlen($uin);
        
$message_size strlen($message);

        
$cookie microtime(true);
        
$snack $this->__header(0x040x06).pack('dnca*'$cookie$this->ibcm_type$uin_size$uin);

        switch (
$this->ibcm_type)
        {
            case 
0x01:
                
$tlv_data pack('c2nc3n3a*'0x050x010x010x010x010x01, ($message_size+4), $this->ibcm_encoding0x00$message);
                
$snack .= $this->packTLV(0x02$tlv_data).$this->packTLV(0x03).$this->packTLV(0x06);
                break;
            case 
0x02:
                
$tlv_data pack('ndH*n5n2v2d2nVn3dn3cvnva*H*'0x00$cookie$this->ibcm_capabilities0x0A0x020x010x0F0x000x2711, ($message_size+62), 0x1B$this->protocol_version0x000x000x000x03$this->request_id0x0E$this->request_id0x000x000x000x010x000x000x01, ($message_size+1), $message'0000000000FFFFFF00');
                
$snack .= $this->packTLV(0x05$tlv_data).$this->packTLV(0x03);
                break;
            default:
                
$this->error 'Warning: Snac ClientIBCM unknown ibcm_type';
                
$snack false;
                break;
        }

        return array(
'return' => $cookie'data' => $snack);
    }

    
/**
     * Unpack message from server
     *
     * @param string $data
     * @return array
     */
    
protected function ServerIBCM($data)
    {
        if (
strlen($data))
        {
            
$msg unpack('dcookie/nchannel/cnamesize'$data);
            
$data substr($data11);

            
$return['from'] = substr($data0$msg['namesize']);
            
$return['channel'] = $msg['channel'];
            
$return['cookie'] = $msg['cookie'];

            
$data substr($data$msg['namesize']);
            
$msg unpack('nwarnlevel/nTLVnumber'$data);

            
$data substr($data4);
            
$tlvs $this->splitTLVsToArray($data);
            foreach (
$tlvs as $type => $data)
            {
                switch (
$type)
                {
                    case 
2:
                        if (
$return['channel'] == 1)
                        {
                            
$subtlvs $this->splitTLVsToArray($datatrue);
                            if (isset(
$subtlvs[0x0101]))
                            {
                                
$return['encoding'] = unpack('nnumset/nsubset'substr($subtlvs[0x0101], 04));
                                
$return['encoding']['numset'] = in_array($return['encoding']['numset'], $this->encodings) ? array_search($return['encoding']['numset'], $this->encodings): $return['encoding']['numset'];
                                
$return['message'] = substr($subtlvs[0x0101], 4);
                                
$return $this->createResponse('message'$return);
                            }
                        }
                        break;
                    case 
5:
                        if (
$return['channel'] == 2)
                        {
                            if (
strlen($data) < 26 )
                            {
                                
// empty message
                                
return false;
                            }
                            
$msg unpack('ntype/dcookie'$data);
                            
$return['type']   = $msg['type'];
                            
$return['cookie'] = $msg['cookie'];
                            
$return['capability'] = bin2hex(substr($data1016));
                            
$data substr($data26);

                            if (
$return['capability'] != $this->message_capabilities['TLV2711'])
                            {
                                
// todo: handel other
                                
echo "Not supported message capability:".$return['capability']."rn";
                                
$this->log('>>'$datatrue);
                                return 
$return;
                            }

                            
$subtlvs $this->splitTLVsToArray($data);

                            foreach (
$subtlvs as $type => $data)
                            {
                                switch (
$type)
                                {
                                    case 
0x00004:
                                        
$return['external_ip'] = long2ip($data);
                                    case 
0x00005:
                                        
$return['listening_port'] = $data;
                                        break;
                                    case 
0x2711:
                                        if (
strlen($data) < 33)
                                        {
                                            
// empty message
                                            
echo "Empty?rn";
                                            
$this->log('>>'$datatrue);
                                            return 
$return;
                                        }
                                        
$meta unpack('vuId/vversion'substr($data04));
                                        
$return['version'] = $meta['version'];
                                        
$return['capability2'] = bin2hex(substr($data416));
                                        
$meta unpack('vuId/vcookie2'substr($data294));
                                        
$return['cookie2'] = $meta['cookie2'];
                                        
$data substr($data33);

                                        switch (
$return['capability2']) {
                                            
// MESSAGE
                                            
case $this->message_capabilities['MESSAGE']:
                                                if (
strlen($data) < 20)
                                                {
                                                    echo 
"Empty message?rn";
                                                    
$this->log('>>'$datatrue);
                                                    return 
$return;
                                                }
                                                
//empty zerous
                                                
$meta unpack('Ctype/Cflag/vstatus/vpriority/vsize'substr($data128));
                                                
$data substr($data20);
                                                switch (
$meta['type'])
                                                {
                                                    
// Chat request message
                                                    
case 0x02:
                                                        
$return['debugmsg'] = 'deprecated?';
                                                        
$return $this->createResponse('chatrequest'$return);
                                                        break;
                                                        
// File request / file ok message
                                                    
case 0x03:
                                                        if (
$return['type'] == || $return['type'] = 1)
                                                        {
                                                            
$return $this->createResponse('filerequest'$return);
                                                        }
                                                        else if (
$return['type'] == 2)
                                                        {
                                                            
$return $this->createResponse('fileresponse'$return);
                                                        }
                                                        else
                                                        {
                                                            
//strange file message
                                                            
$return['debugmsg'] = 'strange file message';
                                                            return 
$return;
                                                        }
                                                        break;
                                                        
// Plugin message described by text string
                                                    
case 0x1A:
                                                        return 
$this->parseServerGreeting($return$meta$data);
                                                        break;
                                                    default:
                                                        return 
$this->parseMessageTypes($return$meta$data);
                                                        break;
                                                }
                                                break;

                                        }
                                        break;
                                }
                            }
                        }
                        break;
                    case 
6:
                        
$status unpack('nstatus/nsubstatus'$data);
                        
$return['status']     = $status['status'];
                        
$return['substatus']  = $status['substatus'];
                        break;
                }
            }
            
$this->writeFlap('ClientIBCMAck'$return);
            return 
$return;
        }
        return 
false;
    }

    private function 
parseMessageTypes($return$meta$data)
    {
        if (
$return['type'] == 2)
        {
            
//todo: hendle message ack
            
return $return;
        }

        
//todo: process 0xFE formatted

        
switch ($meta['type']) {
            
// Plain text (simple) message
            
case 0x01:
                
//todo: DC special check
                
$meta['rtf'] = false;
                if (
strlen($data) > $meta['size']+ 36)
                {
                    
//rtf message
                    
if (strpos($data'{97B12751-243C-4334-AD22-D6ABF73F1492}') !== false)
                    {
                        
$meta['rtf'] = true;
                    }
                    
//utf-8 message
                    
if (strpos($data'{0946134E-4C7F-11D1-8222-444553540000}') !== false)
                    {
                        
$return['encoding'] = array('numset' => 'UTF-8''subset' => 0);
                    }
                }
                
$return['message'] = substr($data0$meta['size']);
                
                if (
$meta['rtf'])
                {
                    
$return['rtf'] = $return['message'];
                    
$return['message'] = RTF::Text($return['message']);
                }
                
$return $this->createResponse('message'$return);
                break;
                
// URL message (0xFE formatted)
            
case 0x04:
                
$this->log('URL'$data);
                
$return['message'] = substr($data0$meta['size']);
                
$return $this->createResponse('urlmessage'$return);
                break;
                
// Authorization request message (0xFE formatted)
            
case 0x06:
                
// Authorization denied message (0xFE formatted)
            
case 0x07:
                
// Authorization given message (empty)
            
case 0x08:
                
// Message from OSCAR server (0xFE formatted)
            
case 0x09:
                
// Web pager message (0xFE formatted)
            
case 0x0D:
                
// Email express message (0xFE formatted)
            
case 0x0E:
                
// Contact list message (0xFE formatted)
            
case 0x13:
                
$this->log($meta['type'].'>>'$datatrue);
                
//$return['message'] = substr($data, 0, $meta['size']);
                
$return array_merge($return$meta);
                break;
                
// "You-were-added" message (0xFE formatted)
            
case 0x0C:
                
$return $this->createResponse('youadded'$return);
                break;
                
// Auto away message
            
case 0xE8:
                
$return['message'] = substr($data0$meta['size']);
                
$return $this->createResponse('autoaway'$return);
                break;
                
// Auto occupied message
            
case 0xE9:
                
$return['message'] = substr($data0$meta['size']);
                
$return $this->createResponse('autooccupied'$return);
                break;
                
// Auto not available message
            
case 0xEA:
                
$return['message'] = substr($data0$meta['size']);
                
$return $this->createResponse('autona'$return);
                break;
                
// Auto do not disturb message
            
case 0xEB:
                
$return['message'] = substr($data0$meta['size']);
                
$return $this->createResponse('autodnd'$return);
                break;
                
// Auto free for chat message
            
case 0xEC:
                
$return['message'] = substr($data0$meta['size']);
                
$return $this->createResponse('autofreeforchat'$return);
                break;
        }
        return 
$return;
    }

    private function 
parseServerGreeting($return$meta$data)
    {
        if (
strlen($data) < 24)
        {
            
$return['debugmsg'] = 'to short to indificate plugin type';
            
$this->log('SG'$datatrue);
            return 
$return;
        }

        
//        $plugin = unpack()
        //
        //
        //        $return['capability'] = bin2hex(substr($data, 10, 16));
        
return false;
    }

    protected function 
ClientIBCMAck($args)
    {
        if (
is_array($args))
        {
            
extract($args);
            
$uin_size strlen($from);
            
$channel  = isset($channel) ? $channel 0x00;
            
$cookie   = isset($cookie) ? $cookie 0x00;
            
$reason   = isset($reason) ? $reason : (in_array($channel, array(0x010x02)) ? 0x03 0x01);
            
$message  = (isset($message) &&  $message != '') ? '' $this->status_message;

            
$snack $this->__header(0x040x0B).pack('dnca*n'$cookie$channel$uin_size$from$reason);

            switch (
$channel)
            {
                case 
0x02:
                    
$data pack('vH*nH*cn'$this->protocol_version'00000000000000000000000000000000'0x00$this->capability_flag0x000x07);
                    
$message pack('nH*va*H*'0x07'000000000000000000000000E80300000000'strlen($message)+1$message'0000000000FFFFFF00');
                    
$data .= pack('v'strlen($message)).$message;
                    
$snack .= pack('v'0x1B).$data;
                    break;
                default:
                    
$message_size strlen($message);
                    
$snack .= pack('c2nc3n3a*'0x050x010x010x010x010x01, ($message_size+4), 0x030x00$message);
                    break;
            }

            return 
$snack;
        }
        else
        {
            
// todo: get answer from server.
        
}
    }

    protected function 
ServerIBCMAck($data)
    {
        if (
strlen($data) > 11) {
            
$msg unpack('did/nchannel/cuin'$data);
            
$msg['uin'] = substr($data11$msg['uin']);
            return 
$this->createResponse('accepted'$msg);
        }
        
$this->error 'ServerIBCMAck: too short';
        return 
false;
    }


    protected function 
ClientPrivicyRights()
    {
        return 
$this->__header(0x090x02);
    }

    
/**
     * @todo 
     */
    
protected function ServerPrivicyRights($data)
    {
        return 
true;
    }

    protected function 
ServerSSIError($data)
    {
        
$response unpack('ncode'substr($data02));
        if (isset(
$this->oscar_errors[$response['code']])) {
            
$response['error'] = $this->oscar_errors[$response['code']];
        }
        return 
$this->createResponse('error'$response);
    }

    protected function 
ClientSSIRights($args)
    {
        return 
$this->__header(0x130x02);
    }

    
/**
     * @todo 
     */
    
protected function ServerSSIRights($data)
    {
        return 
true;
    }

    protected function 
ClientSSI($args)
    {
        return 
$this->__header(0x130x04);
    }

    protected function 
ServerSSI($data$flag 0)
    {
        
$this->error "";
        
$coockie substr($data08);
        
$data substr($data8);
        
$header unpack('cversion/nlength'$data);
        
$data substr($data3);
        for (
$i=0$i $header['length']; $i++) {
            if(
strlen($data) >= 10) {
                
$itemname $this->unpackLV($data);
                
$props unpack('ngroupID/nitemID/ntype/nsize'$data);
                
$data substr($data8);
                if(
strlen($data) >= $props['size']) {
                    
$props['tlvs'] = array();
                    if(
$props['size'] > 0) {
                        
$props['tlvs'] = $this->splitTLVsToArray(substr($data0$props['size']));
                        
$data substr($data$props['size']);
                    }
                    switch (
$props['type']) {
                        case 
0x0000//        Buddy record (name: uin for ICQ and screenname for AIM)
                        
$this->contact_list[$itemname] = $this->convertContact($props);
                        break;
                        case 
0x0001//        Group record
                        
$this->contact_list_groups[$itemname] = $this->convertGroup($props);
                        
$this->contact_list_groups['all_childs_ids'] = array_merge($this->contact_list_groups['all_childs_ids'], $this->contact_list_groups[$itemname]['childs']);
                        case 
0x0002//       Permit record ("Allow" list in AIM, and "Visible" list in ICQ)
                        
case 0x0003//       Deny record ("Block" list in AIM, and "Invisible" list in ICQ)
                        
case 0x0004//       Permit/deny settings or/and bitmask of the AIM classes
                        
case 0x0005//       Presence info (if others can see your idle status, etc)
                        
case 0x0009//       Unknown. ICQ2k shortcut bar items ?
                        
case 0x000E//       Ignore list record.
                        
case 0x000F//       Last update date (name: "LastUpdateDate").
                        
case 0x0010//       Non-ICQ contact (to send SMS). Name: 1#EXT, 2#EXT, etc
                        
case 0x0013//       Item that contain roster import time (name: "Import time")
                        
case 0x0014//       Own icon (avatar) info. Name is an avatar id number as text
                        
break;
                    }
                }
                else {
                    
$this->error .= 'SSI extra data parsing error item #'.($i+1).' Name: '.$itemname."rn";
                }
            }
            else {
                
$this->error .= 'SSI parsing error item #'.($i+1)."rn";
                return 
false;
            }
        }
        if (
$flag == || $flag == 32768) {
            
$this->writeFlap('ClientSSIActivate');
            return 
$this->createResponse("contactlist");
        }
        return 
true;
    }

    private function 
convertContact($item)
    {
        
$contact = array();
        if(
is_array($item['tlvs']))
        {
            foreach (
$item['tlvs'] as $key => $value) {
                switch (
$key) {
                    case 
0x0066:
                        
$contact['authorization'] = $value;
                        break;
                    case 
0x0131:
                        
$contact['name'] = $value;
                        break;
                    case 
0x0137:
                        
$contact['mail'] = $value;
                        break;
                    case 
0x013A:
                        
$contact['sms'] = $value;
                        break;
                    case 
0x013C:
                        
$contact['comment'] = $value;
                        break;
                    case 
0x0145:
                        
$contact['time'] = $value;
                        break;
                    default:
                        
$contact[$key] = $value;
                        break;
                }
            }
        }
        
$contact['id'] = $item['itemID'];
        
$contact['group'] = $item['groupID'];
        return 
$contact;
    }

    private function 
convertGroup($item)
    {
        
$group = array('childs' => array());
        if(
is_array($item['tlvs']))
        {
            foreach (
$item['tlvs'] as $key => $value) {
                switch (
$key) {
                    case 
0x00C8:
                        while (
strlen($value) > 1)
                        {
                            
$id unpack('n'substr($value02));
                            
$group['childs'][] = $id[1];
                            
$value substr($value2);
                        }
                        break;
                }
            }
        }
        
$group['id'] = $item['itemID'];
        
$group['group'] = $item['groupID'];
        return 
$group;
    }

    
/**
     * Check SSI state (time / number of items)
     *
     * @todo implement this functionality
     * @param string $data
     * @return boolean
     */
    
protected function ClientSSICheckout($args)
    {
        
extract($args);
        if(!isset(
$time))
        {
            
$time '47877097';
        }
        return 
$this->__header(0x130x05).pack('Nn'$time0x00);
    }

    protected function 
ClientSSIActivate($args)
    {
        return 
$this->__header(0x130x07);
    }

    protected function 
ClientSSIAdd($args)
    {
        
extract($args);
        
$uins = isset($uins) ? $uins : array();
        
$data '';
        foreach (
$uins as $uin => $name) {
            if(!isset(
$this->contact_list[$uin]))
            {
                
$data .= $this->packContact($uin$name, array('id'=>$this->reservItemId($group), 'group' => $this->getGroupId($group)), true);
            }
        }
        if (isset(
$group) && isset($parent))
        {
            
$data .= $this->packGroup($group$parent);
        }
        if (
strlen($data) > 0) {
            return 
$this->__header(0x130x08).$data;
        }
        
$this->error "SSIAdd error: Nothing to add.";
        return 
false;
    }

    private function 
reservItemId($group)
    {
        if (isset(
$this->contact_list_groups[$group]))
        {
            
$id 0;
            while (
$id++ < 65535)
            {
                if (!
in_array($id$this->contact_list_groups['all_childs_ids']))
                {
                    
$this->contact_list_groups['all_childs_ids'][] = $id;
                    return 
$id;
                }
            }
            return 
$id;
        }
    }

    private function 
getGroupId($name)
    {
        if (isset(
$this->contact_list_groups[$name]))
        {
            return 
$this->contact_list_groups[$name]['group'];
        }
        
$this->error 'getGroupId: No such group '.$name;
        return 
false;
    }

    protected function 
ClientSSIDelete($args)
    {
        
extract($args);
        
$uins = isset($uins) ? $uins : array();
        
$data '';
        foreach (
$uins as $uin) {
            if(isset(
$this->contact_list[$uin]))
            {
                
$data .= $this->packContact($uinfalse$this->contact_list[$uin]);
            }
        }
        if (isset(
$group) && isset($parent))
        {
            
$data .= $this->packGroup($group$parent);
        }
        if (
strlen($data) > 0) {
            return 
$this->__header(0x130x0A).$data;
        }
        
$this->error "SSIDelete error: Nothing to delete.";
        return 
false;
    }

    private function 
packContact($uin$name false$contact = array(), $authorize false)
    {
        
//todo: groups
        
$tlv $name $this->packTLV(0x0131$name) : '';
        if (
$authorize) {
            
$tlv .= $this->packTLV(0x66);
        }
        
$group = isset($contact['group']) ? $contact['group'] : 0;
        
$id    = isset($contact['id']) ? $contact['id'] : 0;
        return 
$this->packLV($uin).pack('nnn'$group$id0x00).$this->packLV($tlv);
    }

    private function 
packGroup($name$parent "")
    {
        
$group $this->getGroupId($parent);
        
$id    $this->getGroupId($name) ? $this->getGroupId($name) : $this->reservItemId($parent);
        return 
$this->packLV($name).pack('nnnn'$id$group0x010x00);
    }

    
/**
     * @todo 
     */
    
protected function ServerSSIModificationDate($data)
    {
        return 
true;
    }

    
/**
     * @todo errors to is
     */
    
protected function ServerSSIAck($data)
    {
        
$errors = array('errors' => array());
        while (
strlen($data) > 1) {
            
$error unpack('ncode'$data);
            if (isset(
$this->oscar_buddy_errors[$error['code']])) {
                
$error['error'] = $this->oscar_buddy_errors[$error['code']];
            }
            
$data substr($data2);
            
$errors['errors'][] = $error;
        }
        return 
$errors;
    }

    protected function 
ClientSSIEditStart()
    {
        return 
$this->__header(0x130x11);
    }

    protected function 
ClientSSIEditEnd()
    {
        
$this->contact_list = array();
        
$this->contact_list_groups = array('all_childs_ids' => array(0));
        
$this->writeFlap('ClientSSICheckout');
        return 
$this->__header(0x130x12);
    }

    protected function 
ClientSSIAuthRequest($args)
    {
        
extract($args);
        
$reason = isset($reason) ? $reason "";
        return 
$this->__header(0x130x18).$this->packLV($uin'c').$this->packLV($reason).pack('n'0x0);
    }

    protected function 
ServerSSIAuthRequest($data)
    {
        
$response $this->createResponse('authrequest');
        
$response['from'] = $this->unpackLV($data1);
        
$response['reason'] = $this->unpackLV($data);
        return 
$response;
    }

    protected function 
ClientSSIAuthResponse($args)
    {
        
extract($args);
        
$reason = isset($reason) ? $reason "";
        
$allow  = isset($allow) ? $allow false;
        return 
$this->__header(0x130x1a).$this->packLV($uin'c').pack('c'$allow).$this->packLV($reason);
    }

    protected function 
ServerSSIAuthResponse($data)
    {
        
$response $this->createResponse('authresponse');
        
$response['from'] = $this->unpackLV($data1);
        
$granted unpack('c'substr($data01));
        
$response['granted'] = $granted[1];
        
$data substr($data1);
        
$response['reason'] = $this->unpackLV($data);
        return 
$response;
    }

    protected function 
ServerSSIYouAdded($data)
    {
        
$response $this->createResponse('youadded');
        
$response['from'] = $this->unpackLV($data1);
        return 
$response;
    }

    private function 
createResponse($type "message"$response = array())
    {
        
$response['type'] = $type;
        return 
$response;
    }

    protected function 
ClientMetaData($args)
    {
        static 
$sequence 1;
        
extract($args);
        
$ret $this->__header(0x150x02);
        switch (
$type)
        {
            case 
'offline'// 003C
            
$ret .= $this->packTLV(0x01pack('vVvv'0x08$uin0x3C$sequence));
            break;
            case 
'delete_offline'// 003E
            
$ret .= $this->packTLV(0x01pack('vVvv'0x08$uin0x3E$sequence));
            break;
            default: 
// todo: 07D0
            
$pack pack('VvvvV'$uin0x07D0$sequence0x04BA$uinsearch); //CLI_SHORTINFO_REQUEST
            
$ret .= $this->packTLV(0x01pack('v'strlen($pack)).$pack);
            break;
        }
        return array(
'return' => $sequence++, 'data' => $ret);
    }

    protected function 
ServerMetaData($data)
    {
        if (
strlen($data))
        {
            
$data substr($data4);

            if (
strlen($data) > 0)
            {
                
$msg unpack('vsize/Vmyuin/vtype'$data);
                
$msg['data'] = substr($data10);
                switch (
$msg['type'])
                {
                    case 
0x41// Offline message
                    
$msg unpack('Vfrom/vyear/Cmonth/Cday/Chour/Cminute/Cmsgtype/Cflag/nlength/a*message'$msg['data']);
                    
$this->createResponse("offlinemessage"$msg);
                    break;
                    case 
0x42// End of offline messages
                    
$this->writeFlap('ClientMetaData', array('uin' => $msg['myuin'], 'type' => 'delete_offline'));
                    
$msg true;
                    break;
                    case 
0x07DA// SRV_META_INFO_REPLY
                    
$msg array_merge($msgunpack('vid'substr($data810)));
                    
$msg1 unpack('vsubtype'$msg['data']);
                    
$msg['data'] = substr($msg['data'], 2);
                    switch (
$msg1['subtype']) {
                        case 
0x0104//Short info
                        
$msg1 unpack('csuccess'$msg['data']);
                        
$msg['data'] = substr($msg['data'], 1);
                        
$msg['type'] = 'shortinfo';
                        if(
$msg1['success'] == 0x0A)
                        {
                            
$size unpack('v'$msg['data']);
                            
$msg['nick'] = substr($msg['data'], 2$size[1]-1);
                            
$msg['data'] = substr($msg['data'], $size[1]+2);
                            
$size unpack('v'$msg['data']);
                            
$msg['firstname'] = substr($msg['data'], 2$size[1]-1);
                            
$msg['data'] = substr($msg['data'], $size[1]+2);
                            
$size unpack('v'$msg['data']);
                            
$msg['lastname'] = substr($msg['data'], 2$size[1]-1);
                            
$msg['data'] = substr($msg['data'], $size[1]+2);
                            
$size unpack('v'$msg['data']);
                            
$msg['email'] = substr($msg['data'], 2$size[1]-1);
                            
$msg['data'] = substr($msg['data'], $size[1]+2);
                            
//                                $this->log("Short info data:", $msg['data']);
                            
$msg1 unpack('cauthorization/cunknown/cgender'$msg['data']);
                            unset(
$msg['data']);
                            
$msg array_merge($msg$msg1);
                            
$this->createResponse("shortinfo"$msg);
                        }
                        break;
                        default:
                            return 
false;
                            break;
                    }
                    break;
                    default:
                        return 
false;
                        break;
                }
            }
            return 
$msg;
        }
        return 
false;
    }

    protected function 
ClientMd5Request($args)
    {
        
extract($args);
        return 
$this->__header(0x170x06).$this->packTLV(0x01$uin);//.$this->packTLV(0x4B).$this->packTLV(0x5A);

    
}

    protected function 
ServerMd5Response($data)
    {
        return 
substr($data2);
    }

    protected function 
ClientMd5Login($args)
    {
        
extract($args);
        
$password pack('H*'md5($password));
        
$password pack('H*'md5($authkey.$password.'AOL Instant Messenger (SM)'));
        return 
$this->__header(0x170x02).
                
$this->packTLV(0x01$uin).
                
$this->packTLV(0x25$password).
                
$this->packTLV(0x4C'').
                
$this->packTLV(0x03'ICQ Client').
                
$this->packTLV(0x160x010A'n').
                
$this->packTLV(0x170x0006'n').
                
$this->packTLV(0x180x00'n').
                
$this->packTLV(0x190x00'n').
                
$this->packTLV(0x1A0x1B67'n').
                
$this->packTLV(0x140x00007535'N').
                
$this->packTLV(0x0F'ru').
                
$this->packTLV(0x0E'ru');//.
                //$this->packTLV(0x94, 0x00, 'c');
    
}

    protected function 
ServerMd5LoginReply($data)
    {
        
$tlv $this->splitTLVsToArray($data);
        if (isset(
$tlv[0x05]) && isset($tlv[0x06]))
        {
            return array(
$tlv[0x05], $tlv[0x06]);
        }
        
$this->error 'Error: can`t parse server answer';
        if (isset(
$tlv[0x08]))
        {
            
$error_no unpack('n',$tlv[0x08]);
            if (
$error_no && isset($this->login_errors[$error_no[1]])) {
                
$this->error $this->login_errors[$error_no[1]];
            }
        }
        return 
false;
    }

    
/**
     * Set message capability
     *
     * @param string $value
     * @return boolean
     */
    
protected function setMessageCapabilities($value 'utf-8')
    {
        switch (
strtolower($value))
        {
            case 
'rtf':
                
$this->ibcm_capabilities '97B12751243C4334AD22D6ABF73F1492';
                break;
            case 
'utf-8':
                
$this->ibcm_capabilities '094613494C7F11D18222444553540000';
                break;
            default:
                
$this->error 'Warning: MessageCapabilities: "'.$value.'" unknown';
                return 
false;
        }
        return 
true;
    }

    protected function 
setUserAgent($value 'webicqpro')
    {
        
$value strtolower($value);
        if (isset(
$this->user_agent_capability[$value]))
        {
            
$this->agent $value;
            return 
true;
        }
        
$this->error 'Warning: UserAgent: "'.$value.'" is not valid user agent or has unknown capability';
        return 
false;
    }

    
/**
     * Set message type
     *
     * @param string $value
     * @return boolean
     */
    
protected function setMessageType($value 'plain_text')
    {
        switch (
strtolower($value))
        {
            case 
'plain_text':
                
$this->ibcm_type 0x01;
                break;
            case 
'rtf':
                
$this->ibcm_type 0x02;
                break;
            case 
'old_style':
                
$this->ibcm_type 0x04;
                break;
            default:
                
$this->error 'Warning: MessageType: "'.$value.'" unknown';
                return 
false;
        }
        return 
true;
    }

    protected function 
setEncoding($value 'LATIN_1')
    {
        if(isset(
$this->encodings[$value])) {
            
$this->ibcm_encoding $this->encodings[$value];
            return 
true;
        }
        
$this->error 'Warning: Encoding not supported';
        return 
false;
    }
}

/**
 * Layer for FLAP data format
 * @access private
 */
class WebIcqPro_FLAP extends WebIcqPro_SNAC{

    protected 
$channel;
    private 
$sequence;
    private 
$body;
    private 
$info = array();

    protected function 
__construct()
    {
        
parent::__construct();
        
$this->sequence rand(0x00000x8000);
    }

    private function 
getSequence()
    {
        if (++
$this->sequence 0x8000)
        {
            
$this->sequence 0x00;
        }
        return 
$this->sequence;
    }

    protected function 
packFlap($body)
    {
        return 
pack('ccnn'0x2A$this->channel$this->getSequence(), strlen($body)).$body;
    }

    protected function 
helloFlap($flap false$extra '')
    {
        if (
$flap)
        {
            if (isset(
$flap['data']) && strlen($flap['data']) == 4)
            {
                return 
unpack('N'$flap['data']);
            }
        }
        else
        {
            return 
$this->packFlap(pack('N'0x01).$extra);
        }
        return 
false;
    }
}

/**
 * Class for simple work with socets
 * @access private
 */
class WebIcqPro_Socet extends WebIcqPro_FLAP
{
    protected 
$socet false;
    private 
$server_url;
    private 
$server_port;
    private 
$timeout_second;
    private 
$timeout_msecond;

    protected function 
__construct()
    {
        
parent::__construct();
        
$this->setServerUrl();
        
$this->setServerPort();
        
$this->setTimeout(6,0);
    }

    protected function 
socetOpen()
    {
        
$this->socet fsockopen($this->server_url$this->server_port$erorno$errormsg, ($this->timeout_second+$this->timeout_msecond/1000));
        if (
$this->socet)
        {
            return 
true;
        }
        
$this->error 'Error: Cant establish connection to: '.$this->server_url.':'.$this->server_port."n".$errormsg;
        return 
false;
    }

    protected function 
socetClose()
    {
        @
fclose($this->socet);
        
$this->socet false;
    }

    protected function 
socetWrite($data)
    {
        if (
$this->socet)
        {
            
stream_set_timeout($this->socet$this->timeout_second$this->timeout_msecond);
            if (!
fwrite($this->socet$data)) {
                
$this->socet false;
                
$this->error 'Error: Server close connection';
                return 
false;
            }
            return 
true;
        }
        
$this->error 'Error: Not connected';
        return 
false;
    }

    protected function 
writeFlap($name$args = array())
    {
        if (
method_exists($this$name))
        {
            
$response $this->$name($args);
            if (
is_array($response) && isset($response['return']) && isset($response['data']))
            {
                
$flap $this->packFlap($response['data']);
                
$this->log(">> ".$name$flap);
                if (
$this->socetWrite($flap))
                {
                    return 
$response['return'];
                }
                return 
false;
            }
            elseif (
$response)
            {
                
$flap $this->packFlap($response);
                
$this->log(">> ".$name$flap);
                return 
$this->socetWrite($flap);
            }
        }
        return 
false;
    }

    private function 
socetRead($size)
    {
        if (
$this->socet)
        {
            
stream_set_timeout($this->socet$this->timeout_second$this->timeout_msecond);
            
$data = @fread($this->socet$size);
            
$socet_status stream_get_meta_data($this->socet);
            if (
$data && !$socet_status['timed_out'])
            {
                return 
$data;
            }
            if (
$socet_status['eof'])
            {
                
$this->socet false;
                
$this->error 'Error: Server close connection';
            }
        }
        return 
false;
    }

    protected function 
readFlap($name false)
    {
        
$data $this->socetRead(6);
        if (
$data)
        {
            
$flap unpack('ccommand/cchanel/nsequence/nsize'$data);
            if (
$flap['chanel'] == 4)
            {
                
$this->error 'Notice: Server close connection';
                
$this->socetClose();
                return 
false;
            }
            
$flap['data'] = $this->socetRead($flap['size']);
            if (
$flap['data'])
            {
                if (
$name)
                {
                    
$snac $this->analizeSnac($flap['data']);
                    if (isset(
$snac['callback']) && $snac['callback'] == $name)
                    {
                        
$this->log("<< ".$snac['callback'].'('.dechex($snac['type']).', '.dechex($snac['subtype']).')'$data.$flap['data']);
                        return 
$this->$name($snac['data'], $snac['flag']);
                    }
                    elseif(isset(
$snac['callback']))
                    {
                        
$this->log("<< ".$snac['callback'].'('.dechex($snac['type']).', '.dechex($snac['subtype']).')'$data.$flap['data']);
                        
$this->$snac['callback']($snac['data'], $snac['flag']);
                        
$this->error 'Warning: Wrong server response "'.$name.'" expected but SNAC('.dechex($snac['type']).', '.dechex($snac['subtype']).') received';
                    }
                    return 
false;
                }
                return 
$flap;
            }
        }
        return 
false;
    }

    protected function 
readSocket()
    {
        
$data $this->socetRead(6);
        if (
$data)
        {
            
$flap unpack('ccommand/cchanel/nsequence/nsize'$data);
            if (
$flap['chanel'] == 4)
            {
                
$this->error 'Notice: Server close connection';
                
$this->socetClose();
                return 
false;
            }
            
$flap['data'] = $this->socetRead($flap['size']);
            if (
$flap['data'])
            {
                
$snac $this->analizeSnac($flap['data']);
                if (isset(
$snac['callback']))
                {
                    
$this->log("<< ".$snac['callback'].' ('.dechex($snac['type']).'x'.dechex($snac['subtype']).')'$data.$flap['data']);
                    return 
$this->$snac['callback']($snac['data'], $snac['flag']);
                }
                else
                {
                    
$this->log('<< Unknown SNAC: '.dechex($snac['type']).'x'.dechex($snac['subtype']), $data.$flap['data']);
                }
            }
        }
        return 
false;
    }

    protected function 
setTimeout($second 1$msecond 0)
    {
        
$this->timeout_second $second;
        
$this->timeout_msecond $msecond;
    }

    protected function 
setServerUrl($value 'login.icq.com')
    {
        
$this->server_url $value;
        return 
true;
    }

    protected function 
setServerPort($value 5190)
    {
        
$this->server_port $value;
        return 
true;
    }

    protected function 
setServer($value 'login.icq.com:5190')
    {
        
$server explode(':'$value);
        if (
count($server) == && is_numeric($server[1]))
        {
            
$this->server_url  $server[0];
            
$this->server_port $server[1];
            return 
true;
        }
        
$this->error 'Error: Wrong server address format';
        return 
false;
    }
}

/**
 * Set of tools for simple and clear work with ICQ(OSCAR) protocol.
 *
 */
class WebIcqPro extends WebIcqPro_Socet {

    private 
$uin;

    
/**
     * Constructor for WebIcqPro class
     * @access internal
     */
    
public function __construct()
    {
        
parent::__construct();
    }

    
/**
     * Establish connection to ICQ server
     * 
     * Method initiate login sequence with MD5 based authorization.
     * 
     * @todo full client request/responses support
     *
     * @param string $uin
     * @param string $pass
     * @return boolean
     */
    
public function connect($uin$pass)
    {
        
$this->uin str_replace('-'''$uin);
        if (
$this->socet)
        {
            
$this->error 'Error: Connection already opened';
            return 
false;
        }
        if (
$this->socetOpen())
        {
            
$flap $this->readFlap();
            
$this->channel 0x01;
            if (
$this->helloFlap($flap))
            {
                
$this->socetWrite($this->helloFlap());
                
$this->channel 0x02;
                
$this->writeFlap('ClientMd5Request', array('uin' => $this->uin));
                
$authkey $this->readFlap('ServerMd5Response');
                if (
$authkey)
                {
                    
$this->writeFlap('ClientMd5Login', array('uin' => $this->uin'password' => $pass'authkey' => $authkey));
                    
$reconect $this->readFlap('ServerMd5LoginReply');
                    
$this->disconnect();
                    if (
$reconect)
                    {
                        
$this->setServer(array_shift($reconect));
                        
$cookie array_shift($reconect);
                        if (
$this->socetOpen())
                        {
                            
$flap $this->readFlap();
                            
$this->channel 0x01;
                            if (
$this->helloFlap($flap))
                            {
                                
$this->socetWrite($this->helloFlap(false$this->packTLV(0x06$cookie)));
                                
$this->channel 2;
                                
$this->readFlap('ServerFamilies');
                                
$this->writeFlap('ClientFamiliesVersions');
                                
$this->readFlap('ServerFamiliesVersions');
                                
$this->writeFlap('ClientRates');
                                
$this->readFlap();
                                if (
is_array($this->rates_groups) && count($this->rates_groups))
                                {
                                    
$this->writeFlap('ClientRatesAck');
                                }
                                
$this->writeFlap('ClientSSIRights');
                                
$this->writeFlap('ClientSSICheckout');
                                
$this->writeFlap('ClientLocationRights');
                                
$this->writeFlap('ClientBuddylistRights');
                                
$this->writeFlap('ClientIBCMRights');
                                
$this->writeFlap('ClientPrivicyRights');
                                
//                                $this->ClientLimitations();
                                
$this->writeFlap('ClientLocationInfo');
                                
$this->writeFlap('ClientReady');
                                return 
true;
                            }
                        }
                    }
                }
                else
                {
                    
$this->readFlap('ServerMd5LoginReply');
                }
            }
        }
        return 
false;
    }

    
/**
     * Method activate Status Notifications
     * 
     * This method make possible to read Status Notifications for contacts in your contact list.
     * 
     * @deprecated Generate a traffic, but handler is not implemented yet!
     * @todo make it useful. Let's class reads status notifications!
     *
     * @return boolean
     */
    
public function activateStatusNotifications()
    {
        return 
$this->writeFlap('ClientSSI');
    }

    
/**
     * Method set status for client
     * 
     * Parameters are case insensitive. Try to set different substatuses for privicy and etc. purposes.
     * You can set any icq status from the list:
     * - STATUS_ONLINE
     * - STATUS_AWAY
     * - STATUS_DND
     * - STATUS_NA
     * - STATUS_OCCUPIED
     * - STATUS_FREE4CHAT
     * - STATUS_INVISIBLE
     * 
     * Also possible to set substutuses:
     * - STATUS_WEBAWARE
     * - STATUS_SHOWIP
     * - STATUS_BIRTHDAY
     * - STATUS_WEBFRONT
     * - STATUS_DCDISABLED
     * - STATUS_DCAUTH
     * - STATUS_DCCONT
     *
     * @param string $status
     * @param string $substatus
     * @return boolean
     */
    
public function setStatus($status 'STATUS_ONLINE'$substatus 'STATUS_DCCONT'$message '')
    {
        
$this->status_message $message;
        if (isset(
$this->statuses[$status]))
        {
            if (isset(
$this->substatuses[$substatus]))
            {
                return 
$this->writeFlap('ClientStatus', array('status' => $status'substatus' => $substatus,)) && $this->writeFlap('ClientIdleTime');
            }
            
$this->error 'setStatus: unknown substatus '.$substatus;
            return 
false;
        }
        
$this->error 'setStatus: unknown status '.$status;
        return 
false;
    }

    
/**
     * Method set X status for client
     * 
     *
     * @param string $status
     * @return boolean
     */
    
public function setXStatus($status '')
    {
        if (isset(
$this->x_statuses[$status]))
        {
            
$this->capabilities['xSatus'] = $this->x_statuses[$status];
        } elseif (
$status == '') {
            unset(
$this->capabilities['xSatus']);
        } else {
            
$this->error 'setXStatus: unknown status '.$status;
            return 
false;
        }
        
$this->writeFlap('ClientIdleTime');
        return 
$this->writeFlap('ClientLocationInfo');
    }

    
/**
     * Method return all available X Statuses
     *
     * @return array
     */
    
public function getXStatuses()
    {
        return 
array_keys($this->x_statuses);
    }

    
/**
     * Method indicate the connection status.
     * 
     * 
     *
     * @return boolean
     */
    
public function isConnected()
    {
        return 
$this->socet;
    }

    
/**
     * Close connection
     *
     */
    
public function disconnect()
    {
        
$this->channel 0x04;
        
$this->socetWrite($this->packFlap(''));
        
$this->socetClose();
    }

    
/**
     * Activate the posibility to read offline messages.
     * 
     * After activation offline messages will be accessable like simple messages.
     *
     * @see readMessage
     * @param string $uin
     * @return boolean
     */
    
public function activateOfflineMessages()
    {
        return 
$this->writeFlap('ClientMetaData', array('uin' => $this->uin'type' => 'offline'));
    }

    
/**
     * Get contact short info.
     * 
     *
     * @see readMessage
     * @param string $uin
     * @return boolean
     */
    
public function getShortInfo($uin)
    {
        
$uin str_replace('-'''$uin);
        return 
$this->writeFlap('ClientMetaData', array('uin' => $this->uin'uinsearch' => $uin'type' => 'shortinfo'));
    }

    
/**
     * Read message from the server.
     * 
     * Return an associated array with different set of values of false if nathisn to read.
     * Available sets of data:
     * - from - sender UIN
     * - message - the message
     * - ... - can be ather information like status and etc.
     *
     * @return array
     */
    
public function readMessage()
    {
        return 
$this->readSocket();
    }

    
/**
     * Send message to the server.
     * 
     * Try to send message to the $uin.
     *
     * @todo check the delivery
     * @param string $uin
     * @param string $message
     * @return boolean
     */
    
public function sendMessage($uin$message)
    {
        
$uin str_replace('-'''$uin);
        
$this->writeFlap('ClientIdleTime');
        return 
$this->writeFlap('ClientIBCM', array('uin' => $uin'message' => $message));
    }

    
/**
     * Sets additional options
     * 
     * Do not change this options if you dont know what it mean.
     * 
     * Usage: $icq->setOption('UserAgent', 'miranda');
     * 
     * Options available:
     * MessageType - rtf, utf, old style(offline message)
     * MessageCapabilities - utf, rtf
     * UserAgent - miranda, sim, trillian, licq, kopete, micq, andrq, randq, mchat, jimm, macicq, icqlite
     * Server - login.icq.com:5190
     * ServerPort - 5190, 80, 8080
     * Timeout - seconds before stop reading from socets.
     * 
     *
     * @param string $name
     * @param mixed $value
     * @return boolean
     */
    
public function setOption($name$value)
    {
        
$method 'set'.$name;
        if (
method_exists($this$method))
        {
            return 
$this->$method($value);
        }
        
$this->error 'Warning: setOption name: "'.$name.'" unknown';
        return 
false;
    }

    
/**
     * Return list of contacts
     * 
     * @return array
     */
    
public function getContactList()
    {
        return 
$this->contact_list;
    }

    
/**
     * Return list of contacts groups
     * 
     * @return array
     */
    
public function getContactListGroups()
    {
        
$groups array_keys($this->contact_list_groups);
        unset(
$groups['all_childs_ids']);
        return 
$groups;
    }

    
/**
     * Add uins to list of contacts. First argument is group name. Ather uins to add.
     * Also posible to add with custom name:
     * 
     * addContact("Buddies", array('uin' => UIN_TO_ADD, 'name' => "Custom name"), ...)
     * 
     * @todo errors handling
     * 
     * @param mixed list of uins
     * @return boolean
     */
    
public function addContact()
    {
        
$uin = array();
        
$args func_get_args();
        
$group array_shift($args);
        foreach (
$args as $id) {
            if(
is_array($id))
            {
                
$uin[str_replace('-'''$id['uin'])] = $id['name'];
            }
            else
            {
                
$uin[str_replace('-'''$id)] = false;
            }
        }
        if(
count($uin) > 0) {
            
$this->writeFlap('ClientSSIEditStart');
            
$this->writeFlap('ClientSSIAdd', array('uins' => $uin'group' => $group));
            
$this->writeFlap('ClientSSIEditEnd');
            return 
true;
        }
        return 
false;
    }

    
/**
     * Delete uins from list of contacts.
     * deleteContact(UIN_TO_DELETE, ...) 
     * 
     * @todo errors handling
     * @param mixed list of uins
     * @return boolean
     */
    
public function deleteContact()
    {
        
$uin = array();
        foreach (
func_get_args() as $id) {
            
$uin[] = str_replace('-'''$id);
        }
        if(
count($uin) > 0) {
            
$this->writeFlap('ClientSSIEditStart');
            
$this->writeFlap('ClientSSIDelete', array('uins' => $uin));
            
$this->writeFlap('ClientSSIEditEnd');
            return 
true;
        }
        return 
true;
    }

    public function 
addContactGroup($name$parent "")
    {
        
$this->writeFlap('ClientSSIEditStart');
        
$this->writeFlap('ClientSSIAdd', array('group' => $name'parent' => $parent));
        
$this->writeFlap('ClientSSIEditEnd');
        return 
true;
    }

    public function 
deleteContactGroup($name$parent)
    {
        
$this->writeFlap('ClientSSIEditStart');
        
$this->writeFlap('ClientSSIDelete', array('group' => $name'parent' => $parent));
        
$this->writeFlap('ClientSSIEditEnd');
        return 
true;
    }

    public function 
getAuthorization($uin$reason='')
    {
        return 
$this->writeFlap('ClientSSIAuthRequest', array('uin' => $uin'reason' => $reason));
    }

    public function 
setAuthorization($uin$granted=true$reason='')
    {
        return 
$this->writeFlap('ClientSSIAuthResponse', array('uin' => $uin'reason' => $reason'allow' => $granted));
    }
}
?>
Онлайн: 1
Реклама