5 Luke Fitzgerald <lw.fitzgerald@googlemail.com>
\r
7 Based on MSN class ver 2.0 by Tommy Wu, Ricky Su
\r
10 Documentation on the MSN protocol can be found at: http://msnpiki.msnfanatic.com/index.php/Main_Page
\r
12 This class uses MSNP15.
\r
14 In addition to PHP5, the additional php modules required are:
\r
15 curl pcre mcrypt bcmath
\r
20 const PROTOCOL = 'MSNP15';
\r
21 const PASSPORT_URL = 'https://login.live.com/RST.srf';
\r
22 const BUILDVER = '8.1.0178';
\r
23 const PROD_KEY = 'PK}_A_0N_K%O?A9S';
\r
24 const PROD_ID = 'PROD0114ES4Z%Q5W';
\r
25 const LOGIN_METHOD = 'SSO';
\r
27 const OIM_SEND_URL = 'https://ows.messenger.msn.com/OimWS/oim.asmx';
\r
28 const OIM_SEND_SOAP = 'http://messenger.live.com/ws/2006/09/oim/Store2';
\r
30 const OIM_MAILDATA_URL = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
31 const OIM_MAILDATA_SOAP = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
\r
32 const OIM_READ_URL = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
33 const OIM_READ_SOAP = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
\r
34 const OIM_DEL_URL = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
35 const OIM_DEL_SOAP = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
\r
37 const MEMBERSHIP_URL = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
38 const MEMBERSHIP_SOAP = 'http://www.msn.com/webservices/AddressBook/FindMembership';
\r
40 const ADDMEMBER_URL = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
41 const ADDMEMBER_SOAP = 'http://www.msn.com/webservices/AddressBook/AddMember';
\r
43 const DELMEMBER_URL = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
44 const DELMEMBER_SOAP = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
\r
46 // the message length (include header) is limited (maybe since WLM 8.5 released)
\r
47 // for WLM: 1664 bytes
\r
48 // for YIM: 518 bytes
\r
49 const MAX_MSN_MESSAGE_LEN = 1664;
\r
50 const MAX_YAHOO_MESSAGE_LEN = 518;
\r
58 private $password = '';
\r
59 private $NSfp = false;
\r
60 private $passport_policy = '';
\r
63 private $retry_wait;
\r
64 private $update_pending;
\r
65 private $PhotoStickerFile = false;
\r
66 private $Emotions = false;
\r
67 private $XFRReqTimeout = 60;
\r
68 private $SBStreamTimeout = 2;
\r
69 private $MsnObjArray = array();
\r
70 private $MsnObjMap = array();
\r
71 private $ABAuthHeader;
\r
75 private $server = 'messenger.hotmail.com';
\r
76 private $port = 1863;
\r
78 private $clientid = '';
\r
80 private $error = '';
\r
82 private $authed = false;
\r
84 private $oim_try = 3;
\r
86 private $font_fn = 'Arial';
\r
87 private $font_co = '333333';
\r
88 private $font_ef = '';
\r
90 // Begin added for StatusNet
\r
92 private $aContactList = array();
\r
93 private $aADL = array();
\r
96 * Holds session information indexed by screenname if
\r
97 * session has no socket or socket if socket present
\r
101 private $switchBoardSessions = array();
\r
104 * Holds sockets indexed by screenname
\r
108 private $switchBoardSessionLookup = array();
\r
111 * Holds references to sessions waiting for XFR
\r
115 private $waitingForXFR = array();
\r
118 * Event Handler Functions
\r
120 private $myEventHandlers = array();
\r
122 // End added for StatusNet
\r
125 * Constructor method
\r
127 * @param array $Configs Array of configuration options
\r
128 * 'user' - Username
\r
129 * 'password' - Password
\r
130 * 'alias' - Bot nickname
\r
131 * 'psm' - Bot personal status message
\r
132 * 'retry_wait' - Time to wait before trying to reconnect
\r
133 * 'update_pending' - Whether to update pending contacts
\r
134 * 'PhotoSticker' - Photo file to use (?)
\r
135 * 'debug' - Enable/Disable debugging mode
\r
136 * @param integer $timeout Connection timeout
\r
137 * @param integer $client_id Client id (hexadecimal)
\r
140 public function __construct ($Configs = array(), $timeout = 15, $client_id = 0x7000800C) {
\r
141 $this->user = $Configs['user'];
\r
142 $this->password = $Configs['password'];
\r
143 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
144 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
145 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
146 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
147 $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;
\r
149 if ($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false) {
\r
150 foreach($this->Emotions as $EmotionFilePath)
\r
151 $this->MsnObj($EmotionFilePath,$Type=2);
\r
153 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
154 $this->timeout = $timeout;
\r
157 if (!function_exists('curl_init')) throw new Exception("curl module not found!\n");
\r
158 if (!function_exists('preg_match')) throw new Exception("pcre module not found!\n");
\r
159 if (!function_exists('mcrypt_cbc')) throw new Exception("mcrypt module not found!\n");
\r
160 if (!function_exists('bcmod')) throw new Exception("bcmath module not found!\n");
\r
163 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
165 normal MSN 8.1 clientid is:
\r
166 01110110 01001100 11000000 00101100
\r
169 we just use following:
\r
170 * 0x04: Your client can send/receive Ink (GIF format)
\r
171 * 0x08: Your client can send/recieve Ink (ISF format)
\r
172 * 0x8000: This means you support Winks receiving (If not set the official Client will warn with 'contact has an older client and is not capable of receiving Winks')
\r
173 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
176 $this->clientid = $client_id;
\r
177 $this->ABService = new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl', array('trace' => 1));
\r
185 * Connect to the NS server
\r
187 * @param String $user Username
\r
188 * @param String $password Password
\r
189 * @param String $redirect_server Redirect server
\r
190 * @param Integer $redirect_port Redirect port
\r
191 * @return Boolean Returns true if successful
\r
193 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
195 if ($redirect_server === '') {
\r
196 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
\r
197 if (!$this->NSfp) {
\r
198 $this->error = "!!! Could not connect to $this->server:$this->port, error => $errno, $errstr";
\r
203 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, $this->timeout);
\r
204 if (!$this->NSfp) {
\r
205 $this->error = "!!! Could not connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
209 $this->authed = false;
\r
211 // NS: >> VER {id} MSNP9 CVR0
\r
213 // NS: >>> VER {id} MSNP15 CVR0
\r
214 $this->ns_writeln("VER $this->id ".self::PROTOCOL.' CVR0');
\r
216 $start_tm = time();
\r
217 while (!self::socketcheck($this->NSfp)) {
\r
218 $data = $this->ns_readln();
\r
220 if ($data === false) {
\r
223 $this->ns_writeln("OUT");
\r
224 @fclose($this->NSfp);
\r
225 $this->error = 'Timeout, maybe protocol changed!';
\r
229 $code = substr($data, 0, 3);
\r
230 $start_tm = time();
\r
235 // NS: <<< VER {id} MSNP9 CVR0
\r
236 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
238 // NS: <<< VER {id} MSNP15 CVR0
\r
239 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
240 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS ".self::BUILDVER." msmsgs $user");
\r
245 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
246 // NS: >>> USR {id} TWN I {user}
\r
248 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
249 // NS: >>> USR {id} SSO I {user}
\r
250 $this->ns_writeln("USR $this->id ".self::LOGIN_METHOD." I $user");
\r
254 // already login for passport site, finish the login process now.
\r
255 // NS: <<< USR {id} OK {user} {verify} 0
\r
256 if ($this->authed) return true;
\r
257 // max. 16 digits for password
\r
258 if (strlen($password) > 16)
\r
259 $password = substr($password, 0, 16);
\r
261 $this->user = $user;
\r
262 $this->password = $password;
\r
263 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
264 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce) = @explode(' ', $data);
\r
266 $this->passport_policy = $policy;
\r
267 $aTickets = $this->get_passport_ticket();
\r
268 if (!$aTickets || !is_array($aTickets)) {
\r
271 $this->ns_writeln("OUT");
\r
272 @fclose($this->NSfp);
\r
273 $this->error = 'Passport authentication failed!';
\r
277 $ticket = $aTickets['ticket'];
\r
278 $secret = $aTickets['secret'];
\r
279 $this->ticket = $aTickets;
\r
280 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
282 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
283 $this->ns_writeln("USR $this->id ".self::LOGIN_METHOD." S $ticket $login_code");
\r
284 $this->authed = true;
\r
288 // main login server will redirect to anther NS after USR command
\r
290 // NS: <<< XFR {id} NS {server} 0 {server}
\r
292 // NS: <<< XFR {id} NS {server} U D
\r
293 @list(/* XFR */, /* id */, $Type, $server) = @explode(' ', $data);
\r
294 if ($Type!='NS') break;
\r
295 @list($ip, $port) = @explode(':', $server);
\r
296 // this connection will close after XFR
\r
297 @fclose($this->NSfp);
\r
299 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, $this->timeout);
\r
300 if (!$this->NSfp) {
\r
301 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
306 // NS: >> VER {id} MSNP9 CVR0
\r
308 // NS: >>> VER {id} MSNP15 CVR0
\r
309 $this->ns_writeln("VER $this->id ".self::PROTOCOL.' CVR0');
\r
313 // return some policy data after 'USR {id} SSO I {user}' command
\r
314 // NS: <<< GCF 0 {size}
\r
315 @list(/* GCF */, /* 0 */, $size) = @explode(' ', $data);
\r
316 // we don't need the data, just read it and drop
\r
317 if (is_numeric($size) && $size > 0)
\r
318 $this->ns_readdata($size);
\r
322 // we'll quit if got any error
\r
323 if (is_numeric($code)) {
\r
326 $this->ns_writeln("OUT");
\r
327 @fclose($this->NSfp);
\r
328 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
331 // unknown response from server, just ignore it
\r
339 * Sign onto the NS server and retrieve the address book
\r
343 public function signon() {
\r
344 /* FIXME Don't implement the signon as a loop or we could hang
\r
345 * the queue handler! */
\r
346 $this->debug_message('*** Trying to connect to MSN network');
\r
348 // Remove any remaining switchboard sessions
\r
349 $this->switchBoardSessions = array();
\r
350 $this->switchBoardSessionLookup = array();
\r
354 if (!$this->connect($this->user, $this->password)) {
\r
355 $this->signonFailure("!!! Could not connect to server: $this->error");
\r
360 if ($this->UpdateContacts() === false) {
\r
361 $this->signonFailure('');
\r
365 // Get membership lists
\r
366 if (($this->aContactList = $this->getMembershipList()) === false) {
\r
367 $this->signonFailure('!!! Get membership list failed');
\r
371 if ($this->update_pending) {
\r
372 if (is_array($this->aContactList)) {
\r
373 $pending = 'Pending';
\r
374 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
375 foreach ($aUserList as $u_name => $aNetworks) {
\r
376 foreach ($aNetworks as $network => $aData) {
\r
377 if (isset($aData[$pending])) {
\r
380 foreach (array('Allow', 'Reverse') as $list) {
\r
381 if (isset($aData[$list]))
\r
384 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
385 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
391 $id = $aData[$pending];
\r
392 // we can delete it from pending now
\r
393 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
394 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
399 foreach (array('Allow', 'Reverse') as $list) {
\r
400 if (!isset($aData[$list])) {
\r
401 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
402 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
414 if (is_array($this->aContactList)) {
\r
415 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
416 $str = '<d n="'.$u_domain.'">';
\r
417 $len += strlen($str);
\r
419 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
422 $len = strlen($str);
\r
425 foreach ($aUserList as $u_name => $aNetworks) {
\r
426 foreach ($aNetworks as $network => $status) {
\r
427 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
428 $len += strlen($str);
\r
429 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
433 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
435 $sList = '<d n="'.$u_domain.'">'.$str;
\r
436 $len = strlen($sList);
\r
445 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
446 // NS: >>> BLP {id} BL
\r
447 $this->ns_writeln("BLP $this->id BL");
\r
448 foreach ($this->aADL as $str) {
\r
449 $len = strlen($str);
\r
450 // NS: >>> ADL {id} {size}
\r
451 $this->ns_writeln("ADL $this->id $len");
\r
452 $this->ns_writedata($str);
\r
454 // NS: >>> PRP {id} MFN name
\r
455 if ($this->alias == '') $this->alias = $user;
\r
456 $aliasname = rawurlencode($this->alias);
\r
457 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
458 //è¨å®šå€‹äººå¤§é è²¼
\r
459 //$MsnObj=$this->PhotoStckObj();
\r
460 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
461 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
462 if ($this->PhotoStickerFile !== false)
\r
463 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
464 // NS: >>> UUX {id} length
\r
465 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
466 $len = strlen($str);
\r
467 $this->ns_writeln("UUX $this->id $len");
\r
468 $this->ns_writedata($str);
\r
469 if (!self::socketcheck($this->NSfp)) {
\r
470 $this->debug_message('*** Connected, waiting for commands');
\r
473 $this->NSRetryWait($this->retry_wait);
\r
479 * Called if there is an error during signon
\r
481 * @param string $message Error message to log
\r
484 private function signonFailure($message) {
\r
485 if(!empty($message)) {
\r
486 $this->debug_message($message);
\r
488 $this->callHandler('ConnectFailed', $message);
\r
489 $this->NSRetryWait($this->retry_wait);
\r
493 * Log out and close the NS connection
\r
497 private function nsLogout() {
\r
498 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
501 $this->ns_writeln("OUT");
\r
502 fclose($this->NSfp);
\r
503 $this->NSfp = false;
\r
504 $this->debug_message("*** Logged out");
\r
509 * NS and SB command handling methods
\r
513 * Read and handle incoming command from NS
\r
517 private function nsReceive() {
\r
518 // Sign in again if not signed in or socket failed
\r
519 if (!is_resource($this->NSfp) || self::socketcheck($this->NSfp)) {
\r
520 $this->callHandler('Reconnect');
\r
521 $this->NSRetryWait($this->retry_wait);
\r
526 $data = $this->ns_readln();
\r
527 if ($data === false) {
\r
528 // There was no data / an error when reading from the socket so reconnect
\r
529 $this->callHandler('Reconnect');
\r
530 $this->NSRetryWait($this->retry_wait);
\r
535 switch (substr($data, 0, 3)) {
\r
537 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
538 // NS: <<< SBS 0 null
\r
544 // refresh ADL, so we re-send it again
\r
545 if (is_array($this->aADL)) {
\r
546 foreach ($this->aADL as $str) {
\r
547 $len = strlen($str);
\r
548 // NS: >>> ADL {id} {size}
\r
549 $this->ns_writeln("ADL $this->id $len");
\r
550 $this->ns_writedata($str);
\r
556 // NS: <<< LST {email} {alias} 11 0
\r
557 @list(/* LST */, $email) = @explode(' ', $data);
\r
558 @list($u_name, $u_domain) = @explode('@', $email);
\r
559 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
560 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
561 $this->debug_message("*** Added to contact list: $u_name@$u_domain");
\r
566 // randomly, we get ADL command, someone add us to their contact list for MSNP15
\r
567 // NS: <<< ADL 0 {size}
\r
568 @list(/* ADL */, /* 0 */, $size) = @explode(' ', $data);
\r
569 if (is_numeric($size) && $size > 0) {
\r
570 $data = $this->ns_readdata($size);
\r
571 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
572 if (is_array($matches) && count($matches) > 0) {
\r
573 $u_domain = $matches[1];
\r
574 $u_name = $matches[2];
\r
575 $network = $matches[4];
\r
576 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
577 $this->debug_message("*** Someone (network: $network) added us to their list (but already in our list): $u_name@$u_domain");
\r
581 foreach (array('Allow', 'Reverse') as $list) {
\r
582 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
584 $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");
\r
587 $aTickets = $this->get_passport_ticket();
\r
588 if (!$aTickets || !is_array($aTickets)) {
\r
589 // failed to login? ignore it
\r
590 $this->debug_message("*** Could not re-login, something wrong here");
\r
591 $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");
\r
595 $this->ticket = $aTickets;
\r
596 $this->debug_message("**** Got new ticket, trying again");
\r
597 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
598 $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");
\r
602 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
605 $this->debug_message("*** Someone (network: $network) added us to their list: $u_name@$u_domain");
\r
607 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
608 $len = strlen($str);
\r
610 $this->callHandler('AddedToList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));
\r
613 $this->debug_message("*** Someone added us to their list: $data");
\r
618 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
619 // NS: <<< RML 0 {size}
\r
620 @list(/* RML */, /* 0 */, $size) = @explode(' ', $data);
\r
621 if (is_numeric($size) && $size > 0) {
\r
622 $data = $this->ns_readdata($size);
\r
623 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
624 if (is_array($matches) && count($matches) > 0) {
\r
625 $u_domain = $matches[1];
\r
626 $u_name = $matches[2];
\r
627 $network = $matches[4];
\r
628 if (isset($this->aContactList[$u_domain][$u_name][$network])) {
\r
629 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
631 foreach ($aData as $list => $id)
\r
632 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
634 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
635 $this->debug_message("*** Someone (network: $network) removed us from their list: $u_name@$u_domain");
\r
638 $this->debug_message("*** Someone (network: $network) removed us from their list (but not in our list): $u_name@$u_domain");
\r
640 $this->callHandler('RemovedFromList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));
\r
643 $this->debug_message("*** Someone removed us from their list: $data");
\r
648 // randomly, we get MSG notification from server
\r
649 // NS: <<< MSG Hotmail Hotmail {size}
\r
650 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size) = @explode(' ', $data);
\r
651 if (is_numeric($size) && $size > 0) {
\r
652 $data = $this->ns_readdata($size);
\r
653 $aLines = @explode("\n", $data);
\r
657 foreach ($aLines as $line) {
\r
658 $line = rtrim($line);
\r
660 if ($line === '') {
\r
664 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
665 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false && strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
666 // we just need text/x-msmsgsinitialmdatanotification
\r
667 // or text/x-msmsgsoimnotification
\r
674 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
675 $maildata = trim(substr($line, 10));
\r
680 $this->debug_message("*** Ignoring MSG for: $line");
\r
683 if ($maildata == '') {
\r
684 $this->debug_message("*** Ignoring MSG not for OIM");
\r
688 if (strcasecmp($maildata, 'too-large') == 0) {
\r
689 $this->debug_message("*** Large mail-data, need to get the data via SOAP");
\r
690 $maildata = $this->getOIM_maildata();
\r
691 if ($maildata === false) {
\r
692 $this->debug_message("*** Could not get mail-data via SOAP");
\r
694 // maybe we need to re-login again
\r
695 $aTickets = $this->get_passport_ticket();
\r
696 if (!$aTickets || !is_array($aTickets)) {
\r
697 // failed to login? ignore it
\r
698 $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");
\r
702 $this->ticket = $aTickets;
\r
703 $this->debug_message("*** Got new ticket, trying again");
\r
704 $maildata = $this->getOIM_maildata();
\r
705 if ($maildata === false) {
\r
706 $this->debug_message("*** Could not get mail-data via SOAP, and re-login already attempted, ignoring this OIM");
\r
711 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
715 $start = strpos($p, '<M>');
\r
716 $end = strpos($p, '</M>');
\r
717 if ($start === false || $end === false || $start > $end) break;
\r
719 $sOIM = substr($p, $start, $end - $start);
\r
721 $p = substr($p, $end);
\r
723 if (count($aOIMs) == 0) {
\r
724 $this->debug_message("*** Ignoring empty OIM");
\r
727 foreach ($aOIMs as $maildata) {
\r
728 // T: 11 for MSN, 13 for Yahoo
\r
729 // S: 6 for MSN, 7 for Yahoo
\r
730 // RT: the datetime received by server
\r
731 // RS: already read or not
\r
732 // SZ: size of message
\r
735 // F: always 00000000-0000-0000-0000-000000000009
\r
737 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
738 if (count($matches) == 0) {
\r
739 $this->debug_message("*** Ignoring OIM maildata without <T>type</T>");
\r
742 $oim_type = $matches[1];
\r
743 if ($oim_type = 13)
\r
747 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
748 if (count($matches) == 0) {
\r
749 $this->debug_message("*** Ignoring OIM maildata without <E>sender</E>");
\r
752 $oim_sender = $matches[1];
\r
753 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
754 if (count($matches) == 0) {
\r
755 $this->debug_message("*** Ignoring OIM maildata without <I>msgid</I>");
\r
758 $oim_msgid = $matches[1];
\r
759 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
760 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
761 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
762 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
763 $this->debug_message("*** OIM received from $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
764 $sMsg = $this->getOIM_message($oim_msgid);
\r
765 if ($sMsg === false) {
\r
766 $this->debug_message("*** Could not get OIM, msgid = $oim_msgid");
\r
768 $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");
\r
771 $aTickets = $this->get_passport_ticket();
\r
772 if (!$aTickets || !is_array($aTickets)) {
\r
773 // failed to login? ignore it
\r
774 $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");
\r
778 $this->ticket = $aTickets;
\r
779 $this->debug_message("*** get new ticket, try it again");
\r
780 $sMsg = $this->getOIM_message($oim_msgid);
\r
781 if ($sMsg === false) {
\r
782 $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");
\r
786 $this->debug_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
787 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
793 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
794 // NS: <<< UBM {email} $network $type {size}
\r
795 @list(/* UBM */, $from_email, $network, $type, $size) = @explode(' ', $data);
\r
796 if (is_numeric($size) && $size > 0) {
\r
797 $data = $this->ns_readdata($size);
\r
798 $aLines = @explode("\n", $data);
\r
802 foreach ($aLines as $line) {
\r
803 $line = rtrim($line);
\r
805 if ($line === '') {
\r
809 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
815 $aSubLines = @explode("\r", $line);
\r
816 foreach ($aSubLines as $str) {
\r
823 $this->debug_message("*** Ignoring message from $from_email: $line");
\r
826 $this->debug_message("*** MSG from $from_email (network: $network): $sMsg");
\r
827 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
832 // randomly, we get UBX notification from server
\r
833 // NS: <<< UBX email {network} {size}
\r
834 @list(/* UBX */, /* email */, /* network */, $size) = @explode(' ', $data);
\r
835 // we don't need the notification data, so just ignore it
\r
836 if (is_numeric($size) && $size > 0)
\r
837 $this->ns_readdata($size);
\r
841 // randomly, we'll get challenge from server
\r
842 // NS: <<< CHL 0 {code}
\r
843 @list(/* CHL */, /* 0 */, $chl_code) = @explode(' ', $data);
\r
844 $fingerprint = $this->getChallenge($chl_code);
\r
845 // NS: >>> QRY {id} {product_id} 32
\r
846 // NS: >>> fingerprint
\r
847 $this->ns_writeln("QRY $this->id ".self::PROD_ID.' 32');
\r
848 $this->ns_writedata($fingerprint);
\r
849 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
850 if ($this->PhotoStickerFile !== false)
\r
851 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
854 // NS: <<< CHG {id} {status} {code}
\r
856 // change our status to online first
\r
860 // sometimes, NS will redirect to another NS
\r
862 // NS: <<< XFR {id} NS {server} 0 {server}
\r
864 // NS: <<< XFR {id} NS {server} U D
\r
865 // for normal switchboard XFR
\r
866 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
867 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code) = @explode(' ', $data);
\r
868 @list($ip, $port) = @explode(':', $server);
\r
869 if ($server_type != 'SB') {
\r
871 // this connection will close after XFR
\r
876 $this->debug_message("NS: <<< XFR SB");
\r
877 $session = array_shift($this->waitingForXFR);
\r
878 $this->connectToSBSession('Active', $ip, $port, $session['to'], array('cki' => $cki_code));
\r
881 // NS: <<< QNG {time}
\r
882 @list(/* QNG */, $ping_wait) = @explode(' ', $data);
\r
883 $this->callHandler('Pong', $ping_wait);
\r
887 if ($this->PhotoStickerFile !== false)
\r
888 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
890 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
891 // someone is trying to talk to us
\r
892 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
893 $this->debug_message("NS: <<< RNG $data");
\r
894 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name) = @explode(' ', $data);
\r
895 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
896 $this->debug_message("*** RING from $email, $sb_ip:$sb_port");
\r
897 $this->addContact($email, 1, $email, true);
\r
898 $this->connectToSBSession('Passive', $sb_ip, $sb_port, $email, array('sid' => $sid, 'ticket' => $ticket));
\r
902 // NS: <<< NLN {status} {email} {networkid} {nickname} {clientid} {dpobj}
\r
903 @list(/* NLN */, $status, $email, $network, $nickname) = @explode(' ', $data);
\r
904 $this->callHandler('StatusChange', array('screenname' => $email, 'status' => $status, 'network' => $network, 'nickname' => $nickname));
\r
908 // force logout from NS
\r
910 $this->debug_message("*** LOGOUT from NS");
\r
911 return $this->nsLogout();
\r
914 $code = substr($data,0,3);
\r
915 if (is_numeric($code)) {
\r
916 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
917 $this->debug_message("*** NS: $this->error");
\r
919 return $this->nsLogout();
\r
926 * Read and handle incoming command/message from
\r
927 * a switchboard session socket
\r
929 private function sbReceive($socket) {
\r
930 $intsocket = (int) $socket;
\r
931 $session = &$this->switchBoardSessions[$intsocket];
\r
933 if (feof($socket)) {
\r
934 // Unset session lookup value
\r
935 unset($this->switchBoardSessionLookup[$session['to']]);
\r
937 // Unset session itself
\r
938 unset($this->switchBoardSessions[$intsocket]);
\r
942 $id = &$session['id'];
\r
944 $data = $this->sb_readln($socket);
\r
945 $code = substr($data, 0, 3);
\r
948 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
949 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
950 $this->debug_message("*** $email joined session");
\r
951 if ($email == $session['to']) {
\r
952 $session['joined'] = true;
\r
953 $this->callHandler('SessionReady', array('to' => $email));
\r
957 $this->debug_message("*** Quit for BYE");
\r
958 $this->endSBSession($socket);
\r
961 // SB: <<< USR {id} OK {user} {alias}
\r
962 // we don't need the data, just ignore it
\r
963 // request user to join this switchboard
\r
964 // SB: >>> CAL {id} {user}
\r
965 $this->sb_writeln($socket, $id, "CAL $id ".$session['to']);
\r
968 // SB: <<< CAL {id} RINGING {?}
\r
969 // we don't need this, just ignore, and wait for other response
\r
973 // SB: <<< JOI {user} {alias} {clientid?}
\r
975 @list(/* JOI */, $email) = @explode(' ', $data);
\r
976 if ($email == $session['to']) {
\r
977 $session['joined'] = true;
\r
978 $this->callHandler('SessionReady', array('to' => $email));
\r
982 // SB: <<< MSG {email} {alias} {len}
\r
983 @list(/* MSG */, $from_email, /* alias */, $len) = @explode(' ', $data);
\r
985 $data = $this->sb_readdata($socket, $len);
\r
986 $aLines = @explode("\n", $data);
\r
991 foreach ($aLines as $line) {
\r
992 $line = rtrim($line);
\r
994 if ($line === '') {
\r
998 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
999 // typing notification, just ignore
\r
1003 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
1004 // we don't handle any split message, just ignore
\r
1008 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
1009 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
1011 $p = strstr($data, "\n\n");
\r
1013 if ($p === false) {
\r
1014 $p = strstr($data, "\r\n\r\n");
\r
1016 $sMsg = substr($p, 4);
\r
1019 $sMsg = substr($p, 2);
\r
1022 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
1023 // ignore all application/x-... message
\r
1025 // application/x-ms-ink => ink message
\r
1029 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
1030 // ignore all text/x-... message
\r
1032 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
1033 // text/x-mms-animemoticon => customized animemotion word
\r
1044 $this->debug_message("*** Ignoring SB data from $from_email: $line");
\r
1048 // we will ignore any p2p message after sending acknowledgement
\r
1050 $len = strlen($sMsg);
\r
1051 $this->debug_message("*** p2p message from $from_email, size $len");
\r
1052 // header = 48 bytes
\r
1053 // content >= 0 bytes
\r
1054 // footer = 4 bytes
\r
1055 // so it need to >= 52 bytes
\r
1056 /*if ($len < 52) {
\r
1057 $this->debug_message("*** p2p: size error, less than 52!");
\r
1060 $aDwords = @unpack("V12dword", $sMsg);
\r
1061 if (!is_array($aDwords)) {
\r
1062 $this->debug_message("*** p2p: header unpack error!");
\r
1065 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
1066 $hdr_SessionID = $aDwords['dword1'];
\r
1067 $hdr_Identifier = $aDwords['dword2'];
\r
1068 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
1069 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
1070 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
1071 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
1072 $hdr_MessageLength = $aDwords['dword7'];
\r
1073 $hdr_Flag = $aDwords['dword8'];
\r
1074 $hdr_AckID = $aDwords['dword9'];
\r
1075 $hdr_AckUID = $aDwords['dword10'];
\r
1076 $hdr_AckSizeLow = $aDwords['dword11'];
\r
1077 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
1078 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
1079 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
1080 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
1081 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
1082 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
1083 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
1084 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
1085 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
1086 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
1087 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
1088 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
1089 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
1090 if ($hdr_Flag == 2) {
\r
1091 //This is an ACK from SB ignore....
\r
1092 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
1095 $MsgBody = $this->linetoArray(substr($sMsg, 48, -4));
\r
1096 $this->debug_message("*** p2p: body".print_r($MsgBody, true));
\r
1097 if (($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context']))) {
\r
1099 if ($this->sb_readln($socket) === false) break;
\r
1101 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg, 0, 48)));
\r
1102 preg_match('/{([0-9A-F\-]*)}/i', $MsgBody['Via'], $Matches);
\r
1103 $BranchGUID = $Matches[1];
\r
1104 //it's an invite to send a display picture.
\r
1105 $new_id = ~$hdr_Identifier;
\r
1107 "LLLLLLLLLLLL", $hdr_SessionID,
\r
1110 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1115 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh
\r
1117 $footer = pack("L", 0);
\r
1118 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
1119 $len = strlen($message);
\r
1120 $this->sb_writeln($socket, $id, "MSG $id D $len");
\r
1121 $this->sb_writedata($socket, $message);
\r
1122 $this->debug_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
1123 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
1124 $this->sb_readln($socket); // Read ACK;
\r
1125 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
1127 //Send 200 OK message
\r
1128 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
1130 "MSNSLP/1.0 200 OK\r\n".
\r
1131 "To: <msnmsgr:".$from_email.">\r\n".
\r
1132 "From: <msnmsgr:".$this->user.">\r\n".
\r
1133 "Via: ".$MsgBody['Via']."\r\n".
\r
1134 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
1135 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
1136 "Max-Forwards: 0\r\n".
\r
1137 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
1138 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
1140 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
1141 $hdr_TotalDataSizeHigh=0;
\r
1143 "LLLLLLLLLLLL", $hdr_SessionID,
\r
1146 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1147 strlen($MessagePayload),
\r
1155 "MIME-Version: 1.0\r\n".
\r
1156 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1157 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
1158 $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));
\r
1159 $this->sb_writedata($socket, $message);
\r
1160 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
1161 $this->sb_readln($socket); // Read ACK;
\r
1163 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
1164 // send data preparation message
\r
1165 // send 4 null bytes as data
\r
1166 $hdr_TotalDataSizeLow = 4;
\r
1167 $hdr_TotalDataSizeHigh = 0 ;
\r
1171 $MsgBody['SessionID'],
\r
1174 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1175 $hdr_TotalDataSizeLow,
\r
1182 "MIME-Version: 1.0\r\n".
\r
1183 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1184 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L', 0)."$footer";
\r
1185 $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));
\r
1186 $this->sb_writedata($socket, $message);
\r
1187 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
1188 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
1189 $this->sb_readln($socket); // Read ACK;
\r
1191 // send Data Content..
\r
1192 $footer=pack('N',1);
\r
1194 $FileSize=filesize($PictureFilePath);
\r
1195 if ($hTitle=fopen($PictureFilePath,'rb')) {
\r
1198 while (!feof($hTitle)) {
\r
1199 $FileContent = fread($hTitle, 1024);
\r
1200 $FileContentSize = strlen($FileContent);
\r
1203 $MsgBody['SessionID'],
\r
1214 "MIME-Version: 1.0\r\n".
\r
1215 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1216 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
1217 $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));
\r
1218 $this->sb_writedata($socket, $message);
\r
1219 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
1220 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
1221 //$this->SB_readln($socket);//Read ACK;
\r
1222 $Offset += $FileContentSize;
\r
1227 $MessageContent="\r\n".pack("C", 0);
\r
1229 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
1230 "To: <msnmsgr:$from_email>\r\n".
\r
1231 "From: <msnmsgr:".$this->user.">\r\n".
\r
1232 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
1234 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
1235 "Max-Forwards: 0\r\n".
\r
1236 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
1237 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
1238 $footer=pack('N',0);
\r
1239 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
1240 $hdr_TotalDataSizeHigh=0;
\r
1242 $hdr = pack("LLLLLLLLLLLL",
\r
1246 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1253 "MIME-Version: 1.0\r\n".
\r
1254 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
1255 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
1256 $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));
\r
1258 $this->sb_writedata($socket, $message);
\r
1259 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
1264 //if ($hdr_Flag == 2) {
\r
1265 // just send ACK...
\r
1266 // $this->sb_writeln($socket, $id, "ACK $id");
\r
1269 if ($hdr_SessionID == 4) {
\r
1271 $this->debug_message("*** p2p: ignore flag 4");
\r
1274 $finished = false;
\r
1275 if ($hdr_TotalDataSizeHigh == 0) {
\r
1276 // only 32 bites size
\r
1277 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
1281 // we won't accept any file transfer
\r
1282 // so I think we won't get any message size need to use 64 bits
\r
1283 // 64 bits size here, can't count directly...
\r
1284 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
1285 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
1286 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
1287 $now_size = bcadd($dataoffset, $messagelength);
\r
1288 if (bccomp($now_size, $totalsize) >= 0)
\r
1292 // ignore not finished split packet
\r
1293 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
1296 //$new_id = ~$hdr_Identifier;
\r
1299 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
1302 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
1307 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
1308 $footer = pack("L", 0);
\r
1309 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
1310 $len = strlen($message);
\r
1311 $this->sb_writeln($socket, $id, "MSG $id D $len");
\r
1313 $this->sb_writedata($socket, $message);
\r
1314 $this->debug_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
1315 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
1319 $this->debug_message("*** MSG from $from_email: $sMsg");
\r
1320 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => 1, 'offline' => false));
\r
1323 $this->debug_message('*** User '.$session['to'].' is offline. Trying OIM.');
\r
1324 $session['offline'] = true;
\r
1327 if (is_numeric($code)) {
\r
1328 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1329 $this->debug_message("*** SB: $this->error");
\r
1336 * Checks for new data and calls appropriate methods
\r
1338 * This method is usually called in an infinite loop to keep checking for new data
\r
1342 public function receive() {
\r
1343 // First, get an array of sockets that have data that is ready to be read
\r
1345 $ready = $this->getSockets();
\r
1346 $numrdy = stream_select($ready, $w = NULL, $x = NULL, NULL);
\r
1348 // Now that we've waited for something, go through the $ready
\r
1349 // array and read appropriately
\r
1351 foreach ($ready as $socket) {
\r
1352 if ($socket == $this->NSfp) {
\r
1353 $this->nsReceive();
\r
1355 $this->sbReceive($socket);
\r
1361 * Switchboard related methods
\r
1365 * Send a request for a switchboard session
\r
1367 * @param string $to Target email for switchboard session
\r
1369 private function reqSBSession($to) {
\r
1370 $this->debug_message("*** Request SB for $to");
\r
1371 $this->ns_writeln("XFR $this->id SB");
\r
1373 // Add to the queue of those waiting for a switchboard session reponse
\r
1374 $this->switchBoardSessions[$to] = array(
\r
1378 'joined' => false,
\r
1379 'offline' => false,
\r
1380 'XFRReqTime' => time()
\r
1382 $this->waitingForXFR[$to] = &$this->switchBoardSessions[$to];
\r
1386 * Following an XFR or RNG, connect to the switchboard session
\r
1388 * @param string $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case of RNG)
\r
1389 * @param string $ip IP of Switchboard
\r
1390 * @param integer $port Port of Switchboard
\r
1391 * @param string $to User on other end of Switchboard
\r
1392 * @param array $param Array of parameters - 'cki', 'ticket', 'sid'
\r
1393 * @return boolean true if successful
\r
1395 private function connectToSBSession($mode, $ip, $port, $to, $param) {
\r
1396 $this->debug_message("*** SB: Trying to connect to switchboard server $ip:$port");
\r
1398 $socket = @fsockopen($ip, $port, $errno, $errstr, $this->timeout);
\r
1400 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
1404 // Store the socket in the lookup array
\r
1405 $this->switchBoardSessionLookup[$to] = $socket;
\r
1407 // Store the socket in the sessions array
\r
1408 $this->switchBoardSessions[$to] = array(
\r
1410 'socket' => $socket,
\r
1412 'joined' => false,
\r
1413 'offline' => false,
\r
1414 'XFRReqTime' => time()
\r
1417 // Change the index of the session to the socket
\r
1418 $intsocket = (int) $socket;
\r
1419 $this->switchBoardSessions[$intsocket] = $this->switchBoardSessions[$to];
\r
1420 unset($this->switchBoardSessions[$to]);
\r
1422 $id = &$this->switchBoardSessions[$intsocket]['id'];
\r
1424 if ($mode == 'Active') {
\r
1425 $cki_code = $param['cki'];
\r
1427 // SB: >>> USR {id} {user} {cki}
\r
1428 $this->sb_writeln($socket, $id, "USR $id $this->user $cki_code");
\r
1431 $ticket = $param['ticket'];
\r
1432 $sid = $param['sid'];
\r
1434 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
1435 $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");
\r
1440 * Called when we want to end a switchboard session
\r
1441 * or a switchboard session ends
\r
1443 * @param resource $socket Socket
\r
1444 * @param boolean $killsession Whether to delete the session
\r
1447 private function endSBSession($socket) {
\r
1448 if (!self::socketcheck($socket)) {
\r
1449 $this->sb_writeln($socket, $fake = 0, 'OUT');
\r
1453 // Unset session lookup value
\r
1454 $intsocket = (int) $socket;
\r
1455 unset($this->switchBoardSessionLookup[$this->switchBoardSessions[$intsocket]['to']]);
\r
1457 // Unset session itself
\r
1458 unset($this->switchBoardSessions[$intsocket]);
\r
1462 * Send a message via an existing SB session
\r
1464 * @param string $to Recipient for message
\r
1465 * @param string $message Message
\r
1466 * @return boolean true on success
\r
1468 private function sendMessageViaSB($to, $message) {
\r
1469 $socket = $this->switchBoardSessionLookup[$to];
\r
1470 if (self::socketcheck($socket)) {
\r
1474 $id = &$this->switchBoardSessions[(int) $socket]['id'];
\r
1476 $aMessage = $this->getMessage($message);
\r
1477 // CheckEmotion...
\r
1478 $MsnObjDefine = $this->GetMsnObjDefine($aMessage);
\r
1479 if ($MsnObjDefine !== '') {
\r
1480 $SendString = "MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
1481 $len = strlen($SendString);
\r
1483 if ($this->sb_writeln($socket, $id, "MSG $id N $len") === false ||
\r
1484 $this->sb_writedata($socket, $SendString) === false) {
\r
1485 $this->endSBSession($socket);
\r
1489 $len = strlen($aMessage);
\r
1491 if ($this->sb_writeln($socket, $id, "MSG $id N $len") === false ||
\r
1492 $this->sb_writedata($socket, $aMessage) === false) {
\r
1493 $this->endSBSession($socket);
\r
1497 // Don't close the SB session, we might as well leave it open
\r
1502 * Send a message to a user on another network
\r
1504 * @param string $to Intended recipient
\r
1505 * @param string $message Message
\r
1506 * @param integer $network Network
\r
1509 private function sendOtherNetworkMessage($to, $message, $network) {
\r
1510 $message = $this->getMessage($message, $network);
\r
1511 $len = strlen($message);
\r
1512 if ($this->ns_writeln("UUM $this->id $to $network 1 $len") === false ||
\r
1513 $this->ns_writedata($Message) === false) {
\r
1516 $this->debug_message("*** Sent to $to (network: $network):\n$Message");
\r
1523 * @param string $to To address in form user@host.com(@network)
\r
1524 * where network is 1 for MSN, 32 for Yahoo
\r
1525 * and 'Offline' for offline messages
\r
1526 * @param string $message Message
\r
1527 * @param boolean &$waitForSession Boolean passed by reference,
\r
1528 * if set to true on return, message
\r
1529 * did not fail to send but is
\r
1530 * waiting for a valid session
\r
1532 * @return boolean true on success
\r
1534 public function sendMessage($to, $message, &$waitForSession) {
\r
1535 if ($message != '') {
\r
1536 $toParts = explode('@', $to);
\r
1537 if(count($toParts) < 3) {
\r
1538 list($name, $host) = $toParts;
\r
1541 list($name, $host, $network) = $toParts;
\r
1544 $recipient = $name.'@'.$host;
\r
1546 if ($network === 1) {
\r
1547 if (!isset($this->switchBoardSessionLookup[$recipient])) {
\r
1548 if (!isset($this->switchBoardSessions[$recipient]) || time() - $this->switchBoardSessions[$recipient]['XFRReqTime'] > $this->XFRReqTimeout) {
\r
1549 $this->debug_message("*** No existing SB session or request has timed out");
\r
1550 $this->reqSBSession($recipient);
\r
1553 $waitForSession = true;
\r
1556 $socket = $this->switchBoardSessionLookup[$recipient];
\r
1557 $intsocket = (int) $socket;
\r
1558 if ($this->switchBoardSessions[$intsocket]['offline']) {
\r
1559 $this->debug_message("*** Contact ($recipient) offline, sending OIM");
\r
1560 $this->endSBSession($socket);
\r
1561 $waitForSession = false;
\r
1562 return $this->sendMessage($recipient.'@Offline', $message);
\r
1564 if ($this->switchBoardSessions[$intsocket]['joined'] !== true) {
\r
1565 $this->debug_message("*** Recipient has not joined session, returning false");
\r
1566 $waitForSession = true;
\r
1570 $this->debug_message("*** Attempting to send message to $recipient using existing SB session");
\r
1572 if ($this->sendMessageViaSB($recipient, $message)) {
\r
1573 $this->debug_message('*** Message sent successfully');
\r
1577 $waitForSession = false;
\r
1581 } elseif ($network == 'Offline') {
\r
1583 //FIXME: ä¿®æ£Send OIM
\r
1585 $re_login = false;
\r
1586 for ($i = 0; $i < $this->oim_try; $i++) {
\r
1587 if (($oim_result = $this->sendOIM($recipient, $message, $lockkey)) === true) break;
\r
1588 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
1589 // need challenge lockkey
\r
1590 $this->debug_message("*** Need challenge code for ".$oim_result['challenge']);
\r
1591 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
1594 if ($oim_result === false || $oim_result['auth_policy'] !== false) {
\r
1596 $this->debug_message("*** Can't send OIM, but we already re-logged-in again, so returning false");
\r
1599 $this->debug_message("*** Can't send OIM, maybe ticket expired, trying to login again");
\r
1601 // Maybe we need to re-login again
\r
1602 if (!$this->get_passport_ticket()) {
\r
1603 $this->debug_message("*** Can't re-login, something went wrong here, returning false");
\r
1606 $this->debug_message("*** Getting new ticket and trying again");
\r
1613 return $this->sendOtherNetworkMessage($recipient, $message, $network);
\r
1624 * Get OIM mail data
\r
1626 * @return string mail data or false on failure
\r
1628 function getOIM_maildata() {
\r
1629 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1630 if (count($matches) == 0) {
\r
1631 $this->debug_message('*** No web ticket?');
\r
1634 $t = htmlspecialchars($matches[1]);
\r
1635 $p = htmlspecialchars($matches[2]);
\r
1636 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1637 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1638 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1639 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1641 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1647 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1649 </soap:Envelope>';
\r
1651 $header_array = array(
\r
1652 'SOAPAction: '.self::OIM_MAILDATA_SOAP,
\r
1653 'Content-Type: text/xml; charset=utf-8',
\r
1654 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'
\r
1657 $this->debug_message('*** URL: '.self::OIM_MAILDATA_URL);
\r
1658 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1659 $curl = curl_init();
\r
1660 curl_setopt($curl, CURLOPT_URL, self::OIM_MAILDATA_URL);
\r
1661 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1662 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1663 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1664 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1665 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1666 curl_setopt($curl, CURLOPT_POST, 1);
\r
1667 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1668 $data = curl_exec($curl);
\r
1669 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1670 curl_close($curl);
\r
1671 $this->debug_message("*** Get Result:\n$data");
\r
1673 if ($http_code != 200) {
\r
1674 $this->debug_message("*** Could not get OIM maildata! http code: $http_code");
\r
1678 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1679 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1680 if (count($matches) == 0) {
\r
1681 $this->debug_message('*** Could not get OIM maildata');
\r
1684 return $matches[2];
\r
1688 * Fetch OIM message with given id
\r
1690 * @param string $msgid
\r
1691 * @return string Message or false on failure
\r
1693 function getOIM_message($msgid) {
\r
1694 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1695 if (count($matches) == 0) {
\r
1696 $this->debug_message('*** No web ticket?');
\r
1699 $t = htmlspecialchars($matches[1]);
\r
1700 $p = htmlspecialchars($matches[2]);
\r
1703 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1704 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1705 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1706 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1708 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1714 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1715 <messageId>'.$msgid.'</messageId>
\r
1716 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1719 </soap:Envelope>';
\r
1721 $header_array = array(
\r
1722 'SOAPAction: '.self::OIM_READ_SOAP,
\r
1723 'Content-Type: text/xml; charset=utf-8',
\r
1724 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'
\r
1727 $this->debug_message('*** URL: '.self::OIM_READ_URL);
\r
1728 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1729 $curl = curl_init();
\r
1730 curl_setopt($curl, CURLOPT_URL, self::OIM_READ_URL);
\r
1731 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1732 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1733 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1734 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1735 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1736 curl_setopt($curl, CURLOPT_POST, 1);
\r
1737 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1738 $data = curl_exec($curl);
\r
1739 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1740 curl_close($curl);
\r
1741 $this->debug_message("*** Get Result:\n$data");
\r
1743 if ($http_code != 200) {
\r
1744 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1748 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1750 $start = strpos($data, '<GetMessageResult>');
\r
1751 $end = strpos($data, '</GetMessageResult>');
\r
1752 if ($start === false || $end === false || $start > $end) {
\r
1753 $this->debug_message("*** Can't get OIM: $msgid");
\r
1756 $lines = substr($data, $start + 18, $end - $start);
\r
1757 $aLines = @explode("\n", $lines);
\r
1761 foreach ($aLines as $line) {
\r
1762 $line = rtrim($line);
\r
1764 if ($line === '') {
\r
1770 // stop at empty lines
\r
1771 if ($line === '') break;
\r
1774 $sMsg = base64_decode($sOIM);
\r
1775 //$this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1778 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1779 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1780 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1781 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1783 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1789 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1791 <messageId>'.$msgid.'</messageId>
\r
1795 </soap:Envelope>';
\r
1797 $header_array = array(
\r
1798 'SOAPAction: '.self::OIM_DEL_SOAP,
\r
1799 'Content-Type: text/xml; charset=utf-8',
\r
1800 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'
\r
1803 $this->debug_message('*** URL: '.self::OIM_DEL_URL);
\r
1804 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1805 $curl = curl_init();
\r
1806 curl_setopt($curl, CURLOPT_URL, self::OIM_DEL_URL);
\r
1807 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1808 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1809 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1810 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1811 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1812 curl_setopt($curl, CURLOPT_POST, 1);
\r
1813 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1814 $data = curl_exec($curl);
\r
1815 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1816 curl_close($curl);
\r
1817 $this->debug_message("*** Get Result:\n$data");
\r
1819 if ($http_code != 200)
\r
1820 $this->debug_message("*** Could not delete OIM: $msgid, http code = $http_code");
\r
1822 $this->debug_message("*** OIM ($msgid) deleted");
\r
1827 * Send offline message
\r
1829 * @param string $to Intended recipient
\r
1830 * @param string $sMessage Message
\r
1831 * @param string $lockkey Lock key
\r
1832 * @return mixed true on success or error data
\r
1834 private function sendOIM($to, $sMessage, $lockkey) {
\r
1835 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1836 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1837 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1838 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1840 <From memberName="'.$this->user.'"
\r
1841 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
1844 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
1845 msnpVer="'.self::PROTOCOL.'"
\r
1846 buildVer="'.self::BUILDVER.'"/>
\r
1847 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
1848 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
1849 appid="'.self::PROD_ID.'"
\r
1850 lockkey="'.$lockkey.'"
\r
1851 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
1852 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
1853 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
1854 <MessageNumber>1</MessageNumber>
\r
1858 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
1859 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
1860 Content-Type: text/plain; charset=UTF-8
\r
1861 Content-Transfer-Encoding: base64
\r
1862 X-OIM-Message-Type: OfflineMessage
\r
1863 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
1864 X-OIM-Sequence-Num: 1
\r
1866 '.chunk_split(base64_encode($sMessage)).'
\r
1869 </soap:Envelope>';
\r
1871 $header_array = array(
\r
1872 'SOAPAction: '.self::OIM_SEND_SOAP,
\r
1873 'Content-Type: text/xml',
\r
1874 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.self::BUILDVER.')'
\r
1877 $this->debug_message('*** URL: '.self::OIM_SEND_URL);
\r
1878 $this->debug_message("*** Sending SOAP:\n$XML");
\r
1879 $curl = curl_init();
\r
1880 curl_setopt($curl, CURLOPT_URL, self::OIM_SEND_URL);
\r
1881 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1882 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1883 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1884 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1885 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1886 curl_setopt($curl, CURLOPT_POST, 1);
\r
1887 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1888 $data = curl_exec($curl);
\r
1889 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1890 curl_close($curl);
\r
1891 $this->debug_message("*** Get Result:\n$data");
\r
1893 if ($http_code == 200) {
\r
1894 $this->debug_message("*** OIM sent for $to");
\r
1898 $challenge = false;
\r
1899 $auth_policy = false;
\r
1900 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
1901 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
1902 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
1903 if (count($matches) != 0) {
\r
1904 // yes, we get new LockKeyChallenge
\r
1905 $challenge = $matches[2];
\r
1906 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
1908 // auth policy error
\r
1909 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
1910 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
1911 if (count($matches) != 0) {
\r
1912 $auth_policy = $matches[2];
\r
1913 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
1915 if ($auth_policy === false && $challenge === false) {
\r
1916 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
1917 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
1918 if (count($matches) == 0) {
\r
1919 // no error, we assume the OIM is sent
\r
1920 $this->debug_message("*** OIM sent for $to");
\r
1923 $err_code = $matches[2];
\r
1924 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
1925 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
1926 if (count($matches) > 0)
\r
1927 $err_msg = $matches[1];
\r
1930 $this->debug_message("*** OIM failed for $to");
\r
1931 $this->debug_message("*** OIM Error code: $err_code");
\r
1932 $this->debug_message("*** OIM Error Message: $err_msg");
\r
1935 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
1939 * Contact / Membership list methods
\r
1943 * Fetch contact list
\r
1945 * @return boolean true on success
\r
1947 private function UpdateContacts() {
\r
1948 $ABApplicationHeaderArray = array(
\r
1949 'ABApplicationHeader' => array(
\r
1950 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
1951 'ApplicationId' => 'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
1952 'IsMigration' => false,
\r
1953 'PartnerScenario' => 'ContactSave'
\r
1957 $ABApplicationHeader = new SoapHeader('http://www.msn.com/webservices/AddressBook', 'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
1958 $ABFindAllArray = array(
\r
1959 'ABFindAll' => array(
\r
1960 ':' => array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
1961 'abId' => '00000000-0000-0000-0000-000000000000',
\r
1962 'abView' => 'Full',
\r
1963 'lastChange' => '0001-01-01T00:00:00.0000000-08:00',
\r
1966 $ABFindAll = new SoapParam($this->Array2SoapVar($ABFindAllArray), 'ABFindAll');
\r
1967 $this->ABService->__setSoapHeaders(array($ABApplicationHeader, $this->ABAuthHeader));
\r
1968 $this->Contacts = array();
\r
1970 $this->debug_message('*** Updating Contacts...');
\r
1971 $Result = $this->ABService->ABFindAll($ABFindAll);
\r
1972 $this->debug_message("*** Result:\n".print_r($Result, true)."\n".$this->ABService->__getLastResponse());
\r
1973 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
1974 $this->Contacts[$Contact->contactInfo->passportName] = $Contact;
\r
1975 } catch(Exception $e) {
\r
1976 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
1985 * @param string $email
\r
1986 * @param integer $network
\r
1987 * @param string $display
\r
1988 * @param boolean $sendADL
\r
1989 * @return boolean true on success
\r
1991 private function addContact($email, $network, $display = '', $sendADL = false) {
\r
1992 if ($network != 1) return true;
\r
1993 if (isset($this->Contacts[$email])) return true;
\r
1995 $ABContactAddArray = array(
\r
1996 'ABContactAdd' => array(
\r
1997 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
1998 'abId' => '00000000-0000-0000-0000-000000000000',
\r
1999 'contacts' => array(
\r
2000 'Contact' => array(
\r
2001 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
2002 'contactInfo' => array(
\r
2003 'contactType' => 'LivePending',
\r
2004 'passportName' => $email,
\r
2005 'isMessengerUser' => true,
\r
2006 'MessengerMemberInfo' => array(
\r
2007 'DisplayName' => $email
\r
2012 'options' => array(
\r
2013 'EnableAllowListManagement' => true
\r
2017 $ABContactAdd = new SoapParam($this->Array2SoapVar($ABContactAddArray), 'ABContactAdd');
\r
2019 $this->debug_message("*** Adding Contact $email...");
\r
2020 $this->ABService->ABContactAdd($ABContactAdd);
\r
2021 } catch(Exception $e) {
\r
2022 $this->debug_message("*** Add Contact Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
2025 if ($sendADL && !feof($this->NSfp)) {
\r
2026 @list($u_name, $u_domain) = @explode('@', $email);
\r
2027 foreach (array('1', '2') as $l) {
\r
2028 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
2029 $len = strlen($str);
\r
2030 // NS: >>> ADL {id} {size}
\r
2031 $this->ns_writeln("ADL $this->id $len");
\r
2032 $this->ns_writedata($str);
\r
2035 $this->UpdateContacts();
\r
2040 * Remove contact from list
\r
2042 * @param integer $memberID
\r
2043 * @param string $email
\r
2044 * @param integer $network
\r
2045 * @param string $list
\r
2047 function delMemberFromList($memberID, $email, $network, $list) {
\r
2048 if ($network != 1 && $network != 32) return true;
\r
2049 if ($memberID === false) return true;
\r
2051 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
2052 if ($network == 1)
\r
2053 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2054 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
2055 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2056 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2057 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
2059 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2060 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
2061 <IsMigration>false</IsMigration>
\r
2062 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
2063 </ABApplicationHeader>
\r
2064 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2065 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
2066 <TicketToken>'.$ticket.'</TicketToken>
\r
2070 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
2073 <Type>Messenger</Type>
\r
2074 <ForeignId></ForeignId>
\r
2078 <MemberRole>'.$list.'</MemberRole>
\r
2080 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
2081 <Type>Passport</Type>
\r
2082 <MembershipId>'.$memberID.'</MembershipId>
\r
2083 <State>Accepted</State>
\r
2090 </soap:Envelope>';
\r
2092 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2093 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
2094 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2095 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2096 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
2098 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2099 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
2100 <IsMigration>false</IsMigration>
\r
2101 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
2102 </ABApplicationHeader>
\r
2103 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2104 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
2105 <TicketToken>'.$ticket.'</TicketToken>
\r
2109 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
2112 <Type>Messenger</Type>
\r
2113 <ForeignId></ForeignId>
\r
2117 <MemberRole>'.$list.'</MemberRole>
\r
2119 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
2120 <Type>Email</Type>
\r
2121 <MembershipId>'.$memberID.'</MembershipId>
\r
2122 <State>Accepted</State>
\r
2129 </soap:Envelope>';
\r
2131 $header_array = array(
\r
2132 'SOAPAction: '.self::DELMEMBER_SOAP,
\r
2133 'Content-Type: text/xml; charset=utf-8',
\r
2134 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
2137 $this->debug_message('*** URL: '.self::DELMEMBER_URL);
\r
2138 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2139 $curl = curl_init();
\r
2140 curl_setopt($curl, CURLOPT_URL, self::DELMEMBER_URL);
\r
2141 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2142 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2143 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2144 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2145 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2146 curl_setopt($curl, CURLOPT_POST, 1);
\r
2147 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2148 $data = curl_exec($curl);
\r
2149 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2150 curl_close($curl);
\r
2151 $this->debug_message("*** Get Result:\n$data");
\r
2153 if ($http_code != 200) {
\r
2154 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
2155 if (count($matches) == 0) {
\r
2156 $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list");
\r
2159 $faultcode = trim($matches[1]);
\r
2160 $faultstring = trim($matches[2]);
\r
2161 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
2162 $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list, error code: $faultcode, $faultstring");
\r
2165 $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list, not present in list");
\r
2168 $this->debug_message("*** Member successfully deleted (network: $network) $email ($memberID) from $list list");
\r
2173 * Add contact to list
\r
2175 * @param string $email
\r
2176 * @param integer $network
\r
2177 * @param string $list
\r
2179 function addMemberToList($email, $network, $list) {
\r
2180 if ($network != 1 && $network != 32) return true;
\r
2181 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
2184 if ($network == 1)
\r
2185 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2186 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
2187 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2188 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2189 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
2191 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2192 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
2193 <IsMigration>false</IsMigration>
\r
2194 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
2195 </ABApplicationHeader>
\r
2196 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2197 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
2198 <TicketToken>'.$ticket.'</TicketToken>
\r
2202 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
2205 <Type>Messenger</Type>
\r
2206 <ForeignId></ForeignId>
\r
2210 <MemberRole>'.$list.'</MemberRole>
\r
2212 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
2213 <Type>Passport</Type>
\r
2214 <State>Accepted</State>
\r
2215 <PassportName>'.$user.'</PassportName>
\r
2222 </soap:Envelope>';
\r
2224 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2225 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
2226 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2227 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2228 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
2230 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2231 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
2232 <IsMigration>false</IsMigration>
\r
2233 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
2234 </ABApplicationHeader>
\r
2235 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2236 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
2237 <TicketToken>'.$ticket.'</TicketToken>
\r
2241 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
2244 <Type>Messenger</Type>
\r
2245 <ForeignId></ForeignId>
\r
2249 <MemberRole>'.$list.'</MemberRole>
\r
2251 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
2252 <Type>Email</Type>
\r
2253 <State>Accepted</State>
\r
2254 <Email>'.$user.'</Email>
\r
2257 <Name>MSN.IM.BuddyType</Name>
\r
2258 <Value>32:YAHOO</Value>
\r
2267 </soap:Envelope>';
\r
2268 $header_array = array(
\r
2269 'SOAPAction: '.self::ADDMEMBER_SOAP,
\r
2270 'Content-Type: text/xml; charset=utf-8',
\r
2271 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
2274 $this->debug_message('*** URL: '.self::ADDMEMBER_URL);
\r
2275 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2276 $curl = curl_init();
\r
2277 curl_setopt($curl, CURLOPT_URL, self::ADDMEMBER_URL);
\r
2278 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2279 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2280 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2281 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2282 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2283 curl_setopt($curl, CURLOPT_POST, 1);
\r
2284 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2285 $data = curl_exec($curl);
\r
2286 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2287 curl_close($curl);
\r
2288 $this->debug_message("*** Get Result:\n$data");
\r
2290 if ($http_code != 200) {
\r
2291 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
2292 if (count($matches) == 0) {
\r
2293 $this->debug_message("*** Could not add member (network: $network) $email to $list list");
\r
2296 $faultcode = trim($matches[1]);
\r
2297 $faultstring = trim($matches[2]);
\r
2298 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
2299 $this->debug_message("*** Could not add member (network: $network) $email to $list list, error code: $faultcode, $faultstring");
\r
2302 $this->debug_message("*** Could not add member (network: $network) $email to $list list, already present");
\r
2305 $this->debug_message("*** Member successfully added (network: $network) $email to $list list");
\r
2310 * Get membership lists
\r
2312 * @param mixed $returnData Membership list or false on failure
\r
2314 function getMembershipList($returnData = false) {
\r
2315 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
2316 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2317 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
2318 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2319 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2320 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
2322 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2323 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
2324 <IsMigration>false</IsMigration>
\r
2325 <PartnerScenario>Initial</PartnerScenario>
\r
2326 </ABApplicationHeader>
\r
2327 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
2328 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
2329 <TicketToken>'.$ticket.'</TicketToken>
\r
2333 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
2336 <ServiceType>Messenger</ServiceType>
\r
2337 <ServiceType>Invitation</ServiceType>
\r
2338 <ServiceType>SocialNetwork</ServiceType>
\r
2339 <ServiceType>Space</ServiceType>
\r
2340 <ServiceType>Profile</ServiceType>
\r
2345 </soap:Envelope>';
\r
2346 $header_array = array(
\r
2347 'SOAPAction: '.self::MEMBERSHIP_SOAP,
\r
2348 'Content-Type: text/xml; charset=utf-8',
\r
2349 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
2351 $this->debug_message('*** URL: '.self::MEMBERSHIP_URL);
\r
2352 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2353 $curl = curl_init();
\r
2354 curl_setopt($curl, CURLOPT_URL, self::MEMBERSHIP_URL);
\r
2355 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2356 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2357 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2358 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2359 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2360 curl_setopt($curl, CURLOPT_POST, 1);
\r
2361 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2362 $data = curl_exec($curl);
\r
2363 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2364 curl_close($curl);
\r
2365 $this->debug_message("*** Get Result:\n$data");
\r
2367 if ($http_code != 200) return false;
\r
2369 $aMemberships = array();
\r
2371 //$this->debug_message("search p = $p");
\r
2372 $start = strpos($p, '<Membership>');
\r
2373 $end = strpos($p, '</Membership>');
\r
2374 if ($start === false || $end === false || $start > $end) break;
\r
2375 //$this->debug_message("start = $start, end = $end");
\r
2377 $sMembership = substr($p, $start, $end - $start);
\r
2378 $aMemberships[] = $sMembership;
\r
2379 //$this->debug_message("add sMembership = $sMembership");
\r
2380 $p = substr($p, $end);
\r
2382 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
2384 $aContactList = array();
\r
2385 foreach ($aMemberships as $sMembership) {
\r
2386 //$this->debug_message("sMembership = $sMembership");
\r
2387 if (isset($matches)) unset($matches);
\r
2388 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
2389 if (count($matches) == 0) continue;
\r
2390 $sMemberRole = $matches[1];
\r
2391 //$this->debug_message("MemberRole = $sMemberRole");
\r
2392 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
2393 $p = $sMembership;
\r
2394 if (isset($aMembers)) unset($aMembers);
\r
2395 $aMembers = array();
\r
2397 //$this->debug_message("search p = $p");
\r
2398 $start = strpos($p, '<Member xsi:type="');
\r
2399 $end = strpos($p, '</Member>');
\r
2400 if ($start === false || $end === false || $start > $end) break;
\r
2401 //$this->debug_message("start = $start, end = $end");
\r
2403 $sMember = substr($p, $start, $end - $start);
\r
2404 $aMembers[] = $sMember;
\r
2405 //$this->debug_message("add sMember = $sMember");
\r
2406 $p = substr($p, $end);
\r
2408 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
2409 foreach ($aMembers as $sMember) {
\r
2410 //$this->debug_message("sMember = $sMember");
\r
2411 if (isset($matches)) unset($matches);
\r
2412 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
2413 if (count($matches) == 0) continue;
\r
2414 $sMemberType = $matches[1];
\r
2415 //$this->debug_message("MemberType = $sMemberType");
\r
2417 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
2418 if (count($matches) == 0) continue;
\r
2419 $id = $matches[1];
\r
2420 if ($sMemberType == 'PassportMember') {
\r
2421 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
2423 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
2425 else if ($sMemberType == 'EmailMember') {
\r
2426 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
2427 // Value is 32: or 32:YAHOO
\r
2428 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
2429 if (count($matches) == 0) continue;
\r
2430 if ($matches[1] != 32) continue;
\r
2432 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
2434 if ($network == -1) continue;
\r
2435 if (count($matches) > 0) {
\r
2436 $email = $matches[1];
\r
2437 @list($u_name, $u_domain) = @explode('@', $email);
\r
2438 if ($u_domain == NULL) continue;
\r
2439 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
2440 $this->debug_message("*** Adding new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
2444 return $aContactList;
\r
2448 * MsnObj related methods
\r
2453 * @param $FilePath 圖檔路徑
\r
2454 * @param $Type 檔案類型 3=>大é è²¼,2表情圖案
\r
2457 private function MsnObj($FilePath, $Type = 3) {
\r
2458 if (!($FileSize=filesize($FilePath))) return '';
\r
2459 $Location = md5($FilePath);
\r
2460 $Friendly = md5($FilePath.$Type);
\r
2461 if (isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
2462 $sha1d = base64_encode(sha1(file_get_contents($FilePath), true));
\r
2463 $sha1c = base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d", true));
\r
2464 $this->MsnObjArray[$Location] = $FilePath;
\r
2465 $MsnObj = '<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
2466 $this->MsnObjMap[$Location] = $MsnObj;
\r
2467 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
2471 private function GetPictureFilePath($Context) {
\r
2472 $MsnObj = base64_decode($Context);
\r
2473 if (preg_match('/location="(.*?)"/i', $MsnObj, $Match))
\r
2474 $location = $Match[1];
\r
2475 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
2476 if ($location && isset($this->MsnObjArray[$location]))
\r
2477 return $this->MsnObjArray[$location];
\r
2481 private function GetMsnObjDefine($Message) {
\r
2482 $DefineString = '';
\r
2483 if (is_array($this->Emotions))
\r
2484 foreach ($this->Emotions as $Pattern => $FilePath) {
\r
2485 if (strpos($Message, $Pattern) !== false)
\r
2486 $DefineString .= "$Pattern\t".$this->MsnObj($FilePath, 2)."\t";
\r
2488 return $DefineString;
\r
2496 * Read data of specified size from NS socket
\r
2498 * @param integer $size Size to read
\r
2499 * @return string Data read
\r
2501 private function ns_readdata($size) {
\r
2504 while (!feof($this->NSfp)) {
\r
2505 $buf = @fread($this->NSfp, $size - $count);
\r
2507 $count += strlen($buf);
\r
2508 if ($count >= $size) break;
\r
2510 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
2515 * Read line from the NS socket
\r
2517 * @return string Data read
\r
2519 private function ns_readln() {
\r
2520 $data = @fgets($this->NSfp, 4096);
\r
2521 if ($data !== false) {
\r
2522 $data = trim($data);
\r
2523 $this->debug_message("NS: <<< $data");
\r
2529 * Write line to NS socket
\r
2531 * Also increments id
\r
2533 * @param string $data Line to write to socket
\r
2534 * @return mixed Bytes written or false on failure
\r
2536 private function ns_writeln($data) {
\r
2537 $result = @fwrite($this->NSfp, $data."\r\n");
\r
2538 if ($result !== false) {
\r
2539 $this->debug_message("NS: >>> $data");
\r
2546 * Write data to NS socket
\r
2548 * @param string $data Data to write to socket
\r
2549 * @return mixed Bytes written or false on failure
\r
2551 private function ns_writedata($data) {
\r
2552 $result = @fwrite($this->NSfp, $data);
\r
2553 if ($result !== false) {
\r
2554 $this->debug_message("NS: >>> $data");
\r
2560 * Read data of specified size from given SB socket
\r
2562 * @param resource $socket SB socket
\r
2563 * @param integer $size Size to read
\r
2564 * @return string Data read
\r
2566 private function sb_readdata($socket, $size) {
\r
2569 while (!feof($socket)) {
\r
2570 $buf = @fread($socket, $size - $count);
\r
2572 $count += strlen($buf);
\r
2573 if ($count >= $size) break;
\r
2575 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
2580 * Read line from given SB socket
\r
2582 * @param resource $socket SB Socket
\r
2583 * @return string Line read
\r
2585 private function sb_readln($socket) {
\r
2586 $data = @fgets($socket, 4096);
\r
2587 if ($data !== false) {
\r
2588 $data = trim($data);
\r
2589 $this->debug_message("SB: <<< $data");
\r
2595 * Write line to given SB socket
\r
2597 * Also increments id
\r
2599 * @param resource $socket SB socket
\r
2600 * @param integer $id Reference to SB id
\r
2601 * @param string $data Line to write
\r
2602 * @return mixed Bytes written or false on error
\r
2604 private function sb_writeln($socket, &$id, $data) {
\r
2605 $result = @fwrite($socket, $data."\r\n");
\r
2606 if ($result !== false) {
\r
2607 $this->debug_message("SB: >>> $data");
\r
2614 * Write data to given SB socket
\r
2616 * @param resource $socket SB socket
\r
2617 * @param $data Data to write to socket
\r
2618 * @return mixed Bytes written or false on error
\r
2620 private function sb_writedata($socket, $data) {
\r
2621 $result = @fwrite($socket, $data);
\r
2622 if ($result !== false) {
\r
2623 $this->debug_message("SB: >>> $data");
\r
2629 * Get all the sockets currently in use
\r
2631 * @return array Array of socket resources
\r
2633 public function getSockets() {
\r
2634 return array_merge(array($this->NSfp), $this->switchBoardSessionLookup);
\r
2638 * Checks socket for end of file
\r
2640 * @param resource $socket Socket to check
\r
2641 * @return boolean true if end of file (socket)
\r
2643 private static function socketcheck($socket){
\r
2644 $info = stream_get_meta_data($socket);
\r
2645 return $info['eof'];
\r
2649 * Key generation methods
\r
2652 private function derive_key($key, $magic) {
\r
2653 $hash1 = $this->mhash_sha1($magic, $key);
\r
2654 $hash2 = $this->mhash_sha1($hash1.$magic, $key);
\r
2655 $hash3 = $this->mhash_sha1($hash1, $key);
\r
2656 $hash4 = $this->mhash_sha1($hash3.$magic, $key);
\r
2657 return $hash2.substr($hash4, 0, 4);
\r
2660 private function generateLoginBLOB($key, $challenge) {
\r
2661 $key1 = base64_decode($key);
\r
2662 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
2663 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
2665 // get hash of challenge using key2
\r
2666 $hash = $this->mhash_sha1($challenge, $key2);
\r
2668 // get 8 bytes random data
\r
2669 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
2671 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
2673 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
2678 return base64_encode($blob);
\r
2682 * Generate challenge response
\r
2684 * @param string $code
\r
2685 * @return string challenge response code
\r
2687 private function getChallenge($code) {
\r
2689 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
2690 // Step 1: The MD5 Hash
\r
2691 $md5Hash = md5($code.self::PROD_KEY);
\r
2692 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2693 for ($i = 0; $i < 4; $i++) {
\r
2694 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
2695 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
2698 // Step 2: A new string
\r
2699 $chl_id = $code.self::PROD_ID;
\r
2700 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
2702 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
2703 for ($i = 0; $i < count($aID); $i++) {
\r
2704 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
2705 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
2708 // Step 3: The 64 bit key
\r
2709 $magic_num = 0x0E79A9C1;
\r
2710 $str7f = 0x7FFFFFFF;
\r
2713 for ($i = 0; $i < count($aID); $i += 2) {
\r
2715 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
2716 $temp = bcadd($temp, $high);
\r
2717 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
2718 $temp = bcmod($temp, $str7f);
\r
2720 $high = $aID[$i+1];
\r
2721 $high = bcmod(bcadd($high, $temp), $str7f);
\r
2722 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
2723 $high = bcmod($high, $str7f);
\r
2725 $low = bcadd(bcadd($low, $high), $temp);
\r
2728 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
2729 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
2731 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
2732 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
2733 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
2734 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
2735 // we need integer here
\r
2736 $high = 0+$new_high;
\r
2738 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
2739 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
2740 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
2741 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
2742 // we need integer here
\r
2743 $low = 0+$new_low;
\r
2745 // we just use 32 bits integer, don't need the key, just high/low
\r
2746 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
2748 // Step 4: Using the key
\r
2749 $md5Hash = md5($code.self::PROD_KEY);
\r
2750 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
2753 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
2754 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
2755 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
2756 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
2765 private function Array2SoapVar($Array, $ReturnSoapVarObj = true, $TypeName = null, $TypeNameSpace = null) {
\r
2766 $ArrayString = '';
\r
2767 foreach($Array as $Key => $Val) {
\r
2768 if ($Key{0} == ':') continue;
\r
2770 if (is_array($Val[':'])) {
\r
2771 foreach ($Val[':'] as $AttribName => $AttribVal)
\r
2772 $Attrib .= " $AttribName = '$AttribVal'";
\r
2774 if ($Key{0} == '!') {
\r
2775 //List Type Define
\r
2776 $Key = substr($Key,1);
\r
2777 foreach ($Val as $ListKey => $ListVal) {
\r
2778 if ($ListKey{0} == ':') continue;
\r
2779 if (is_array($ListVal)) $ListVal = $this->Array2SoapVar($ListVal, false);
\r
2780 elseif (is_bool($ListVal)) $ListVal = $ListVal ? 'true' : 'false';
\r
2781 $ArrayString .= "<$Key$Attrib>$ListVal</$Key>";
\r
2785 if (is_array($Val)) $Val = $this->Array2SoapVar($Val, false);
\r
2786 elseif (is_bool($Val)) $Val = $Val ? 'true' : 'false';
\r
2787 $ArrayString .= "<$Key$Attrib>$Val</$Key>";
\r
2789 if ($ReturnSoapVarObj) return new SoapVar($ArrayString, XSD_ANYXML, $TypeName, $TypeNameSpace);
\r
2790 return $ArrayString;
\r
2793 private function linetoArray($lines) {
\r
2794 $lines = str_replace("\r", '', $lines);
\r
2795 $lines = explode("\n", $lines);
\r
2796 foreach ($lines as $line) {
\r
2797 if (!isset($line{3})) continue;
\r
2798 list($Key, $Val) = explode(':', $line);
\r
2799 $Data[trim($Key)] = trim($Val);
\r
2805 * Get Passport ticket
\r
2807 * @param string $url URL string (Optional)
\r
2808 * @return mixed Array of tickets or false on failure
\r
2810 private function get_passport_ticket($url = '') {
\r
2811 $user = $this->user;
\r
2812 $password = htmlspecialchars($this->password);
\r
2815 $passport_url = self::PASSPORT_URL;
\r
2817 $passport_url = $url;
\r
2819 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
2820 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
2821 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
2822 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
2823 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
2824 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
2825 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
2826 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
2827 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
2829 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
2830 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
2831 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
2832 <ps:UIVersion>1</ps:UIVersion>
\r
2833 <ps:Cookies></ps:Cookies>
\r
2834 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
2837 <wsse:UsernameToken Id="user">
\r
2838 <wsse:Username>'.$user.'</wsse:Username>
\r
2839 <wsse:Password>'.$password.'</wsse:Password>
\r
2840 </wsse:UsernameToken>
\r
2844 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
2845 <wst:RequestSecurityToken Id="RST0">
\r
2846 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
2848 <wsa:EndpointReference>
\r
2849 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
2850 </wsa:EndpointReference>
\r
2852 </wst:RequestSecurityToken>
\r
2853 <wst:RequestSecurityToken Id="RST1">
\r
2854 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
2856 <wsa:EndpointReference>
\r
2857 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
2858 </wsa:EndpointReference>
\r
2860 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
2861 </wst:RequestSecurityToken>
\r
2862 <wst:RequestSecurityToken Id="RST2">
\r
2863 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
2865 <wsa:EndpointReference>
\r
2866 <wsa:Address>messenger.msn.com</wsa:Address>
\r
2867 </wsa:EndpointReference>
\r
2869 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
2870 </wst:RequestSecurityToken>
\r
2871 <wst:RequestSecurityToken Id="RST3">
\r
2872 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
2874 <wsa:EndpointReference>
\r
2875 <wsa:Address>contacts.msn.com</wsa:Address>
\r
2876 </wsa:EndpointReference>
\r
2878 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
2879 </wst:RequestSecurityToken>
\r
2880 <wst:RequestSecurityToken Id="RST4">
\r
2881 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
2883 <wsa:EndpointReference>
\r
2884 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
2885 </wsa:EndpointReference>
\r
2887 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
2888 </wst:RequestSecurityToken>
\r
2889 <wst:RequestSecurityToken Id="RST5">
\r
2890 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
2892 <wsa:EndpointReference>
\r
2893 <wsa:Address>spaces.live.com</wsa:Address>
\r
2894 </wsa:EndpointReference>
\r
2896 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
2897 </wst:RequestSecurityToken>
\r
2898 <wst:RequestSecurityToken Id="RST6">
\r
2899 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
2901 <wsa:EndpointReference>
\r
2902 <wsa:Address>storage.msn.com</wsa:Address>
\r
2903 </wsa:EndpointReference>
\r
2905 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
2906 </wst:RequestSecurityToken>
\r
2907 </ps:RequestMultipleSecurityTokens>
\r
2911 $this->debug_message("*** URL: $passport_url");
\r
2912 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2913 $curl = curl_init();
\r
2914 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
2915 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2916 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2917 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2918 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2919 curl_setopt($curl, CURLOPT_POST, 1);
\r
2920 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2921 $data = curl_exec($curl);
\r
2922 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2923 curl_close($curl);
\r
2924 $this->debug_message("*** Get Result:\n$data");
\r
2926 if ($http_code != 200) {
\r
2927 // sometimes, redirect to another URL
\r
2929 //<faultcode>psf:Redirect</faultcode>
\r
2930 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
2931 //<faultstring>Authentication Failure</faultstring>
\r
2932 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
2933 $this->debug_message("*** Could not get passport ticket! http code = $http_code");
\r
2936 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
2937 if (count($matches) == 0) {
\r
2938 $this->debug_message('*** Redirected, but could not get redirect URL!');
\r
2941 $redirect_url = $matches[1];
\r
2942 if ($redirect_url == $passport_url) {
\r
2943 $this->debug_message('*** Redirected, but to same URL!');
\r
2946 $this->debug_message("*** Redirected to $redirect_url");
\r
2947 return $this->get_passport_ticket($redirect_url);
\r
2950 // sometimes, redirect to another URL, also return 200
\r
2952 //<faultcode>psf:Redirect</faultcode>
\r
2953 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
2954 //<faultstring>Authentication Failure</faultstring>
\r
2955 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
2956 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
2957 if (count($matches) != 0) {
\r
2958 $redirect_url = $matches[1];
\r
2959 if ($redirect_url == $passport_url) {
\r
2960 $this->debug_message('*** Redirected, but to same URL!');
\r
2963 $this->debug_message("*** Redirected to $redirect_url");
\r
2964 return $this->get_passport_ticket($redirect_url);
\r
2968 // no Redurect faultcode or URL
\r
2969 // we should get the ticket here
\r
2971 // we need ticket and secret code
\r
2972 // RST1: messengerclear.live.com
\r
2973 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
2974 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
2975 // RST2: messenger.msn.com
\r
2976 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
2977 // RST3: contacts.msn.com
\r
2978 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
2979 // RST4: messengersecure.live.com
\r
2980 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
2981 // RST5: spaces.live.com
\r
2982 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
2983 // RST6: storage.msn.com
\r
2984 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
2986 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
2987 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
2988 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
2989 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
2990 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
2991 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
2992 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
2996 // no ticket found!
\r
2997 if (count($matches) == 0) {
\r
2998 $this->debug_message('*** Could not get passport ticket!');
\r
3002 //$this->debug_message(var_export($matches, true));
\r
3003 // matches[0]: all data
\r
3004 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
3005 // matches[2]: ...
\r
3006 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
3007 // matches[4]: ...
\r
3008 // matches[5]: RST2 (messenger.msn.com) ticket
\r
3009 // matches[6]: ...
\r
3010 // matches[7]: RST3 (contacts.msn.com) ticket
\r
3011 // matches[8]: ...
\r
3012 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
3013 // matches[10]: ...
\r
3014 // matches[11]: RST5 (spaces.live.com) ticket
\r
3015 // matches[12]: ...
\r
3016 // matches[13]: RST6 (storage.live.com) ticket
\r
3017 // matches[14]: ...
\r
3020 // ticket => $matches[1]
\r
3021 // secret => $matches[3]
\r
3022 // web_ticket => $matches[5]
\r
3023 // contact_ticket => $matches[7]
\r
3024 // oim_ticket => $matches[9]
\r
3025 // space_ticket => $matches[11]
\r
3026 // storage_ticket => $matches[13]
\r
3028 // yes, we get ticket
\r
3029 $aTickets = array(
\r
3030 'ticket' => html_entity_decode($matches[1]),
\r
3031 'secret' => html_entity_decode($matches[3]),
\r
3032 'web_ticket' => html_entity_decode($matches[5]),
\r
3033 'contact_ticket' => html_entity_decode($matches[7]),
\r
3034 'oim_ticket' => html_entity_decode($matches[9]),
\r
3035 'space_ticket' => html_entity_decode($matches[11]),
\r
3036 'storage_ticket' => html_entity_decode($matches[13])
\r
3038 $this->ticket = $aTickets;
\r
3039 //$this->debug_message(var_export($aTickets, true));
\r
3040 $ABAuthHeaderArray = array(
\r
3041 'ABAuthHeader' => array(
\r
3042 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
3043 'ManagedGroupRequest' => false,
\r
3044 'TicketToken' => htmlspecialchars($this->ticket['contact_ticket']),
\r
3047 $this->ABAuthHeader = new SoapHeader('http://www.msn.com/webservices/AddressBook', 'ABAuthHeader', $this->Array2SoapVar($ABAuthHeaderArray));
\r
3052 * Generate the data to send a message
\r
3054 * @param string $sMessage Message
\r
3055 * @param integer $network Network
\r
3056 * @return string Message data
\r
3058 private function getMessage($sMessage, $network = 1) {
\r
3059 $msg_header = "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\nX-MMS-IM-Format: FN=$this->font_fn; EF=$this->font_ef; CO=$this->font_co; CS=0; PF=22\r\n\r\n";
\r
3060 $msg_header_len = strlen($msg_header);
\r
3061 if ($network == 1)
\r
3062 $maxlen = self::MAX_MSN_MESSAGE_LEN - $msg_header_len;
\r
3064 $maxlen = self::MAX_YAHOO_MESSAGE_LEN - $msg_header_len;
\r
3065 $sMessage = str_replace("\r", '', $sMessage);
\r
3066 $msg = substr($sMessage, 0, $maxlen);
\r
3067 return $msg_header.$msg;
\r
3071 * Sleep for the given number of seconds
\r
3073 * @param integer $wait Number of seconds to sleep for
\r
3075 private function NSRetryWait($wait) {
\r
3076 $this->debug_message("*** Sleeping for $wait seconds before retrying");
\r
3081 * Sends a ping command
\r
3083 * Should be called about every 50 seconds
\r
3087 public function sendPing() {
\r
3089 $this->ns_writeln("PNG");
\r
3093 * Methods to add / call callbacks
\r
3097 * Calls User Handler
\r
3099 * Calls registered handler for a specific event.
\r
3101 * @param string $event Command (event) name (Rvous etc)
\r
3102 * @param array $data Data
\r
3103 * @see registerHandler
\r
3106 private function callHandler($event, $data = NULL) {
\r
3107 if (isset($this->myEventHandlers[$event])) {
\r
3108 if ($data !== NULL) {
\r
3109 call_user_func($this->myEventHandlers[$event], $data);
\r
3111 call_user_func($this->myEventHandlers[$event]);
\r
3117 * Registers a user handler
\r
3120 * IMIn, SessionReady, Pong, ConnectFailed, Reconnect,
\r
3121 * AddedToList, RemovedFromList, StatusChange
\r
3123 * @param string $event Event name
\r
3124 * @param string $handler User function to call
\r
3125 * @see callHandler
\r
3126 * @return boolean true if successful
\r
3128 public function registerHandler($event, $handler) {
\r
3129 if (is_callable($handler)) {
\r
3130 $this->myEventHandlers[$event] = $handler;
\r
3138 * Debugging methods
\r
3142 * Print message if debugging is enabled
\r
3144 * @param string $str Message to print
\r
3146 private function debug_message($str) {
\r
3147 if (!$this->debug) return;
\r
3152 * Dump binary data
\r
3154 * @param string $str Data string
\r
3155 * @return Binary data
\r
3157 private function dump_binary($str) {
\r
3161 $len = strlen($str);
\r
3162 for ($i = 0; $i < $len; $i++) {
\r
3163 if (($i % 16) == 0) {
\r
3164 if ($buf !== '') {
\r
3165 $buf .= "$h_str $a_str\n";
\r
3167 $buf .= sprintf("%04X:", $i);
\r
3171 $ch = ord($str[$i]);
\r
3175 $a_str .= chr($ch);
\r
3176 $h_str .= sprintf(" %02X", $ch);
\r
3178 if ($h_str !== '')
\r
3179 $buf .= "$h_str $a_str\n";
\r
3183 function mhash_sha1($data, $key)
\r
3185 if (extension_loaded("mhash"))
\r
3186 return mhash(MHASH_SHA1, $data, $key);
\r
3188 if (function_exists("hash_hmac"))
\r
3189 return hash_hmac('sha1', $data, $key, true);
\r
3191 // RFC 2104 HMAC implementation for php. Hacked by Lance Rushing
\r
3193 if (strlen($key) > $b)
\r
3194 $key = pack("H*", sha1($key));
\r
3195 $key = str_pad($key, $b, chr(0x00));
\r
3196 $ipad = str_pad("", $b, chr(0x36));
\r
3197 $opad = str_pad("", $b, chr(0x5c));
\r
3198 $k_ipad = $key ^ $ipad ;
\r
3199 $k_opad = $key ^ $opad;
\r
3201 $sha1_value = sha1($k_opad . pack("H*", sha1($k_ipad . $data)));
\r
3204 $str = join('',explode('\x', $sha1_value));
\r
3205 $len = strlen($str);
\r
3206 for ($i = 0; $i < $len; $i += 2)
\r
3207 $hash_data .= chr(hexdec(substr($str, $i, 2)));
\r
3208 return $hash_data;
\r