<?php
class WebIcqLite_TLV {
var $type;
var $size;
var $error;
var $types = array
(
'UIN' => 1, // 0x01
'DATA' => 2, // 0x02
'CLIENT' => 3, // 0x03
'ERROR_URL' => 4, // 0x04
'RECONECT_HERE' => 5, // 0x05
'COOKIE' => 6, // 0x06
'SNAC_VERSION' => 7, // 0x07
'ERROR_SUBCODE' => 8, // 0x08
'DISCONECT_REASON' => 9, // 0x09
'RECONECT_HOST' => 10, // 0x0A
'URL' => 11, // 0x0B
'DEBUG_DATA' => 12, // 0x0C
'SERVICE' => 13, // 0x0D
'CLIENT_COUNTRY' => 14, // 0x0E
'CLIENT_LNG' => 15, // 0x0F
'SCRIPT' => 16, // 0x10
'USER_EMAIL' => 17, // 0x11
'OLD_PASSWORD' => 18, // 0x12
'REG_STATUS' => 19, // 0x13
'DISTRIB_NUMBER' => 20, // 0x14
'PERSONAL_TEXT' => 21, // 0x15
'CLIENT_ID' => 22, // 0x16
'CLI_MAJOR_VER' => 23, // 0x17
'CLI_MINOR_VER' => 24, // 0x18
'CLI_LESSER_VER' => 25, // 0x19
'CLI_BUILD_NUMBER' => 26, // 0x1A
// 'PASSWORD' => 37
);
function setTLV($type, $value, $length = false)
{
switch ($length) {
case 1:
$format = 'c';
break;
case 2:
$format = 'n';
break;
case 4:
$format = 'N';
break;
default:
$format = 'a*';
break;
}
if ($length === false) {
$length = strlen($value);
}
return pack('nn'.$format, $this->types[$type], $length, $value);
}
function getTLV($data)
{
$arr = unpack('n2', substr($data, 0, 4));
$this->type = $arr[1];
$this->size = $arr[2];
return substr($data, 4, $this->size);
}
}
class WebIcqLite_SNAC extends WebIcqLite_TLV {
var $request_id = 0;
function setSNAC12()
{
$this->request_id++;
$out = pack('nnnN', 1, 2, 0, $this->request_id);
$out .= pack('n*', 1, 3, 272, 650);
$out .= pack('n*', 2, 1, 272, 650);
$out .= pack('n*', 3, 1, 272, 650);
$out .= pack('n*', 21, 1, 272, 650);
$out .= pack('n*', 4, 1, 272, 650);
$out .= pack('n*', 6, 1, 272, 650);
$out .= pack('n*', 9, 1, 272, 650);
$out .= pack('n*', 10, 1, 272, 650);
return $out;
}
function setSNAC46($uin, $message)
{
$this->request_id++;
$cookie = microtime();
$out = pack('nnnNdnca*', 4, 6, 0, $this->request_id, $cookie, 2, strlen($uin), $uin);
$capabilities = pack('H*', '094613494C7F11D18222444553540000'); // utf-8 support
// '97B12751243C4334AD22D6ABF73F1492' rtf support
$data = pack('nd', 0, $cookie).$capabilities;
$data .= pack('nnn', 10, 2, 1);
$data .= pack('nn', 15, 0);
$data .= pack('nnvvddnVn', 10001, strlen($message)+62, 27, 8, 0, 0, 0, 3, $this->request_id);
$data .= pack('nndnn', 14, $this->request_id, 0, 0, 0); //45
$data .= pack('ncvnva*', 1, 0, 0, 1, (strlen($message)+1), $message);
$data .= pack('H*', '0000000000FFFFFF00');
$out .= $this->setTLV('RECONECT_HERE', $data);
$out .= $this->setTLV('CLIENT', '');
return $out;
}
function setSNAC46offline($uin, $message)
{
$this->request_id++;
$cookie = microtime();
$out = pack('nnnNdnca*', 4, 6, 0, $this->request_id, $cookie, 1, strlen($uin), $uin);
$data = pack('ccnc', 5, 1, 1, 1);
$data .= pack('ccnnna*', 1, 1, strlen($message)+4, 3, 0, $message);
$out .= $this->setTLV('DATA', $data);
$out .= $this->setTLV('CLIENT', '');
$out .= $this->setTLV('COOKIE', '');
return $out;
}
}
class WebIcqLite_FLAP extends WebIcqLite_SNAC{
var $socet;
var $command = 0x2A;
var $channel;
var $sequence;
var $body;
var $info = array();
function WebIcqLite_FLAP() {
$this->sequence = rand(1, 30000);
}
function dump($str)
{
$f = fopen('dump', 'a');
fwrite($f, $str);
fclose($f);
}
function getFLAP()
{
if(!feof($this->socet)){
$header = fread($this->socet, 6);
$header = unpack('c2channel/n2size', $header);
$this->channel = $header['channel2'];
$this->body = fread($this->socet, $header['size2']);
}
}
function parseCookieFLAP()
{
$this->getFLAP();
$this->info = array();
while($this->body != ''){
$info = $this->getTLV($this->body);
$key = array_search($this->type, $this->types);
if($key){
$this->info[$key] = $info;
}
$this->body = substr($this->body, ($this->size+4));
}
}
function parseAnswerFLAP()
{
$this->getFLAP();
$array = unpack('n3int/Nint', $this->body);
while ($array['int'] != $this->request_id) {
$this->getFLAP();
$array = unpack('n3int/Nint', $this->body);
}
$this->error = 'Неизвестный ответ сервера.';
if ($array['int1'] == 4) {
switch ($array['int2']) {
case 1:
$this->error = 'Ошибка. Сообщение не отправлено.';
return false;
break;
case 0x0c:
return true;
break;
};
}
$this->error = 'Неизвестный ответ сервера.';
return false;
}
function prepare()
{
$this->sequence++;
$out = pack('ccnn', $this->command, $this->channel, $this->sequence, strlen($this->body)).$this->body;
return $out;
}
function login($uin, $password)
{
$this->getFLAP();
$this->body .= $this->setTLV('UIN', "$uin");
$this->body .= $this->setTLV('DATA', $this->xorpass($password));
$this->body .= $this->setTLV('CLIENT', 'ICQBasic');
$this->body .= $this->setTLV('CLIENT_ID', 266, 2);
$this->body .= $this->setTLV('CLI_MAJOR_VER', 20, 2);
$this->body .= $this->setTLV('CLI_MINOR_VER', 34, 2);
$this->body .= $this->setTLV('CLI_LESSER_VER', 0, 2);
$this->body .= $this->setTLV('CLI_BUILD_NUMBER', 2321, 2);
$this->body .= $this->setTLV('DISTRIB_NUMBER', 1085, 4);
$this->body .= $this->setTLV('CLIENT_LNG', 'en');
$this->body .= $this->setTLV('CLIENT_COUNTRY', 'us');
$this->channel = 1;
fwrite($this->socet, $this->prepare());
$this->parseCookieFLAP();
$this->body = 0x0000;
fwrite($this->socet, $this->prepare());
$this->close();
if(isset($this->info['RECONECT_HERE']))
{
$url = explode(':', $this->info['RECONECT_HERE']);
if(!$this->open($url))
{
$this->error = 'Unable to reconnect';
return false;
}
}
else
{
$this->error = 'Server don`t return reconnect url';
return false;
}
$this->getFLAP();
$this->body .= $this->setTLV('COOKIE', $this->info['COOKIE']);
if (!fwrite($this->socet, $this->prepare()))
{
$this->error = 'Can`t send cookie, server close connection';
return false;
}
$this->getFLAP();
$this->body = $this->setSNAC12();
if (!fwrite($this->socet, $this->prepare()))
{
$this->error = 'Can`t send ready signal, server close connection';
return false;
}
return true;
}
function write_message($uin, $message)
{
$this->body = $this->setSNAC46($uin, $message);
if (!fwrite($this->socet, $this->prepare()))
{
$this->error = 'Can`t send message, server close connection';
return false;
}
if (! $this->parseAnswerFLAP()) {
// try to send offline message
$this->body = $this->setSNAC46offline($uin, $message);
if (!fwrite($this->socet, $this->prepare()))
{
$this->error = 'Can`t send offline message, server close connection';
return false;
}
if (! $this->parseAnswerFLAP()) {
return false;
}else{
$this->error = 'Клиент не в сети. Сообщение отправлено на сервер.';
return false;
}
}
return true;
}
function xorpass($pass)
{
$roast = array(0xF3, 0x26, 0x81, 0xC4, 0x39, 0x86, 0xDB, 0x92, 0x71, 0xA3, 0xB9, 0xE6, 0x53, 0x7A, 0x95, 0x7c);
$roasting_pass = '';
for ($i=0; $i<strlen($pass); $i++) {
$roasting_pass .= chr($roast[$i] ^ ord($pass{$i}));
}
return($roasting_pass);
}
function open($url = array('login.icq.com', 5190))
{
$this->socet = fsockopen($url[0], $url[1], $errno, $errstr);
if (!$this->socet)
{
$this->error = $errstr;
return false;
}
stream_set_timeout($this->socet, 2);
return true;
}
function close()
{
return fclose($this->socet);
}
}
class WebIcqLite extends WebIcqLite_FLAP {
function WebIcqLite ()
{
$this->WebIcqLite_FLAP();
}
function connect($uin, $pass)
{
if (!$this->open()) {
return false;
}
return $this->login($uin, $pass);
}
function disconnect()
{
return $this->close();
}
function read()
{
$this->getFLAP();
}
function send_message($uin, $message)
{
return $this->write_message($uin, $message);
}
}
?>