<?php

// © Navigator, 2005-2006
// icq: 117886
// при распространении ссылка на меня обязательна
//
// скрипт позволяет работать с протоколом icq из php. реализованы
// только базовые функции, такие как подключение к сети, приём и отправка
// сообщений. возможно также добавление своих функций.


ignore_user_abort(true);

error_reporting(E_ALL);

set_time_limit(0);

$uin = "uin"; // номер
$pass = EncDecPassWord("password"); // пароль к номеру

/********/

function EncDecPassWord($pass) {
    
$enc = "\xF3\x26\x81\xC4\x39\x86\xDB\x92\x71\xA3\xB9\xE6\x53\x7A\x95\x7C";
    
$ret = "";
    for (
$i = 0; $i < strlen($pass); $i++)
        
$ret .= chr(ord($pass{$i}) ^ ord($enc{$i % 16}));
    return
$ret;
}

function
hexDump($raw) {
    
$raw = (string)$raw;
    
$len = strlen($raw);
    
$raz = ceil($len / 16);
    
$i = 0;
    
$ret = "Length: $len\n";
    while (
$i++ < $raz) {
        
$tempHex = "";
        
$tempRaw = "";
        
$tempStr = substr($raw, 16 * ($i - 1), 16);
        
$j = -1;
        while (
$j++ < 16) {
            if (!isset(
$tempStr{$j})) break;
            
$ord = dechex(ord($tempStr{$j}));
            if (
strlen((string)$ord) < 2) $ord = "0$ord";
            
$tempHex .= $ord." ";
            
$tempRaw .= ($tempStr{$j} >= " ") ? $tempStr{$j} : ".";
        }
        if (
strlen($tempHex) < 48) while (strlen($tempHex) < 48) $tempHex .= " ";
        if (
strlen($tempRaw) < 16) while (strlen($tempRaw) < 16) $tempRaw .= " ";
        
$ret .= "$tempHex| $tempRaw |\n";
    }
    return
$ret;
}

$charmap =
"%u0402%u0403%u201A%u0453%u201E%u2026%u2020%u2021%u20AC%u2030%u0409%u2039%u040A%u040C%u040B%u040F".
"%u0452%u2018%u2019%u201C%u201D%u2022%u2013%u2014%u0000%u2122%u0459%u203A%u045A%u045C%u045B%u045F".
"%u00A0%u040E%u045E%u0408%u00A4%u0490%u00A6%u00A7%u0401%u00A9%u0404%u00AB%u00AC%u00AD%u00AE%u0407".
"%u00B0%u00B1%u0406%u0456%u0491%u00B5%u00B6%u00B7%u0451%u2116%u0454%u00BB%u0458%u0405%u0455%u0457";
function
ucs2win($str) {
    global
$charmap;
    
$ret = "";
    if (
strlen($str) % 2 != 0) return "";
    for (
$i = 1; $i < strlen($str); $i++)
    {
        
$first_char = ord($str{$i++}) & 0xff;
        
$i++;
        
$second_char = ord($str{$i++}) & 0xff;
        
$char = ($first_char << 8) | $second_char;
        if ((
$char >= 0x0410) && ($char <= 0x044f))
        {
            
$ret .= chr($char - 0x0350);
        }
        else if (
$char < 128)
        {
            
$ret .= chr($char & 0x7f);
        }
        else
        {
            if ((
$pos = strpos($charmap, sprintf("%%u%04X", $char))) != false)
            {
                
$ret .= chr(128 + ($pos / 6));
            }
            else
            {
                
$ret .= chr($char & 0xff);
            }
        }
    }
    return
$ret;
}
function
utf2ucs($utf8)
{
    
$ret = "";

    for (
$i = 0; $i < strlen($utf8); $i++)
    {
        
$charcode = 0;
        
$first = ord($utf8{$i});
        if ((
$first & 0x80) == 0)
        {
            
$charcode = $first;
        }
        else
        {
            if (((
$first & 224) != 192) | !isset($utf8{$i++})) return false;
            
$second = ord($utf8{$i});
            if ((
$second & 192) != 0x80) return false;
            if ((
$first & 240) == 224)
            {
                if (!isset(
$utf8{$i++})) return false;
                
$third = ord($utf8{$i});
                if ((
$third & 192) != 0x80) return false;
                
$charcode = (($first & 0x0F) << 12) | (($second & 0x3F) << 6) | ($third & 0x3F);
            }
            else
            {
                
$charcode = ($first & 0x1F) << 6 | ($second & 0x3F);
            }
        }
        
$ret .= pack("n", $charcode);
    }

    return
$ret;
}
function
getW($str, $pos, $le = false)
{
    
$ret = 0;
    if (
$le)
    {
        
$ret = ((ord($str{$pos + 1}) & 0xFF) << 8) | (ord($str{$pos}) & 0xFF);
    }
    else
    {
        
$ret = ((ord($str{$pos}) & 0xFF) << 8) | (ord($str{$pos + 1}) & 0xFF);
    }
    return
$ret;
}

function
readSRV() {
    global
$serv;
    
$data = fread($serv,6);
    if (!
$data)
    {
        
//print "No answer from server.\n";
        
return "";
    }
    if (
$data{0} != "*")
    {
        print
"Bad server answer:\n";
        print
hexDump($data);
        return
false;
    }
    
$channel_id = ord($data{1});
    
$seqid = getW($data, 2);
    
$len = getW($data, 4);
    
$content = $len > 0 ? fread($serv,$len) : "";
    print
hexDump($data.$content);
    return array(
        
'CHANNEL_ID' => $channel_id,
        
'SEQ' => $seqid,
        
'LENGTH' => $len,
        
'DATA' => $content
        
);
}

function
sndFLAP($channel,$raw="") {
    static
$id;
    global
$serv;
    if (!
is_integer($id)) $id = (int)rand(0,0x7fff);
    
$to_send = "*";
    
$to_send .= chr($channel);
    
$to_send .= pack("n",$id);
    
$to_send .= pack("n",strlen($raw));
    
$to_send .= $raw;
    
$id++;
    
$id &= 0x7fff;
    print
hexDump($to_send);
    return
fwrite($serv,$to_send,strlen($to_send));
}

function
splitTLVS($raw) {
    
$i = -1;
    
$na = 0;
    
$result = array();
    while (
$i++ < strlen($raw)) {
        if (!isset(
$raw{$i})) break;
        
$unp = substr($raw,$i,2);
        
$unp = unpack("n",$unp);
        
$tlv_id = $unp[1];
        
$unp = substr($raw,$i+2,2);
        
$unp = unpack("n",$unp);
        
$tlv_len = $unp[1];
        
$tlv_data = substr($raw,$i+4,$tlv_len);
        if (isset(
$result[$tlv_id])) $na++;
        
$result[isset($result[$tlv_id]) ? dechex($tlv_id)."_$na" : $tlv_id] = $tlv_data;
        
$i += $tlv_len + 3;
    }
    return
$result;
}

function
sendMSG($to,$what="",$canBeOffline=false) {
    global
$ads, $serv;
    static
$cnt;
    
$cnt++;
    
$what = trim($what);
    
$stlv = "\x05\x01\x00\x02\x01\x01\x01\x01";
    
$stlv .= pack("n",strlen($what)+4);
    
$stlv .= "\x00\x00\x00\x00";
    
$stlv .= $what;
    
$return = "\x00\x04\x00\x06\x00\x00\x00\x01\x00\x06";
    
$return .= "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01";
    
$return .= chr(strlen($to)).$to."\x00\x02".pack("n",strlen($stlv)).$stlv . ($canBeOffline ? "\x00\x06\x00\x00" : "");
    
sndFLAP(2,$return);
    
usleep(1250);
}
function
recieveMSG($data) {
    if (
getW($data, 0) == 4 && getW($data, 2) == 7) {
        
$msg_channel = ord($data{19});
        
$msg_sender_screenname_len = ord($data{20});
        
$msg_sender_screenname = substr($data,21,$msg_sender_screenname_len);
        
$msg_tlvs = splitTLVS(substr($data,25+$msg_sender_screenname_len));
        
$decodeUCS = false;
        if (
$msg_channel == 1) {
            
$msg_inttlvs = splitTLVS($msg_tlvs[2]);
            
$tlvx101 = $msg_inttlvs[0x0101];
            
$charset = getW($tlvx101, 0);
            
$msg_text = substr($tlvx101, 4);
            if (
preg_match('#[^\x01-\x7f]#x', $msg_text))
            {
                
$utf2ucs = utf2ucs($msg_text);
                if (
$utf2ucs != false)
                {
                    
$msg_text = $utf2ucs;
                    
$decodeUCS = true;
                    
$utf2ucs = null;
                }
                else
                {
                    
$decodeUCS = ($charset == 2);
                }
                if (
$decodeUCS) $msg_text = ucs2win($msg_text);
            }
        }
        else if (
$msg_channel == 4)
        {
            
$tlv5 = $msg_tlvs[5];
            if (
getW($tlv5, 4) != 0x0101) return;
            
$len = getW($tlv5, 6, true);
            
$msg_text = substr($tlv5, 8, $len - 1);
            if (
preg_match('#[^\x01-\x7f]#x', $msg_text))
            {
                
$utf2ucs = utf2ucs($msg_text);
                if (
$utf2ucs != false)
                {
                    
$msg_text = ucs2win($utf2ucs);
                    
$utf2ucs = null;
                }
            }
        } else {
            print
"Unknown MSG channel. Ignoring . . .\n\n";
            return;
        }
        print
"RECIEVED MESSAGE.\n";
        print
"MESSAGE TYPE: $msg_channel\n";
        print
"FROM: $msg_sender_screenname\n";
        print
"MESSAGE:\n";
        print
"$msg_text\n\n";

        
// тут обрабатываем пришедшее сообщение так, как нам нужно
        
    
}
}

function
LogIn() {
    global
$serv, $uin, $pass;
    print
"Connecting . . .\n";
    
$serv = fsockopen("login.icq.com", "5190");
    
readSRV();
    print
"Authentication . . .\n";
    
sndFLAP(1,"\x00\x00\x00\x01\x00\x01\x00".chr(strlen($uin)).$uin."\x00\x02\x00".chr(strlen($pass)).$pass);
    print
"Recieving auth data . . .\n";
    if (!(
$data = readSRV())) {
        print
"Connection failed.\n";
        
fclose($serv);
        print
"Reconnecting after 1 sec. . .\n";
        
sleep(1);
        
LogIn();
        return;
    }
    
$tlv = splitTLVS($data['DATA']);
    print
"Server name: {$tlv[5]}. Disconnecting from login.icq.com . . .\n";
    
sndFLAP(4);
    
fclose($serv);
    print
"Connecting to {$tlv[5]} . . .\n";
    
$connect = explode(':',$tlv[5]);
    
$serv = fsockopen($connect[0],isset($connect[1]) ? $connect[1] : "5190");
    
readSRV();
    
socket_set_blocking($serv, false);
    print
"Authentication . . .\n";
    
sndFLAP(1,"\x00\x00\x00\x01\x00\x06".pack("n",strlen($tlv[6])).$tlv[6]);
    print
"Sending CLI_SETUSERINFO . . .\n";
    
$caps  = pack("H*", "0946134D4C7F11D18222444553540000");
    
$caps .= pack("H*", "0946134E4C7F11D18222444553540000");
    
sndFLAP(2,"\x00\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x05".pack("n", strlen($caps)).$caps);
    print
"Set status to Online . . .\n";
    
sndFLAP(2,"\x00\x01\x00\x1e\x00\x00\x00\x00\x00\x00".implode(array(
        
"\x00", "\x06", "\x00", "\x04", "\x10", "\x10", "\x00", "\x00",
        
"\x00", "\x0C", "\x00", "\x25",
        
"\x00", "\x00", "\x00", "\x00",
        
"\x00", "\x00", "\x00", "\x00",
        
"\x00",
        
"\x00", "\x06",
        
"\x00", "\x00", "\x00", "\x00",
        
"\x00", "\x00",
        
"\x00", "\x50",
        
"\x00", "\x00",
        
"\x00", "\x03",
        
"\x35", "\x66", "\x66", "\x66",
        
"\x00", "\x00", "\x00", "\x00",
        
"\x00", "\x00", "\x00", "\x00",
        
"\x00", "\x00"
        
)));
    print
"Sending CLI_SETICBM packets . . .\n";
    
sndFLAP(2, pack("H*", "00040002000000000002") . pack("H*", "000100000001FFFF03E703E700000000"));
    
sndFLAP(2, pack("H*", "00040002000000000002") . pack("H*", "00020000000000000000000000000000"));
    
sndFLAP(2, pack("H*", "00040002000000000002") . pack("H*", "000400000001FFFF03E703E700000000"));
    print
"Sending CLI_READY . . .\n";
    
sndFLAP(2,"\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00".implode(array(
        
"\x00", "\x01", "\x00", "\x03",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x13", "\x00", "\x02",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x02", "\x00", "\x01",
        
"\x01", "\x01", "\x04", "\x7B",
        
"\x00", "\x03", "\x00", "\x01",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x15", "\x00", "\x01",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x04", "\x00", "\x01",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x06", "\x00", "\x01",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x09", "\x00", "\x01",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x0A", "\x00", "\x01",
        
"\x01", "\x10", "\x04", "\x7B",
        
"\x00", "\x0B", "\x00", "\x01",
        
"\x01", "\x10", "\x04", "\x7B"
        
)));
    print
"Connected.\n";
}

LogIn();

while (
1) // в этом цикле, собственно, и работает бот
{
    
usleep(1);
    if (
$data = readSRV()) {
        if (
$data['CHANNEL_ID'] == 3) {
            
fclose($serv);
            
LogIn();
        }
        if (
$data['CHANNEL_ID'] == 2)
        {
            
$data = $data['DATA'];
            switch (
getW($data, 0))
            {
                case
4:
                    
recieveMSG($data);
                    break;
            }
        }
    }
}
?>