Всем привет, на днях решил вспомнить пыхыпы и написать немного о ICQ протоколе и с чем его едят. За основу своих трудов взял несколько готовых ICQ классов (рабочих) и пару пачек пельменей. Разберу основные моменты (реализацию проксей не сделал правда - времени мало), но конечный код можно будет использовать, как минимум, для проверки номеров на валидность. 1. Начало. Для начала зададим параметры подключения: Code: $uin = "122211"; # номер ICQ $password = "bazzinga"; # соответственно пароль Далее установим соединение с сервером авторизации (lоgin.iсq.соm): Code: $socket = @fsockopen("login.icq.com", 5190); Q: А почему бы для скорости не сделать случайную выборку сервера? A: На личном опыте убедился, что быстрее сервера авторизации, чем этот - нет. Да и в инете полно дохлых. Поэтому это реализуется только при наличие работающих и быстрых серверов. 2. Первые FLAP и SNAC. После создания подключения, считаем ответ сервера - FLAP и SNAC (о них можно почитать в гугле). Эта функция чтения ответа сервера будет постоянно, но выделять как отдельную функцию не стал. Code: $FLAP = @fread($socket, 6); list(, $length) = @unpack("n", substr($FLAP, 4, 2)); $SNAC = @fread($socket, $length); 3. Подготовка данных перед отправкой на сервер. Теперь перед нами стоит задача, сделать наши данные такие, чтобы их смог понять сервер авторизации (UIN просто пакуем,а пароль - XOR'им). Выделять XOR пароля в отдельную функцию тоже не стал: Code: $data = base64_decode("8yaBxDmG25Jxo7nmU3qVfA=="); for ($i=0; $i<strlen($password); $i++) @$out .= chr(ord($data[$i]) ^ ord($password[$i])); Теперь у нас всё готово, осталось данные упаковать в FLAP и SNAC: Code: $SNAC = base64_decode('AAAAAQ==').pack("nna*", 0x01, strlen($uin), $uin).pack("nna*", 0x02, strlen($out), $out).base64_decode('AAMACElDUUJhc2ljABYAAgEKABcAAgABABgAAgAAABkAAgAAABoAAgABABQABAAAAFUADwACZW4ADgACdXM='); $FLAP = pack("CCnn", 0x2A, 1, $random=rand(0x0000, 0xFFFF), strlen($SNAC)); $random++; Я сделал за вас всю грязную работу, упаковав постоянные данные в base64, также убрал возможности смены версии клиента, поставив кошерную, ибо сервер ругается ссылками на ICQ7, если там поставить свой :\ И, собсна, сама отправка: Code: @fwrite($socket, $FLAP.$SNAC); 4. Прием ответа. Для считывания ответа сервера и данных и BOS-сервере (BOS-сервер -- сервер, на который перенаправляет нас центр авторизации при верно введенном UIN'e+пароле. Собственно так мы и будем определять, верные данные отправили или нет.) Code: $FLAP = @fread($socket, 6); list(, $length) = @unpack("n", substr($FLAP, 4, 2)); $SNAC = @fread($socket, $length); while (strlen($SNAC) > 0) { list(, $type, $length) = unpack("n2", substr($SNAC, 0, 4)); $TLV[$type] = substr($SNAC, 4, $length); $SNAC = substr($SNAC, 4+$length); } У нас образовался массив $TLV, (Type, Length, Value - ничто иное, как указание числового идентификатора типа данных, их длины, и, собственно, самих данных (с)destym) В своём труде я добавил парочку проверок для блокировку IP (из-за частых подключений), неверный UIN/пароль и на кривой сервер авторизации. Также, если вы хотите сделать из этого чеккер, в результате собственных наблюдений установил, что задержку между UIN'ами нужно делать примерно так: Code: sleep(rand(4,5)); Это позволяет не схватить временный бан IP при использовании только одного сервера авторизации. Конечный код: PHP: $uin = "UIN"; $password = "PWD"; $socket = @fsockopen("login.icq.com", 5190); if ($socket) { $start = getmicrotime(); $FLAP = @fread($socket, 6); list(, $length) = @unpack("n", substr($FLAP, 4, 2)); $SNAC = @fread($socket, $length); $data = base64_decode("8yaBxDmG25Jxo7nmU3qVfA=="); for ($i=0; $i<strlen($password); $i++) @$out .= chr(ord($data[$i]) ^ ord($password[$i])); $SNAC = base64_decode('AAAAAQ==').pack("nna*", 0x01, strlen($uin), $uin).pack("nna*", 0x02, strlen($out), $out).base64_decode('AAMACElDUUJhc2ljABYAAgEKABcAAgABABgAAgAAABkAAgAAABoAAgABABQABAAAAFUADwACZW4ADgACdXM='); $FLAP = pack("CCnn", 0x2A, 1, $random=rand(0x0000, 0xFFFF), strlen($SNAC)); $random++; @fwrite($socket, $FLAP.$SNAC); $FLAP = @fread($socket, 6); list(, $length) = @unpack("n", substr($FLAP, 4, 2)); $SNAC = @fread($socket, $length); while (strlen($SNAC) > 0) { list(, $type, $length) = unpack("n2", substr($SNAC, 0, 4)); $TLV[$type] = substr($SNAC, 4, $length); $SNAC = substr($SNAC, 4+$length); } echo "<br>"; echo $uin.":".$password." - "; if ( @trim($TLV[0x04]) == 'http://www.aol.com?ccode=us&lang=en' ) $debug = 'Слишком быстро...'; else if( @strstr( $TLV[0x04], 'MISMATCH_PASSWD' ) ) $debug = 'Неверный пароль.'; else if (!(@$TLV[0x05] && @$TLV[0x06])) $debug = 'Ошибка при авторизации: не выдан BOS-сервер (слишком много переподключений или выбран корявый сервер).'; else $debug = 'Пароль соответствует UIN\'у.'; @fclose($socket); echo "<b>".$debug."</b>", " (time: ".substr((getmicrotime()-$start), 0, 4)." s.)", "<br><br>"; } else echo $debug = 'Невозможно создать подключение с сервером :('; function getmicrotime() { $mt = explode(" ", microtime() ); return ((float)$mt[0] + (float)$mt[1]); } 5. Выведение номера в On-line. Для выведения номера в онлайн, придётся сделать следующиее: - Закрыть подключение с сервером авторизации - Подключиться к BOS-серверу - Отправить Cookies - Отправить пакет для готовности работы - Выполнить необходимые действия и завершить работу закрыванием соединения Реализация: Code: if( $debug == 'Пароль соответствует UIN\'у.') # Проверяем на валидность UIN { echo '<br>Подключаемся...'; # Выведем сообщение, что всё ОК и сейчас подключимся list( $server, $port ) = explode( ':', $TLV[0x05] ); $socket = @fsockopen($server, $port); # Создаем подключение с выданным нам BOS-сервером if( !$socket ) die('<b>BOS-сервер ( </b>'.$TLV[0x05].'<b> ) недоступен.Попробуйте ещё раз.</b>'); # Выведем сообщение, если BOS-сервер недоступен $SNAC = base64_decode("AAAAAQ==").pack("nna*", 0x06, strlen($TLV[0x06]), $TLV[0x06]); # Отдаём запакованые Cookies $FLAP = pack("CCnn", 0x2A, 1, $random, strlen($SNAC)); $random++; # Создаем FLAP для этих данных @fwrite($socket, $FLAP.$SNAC); # Отправляем данные серверу $SNAC = base64_decode("AAEAAgAAAAAAAgABAAMBEAKKAAIAAQEBAooAAwABARACigAVAAEBEAKKAAQAAQEQAooABgABARACigAJAAEBEAKKAAoAAQEQAoo="); # Опять делаю всю грязную работу, т.е. отсылаем серверу пакет о готовности работы $FLAP = pack("CCnn", 0x2A, 2, $random, strlen($SNAC)); # Создаем FLAP для этих данных @fwrite($socket, $FLAP.$SNAC); # Отправляем данные серверу /* Цикл */ for( $i=0; $i <= 3; $i++) # Цикл для работы с номером { sleep(1); } /* Конец цикла */ @fclose($socket); # Закрываем соединение, работа с ICQ номером завершена. } Конечный код, с выведением номера в онлайн: PHP: $uin = "UIN"; $password = "PWD"; $socket = @fsockopen("login.icq.com", 5190); if ($socket) { $start = getmicrotime(); $FLAP = @fread($socket, 6); list(, $length) = @unpack("n", substr($FLAP, 4, 2)); $SNAC = @fread($socket, $length); $data = base64_decode("8yaBxDmG25Jxo7nmU3qVfA=="); for ($i=0; $i<strlen($password); $i++) @$out .= chr(ord($data[$i]) ^ ord($password[$i])); $SNAC = base64_decode('AAAAAQ==').pack("nna*", 0x01, strlen($uin), $uin).pack("nna*", 0x02, strlen($out), $out).base64_decode('AAMACElDUUJhc2ljABYAAgEKABcAAgABABgAAgAAABkAAgAAABoAAgABABQABAAAAFUADwACZW4ADgACdXM='); $FLAP = pack("CCnn", 0x2A, 1, $random=rand(0x0000, 0xFFFF), strlen($SNAC)); $random++; @fwrite($socket, $FLAP.$SNAC); $FLAP = @fread($socket, 6); list(, $length) = @unpack("n", substr($FLAP, 4, 2)); $SNAC = @fread($socket, $length); while (strlen($SNAC) > 0) { list(, $type, $length) = unpack("n2", substr($SNAC, 0, 4)); $TLV[$type] = substr($SNAC, 4, $length); $SNAC = substr($SNAC, 4+$length); } echo "<br>"; echo $uin.":".$password." - "; if ( @trim($TLV[0x04]) == 'http://www.aol.com?ccode=us&lang=en' ) $debug = 'Слишком быстро...'; else if( @strstr( $TLV[0x04], 'MISMATCH_PASSWD' ) ) $debug = 'Неверный пароль.'; else if (!(@$TLV[0x05] && @$TLV[0x06])) $debug = 'Ошибка при авторизации: не выдан BOS-сервер (слишком много переподключений или выбран корявый сервер).'; else $debug = 'Пароль соответствует UIN\'у.'; @fclose($socket); echo "<b>".$debug."</b>", "(time: ".substr((getmicrotime()-$start), 0, 4)." s.)", "<br><br>"; if( $debug == 'Пароль соответствует UIN\'у.') # Проверяем на валидность UIN { echo '<br>Подключаемся...'; # Выведем сообщение, что всё ОК и сейчас подключимся list( $server, $port ) = explode( ':', $TLV[0x05] ); $socket = @fsockopen($server, $port); # Создаем подключение с выданным нам BOS-сервером if( !$socket ) die('<b>BOS-сервер ( </b>'.$TLV[0x05].'<b> ) недоступен.Попробуйте ещё раз.</b>'); # Выведем сообщение, если BOS-сервер недоступен $SNAC = base64_decode("AAAAAQ==").pack("nna*", 0x06, strlen($TLV[0x06]), $TLV[0x06]); # Отдаём запакованые Cookies $FLAP = pack("CCnn", 0x2A, 1, $random, strlen($SNAC)); $random++; # Создаем FLAP для этих данных @fwrite($socket, $FLAP.$SNAC); # Отправляем данные серверу $SNAC = base64_decode("AAEAAgAAAAAAAgABAAMBEAKKAAIAAQEBAooAAwABARACigAVAAEBEAKKAAQAAQEQAooABgABARACigAJAAEBEAKKAAoAAQEQAoo="); # Опять делаю всю грязную работу, т.е. отсылаем серверу пакет о готовности работы $FLAP = pack("CCnn", 0x2A, 2, $random, strlen($SNAC)); # Создаем FLAP для этих данных @fwrite($socket, $FLAP.$SNAC); # Отправляем данные серверу /* Цикл */ for( $i=0; $i <= 3; $i++) # Цикл для работы с номером { sleep(1); } /* Конец цикла */ @fclose($socket); # Закрываем соединение, работа с ICQ номером завершена. } } else echo $debug = 'Невозможно создать подключение с сервером :('; function getmicrotime() { $mt = explode(" ", microtime() ); return ((float)$mt[0] + (float)$mt[1]); } Написать данное введение мне помогли 3 известных класса: [http://kanicq.ru/invisible/icqlib.rar], [http://destym.ru/sources/mlCQ.phps], [http://wip.asminog.com/releases/WebIcqPro1.4.8b.zip] P.S. Не откажусь от вашей критики
Достаточно серверу отправить дополнительные данные и можно выводить в онлайн. Только зачем ? Для этого есть громосткие классы.
[UPD] Добавил функцию выведения номера в онлайн (по просьбе читающих) P.s. придётся поудалять пробелы, которые создаёт форум
Deathdreams чем оправдано использование @? Пожалуйста, не используйте магические переменные, если это, конечно, возможно Обрамление повторно используемого кода в функции - все же плюс, чем минус
Мб скрипт будет использоваться не только в личных целях ? Хотелось показать весь процесс авторизации поэтапно. Да, я согласен с вами, но зрительно воспринимается больше такой метод.
Deathdreams про это-то и спрашиваю, как вы предлагаете локализовывать проблему пользователям? При вашем коде - потребуется заменять все знаки @ на пропуски, а вдруг там где нить используется этот знак, не только для заглушения ошибок. Не лучше ли использовать error_reporting(0); Зрительно лучше воспринимается абстрактный код, например: PHP: function do_authorise() { connect(); send_loginpassword(); return check_answer(); } function mainloop() { receive_message(); dispatch_message(); } if do_authorise() { mainloop(); }
Всегда пренебрежительно относился к error_reporting(0); Ставил "@" там, где могут произойти ошибки на стороне сокета или принятых данных... Ну, если вчитаться, это можно легко заметить. Ставлю при открытии сокета (т.к. сервер регулируется пользователем), ставлю при принятии TLV от сервера (а вдруг он пустой или вообще массив ?). З.Ы. Неужели нет интересующихся ICQ протоколом людей ? о_О Зря писал что ли
R1no, у меня вообще скачаная с офф сайта последняя версия не работала(( Щас использую туже либу, но исправленную и выковыряную со steelbot
В steelbot хорошая допиленная библиотека, но она тоже не позволяет поставить текст в xStatus. В ссылках, которые тс дал, я не увидел методов для установки текста в xStatus( Может кто-то знает такую библиотеку или в состоянии доделать это в WebIcqPro ?