PHP YMSG Class
From D3xt3r01.tk
WHY
I remember asking some people ( lamers probably ) about how is a yahoo robot actually made ? And they replied "tcpdump" .. I asked them to explain and help me ... I just wanted to see how the protocol actually works ... none helped like this was a big state secret. Then I remembered pidgin is open source .. so are their libraries .. and finally found someone to help .. so in about 2 days this is the result.
I hope you do know how to read php and use tcpdump. Once you read this code ( pretty verbose ) you should have a general idea of what does what, where and how !
WHAT
This is the main class with a usage example. The class includes some basic functions of a client ( sending/receiving messages ) ..
<?php
/*
*
* @author: Dexter<dexter@d3xt3r01.tk>
* @date: 25.06.2009 3:54 AM
* @Description: Basic class with some functions to connect to yahoo servers. Don't rip the credits.
*
*/
class YMSG{
// Login stuff
protected $user = NULL;
protected $pass = NULL;
// Details info
private $verbtype = "Y-m-d H:i:s"; // Output date type for verbosity
private $verbose = 0; // 0 = no Debug messages ; 1 = Debug messages ; 2 = include curl verbose
// Protocol info
protected $version = "\x10"; // In hex ! 16 = 10 => YMSG16 ; 15 - 0f => YMSG15
protected $key = "\x00\x00\x00\x00"; // initial Key
protected $delim = "\xC0\x80"; // Packet delimitator
// Connection info
protected $status = 0; // 1 = Online ; 0 = Invisible
protected $newtoken = 0; // 0 = Use old token if available ; 1 = Always get new token
protected $connected = 0; // 0 - Disconnected ; 1 - Connected
// Connection Variables
private $challenge = NULL; // The seed
private $crumb = NULL; // Crumb
private $y = NULL; // One of the cookies
private $t = NULL; // Another cookie
private $string307 = NULL; // Calculated string for auth
private $token = NULL; // Connection token
// Yahoo Variables
private $buddylist = NULL;
private $ignorelist = NULL;
protected $fname = NULL;
protected $lname = NULL;
protected $pinginterval = NULL;
protected $pingnr = NULL;
// Resources
protected $ch = NULL; // Curl Resource
protected $yahoo_server = 'scs.msg.yahoo.com'; // YMSGR server
protected $yahoo_port = 5050; // YMSGR port
protected $conn_yahoo = NULL; // Socket Resource
// Colors
protected $black = "\033[30m";
protected $red = "\033[31m";
protected $green = "\033[32m";
protected $blue = "\033[34m";
protected $magenta = "\033[35m";
protected $cyan = "\033[36m";
protected $white = "\033[37m";
protected function verblog($message, $color = "white"){
switch($color){
case "black":
$color = $this->black;
break;
case "red":
$color = $this->red;
break;
case "green":
$color = $this->green;
break;
case "blue":
$color = $this->blue;
break;
case "magenta":
$color = $this->magenta;
break;
case "cyan":
$color = $this->cyan;
break;
case "white":
default:
$color = $this->white;
break;
}
if($this->verbose > 0){
echo date($this->verbtype)." ".$color.$message.$this->white."\n";
}
return TRUE;
}
public function usenewtoken($token){
if($token == 0 || $token == 1){
if($token == 0){
$this->verblog("usenewtoken() Use old token if available.", 'blue');
}else{
$this->verblog("usenewtoken() Always get new token.", 'blue');
}
$this->newtoken = $token;
}
return TRUE;
}
public function ConnectedStatus(){
return $this->connected;
}
private function verbtype($type){
$this->verblog("verbtype() Setting Verbosity date type to ".$type." .", 'blue');
$this->verbtype = $type;
}
public function YahooUser($user){
$this->user = trim($user);
$this->verblog("YahooUser() set to ".$this->user, 'blue');
}
public function YahooPass($pass){
$this->pass = trim($pass);
$this->verblog("YahooPass() set pass to ".$this->pass, 'blue');
}
public function Verbosity($verbose){
switch($verbose){
case 0:
if($this->verbose == 0){
echo "Verbosity already off.\n";
}else{
echo "Verbosity turned off.\n";
$this->verbose = 0;
}
break;
case 1:
if($this->verbose > 0){
echo "Verbosity already on.\n";
}else{
echo "Verbosity turned on (L1).\n";
$this->verbose = 1;
}
break;
case 2:
if($this->verbose > 1){
echo "Verbosity already at L2.\n";
}else{
echo "Verbosity set at L2.\n";
$this->verbose = 2;
}
break;
default:
echo "Unknown verbosity value\n";
return FALSE;
break;
}
return TRUE;
}
private function MyCurlInit(){
if(($this->ch = curl_init()) === FALSE){
$this->verblog("MyCurlInit() Failed to initialize curl session.", 'red');
return FALSE;
}
$this->verblog("MyCurlInit() Created curl session.", 'green');
}
private function MyCurlGet($url){
if(!is_resource($this->ch)){
$this->verblog("MyCurlGet() CH isn't a valid resource.", 'red');
if(($this->MyCurlInit()) === FALSE){
$this->verblog("MyCurlGet()::MyCurlInit() failed creating again.", 'red');
return FALSE;
}
}
curl_setopt($this->ch, CURLOPT_URL, $url);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
if($this->verbose > 1){
curl_setopt($this->ch, CURLOPT_VERBOSE, 1);
}
return(curl_exec($this->ch));
}
private function MyCurlClose(){
$this->verblog("MyCurlClose() Closed curl resource.", 'green');
curl_close($this->ch);
return TRUE;
}
public function ascii2hex($ascii){
$hex = '';
for($i=0;$i<strlen($ascii);$i++){
$byte = dechex(ord($ascii{$i}));
$byte = str_repeat('0', 2 - strlen($byte)).$byte;
$hex .= $byte;
}
return($hex);
}
public function hex2ascii($hex){
$ascii = '';
$hex = str_replace(" ", "", $hex);
for($i=0;$i<strlen($hex);$i=$i+2){
$ascii .= chr(hexdec(substr($hex, $i, 2)));
}
return($ascii);
}
private function SockINIT(){
$this->verblog("YahooConnect() Creating conn_yahooo socket.", 'blue');
if(($this->conn_yahoo = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === FALSE){
$this->verblog("SockINIT()::socket_create() failed with reason: ".socket_strerror(socket_last_error($this->conn_yahoo)), 'red');
return FALSE;
}
if((@socket_connect($this->conn_yahoo, $this->yahoo_server, $this->yahoo_port)) === FALSE){
$this->verblog("SockINIT()::socket_connect() failed with reason: ".socket_strerror(socket_last_error($this->conn_yahoo)), 'red');
return FALSE;
}
socket_set_option($this->conn_yahoo, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 5, "usec" => 0));
$this->verblog("SockINIT() created connection socket.", 'green');
return TRUE;
}
private function sendsock($packet){
if(!is_resource($this->conn_yahoo)){
$this->verblog("sendsock() If conn_yahoo isn't a resource, I can't work with it.", 'red');
return FALSE;
}
$header = substr($packet, 11, 1);
$this->key = substr($packet, 16, 4);
$this->verblog("CLIENT > SERVER: Sending Packet Size: ".strlen($packet)." Bytes", 'magenta');
$this->verblog("CLIENT > SERVER: Sending Header: 0x".strtoupper($this->ascii2hex($header)), 'magenta');
$this->verblog("CLIENT > SERVER: Session: ".$this->ascii2hex($this->key), 'magenta');
$this->verblog("CLIENT > SERVER: Sending Packet: ".substr($packet, 20), 'magenta');
if($this->verbose > 1){
$this->verblog("CLIENT > SERVER: FULL Sending packet in HEX:\n".$this->ascii2hex($packet), 'magenta');
}
socket_write($this->conn_yahoo, $packet, strlen($packet));
return TRUE;
}
private function recvsock(){
if(!is_resource($this->conn_yahoo)){
$this->verblog("recvsock() Conn_yahoo isn't a resource. I can't work with it.", 'red');
return FALSE;
}
$return = '';
do{
if(!empty($return)){
$this->verblog("recvsock() Packet didn't finish in C080 Getting next.", 'blue');
}
if(($return .= socket_read($this->conn_yahoo, 1441)) === FALSE){
$this->verblog("recvsock() Error reading.", 'red');
return FALSE;
}
if(substr($return, 11, 1) == "\x4C" || empty($return)){ break; }
usleep(100);
}while(substr($return, -2) != "\xC0\x80" && substr($return, -3) != "\xC0\x80\x00");
if(empty($return)){ return FALSE; }
$packetcount = substr_count($return, "YMSG\x00\x10\x00");
if($packetcount > 1){
$this->verblog("SERVER > CLIENT: Received ".$packetcount." packets from server.", 'cyan');
$return = explode("YMSG\x00\x10\x00", $return);
for($i = 1; $i <= $packetcount; $i++){
$packet[$i] = "YMSG\x00".$this->version."\x00".$return[$i];
$header = substr($packet[$i], 11, 1);
$this->key = substr($packet[$i], 16, 4);
$this->verblog("SERVER > CLIENT: [".$i."] Receiving Packet Size: ".strlen($packet[$i])." Bytes", 'cyan');
$this->verblog("SERVER > CLIENT: [".$i."] Receiving Header: 0x".strtoupper($this->ascii2hex($header)), 'cyan');
$this->verblog("SERVER > CLIENT: [".$i."] Session: ".$this->ascii2hex($this->key), 'cyan');
$this->verblog("SERVER > CLIENT: [".$i."] Receiving Packet: ".substr($packet[$i], 20), 'cyan');
if($this->verbose > 1){
$this->verblog("SERVER > CLIENT: [".$i."] FULL Receiving packet in HEX:\n".$this->ascii2hex($packet[$i]), 'cyan');
}
$packet[$i] = array($header, $packet[$i]);
}
return $packet;
}else if(strlen($return) > 0){
$header = substr($return, 11, 1);
$this->key = substr($return, 16, 4);
$this->verblog("SERVER > CLIENT: Receiving Packet Size: ".strlen($return)." Bytes", 'cyan');
$this->verblog("SERVER > CLIENT: Receiving Header: 0x".strtoupper($this->ascii2hex($header)), 'cyan');
$this->verblog("SERVER > CLIENT: Session: ".$this->ascii2hex($this->key), 'cyan');
$this->verblog("SERVER > CLIENT: Receiving Packet: ".substr($return, 20), 'cyan');
if($this->verbose > 1){
$this->verblog("SERVER > CLIENT: FULL Receiving packet in HEX:\n".$this->ascii2hex($return), 'cyan');
}
return(array($header, $return));
}else{
$this->verblog("SERVER->CLIENT: There was a problem here.", 'red');
return FALSE;
}
}
private function SockClose(){
if(!is_resource($this->conn_yahoo)){
$this->verblog("Can't close conn_yahoo if it's not a socket resource.", 'red');
return FALSE;
}
$this->verblog("Closing conn_yahoo socket.", 'green');
socket_close($this->conn_yahoo);
$this->connected = 0;
return TRUE;
}
private function Y64E($source){
$yahoo64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
$inlen = 16;
$in = 0;
$dest = "";
for(; $inlen >=3; $inlen -=3){
$dest .= $yahoo64[ord($source[$in])>>2];
$dest .= $yahoo64[((ord($source[$in])<<4)&0x30) | (ord($source[$in+1])>>4)];
$dest .= $yahoo64[((ord($source[$in+1])<<2)&0x3C) | (ord($source[$in+2])>>6)];
$dest .= $yahoo64[ord($source[$in+2])&0x3F];
$in += 3;
}
if($inlen > 0){
$dest .= $yahoo64[ord($source[$in])>>2];
$fragment = ((ord($source[$in])<<4)&0x30);
if($inlen > 1){
$fragment |= (ord($source[$in+1])>>4);
}
$dest.=$yahoo64[$fragment];
if($inlen < 2){
$dest .= "-";
}else{
$dest .= $yahoo64[((ord($source[$in+1])<<2)&0x3c)];
}
$dest .= "-";
}
return($dest);
}
private function pachet($header, $packet = ''){
if(empty($header)){
$this->verblog("pachet() Empty header.", 'red');
return FALSE;
}
if(strlen($header) != 1){
$this->verblog("pachet() Wrong packet length.", 'red');
return FALSE;
}
$value = "YMSG"; // Start # 4 bytes
$value .= "\x00"; // NULL # 1 byte
$value .= $this->version; // Version # 1 byte
$value .= str_repeat("\x00", 2); // 2 NULLs # 2 bytes
$value .= chr(intval(strlen($packet)/256)).chr(fmod(strlen($packet),256)); // Size # 2 bytes
$value .= "\x00"; // NULL # 1 byte
$value .= $header; // Packet type # 1 byte
if($this->status == 0){
$value .= "\x00\x00\x00\x0C"; // 00 00 00 0C ( Invisible ) # 4 bytes
}else{
$value .= str_repeat("\x00", 4); // 4 NULLs ( Available ) # 4 bytes
}
$value .= $this->key; // The Session Key # 4 bytes
if(strlen($value) != 20){
$this->verblog("pachet() Malformed 20byte packet header: ".$this->ascii2hex($value)." ".strlen($value)." size.", 'red');
$this->verblog("pachet() Packet content: ".$this->ascii2hex($value.$packet), 'red');
return FALSE;
}
return $value.$packet;
}
protected function YahooVRFY(){
if($this->connected == 1){
$this->verblog("YahooVRFY() Don't do this if already connected.", 'red');
return FALSE;
}
if(!is_resource($this->conn_yahoo)){
$this->verblog("YahooVRFY() Can't verify if there is no connection.", 'red');
return FALSE;
}
$this->verblog("YahooConnect() Sending VRFY 0x4C packet.", 'green');
if(($this->sendsock($this->pachet("\x4C"))) === FALSE){
$this->verblog("YahooVRFY() Problem sending packet.", 'red');
return FALSE;
}
$return = $this->recvsock();
if($return[0] != "\x4C"){
$this->verblog("YahooVRFY() Expected 0x4C but got 0x".$this->ascii2hex($return[0]), 'red');
return FALSE;
}else{
return $return[1];
}
}
protected function processtokenerrors($error){
switch($error){
case 1212:
$this->verblog("processtokenerrors() Invalid ID or password. Please try again.", 'red');
return FALSE;
break;
case 1213:
$this->verblog("processtokenerrors() As a security precaution please enter your Yahoo! ID and password and type in the code you see in the picture below.", 'red');
return FALSE;
break;
case 1214:
$this->verblog("processtokenerrors() Invalid ID or password. Please try again and type the text you see in the picture below", 'red');
return FALSE;
break;
case 1235:
$this->verblog("processtokenerrors() This ID is not yet taken", 'red');
return FALSE;
break;
case 1236:
$this->verblog("processtokenerrors() Invalid ID or password. Please try again.", 'red');
return FALSE;
break;
case 100:
$this->verblog("processtokenerrors() Required field missing", 'red');
return FALSE;
break;
default:
$this->verblog("processtokenerrors() No errors, continue.", 'red');
return TRUE;
break;
}
return TRUE;
}
protected function getyt(){
if($this->connected == 1){
$this->verblog("getyt() Don't do this if already connected.", 'red');
return FALSE;
}
if(($this->MyCurlInit()) === FALSE){
$this->verblog("getyt()::MyCurlInit() Can't create resource.", 'red');
return FALSE;
}
$fh = @file_get_contents("yahoo_tokens");
$search = "|".$this->user.":([^\n]*)|is";
if($this->newtoken == 1){
$this->verblog("getyt() Always getting new token.", 'blue');
$ourtoken = $this->MyCurlGet('https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login='.$this->user.'&passwd='.urlencode($this->pass));
$this->processtokenerrors($ourtoken);
preg_match_all("|ymsgr=([^\n]*)\n|", $ourtoken, $token);
if(empty($token[1][0])){
$this->verblog("getyt() Got empty token: ".trim($ourtoken), 'red');
return FALSE;
}else{
$this->token = trim($token[1][0]);
$this->verblog("getyt() Token set to ".$this->token, 'green');
$fh = fopen("yahoo_tokens", "a+");
fwrite($fh, $this->user.":".$this->token."\n");
fclose($fh);
$this->verblog("getyt() Token saved to file.", 'green');
}
}else if(!preg_match($search, $fh, $out)){
$this->verblog("getyt() No saved token, getting one.", 'blue');
$ourtoken = $this->MyCurlGet('https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login='.$this->user.'&passwd='.urlencode($this->pass));
$this->processtokenerrors($ourtoken);
preg_match_all("|ymsgr=([^\n]*)\n|", $ourtoken, $token);
if(empty($token[1][0])){
$this->verblog("getyt() Got empty token: ".trim($ourtoken), 'red');
return FALSE;
}else{
$this->token = trim($token[1][0]);
$this->verblog("getyt() Token set to ".$this->token, 'green');
$fh = fopen("yahoo_tokens", "a+");
fwrite($fh, $this->user.":".$this->token."\n");
fclose($fh);
$this->verblog("getyt() Token saved to file.", 'green');
}
}else{
$this->token = trim($out[1]);
$this->verblog("getyt() Using already saved token: ".$out[0], 'green');
$this->verblog("getyt() Token set to: ".$this->token, 'green');
}
$yt307 = $this->MyCurlGet('https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token='.$this->token);
if($this->verbose > 1){
$this->verblog($yt307);
}
if(!preg_match_all("|crumb=([^\n]*)\nY=([^;]*);([^\n]*)\nT=([^;]*);|s", $yt307, $yt)){
$this->verblog("getyt() Existing token is invalid, getting new one.", 'blue');
$ourtoken = $this->MyCurlGet('https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login='.$this->user.'&passwd='.urlencode($this->pass));
preg_match_all("|ymsgr=([^\n]*)\n|", $ourtoken, $token);
if(empty($token[1][0])){
$this->verblog("getyt() Got empty token: ".trim($ourtoken), 'red');
return FALSE;
}else{
$this->token = trim($token[1][0]);
$this->verblog("getyt() Token set to ".$this->token, 'green');
$yahoo_tokens = file_get_contents("yahoo_tokens");
$yahoo_tokens = preg_replace('|'.$this->user.':(.*)\n|', $this->user.":".$this->token."\n", $yahoo_tokens);
$fh = fopen("yahoo_tokens", "w+");
fwrite($fh, $yahoo_tokens);
fclose($fh);
$this->verblog("getyt() Old saved token replaced.", 'green');
}
$yt307 = $this->MyCurlGet('https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=&token='.$this->token);
preg_match_all("|crumb=([^\n]*)\nY=([^;]*);([^\n]*)\nT=([^;]*);|s", $yt307, $yt);
}
$this->MyCurlClose();
if(empty($yt[1][0])){
$this->verblog("getyt() Empty crumb.");
return FALSE;
}else{
$this->crumb = trim($yt[1][0]);
$this->verblog("getyt() Crumb set to ".$this->crumb);
}
if(empty($yt[2][0])){
$this->verblog("getyt() Empty Y cookie.");
return FALSE;
}else{
$this->y = $yt[2][0];
$this->verblog("getyt() Y set to ".$this->y);
}
if(empty($yt[4][0])){
$this->verblog("getyt() Empty T cookie.");
return FALSE;
}else{
$this->t = $yt[4][0];
$this->verblog("getyt() T set to ".$this->t);
}
return TRUE;
}
protected function gen307(){
if(!$this->getyt()){
$this->verblog("gen307()::getyt Getting CRUMB, Y and T");
return FALSE;
}
$this->verblog("YahooConnect() Getting Y, T, Crumb and generating String307.");
if($this->verbose > 1){
$this->verblog("gen307() Step1: crumb.challenge : ".$this->crumb.$this->challenge);
$this->verblog("gen307() Step2: md5(crumb.challenge): ".md5($this->crumb.$this->challenge));
$this->verblog("gen307() Step3: hex2ascii(md5(crumb.challenge)): ".$this->hex2ascii(md5($this->crumb.$this->challenge)));
$this->verblog("gen307() Step4: y64e(hex2ascii(md5(crumb.challenge))): ".$this->Y64E($this->hex2ascii(md5($this->crumb.$this->challenge))));
}
$this->string307 = $this->Y64E($this->hex2ascii(md5($this->crumb.$this->challenge)));
$this->verblog("genyt307() String307 set to ".$this->string307." .");
return TRUE;
}
private function YahooAUTH(){
if($this->connected == 1){
$this->verblog("YahooAUTH() Can't be used if already connected.", 'red');
return FALSE;
}
if(!is_resource($this->conn_yahoo)){
$this->verblog("YahooAUTH()::SockINIT() Can't auth without an open socket.", 'red');
return FALSE;
}
$this->verblog("YahooConnect() Sending AUTH 0x57 packet.", 'green');
$packet = "1".$this->delim.$this->user.$this->delim;
$this->sendsock($this->pachet("\x57", $packet));
$return = $this->recvsock();
if($return[0] != "\x57"){
$this->verblog("YahooAUTH() Expected 0x57 on return but got 0x".$this->ascii2hex($return[0]), 'red');
return FALSE;
}
if($this->hex2ascii($this->key) == "00000000"){
$this->verblog("YahooAUTH() Empty KEY.", 'red');
return FALSE;
}
if(!stristr($return[1], "\xC0\x8094\xC0\x80")){
$this->verblog("YahooAUTH() Expected to have 0xC0 0x80 94 0xC0 0x80.", 'red');
return FALSE;
}
preg_match_all("|\xC0\x8094\xC0\x80(.*)\xC0\x80|", $return[1], $challenge);
if(empty($challenge[1][0])){
$this->verblog("YahooAUTH() Challenge is empty.", 'red');
return FALSE;
}else{
$this->challenge = trim($challenge[1][0]);
$this->verblog("YahooAUTH() Challenge set to ".$this->challenge, 'green');
}
return TRUE;
}
private function YahooLogin(){
if($this->connected == 1){
$this->verblog("YahooLogin() don't do this if you're already connected.", 'red');
return FALSE;
}
if(!is_resource($this->conn_yahoo)){
$this->verblog("YahooLogin() Can't work if conn_yahoo isn't a resource.", 'red');
return FALSE;
}
$this->verblog("YahooConnect() Sending Login 0x54 packet.");
$packet = "1".$this->delim.$this->user.$this->delim;
$packet .= "0".$this->delim.$this->user.$this->delim;
$packet .= "277".$this->delim.$this->y."; path=/; domain=.yahoo.com".$this->delim;
$packet .= "278".$this->delim.$this->t."; path=/; domain=.yahoo.com".$this->delim;
$packet .= "307".$this->delim.$this->string307.$this->delim;
$packet .= "244".$this->delim."4194239".$this->delim;
$packet .= "2".$this->delim.$this->user.$this->delim;
$packet .= "2".$this->delim."1".$this->delim;
$packet .= "98".$this->delim."us".$this->delim;
$packet .= "135".$this->delim."9.0.0.2162".$this->delim;
$this->sendsock($this->pachet("\x54", $packet));
$return = $this->recvsock();
if($return[0] != "\x55"){
$this->verblog("YahooLogin() expects a 0x55 but got 0x".$this->ascii2hex($return[0])." .");
return FALSE;
}else{
preg_match_all('|c080323136c080(.*)c080323534c080(.*)c080323735c080|is', $this->ascii2hex($return[1]), $out);
$this->fname = $this->hex2ascii($out[1][0]);
$this->verblog("YahooLogin() First name set to: ".$this->fname);
$this->lname = $this->hex2ascii($out[2][0]);
$this->verblog("YahooLogin() Last name set to: ".$this->lname);
return TRUE;
}
}
private function getusergroup($user){
foreach($this->buddylist as $buddylist => $name){
foreach($name as $userul => $info){
if($userul == $user){
return $buddylist;
}
}
}
return FALSE;
}
public function YahooBuddyList($packet = NULL){
// We may receive multiple packets here.
if(!is_resource($this->conn_yahoo)){
$this->verblog("YahooBuddyList() Can't work if conn_yahoo isn't a resource.");
return FALSE;
}
if($this->connected == 1){
return($this->buddylist);
}
switch($packet[0]){
case "\xF1":
$this->verblog("YahooBuddyList() (0xF1) Parsing Buddy list.");
$packet = $this->ascii2hex($packet[1]);
preg_match_all('|c080333030c080333138c080([a-z0-9]+?)c080333033c080333139c080|', $packet, $outf1);
if(is_array($outf1[1]) && !empty($outf1[1][0])){
foreach($outf1[1] as $list){
preg_match('|3635c080(.*)c080333032c080333139c080|', $list, $name);
$name = $this->hex2ascii($name[1]);
preg_match_all('|c080333030c080333139c08037c080([a-z0-9]+?)c080333031c080333139|', $list, $buddys);
foreach($buddys[1] as $buddy){
$buddy = $this->hex2ascii($buddy);
$status = "offline";
if(stristr($buddy, "\xC0\x80223\xC0\x801")){
$buddy = str_replace("\xC0\x80223\xC0\x801", '', $buddy);
$status = "Didn't accept your add request yet.";
}
$buddylist[$name][$buddy] = NULL;
$this->buddylist[$name][$buddy]['status'] = $status;
$status = NULL;
}
$name = NULL;
}
}
preg_match_all('|c080333030c080333230c08037c080([a-z0-9]+?)c080333031c080333230|', $packet, $ignored);
if(is_array($ignored[1]) && !empty($ignored[1][0])){
foreach($ignored[1] as $person){
$this->ignorelist[] = $this->hex2ascii($person);
}
}
$outf1 = NULL;
break;
case "\xF0":
$this->verblog("YahooBuddyList() (0xF0) Parsing Buddy statuses.");
$packet = $this->ascii2hex($packet[1]);
preg_match_all('|c080333030c080333135c08037c080([a-z0-9]+?)c0803130c080([a-f0-9]+?)c080([a-f0-9]+)c080333031c080333135|', $packet, $outf0);
if(is_array($outf0[1]) && !empty($outf0[1][0])){
foreach($outf0[1] as $key => $user){
$user = $this->hex2ascii($user);
$buddylist = $this->getusergroup($user);
$this->buddylist[$buddylist][$user]['status'] = "online";
switch($outf0[2][$key]){
case "31":
$msg = "(AWAY) Be Right Back";
break;
case "32":
$msg = "(AWAY) Busy";
break;
case "33":
$msg = "(AWAY) Not At Home";
break;
case "34":
$msg = "(AWAY) Not At Desk";
break;
case "35":
$msg = "(AWAY) Not in Office";
break;
case "36":
$msg = "(AWAY) On the Phone";
break;
case "37":
$msg = "(AWAY) On Vacation";
break;
case "38":
$msg = "(AWAY) Out to lunch";
break;
case "39":
$msg = "(AWAY) Stepped out";
break;
case "3939":
preg_match('|c0803139c080([a-f0-9]+?)c0803937c08031c080|is', $outf0[3][0], $sts);
$msg = "(AVAILABLE) ".$this->hex2ascii($sts[1]);
break;
default:
$msg = "";
break;
}
$this->buddylist[$buddylist][$user]['message'] = $msg;
}
}
break;
default:
return FALSE;
break;
}
return TRUE;
}
public function YahooKeepAlive(){
// This has to be send every 60 seconds.
if($this->connected == 0){
$this->verblog("YahooKeepAlive() Can't do this if not connected.");
return FALSE;
}
$this->verblog("YahooKeepAlive() Sending Keep Alive packet (0x8A) .");
$packet = "0".$this->delim.$this->user.$this->delim;
return $this->sendsock($this->pachet("\x8A", $packet));
}
public function YahooAddAbuddy($dest, $msg = '', $list = 'Buddies'){
if($this->connected == 0){
$this->verblog("YahooAddABuddy() Can't do this if not connected.", 'red');
return FALSE;
}
if(empty($dest)){
$this->verblog("YahooAddABuddy() No user given.", 'red');
return FALSE;
}
$slist = $this->getusergroup($dest);
if(!empty($slist)){
$this->verblog("YahooAddABuddy() Can't add ".$dest." since he/she already is in '".$slist."'", 'red');
return FALSE;
}
if(!empty($msg)){
$this->verblog("YahooAddABuddy() Adding ".$dest." in ".$list." with message: ".$msg, 'green');
}else{
$this->verblog("YahooAddABuddy() Adding ".$dest." in ".$list, 'green');
}
$packet = "1".$this->delim.$this->user.$this->delim;
$packet .= "14".$this->delim.$msg.$this->delim;
$packet .= "65".$this->delim.$list.$this->delim;
$packet .= "97".$this->delim."1".$this->delim;
if(!empty($this->fname)){
$packet .= "216".$this->delim.$this->fname.$this->delim;
}
if(!empty($this->lname)){
$packet .= "254".$this->delim.$this->lname.$this->delim;
}
$packet .= "302".$this->delim."319".$this->delim;
$packet .= "300".$this->delim."319".$this->delim;
$packet .= "7".$this->delim.$dest.$this->delim;
$packet .= "301".$this->delim."319".$this->delim;
$packet .= "303".$this->delim."319".$this->delim;
return $this->sendsock($this->pachet("\x83", $packet));
}
public function YahooDenyABuddy($dest, $msg = ''){
// The great fun here is that even after you have accepted the add request, you can still deny it and remove yourself from his list.
if($this->connected == 0){
$this->verblog("YahooDenyABuddy() Can't do this if not connected.");
return FALSE;
}
if(empty($dest)){
$this->verblog("YahooDenyABuddy() No user given.");
return FALSE;
}
if(empty($msg)){
$this->verblog("YahooDenyABuddy() Denying buddy ".$dest);
}else{
$this->verblog("YahooDenyABuddy() Denying buddy ".$dest." with message: ".$msg);
}
$packet = "1".$this->delim.$this->user.$this->delim;
$packet .= "5".$this->delim.$dest.$this->delim;
$packet .= "13".$this->delim."2".$this->delim; // Reject Authorization
if(!empty($msg)){
$packet .= "14".$this->delim.$msg.$this->delim;
}
return $this->sendsock($this->pachet("\xD6", $packet));
}
private function YahooAddABuddyRequest($packet){
if(empty($packet[1])){
$this->verblog("YahooAddABuddyRequest() No packet given.");
return FALSE;
}
if($packet[0] != "\xD6"){
$this->verblog("YahooAddABuddyRequest() You've given me a wrong packet type to parse, I need 0xD6, got 0x".$this->ascii2hex($packet[0]));
return FALSE;
}
$packet = $this->ascii2hex($packet[1]);
preg_match('|34c080([a-f0-9]+?)c08035c080([a-f0-9]+?)c080|is', $packet, $request);
if(stristr($packet, 'c0803133c08032c080')){
if(preg_match('|c0803134c080([a-f0-9]+?)c080|is', $packet, $message)){
$this->verblog("YahooAddABuddyRequest() ".$this->hex2ascii($request[1])." denied our add buddy request: ".$this->hex2ascii($message[1]), 'red');
}else{
$this->verblog("YahooAddABuddyRequest() ".$this->hex2ascii($request[1])." denied our request.", 'red');
}
unset($this->buddylist[$this->getusergroup($this->hex2ascii($request[1]))][$this->hex2ascii($request[1])]);
return TRUE;
}
if(isset($request[1]) && isset($request[2]) && $this->hex2ascii($request[2]) == $this->user){
if(stristr($packet, 'c080323136c080')){
preg_match('|c080323136c080([a-f0-9]+?)c080|is', $packet, $fname);
$fname = $this->hex2ascii($fname[1]);
}
if(stristr($packet, 'c080323534c080')){
preg_match('|c080323534c080([a-f0-9]+?)c080|is', $packet, $lname);
$lname = $this->hex2ascii($lname[1]);
}
if(stristr($packet, 'c0803134c080')){
preg_match('|c0803134c080([a-f0-9]+?)c080|is', $packet, $message);
$message = $this->hex2ascii($message[1]);
}
$reply = "YahooAddABuddyRequest() ".$this->hex2ascii($request[1]);
if((isset($fname) && !empty($fname)) || (isset($lname) && !empty($lname))){
$reply .= " (";
if((isset($fname) && !empty($fname)) && (isset($lname) && !empty($lname))){
$reply .= $fname." ".$lname;
}else{
if(isset($fname) && !empty($fname)){
$reply .= $fname;
}else{
$reply .= $lname;
}
}
$reply .= ")";
}
if(isset($message[1]) && !empty($message[1])){
$reply .= " wants to add you to his/hers buddy list: ".$message;
}else{
$reply .= " wants to add you to his/hers buddy list.";
}
$this->verblog($reply);
}else{
$this->verblog("YahooAddABuddyRequest() Error getting buddy request.");
return FALSE;
}
return TRUE;
}
public function YahooRemoveABuddy($user){
if($this->connected == 0){
$this->verblog("YahooRemoveABuddy() Unable to process this if not connected.");
return FALSE;
}
if(!$this->getusergroup($user)){
$this->verblog("YahooRemoveABuddy() ".$user." isn't in your buddy list.");
return FALSE;
}
$group = $this->getusergroup($user);
$packet = "1".$this->delim.$this->user.$this->delim;
$packet .= "7".$this->delim.$user.$this->delim;
$packet .= "65".$this->delim.$group.$this->delim;
$packet .= "66".$this->delim."0".$this->delim;
$this->verblog("YahooRemoveABuddy() Removing ".$user." from your '".$group."' list.");
unset($this->buddylist[$group][$user]);
return $this->sendsock($this->pachet("\x84", $packet));
}
public function YahooAvatarLink($dest){
if($this->connected == 0){
$this->verblog("YahooAvatarLink() Can't do this if not connected.");
return FALSE;
}
if(empty($dest)){
$this->verblog("YahooAvatarLink() No user given.");
return FALSE;
}
$this->verblog("YahooAvatarLink() Getting ".$dest."'s avatar link.");
// C0 $packeti="1".$delim.$user.$delim."5".$delim.$vict.$delim."198".$delim."0".$delim.'197'.$delim.$delim; // not working anymore
// C1 $packeti="1".$delim.$user.$delim."5".$delim.$vict.$delim."206".$delim."2".$delim; // not working anymore
// BE $packeti="1".$delim.$user.$delim."5".$delim.$vict.$delim."13".$delim."1".$delim; // Only works for people using yahoo messenger
$packet = "1".$this->delim.$this->user.$this->delim;
$packet .= "5".$this->delim.$dest.$this->delim;
$packet .= "13".$this->delim."1".$this->delim;
return $this->sendsock($this->pachet("\xBE", $packet));
}
public function YahooPrivateMessage($dest, $msg){
/*
63 - imvironment string;11
64 - imvironment enabled/allowed
0 - enabled imwironment ;0 - no imvironment
2 - disabled '' - empty cause we don;t do these
*/
if($this->connected == 0){
$this->verblog("YahooPrivateMessage() Can't do this if not connected.");
return FALSE;
}
if(empty($dest)){
$this->verblog("YahooPrivateMessage() No user given.");
return FALSE;
}
if(empty($msg)){
$this->verblog("YahooPrivateMessage() No message given.");
return FALSE;
}
$this->verblog("YahooPrivateMessage() Sending to ".$dest." message: ".$msg);
$packet = "1".$this->delim.$this->user.$this->delim;
$packet .= "5".$this->delim.$dest.$this->delim;
$packet .= '97'.$this->delim.'1'.$this->delim;
$packet .= "14".$this->delim.$msg.$this->delim;
$packet .= '63'.$this->delim.';0'.$this->delim;
$packet .= '64'.$this->delim.'0'.$this->delim;
$packet .= '1002'.$this->delim.'1'.$this->delim;
$packet .= '206'.$this->delim.'0'.$this->delim;
return $this->sendsock($this->pachet("\x06", $packet));
}
private function YahooIncommingMessage($packet){
if(empty($packet[1])){
$this->verblog("YahooIncommingMessage() No packet given.");
return FALSE;
}
if($packet[0] != "\x06"){
$this->verblog("YahooIncommingMessage() You've given me a wrong packet type to parse, I need 0x06, got 0x".$this->ascii2hex($packet[0]));
return FALSE;
}
$this->verblog("YahooIncommingMessage() (0x06) parsing incomming message.");
$packet = $this->ascii2hex($packet[1]);
if(stristr($packet, "c0803633c080") && stristr($packet, "c0803634c080")){
preg_match_all('|34c080([a-f0-9]+?)c08035c080'.$this->ascii2hex($this->user).'c0803134c080([a-f0-9]+?)c080|is', $packet, $messages);
foreach($messages[1] as $key => $source){
$this->verblog("YahooIncommingMessage() Message at ".date("Y-m-d H:i:s")." from ".$this->hex2ascii($source)." : ".$this->hex2ascii($messages[2][$key]));
}
}else{
preg_match_all('|3331c08036c0803332c08036c08034c080([a-f0-9]+?)c08035c080'.$this->ascii2hex($this->user).'c0803134c080([a-f0-9]+?)c0803135c080([a-f0-9]+?)c0803937c08031c080|is', $packet, $messages);
foreach($messages[1] as $key => $source){
$this->verblog("YahooIncommingMessage() Offline message at ".date("Y-m-d H:i:s", $this->hex2ascii($messages[3][$key]))." from ".$this->hex2ascii($source)." : ".$this->hex2ascii($messages[2][$key]));
}
}
return TRUE;
}
private function YahooNewMail($packet){
if(empty($packet[1])){
$this->verblog("YahooNewMail() No packet given.");
return FALSE;
}
if($packet[0] != "\x0B"){
$this->verblog("YahooNewMail() You've given me a wrong packet type to parse, I need 0x0B, got 0x".$this->ascii2hex($packet[0]));
return FALSE;
}
$this->verblog("YahooNewMail() (0x0B) parsing mail number.");
$packet = $this->ascii2hex($packet[1]);
if(stristr($packet, "c0803138c080")){
preg_match('|39c08031c0803138c080([a-f0-9]+?)c0803432c080([a-f0-9]+?)c0803433c080([a-f0-9]+?)c080|is', $packet, $mails);
$this->verblog("YahooNewMail() You've got a mail from ".$this->hex2ascii($mails[3])." (".$this->hex2ascii($mails[2]).") : ".$this->hex2ascii($mails[1]));
}else{
preg_match('|39c080([a-f0-9]+?)c080|is', $packet, $mails);
if(isset($mails[1]) && is_numeric($mails[1])){
if($mails[1] != '30'){
if($mails[1] > 31){
$this->verblog("YahooNewMail() You've got ".$this->hex2ascii($mails[1])." new mails.");
}else{
$this->verblog("YahooNewMail() You've got ".$this->hex2ascii($mails[1])." new mail.");
}
}else{
$this->verblog("YahooNewMail() You've got no new mail.");
}
}else{
$this->verblog("YahooNewMail() Error getting mail number.");
return FALSE;
}
}
return TRUE;
}
private function YahooSkinname(){
$packet = "211".$this->delim."Skin:Name=Purple\nSettings:StartLaunchcast=0\tShowInsider=0\tAutologin=1\tArchiving=2\tHiddenLogin=1\nEnv:OS=7\nPlugins:ContainerWindowState=0\n";
return $this->sendsock($this->pachet("\x15", $packet));
}
private function YahooPing($packet){
if(empty($packet[1])){
$this->verblog("YahooPing() No packet given.", 'red');
return FALSE;
}
if($packet[0] != "\x12"){
$this->verblog("YahooPing() You've given me a wrong packet type to parse, I need 0x12, got 0x".$this->ascii2hex($packet[0]), 'red');
return FALSE;
}
$this->verblog("YahooPing() (0x12) parsing incomming ping request.", 'green');
$packet = $this->ascii2hex($packet[1]);
preg_match('|313433c080([a-f0-9]+?)c080313434c080([a-f0-9]+?)c080|is', $packet, $pings);
if(isset($pings[1]) && isset($pings[2]) && is_numeric($this->hex2ascii($pings[1])) && is_numeric($this->hex2ascii($pings[2]))){
$this->pinginterval = $this->hex2ascii($pings[1]);
$this->pingnr = $this->hex2ascii($pings[2]);
$this->verblog("YahooPing() Yahoo wants ".$this->pingnr." ping every ".$this->pinginterval." seconds.");
}else{
$this->verblog("YahooPing() Error getting ping numbers.");
return FALSE;
}
return TRUE;
}
private function YahooPersonLogOff($packet){
if(empty($packet[1])){
$this->verblog("YahooPersonLogOff() No packet given.", 'red');
return FALSE;
}
if($packet[0] != "\x02"){
$this->verblog("YahooPersonLogOff() You've given me a wrong packet type to parse, I need 0x02, got 0x".$this->ascii2hex($packet[0]), 'red');
return FALSE;
}
$this->verblog("YahooPersonLogOff() (0x02) parsing incomming log off persson.", 'green');
$packet = $this->ascii2hex($packet[1]);
if(preg_match('|37c080([a-f0-9]+?)c080|', $packet, $out)){
$this->verblog('YahooPersonLogOff() '.$this->hex2ascii($out[1]).' logged off.', 'green');
}else{
$this->verblog("YahooPersonLogOff() Regex failed, here's the packet: ".$packet, 'red');
return FALSE;
}
return TRUE;
}
public function YahooAddABuddyProcess($packet){
if($this->connected == 0){
$this->verblog("YahooAddABuddyProcess() Can't do this if not connected.", 'red');
return FALSE;
}
if($packet[0] != "\x83"){
$this->verblog("YahooAddABuddyProcess() You've given me a wrong packet type to parse, I need 0x83, got 0x".$this->ascii2hex($packet[0]), 'red');
return FALSE;
}
$packet = $this->ascii2hex($packet[1]);
if(preg_match('|31c080'.$this->ascii2hex($this->user).'c08037c080([a-f0-9]+?)c0803635c080([a-f0-9]+?)c0803636c080([a-f0-9]+?)c080|', $packet, $out)){
switch($out[3]){
case "30":
$this->buddylist[$this->hex2ascii($out[2])][$this->hex2ascii($out[1])]['status'] = "Didn't accept your add request yet.";
$this->verblog("YahooAddABuddyProcess() Everything's ok, awayting buddy deny/accept.", 'green');
return TRUE;
break;
case "32":
$this->verblog("YahooAddABuddyProcess() ".$this->hex2ascii($out[1])." is already in the buddy list.", 'red');
return FALSE;
break;
case "33":
$this->verblog("YahooAddABuddyProcess() ".$this->hex2ascii($out[1])." doesn't exist.", 'red');
return FALSE;
break;
}
}else{
$this->verblog("YahooAddABuddyProcess() Regex failed, here's the packet: ".$packet, 'red');
return FALSE;
}
return TRUE;
}
private function YahooDisconnect(){
$this->verblog("YahooDisconnect() Got disconnection packet.");
$this->SockClose();
$this->connected = 0;
}
private function ParseAvatarLink($packet){
if($packet[0] != "\xBE"){
$this->verblog("ParseAvatarLink() You've given me the wrong packet type to parse, I need 0xBE, got 0x".$this->ascii2hex($packet[0]), 'red');
return FALSE;
}
$avatarlink = explode("c080", $this->ascii2hex($packet[1]));
$yahooid = $this->hex2ascii($avatarlink[1]);
$avatarlink = $this->hex2ascii($avatarlink[7]);
$this->verblog("ParseAvatarLink() ".$yahooid."'s avatar link is: ".$avatarlink, 'green');
return($avatarlink);
}
private function parseaccess($packet){
switch($packet[0]){
case "\x02":
// Person logs off (or goes invisible)
$this->YahooPersonLogOff($packet);
break;
case "\x06":
// Incomming messages
$this->YahooIncommingMessage($packet);
break;
case "\x0B":
// Mail notifications
$this->YahooNewMail($packet);
break;
case "\x12":
// Ping
$this->YahooPing($packet);
break;
case "\x4B":
// Typing notification ( or other kind )
break;
case "\x83":
// Buddy request response
$this->YahooAddABuddyProcess($packet);
break;
case "\xBD":
// Avatar checksum
break;
case "\xC6":
// Person status update
break;
case "\xD1":
// Disconnect
$this->YahooDisconnect($packet);
break;
case "\xD6":
// Add request
$this->YahooAddABuddyRequest($packet);
break;
case "\xF0":
// Status updates
case "\xF1":
// Buddy List
$this->YahooBuddyList($packet);
break;
case "\xBE":
// Avatar link
$this->ParseAvatarLink($packet);
break;
default:
// Discard the rest
$this->verblog("parseaccess() Don't know what to do with 0x".$this->ascii2hex($packet[0])." packet.");
break;
}
}
public function parsepackets(){
$return = $this->recvsock();
if(is_array($return[1])){
foreach($return as $packet){
$this->parseaccess($packet);
}
}else if(!empty($return)){
$this->parseaccess($return);
}
return TRUE;
}
public function YahooConnect(){
if($this->connected == 1){
$this->verblog("YahooConnect() Can't reconned since I'm already connected.");
return FALSE;
}
if(!$this->SockINIT()){
$this->verblog("YahooConnect()::SockINIT() Can't create socket.");
return FALSE;
}
usleep(100);
if(!$this->YahooVRFY()){
$this->verblog("YahooConnect()::YahooVRFY() failed.");
$this->YahooDisconnect();
return FALSE;
}
usleep(100);
if(!$this->YahooAUTH()){
$this->verblog("YahooConnect()::YahooAUTH() failed.");
$this->YahooDisconnect();
return FALSE;
}
usleep(100);
if(!$this->gen307()){
$this->verblog("YahooConnect()::genyt307() failed.");
$this->YahooDisconnect();
return FALSE;
}
usleep(100);
if(!$this->YahooLogin()){
$this->verblog("YahooConnect()::YahooLogin() failed.");
$this->YahooDisconnect();
return FALSE;
}
usleep(100);
if(!$this->YahooSkinname()){
$this->verblog("YahooConnect()::YahooSkinname() failed.");
$this->YahooDisconnect();
return FALSE;
}
$this->parsepackets();
$this->verblog("YahooConnect() Finished login procedure, feel free to do your thing now.");
$this->connected = 1;
$this->YahooKeepAlive();
}
}
$ymsg = new YMSG();
$ymsg->Verbosity(2);
$ymsg->YahooUser("your username here");
$ymsg->YahooPass("the password goes here");
$ymsg->YahooConnect();
if($ymsg->ConnectedStatus() == 0){
exit;
}
while(TRUE){
// $ymsg->YahooDenyABuddy($argv[1]);
// $ymsg->YahooAvatarLink($argv[1]);
$ymsg->parsepackets();
sleep(2);
}
?>