4 MSN class ver 2.0 by Tommy Wu, Ricky Su
\r
7 You can find MSN protocol from this site: http://msnpiki.msnfanatic.com/index.php/Main_Page
\r
9 This class support MSNP15 for send message. The PHP module needed:
\r
11 MSNP15: curl pcre mhash mcrypt bcmath
\r
13 Usually, this class will try to use MSNP15 if your system can support it, if your system can't support it,
\r
14 it will switch to use MSNP9. But if you use MSNP9, it won't support OIM (Offline Messages).
\r
20 private $protocol = 'MSNP15';
\r
21 private $passport_url = 'https://login.live.com/RST.srf';
\r
22 private $buildver = '8.1.0178';
\r
23 private $prod_key = 'PK}_A_0N_K%O?A9S';
\r
24 private $prod_id = 'PROD0114ES4Z%Q5W';
\r
25 private $login_method = 'SSO';
\r
26 private $oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';
\r
27 private $oim_send_soap = 'http://messenger.live.com/ws/2006/09/oim/Store2';
\r
31 private $password = '';
\r
32 private $NSfp=false;
\r
33 private $passport_policy = '';
\r
36 private $retry_wait;
\r
37 private $update_pending;
\r
38 private $PhotoStickerFile=false;
\r
39 private $Emotions=false;
\r
40 private $XFRReqTimeout=60;
\r
42 private $ping_wait=50;
\r
43 private $SBIdleTimeout=10;
\r
44 private $SBStreamTimeout=2;
\r
45 private $MsnObjArray=array();
\r
46 private $MsnObjMap=array();
\r
47 private $ABAuthHeader;
\r
51 private $server = 'messenger.hotmail.com';
\r
52 private $port = 1863;
\r
55 public $clientid = '';
\r
57 public $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
58 public $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
\r
59 public $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
60 public $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
\r
61 public $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
\r
62 public $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
\r
64 public $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
65 public $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';
\r
67 public $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
68 public $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';
\r
70 public $addcontact_url = 'https://contacts.msn.com/abservice/abservice.asmx';
\r
71 public $addcontact_soap = 'http://www.msn.com/webservices/AddressBook/ABContactAdd';
\r
73 public $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
\r
74 public $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
\r
79 public $authed = false;
\r
81 public $oim_try = 3;
\r
83 public $font_fn = 'Arial';
\r
84 public $font_co = '333333';
\r
85 public $font_ef = '';
\r
88 // the message length (include header) is limited (maybe since WLM 8.5 released)
\r
89 // for WLM: 1664 bytes
\r
90 // for YIM: 518 bytes
\r
91 public $max_msn_message_len = 1664;
\r
92 public $max_yahoo_message_len = 518;
\r
94 // Begin added for StatusNet
\r
96 private $aContactList = array();
\r
97 private $aADL = array();
\r
100 * Holds session information indexed by screenname if
\r
101 * session has no socket or socket if socket present
\r
105 private $switchBoardSessions = array();
\r
108 * Holds sockets indexed by screenname
\r
112 private $switchBoardSessionLookup = array();
\r
115 * Holds references to sessions waiting for XFR
\r
119 private $waitingForXFR = array();
\r
122 * Event Handler Functions
\r
124 private $myEventHandlers = array();
\r
126 // End added for StatusNet
\r
129 * Constructor method
\r
131 * @param array $Configs Array of configuration options
\r
132 * 'user' - Username
\r
133 * 'password' - Password
\r
134 * 'alias' - Bot nickname
\r
135 * 'psm' - Bot personal status message
\r
136 * 'retry_wait' - Time to wait before trying to reconnect
\r
137 * 'update_pending' - Whether to update pending contacts
\r
138 * 'PhotoSticker' - Photo file to use (?)
\r
139 * 'debug' - Enable/Disable debugging mode
\r
140 * @param integer $timeout Connection timeout
\r
141 * @param integer $client_id Client id (hexadecimal)
\r
144 public function __construct ($Configs=array(), $timeout = 15, $client_id = 0x7000800C) {
\r
145 $this->user = $Configs['user'];
\r
146 $this->password = $Configs['password'];
\r
147 $this->alias = isset($Configs['alias']) ? $Configs['alias'] : '';
\r
148 $this->psm = isset($Configs['psm']) ? $Configs['psm'] : '';
\r
149 $this->retry_wait = isset($Configs['retry_wait']) ? $Configs['retry_wait'] : 30;
\r
150 $this->update_pending = isset($Configs['update_pending']) ? $Configs['update_pending'] : true;
\r
151 $this->PhotoStickerFile=isset($Configs['PhotoSticker']) ? $Configs['PhotoSticker'] : false;
\r
153 if ($this->Emotions = isset($Configs['Emotions']) ? $Configs['Emotions']:false) {
\r
154 foreach($this->Emotions as $EmotionFilePath)
\r
155 $this->MsnObj($EmotionFilePath,$Type=2);
\r
157 $this->debug = isset($Configs['debug']) ? $Configs['debug'] : false;
\r
158 $this->timeout = $timeout;
\r
161 if (!function_exists('curl_init')) throw new Exception("curl module not found!\n");
\r
162 if (!function_exists('preg_match')) throw new Exception("pcre module not found!\n");
\r
163 if (!function_exists('mhash')) throw new Exception("mhash module not found!\n");
\r
164 if (!function_exists('mcrypt_cbc')) throw new Exception("mcrypt module not found!\n");
\r
165 if (!function_exists('bcmod')) throw new Exception("bcmath module not found!\n");
\r
168 http://msnpiki.msnfanatic.com/index.php/Client_ID
\r
170 normal MSN 8.1 clientid is:
\r
171 01110110 01001100 11000000 00101100
\r
174 we just use following:
\r
175 * 0x04: Your client can send/receive Ink (GIF format)
\r
176 * 0x08: Your client can send/recieve Ink (ISF format)
\r
177 * 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
178 * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
\r
181 $this->clientid = $client_id;
\r
182 $this->ABService=new SoapClient(realpath(dirname(__FILE__)).'/soap/msnab_sharingservice.wsdl',array('trace' => 1));
\r
185 private function Array2SoapVar($Array, $ReturnSoapVarObj = true, $TypeName = null, $TypeNameSpace = null) {
\r
187 foreach($Array as $Key => $Val) {
\r
188 if ($Key{0} == ':') continue;
\r
190 if (is_array($Val[':'])) {
\r
191 foreach ($Val[':'] as $AttribName => $AttribVal)
\r
192 $Attrib .= " $AttribName='$AttribVal'";
\r
194 if ($Key{0} == '!') {
\r
196 $Key = substr($Key,1);
\r
197 foreach ($Val as $ListKey => $ListVal) {
\r
198 if ($ListKey{0} == ':') continue;
\r
199 if (is_array($ListVal)) $ListVal = $this->Array2SoapVar($ListVal, false);
\r
200 elseif (is_bool($ListVal)) $ListVal = $ListVal ? 'true' : 'false';
\r
201 $ArrayString .= "<$Key$Attrib>$ListVal</$Key>";
\r
205 if (is_array($Val)) $Val = $this->Array2SoapVar($Val, false);
\r
206 elseif (is_bool($Val)) $Val = $Val ? 'true' : 'false';
\r
207 $ArrayString .= "<$Key$Attrib>$Val</$Key>";
\r
209 if ($ReturnSoapVarObj) return new SoapVar($ArrayString, XSD_ANYXML, $TypeName, $TypeNameSpace);
\r
210 return $ArrayString;
\r
214 * Get Passport ticket
\r
216 * @param string $url URL string (Optional)
\r
217 * @return mixed Array of tickets or false on failure
\r
219 private function get_passport_ticket($url = '') {
\r
220 $user = $this->user;
\r
221 $password = htmlspecialchars($this->password);
\r
224 $passport_url = $this->passport_url;
\r
226 $passport_url = $url;
\r
228 $XML = '<?xml version="1.0" encoding="UTF-8"?>
\r
229 <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
\r
230 xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
\r
231 xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
\r
232 xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
\r
233 xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
\r
234 xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
\r
235 xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
\r
236 xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
\r
238 <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
\r
239 <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
\r
240 <ps:BinaryVersion>4</ps:BinaryVersion>
\r
241 <ps:UIVersion>1</ps:UIVersion>
\r
242 <ps:Cookies></ps:Cookies>
\r
243 <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
\r
246 <wsse:UsernameToken Id="user">
\r
247 <wsse:Username>'.$user.'</wsse:Username>
\r
248 <wsse:Password>'.$password.'</wsse:Password>
\r
249 </wsse:UsernameToken>
\r
253 <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
\r
254 <wst:RequestSecurityToken Id="RST0">
\r
255 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
257 <wsa:EndpointReference>
\r
258 <wsa:Address>http://Passport.NET/tb</wsa:Address>
\r
259 </wsa:EndpointReference>
\r
261 </wst:RequestSecurityToken>
\r
262 <wst:RequestSecurityToken Id="RST1">
\r
263 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
265 <wsa:EndpointReference>
\r
266 <wsa:Address>messengerclear.live.com</wsa:Address>
\r
267 </wsa:EndpointReference>
\r
269 <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
\r
270 </wst:RequestSecurityToken>
\r
271 <wst:RequestSecurityToken Id="RST2">
\r
272 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
274 <wsa:EndpointReference>
\r
275 <wsa:Address>messenger.msn.com</wsa:Address>
\r
276 </wsa:EndpointReference>
\r
278 <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
\r
279 </wst:RequestSecurityToken>
\r
280 <wst:RequestSecurityToken Id="RST3">
\r
281 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
283 <wsa:EndpointReference>
\r
284 <wsa:Address>contacts.msn.com</wsa:Address>
\r
285 </wsa:EndpointReference>
\r
287 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
288 </wst:RequestSecurityToken>
\r
289 <wst:RequestSecurityToken Id="RST4">
\r
290 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
292 <wsa:EndpointReference>
\r
293 <wsa:Address>messengersecure.live.com</wsa:Address>
\r
294 </wsa:EndpointReference>
\r
296 <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
\r
297 </wst:RequestSecurityToken>
\r
298 <wst:RequestSecurityToken Id="RST5">
\r
299 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
301 <wsa:EndpointReference>
\r
302 <wsa:Address>spaces.live.com</wsa:Address>
\r
303 </wsa:EndpointReference>
\r
305 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
306 </wst:RequestSecurityToken>
\r
307 <wst:RequestSecurityToken Id="RST6">
\r
308 <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
\r
310 <wsa:EndpointReference>
\r
311 <wsa:Address>storage.msn.com</wsa:Address>
\r
312 </wsa:EndpointReference>
\r
314 <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
\r
315 </wst:RequestSecurityToken>
\r
316 </ps:RequestMultipleSecurityTokens>
\r
320 //$this->debug_message("*** URL: $passport_url");
\r
321 //$this->debug_message("*** Sending SOAP:\n$XML");
\r
322 $curl = curl_init();
\r
323 curl_setopt($curl, CURLOPT_URL, $passport_url);
\r
324 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
325 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
326 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
327 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
328 curl_setopt($curl, CURLOPT_POST, 1);
\r
329 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
330 $data = curl_exec($curl);
\r
331 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
333 //$this->debug_message("*** Get Result:\n$data");
\r
335 if ($http_code != 200) {
\r
336 // sometimes, redirect to another URL
\r
338 //<faultcode>psf:Redirect</faultcode>
\r
339 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
340 //<faultstring>Authentication Failure</faultstring>
\r
341 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
\r
342 $this->debug_message("*** Could not get passport ticket! http code = $http_code");
\r
345 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
346 if (count($matches) == 0) {
\r
347 $this->debug_message('*** Redirected, but could not get redirect URL!');
\r
350 $redirect_url = $matches[1];
\r
351 if ($redirect_url == $passport_url) {
\r
352 $this->debug_message('*** Redirected, but to same URL!');
\r
355 $this->debug_message("*** Redirected to $redirect_url");
\r
356 return $this->get_passport_ticket($redirect_url);
\r
359 // sometimes, redirect to another URL, also return 200
\r
361 //<faultcode>psf:Redirect</faultcode>
\r
362 //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
\r
363 //<faultstring>Authentication Failure</faultstring>
\r
364 if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
\r
365 preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
\r
366 if (count($matches) != 0) {
\r
367 $redirect_url = $matches[1];
\r
368 if ($redirect_url == $passport_url) {
\r
369 $this->debug_message('*** Redirected, but to same URL!');
\r
372 $this->debug_message("*** Redirected to $redirect_url");
\r
373 return $this->get_passport_ticket($redirect_url);
\r
377 // no Redurect faultcode or URL
\r
378 // we should get the ticket here
\r
380 // we need ticket and secret code
\r
381 // RST1: messengerclear.live.com
\r
382 // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
\r
383 // <wst:BinarySecret>binary secret</wst:BinarySecret>
\r
384 // RST2: messenger.msn.com
\r
385 // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
\r
386 // RST3: contacts.msn.com
\r
387 // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
\r
388 // RST4: messengersecure.live.com
\r
389 // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
\r
390 // RST5: spaces.live.com
\r
391 // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
\r
392 // RST6: storage.msn.com
\r
393 // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
\r
395 "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
396 "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
\r
397 "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
398 "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
399 "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
400 "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
401 "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
\r
405 // no ticket found!
\r
406 if (count($matches) == 0) {
\r
407 $this->debug_message('*** Could not get passport ticket!');
\r
411 //$this->debug_message(var_export($matches, true));
\r
412 // matches[0]: all data
\r
413 // matches[1]: RST1 (messengerclear.live.com) ticket
\r
415 // matches[3]: RST1 (messengerclear.live.com) binary secret
\r
417 // matches[5]: RST2 (messenger.msn.com) ticket
\r
419 // matches[7]: RST3 (contacts.msn.com) ticket
\r
421 // matches[9]: RST4 (messengersecure.live.com) ticket
\r
422 // matches[10]: ...
\r
423 // matches[11]: RST5 (spaces.live.com) ticket
\r
424 // matches[12]: ...
\r
425 // matches[13]: RST6 (storage.live.com) ticket
\r
426 // matches[14]: ...
\r
429 // ticket => $matches[1]
\r
430 // secret => $matches[3]
\r
431 // web_ticket => $matches[5]
\r
432 // contact_ticket => $matches[7]
\r
433 // oim_ticket => $matches[9]
\r
434 // space_ticket => $matches[11]
\r
435 // storage_ticket => $matches[13]
\r
437 // yes, we get ticket
\r
439 'ticket' => html_entity_decode($matches[1]),
\r
440 'secret' => html_entity_decode($matches[3]),
\r
441 'web_ticket' => html_entity_decode($matches[5]),
\r
442 'contact_ticket' => html_entity_decode($matches[7]),
\r
443 'oim_ticket' => html_entity_decode($matches[9]),
\r
444 'space_ticket' => html_entity_decode($matches[11]),
\r
445 'storage_ticket' => html_entity_decode($matches[13])
\r
447 $this->ticket = $aTickets;
\r
448 //$this->debug_message(var_export($aTickets, true));
\r
449 $ABAuthHeaderArray = array(
\r
450 'ABAuthHeader' => array(
\r
451 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
452 'ManagedGroupRequest' => false,
\r
453 'TicketToken' => htmlspecialchars($this->ticket['contact_ticket']),
\r
456 $this->ABAuthHeader = new SoapHeader('http://www.msn.com/webservices/AddressBook', 'ABAuthHeader', $this->Array2SoapVar($ABAuthHeaderArray));
\r
461 * Fetch contact list
\r
463 * @return boolean true on success
\r
465 private function UpdateContacts() {
\r
466 $ABApplicationHeaderArray = array(
\r
467 'ABApplicationHeader' => array(
\r
468 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
469 'ApplicationId' => 'CFE80F9D-180F-4399-82AB-413F33A1FA11',
\r
470 'IsMigration' => false,
\r
471 'PartnerScenario' => 'ContactSave'
\r
475 $ABApplicationHeader = new SoapHeader('http://www.msn.com/webservices/AddressBook', 'ABApplicationHeader', $this->Array2SoapVar($ABApplicationHeaderArray));
\r
476 $ABFindAllArray = array(
\r
477 'ABFindAll' => array(
\r
478 ':' => array('xmlns'=>'http://www.msn.com/webservices/AddressBook'),
\r
479 'abId' => '00000000-0000-0000-0000-000000000000',
\r
480 'abView' => 'Full',
\r
481 'lastChange' => '0001-01-01T00:00:00.0000000-08:00',
\r
484 $ABFindAll = new SoapParam($this->Array2SoapVar($ABFindAllArray), 'ABFindAll');
\r
485 $this->ABService->__setSoapHeaders(array($ABApplicationHeader, $this->ABAuthHeader));
\r
486 $this->Contacts = array();
\r
488 $this->debug_message('*** Updating Contacts...');
\r
489 $Result = $this->ABService->ABFindAll($ABFindAll);
\r
490 $this->debug_message("*** Result:\n".print_r($Result, true)."\n".$this->ABService->__getLastResponse());
\r
491 foreach($Result->ABFindAllResult->contacts->Contact as $Contact)
\r
492 $this->Contacts[$Contact->contactInfo->passportName] = $Contact;
\r
493 } catch(Exception $e) {
\r
494 $this->debug_message("*** Update Contacts Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
503 * @param string $email
\r
504 * @param integer $network
\r
505 * @param string $display
\r
506 * @param boolean $sendADL
\r
507 * @return boolean true on success
\r
509 private function addContact($email, $network, $display = '', $sendADL = false) {
\r
510 if ($network != 1) return true;
\r
511 if (isset($this->Contacts[$email])) return true;
\r
513 $ABContactAddArray = array(
\r
514 'ABContactAdd' => array(
\r
515 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
516 'abId' => '00000000-0000-0000-0000-000000000000',
\r
517 'contacts' => array(
\r
518 'Contact' => array(
\r
519 ':' => array('xmlns' => 'http://www.msn.com/webservices/AddressBook'),
\r
520 'contactInfo' => array(
\r
521 'contactType' => 'LivePending',
\r
522 'passportName' => $email,
\r
523 'isMessengerUser' => true,
\r
524 'MessengerMemberInfo' => array(
\r
525 'DisplayName' => $email
\r
530 'options' => array(
\r
531 'EnableAllowListManagement' => true
\r
535 $ABContactAdd = new SoapParam($this->Array2SoapVar($ABContactAddArray), 'ABContactAdd');
\r
537 $this->debug_message("*** Adding Contact $email...");
\r
538 $this->ABService->ABContactAdd($ABContactAdd);
\r
539 } catch(Exception $e) {
\r
540 $this->debug_message("*** Add Contact Error \nRequest:".$this->ABService->__getLastRequest()."\nError:".$e->getMessage());
\r
543 if ($sendADL && !feof($this->NSfp)) {
\r
544 @list($u_name, $u_domain) = @explode('@', $email);
\r
545 foreach (array('1', '2') as $l) {
\r
546 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="'.$l.'" t="'.$network.'" /></d></ml>';
\r
547 $len = strlen($str);
\r
548 // NS: >>> ADL {id} {size}
\r
549 //TODO introduce error checking
\r
550 $this->ns_writeln("ADL $this->id $len");
\r
551 $this->ns_writedata($str);
\r
554 $this->UpdateContacts();
\r
559 * Remove contact from list
\r
561 * @param integer $memberID
\r
562 * @param string $email
\r
563 * @param integer $network
\r
564 * @param string $list
\r
566 function delMemberFromList($memberID, $email, $network, $list) {
\r
567 if ($network != 1 && $network != 32) return true;
\r
568 if ($memberID === false) return true;
\r
570 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
572 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
573 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
574 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
575 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
576 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
578 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
579 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
580 <IsMigration>false</IsMigration>
\r
581 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
582 </ABApplicationHeader>
\r
583 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
584 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
585 <TicketToken>'.$ticket.'</TicketToken>
\r
589 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
592 <Type>Messenger</Type>
\r
593 <ForeignId></ForeignId>
\r
597 <MemberRole>'.$list.'</MemberRole>
\r
599 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
600 <Type>Passport</Type>
\r
601 <MembershipId>'.$memberID.'</MembershipId>
\r
602 <State>Accepted</State>
\r
611 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
612 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
613 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
614 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
615 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
617 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
618 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
619 <IsMigration>false</IsMigration>
\r
620 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
621 </ABApplicationHeader>
\r
622 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
623 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
624 <TicketToken>'.$ticket.'</TicketToken>
\r
628 <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
631 <Type>Messenger</Type>
\r
632 <ForeignId></ForeignId>
\r
636 <MemberRole>'.$list.'</MemberRole>
\r
638 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
640 <MembershipId>'.$memberID.'</MembershipId>
\r
641 <State>Accepted</State>
\r
650 $header_array = array(
\r
651 'SOAPAction: '.$this->delmember_soap,
\r
652 'Content-Type: text/xml; charset=utf-8',
\r
653 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
656 //$this->debug_message("*** URL: $this->delmember_url");
\r
657 //$this->debug_message("*** Sending SOAP:\n$XML");
\r
658 $curl = curl_init();
\r
659 curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
\r
660 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
661 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
662 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
663 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
664 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
665 curl_setopt($curl, CURLOPT_POST, 1);
\r
666 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
667 $data = curl_exec($curl);
\r
668 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
670 //$this->debug_message("*** Get Result:\n$data");
\r
672 if ($http_code != 200) {
\r
673 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
674 if (count($matches) == 0) {
\r
675 $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list");
\r
678 $faultcode = trim($matches[1]);
\r
679 $faultstring = trim($matches[2]);
\r
680 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
\r
681 $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list, error code: $faultcode, $faultstring");
\r
684 $this->debug_message("*** Could not delete member (network: $network) $email ($memberID) from $list list, not present in list");
\r
687 $this->debug_message("*** Member successfully deleted (network: $network) $email ($memberID) from $list list");
\r
692 * Add contact to list
\r
694 * @param string $email
\r
695 * @param integer $network
\r
696 * @param string $list
\r
698 function addMemberToList($email, $network, $list) {
\r
699 if ($network != 1 && $network != 32) return true;
\r
700 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
704 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
705 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
706 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
707 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
708 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
710 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
711 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
712 <IsMigration>false</IsMigration>
\r
713 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
714 </ABApplicationHeader>
\r
715 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
716 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
717 <TicketToken>'.$ticket.'</TicketToken>
\r
721 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
724 <Type>Messenger</Type>
\r
725 <ForeignId></ForeignId>
\r
729 <MemberRole>'.$list.'</MemberRole>
\r
731 <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
732 <Type>Passport</Type>
\r
733 <State>Accepted</State>
\r
734 <PassportName>'.$user.'</PassportName>
\r
743 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
744 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
745 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
746 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
747 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
749 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
750 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
751 <IsMigration>false</IsMigration>
\r
752 <PartnerScenario>ContactMsgrAPI</PartnerScenario>
\r
753 </ABApplicationHeader>
\r
754 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
755 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
756 <TicketToken>'.$ticket.'</TicketToken>
\r
760 <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
\r
763 <Type>Messenger</Type>
\r
764 <ForeignId></ForeignId>
\r
768 <MemberRole>'.$list.'</MemberRole>
\r
770 <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
\r
772 <State>Accepted</State>
\r
773 <Email>'.$user.'</Email>
\r
776 <Name>MSN.IM.BuddyType</Name>
\r
777 <Value>32:YAHOO</Value>
\r
787 $header_array = array(
\r
788 'SOAPAction: '.$this->addmember_soap,
\r
789 'Content-Type: text/xml; charset=utf-8',
\r
790 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
793 //$this->debug_message("*** URL: $this->addmember_url");
\r
794 //$this->debug_message("*** Sending SOAP:\n$XML");
\r
795 $curl = curl_init();
\r
796 curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
\r
797 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
798 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
799 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
800 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
801 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
802 curl_setopt($curl, CURLOPT_POST, 1);
\r
803 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
804 $data = curl_exec($curl);
\r
805 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
807 //$this->debug_message("*** Get Result:\n$data");
\r
809 if ($http_code != 200) {
\r
810 preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
\r
811 if (count($matches) == 0) {
\r
812 $this->debug_message("*** Could not add member (network: $network) $email to $list list");
\r
815 $faultcode = trim($matches[1]);
\r
816 $faultstring = trim($matches[2]);
\r
817 if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
\r
818 $this->debug_message("*** Could not add member (network: $network) $email to $list list, error code: $faultcode, $faultstring");
\r
821 $this->debug_message("*** Could not add member (network: $network) $email to $list list, already present");
\r
824 $this->debug_message("*** Member successfully added (network: $network) $email to $list list");
\r
829 * Get membership lists
\r
831 * @param mixed $returnData Membership list or false on failure
\r
833 function getMembershipList($returnData = false) {
\r
834 $ticket = htmlspecialchars($this->ticket['contact_ticket']);
\r
835 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
836 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
\r
837 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
838 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
839 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
\r
841 <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
842 <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
\r
843 <IsMigration>false</IsMigration>
\r
844 <PartnerScenario>Initial</PartnerScenario>
\r
845 </ABApplicationHeader>
\r
846 <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
\r
847 <ManagedGroupRequest>false</ManagedGroupRequest>
\r
848 <TicketToken>'.$ticket.'</TicketToken>
\r
852 <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
\r
855 <ServiceType>Messenger</ServiceType>
\r
856 <ServiceType>Invitation</ServiceType>
\r
857 <ServiceType>SocialNetwork</ServiceType>
\r
858 <ServiceType>Space</ServiceType>
\r
859 <ServiceType>Profile</ServiceType>
\r
865 $header_array = array(
\r
866 'SOAPAction: '.$this->membership_soap,
\r
867 'Content-Type: text/xml; charset=utf-8',
\r
868 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
\r
870 //$this->debug_message("*** URL: $this->membership_url");
\r
871 //$this->debug_message("*** Sending SOAP:\n$XML");
\r
872 $curl = curl_init();
\r
873 curl_setopt($curl, CURLOPT_URL, $this->membership_url);
\r
874 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
875 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
876 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
877 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
878 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
879 curl_setopt($curl, CURLOPT_POST, 1);
\r
880 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
881 $data = curl_exec($curl);
\r
882 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
884 //$this->debug_message("*** Get Result:\n$data");
\r
885 if ($http_code != 200) return false;
\r
887 $aMemberships = array();
\r
889 //$this->debug_message("search p = $p");
\r
890 $start = strpos($p, '<Membership>');
\r
891 $end = strpos($p, '</Membership>');
\r
892 if ($start === false || $end === false || $start > $end) break;
\r
893 //$this->debug_message("start = $start, end = $end");
\r
895 $sMembership = substr($p, $start, $end - $start);
\r
896 $aMemberships[] = $sMembership;
\r
897 //$this->debug_message("add sMembership = $sMembership");
\r
898 $p = substr($p, $end);
\r
900 //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
\r
902 $aContactList = array();
\r
903 foreach ($aMemberships as $sMembership) {
\r
904 //$this->debug_message("sMembership = $sMembership");
\r
905 if (isset($matches)) unset($matches);
\r
906 preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
\r
907 if (count($matches) == 0) continue;
\r
908 $sMemberRole = $matches[1];
\r
909 //$this->debug_message("MemberRole = $sMemberRole");
\r
910 if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
\r
912 if (isset($aMembers)) unset($aMembers);
\r
913 $aMembers = array();
\r
915 //$this->debug_message("search p = $p");
\r
916 $start = strpos($p, '<Member xsi:type="');
\r
917 $end = strpos($p, '</Member>');
\r
918 if ($start === false || $end === false || $start > $end) break;
\r
919 //$this->debug_message("start = $start, end = $end");
\r
921 $sMember = substr($p, $start, $end - $start);
\r
922 $aMembers[] = $sMember;
\r
923 //$this->debug_message("add sMember = $sMember");
\r
924 $p = substr($p, $end);
\r
926 //$this->debug_message("aMembers = ".var_export($aMembers, true));
\r
927 foreach ($aMembers as $sMember) {
\r
928 //$this->debug_message("sMember = $sMember");
\r
929 if (isset($matches)) unset($matches);
\r
930 preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
\r
931 if (count($matches) == 0) continue;
\r
932 $sMemberType = $matches[1];
\r
933 //$this->debug_message("MemberType = $sMemberType");
\r
935 preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
\r
936 if (count($matches) == 0) continue;
\r
938 if ($sMemberType == 'PassportMember') {
\r
939 if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
\r
941 preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
\r
943 else if ($sMemberType == 'EmailMember') {
\r
944 if (strpos($sMember, '<Type>Email</Type>') === false) continue;
\r
945 // Value is 32: or 32:YAHOO
\r
946 preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
\r
947 if (count($matches) == 0) continue;
\r
948 if ($matches[1] != 32) continue;
\r
950 preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
\r
952 if ($network == -1) continue;
\r
953 if (count($matches) > 0) {
\r
954 $email = $matches[1];
\r
955 @list($u_name, $u_domain) = @explode('@', $email);
\r
956 if ($u_domain == NULL) continue;
\r
957 $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
\r
958 $this->debug_message("*** Adding new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
\r
962 return $aContactList;
\r
966 * Connect to the NS server
\r
968 * @param String $user Username
\r
969 * @param String $password Password
\r
970 * @param String $redirect_server Redirect server
\r
971 * @param Integer $redirect_port Redirect port
\r
972 * @return Boolean Returns true if successful
\r
974 private function connect($user, $password, $redirect_server = '', $redirect_port = 1863) {
\r
976 if ($redirect_server === '') {
\r
977 $this->NSfp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout);
\r
978 if (!$this->NSfp) {
\r
979 $this->error = "!!! Could not connect to $this->server:$this->port, error => $errno, $errstr";
\r
984 $this->NSfp = @fsockopen($redirect_server, $redirect_port, $errno, $errstr, $this->timeout);
\r
985 if (!$this->NSfp) {
\r
986 $this->error = "!!! Could not connect to $redirect_server:$redirect_port, error => $errno, $errstr";
\r
990 $this->authed = false;
\r
992 // NS: >> VER {id} MSNP9 CVR0
\r
994 // NS: >>> VER {id} MSNP15 CVR0
\r
995 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
997 $start_tm = time();
\r
998 while (!self::socketcheck($this->NSfp)) {
\r
999 $data = $this->ns_readln();
\r
1001 if ($data === false) {
\r
1004 $this->ns_writeln("OUT");
\r
1005 @fclose($this->NSfp);
\r
1006 $this->error = 'Timeout, maybe protocol changed!';
\r
1010 $code = substr($data, 0, 3);
\r
1011 $start_tm = time();
\r
1016 // NS: <<< VER {id} MSNP9 CVR0
\r
1017 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
\r
1019 // NS: <<< VER {id} MSNP15 CVR0
\r
1020 // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
\r
1021 $this->ns_writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
\r
1026 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
1027 // NS: >>> USR {id} TWN I {user}
\r
1029 // NS: <<< CVR {id} {ver_list} {download_serve} ....
\r
1030 // NS: >>> USR {id} SSO I {user}
\r
1031 $this->ns_writeln("USR $this->id $this->login_method I $user");
\r
1035 // already login for passport site, finish the login process now.
\r
1036 // NS: <<< USR {id} OK {user} {verify} 0
\r
1037 if ($this->authed) return true;
\r
1038 // max. 16 digits for password
\r
1039 if (strlen($password) > 16)
\r
1040 $password = substr($password, 0, 16);
\r
1042 $this->user = $user;
\r
1043 $this->password = $password;
\r
1044 // NS: <<< USR {id} SSO S {policy} {nonce}
\r
1045 @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
\r
1047 $this->passport_policy = $policy;
\r
1048 $aTickets = $this->get_passport_ticket();
\r
1049 if (!$aTickets || !is_array($aTickets)) {
\r
1052 $this->ns_writeln("OUT");
\r
1053 @fclose($this->NSfp);
\r
1054 $this->error = 'Passport authenticated fail!';
\r
1058 $ticket = $aTickets['ticket'];
\r
1059 $secret = $aTickets['secret'];
\r
1060 $this->ticket = $aTickets;
\r
1061 $login_code = $this->generateLoginBLOB($secret, $nonce);
\r
1063 // NS: >>> USR {id} SSO S {ticket} {login_code}
\r
1064 $this->ns_writeln("USR $this->id $this->login_method S $ticket $login_code");
\r
1065 $this->authed = true;
\r
1069 // main login server will redirect to anther NS after USR command
\r
1071 // NS: <<< XFR {id} NS {server} 0 {server}
\r
1073 // NS: <<< XFR {id} NS {server} U D
\r
1074 @list(/* XFR */, /* id */, $Type, $server, /* ... */) = @explode(' ', $data);
\r
1075 if ($Type!='NS') break;
\r
1076 @list($ip, $port) = @explode(':', $server);
\r
1077 // this connection will close after XFR
\r
1078 @fclose($this->NSfp);
\r
1080 $this->NSfp = @fsockopen($ip, $port, $errno, $errstr, $this->timeout);
\r
1081 if (!$this->NSfp) {
\r
1082 $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
\r
1087 // NS: >> VER {id} MSNP9 CVR0
\r
1089 // NS: >>> VER {id} MSNP15 CVR0
\r
1090 $this->ns_writeln("VER $this->id $this->protocol CVR0");
\r
1094 // return some policy data after 'USR {id} SSO I {user}' command
\r
1095 // NS: <<< GCF 0 {size}
\r
1096 @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
\r
1097 // we don't need the data, just read it and drop
\r
1098 if (is_numeric($size) && $size > 0)
\r
1099 $this->ns_readdata($size);
\r
1103 // we'll quit if got any error
\r
1104 if (is_numeric($code)) {
\r
1107 $this->ns_writeln("OUT");
\r
1108 @fclose($this->NSfp);
\r
1109 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
1112 // unknown response from server, just ignore it
\r
1116 // never goto here
\r
1120 * Sign onto the NS server and retrieve the address book
\r
1124 public function signon() {
\r
1125 /* FIXME Don't implement the signon as a loop or we could hang
\r
1126 * the queue handler! */
\r
1127 $this->debug_message('*** Trying to connect to MSN network');
\r
1131 if (!$this->connect($this->user, $this->password)) {
\r
1132 $this->signonFailure("!!! Could not connect to server: $this->error");
\r
1136 // Update contacts
\r
1137 if ($this->UpdateContacts() === false) continue;
\r
1139 // Get membership lists
\r
1140 if (($this->aContactList = $this->getMembershipList()) === false) {
\r
1141 $this->signonFailure('!!! Get membership list failed');
\r
1145 if ($this->update_pending) {
\r
1146 if (is_array($this->aContactList)) {
\r
1147 $pending = 'Pending';
\r
1148 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1149 foreach ($aUserList as $u_name => $aNetworks) {
\r
1150 foreach ($aNetworks as $network => $aData) {
\r
1151 if (isset($aData[$pending])) {
\r
1154 foreach (array('Allow', 'Reverse') as $list) {
\r
1155 if (isset($aData[$list]))
\r
1158 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1159 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1165 $id = $aData[$pending];
\r
1166 // we can delete it from pending now
\r
1167 if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
\r
1168 unset($this->aContactList[$u_domain][$u_name][$network][$pending]);
\r
1173 foreach (array('Allow', 'Reverse') as $list) {
\r
1174 if (!isset($aData[$list])) {
\r
1175 if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
\r
1176 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1188 if (is_array($this->aContactList)) {
\r
1189 foreach ($this->aContactList as $u_domain => $aUserList) {
\r
1190 $str = '<d n="'.$u_domain.'">';
\r
1191 $len += strlen($str);
\r
1192 if ($len > 7400) {
\r
1193 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1196 $len = strlen($str);
\r
1199 foreach ($aUserList as $u_name => $aNetworks) {
\r
1200 foreach ($aNetworks as $network => $status) {
\r
1201 $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
\r
1202 $len += strlen($str);
\r
1203 // max: 7500, but <ml l="1"></d></ml> is 19,
\r
1205 if ($len > 7475) {
\r
1207 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1209 $sList = '<d n="'.$u_domain.'">'.$str;
\r
1210 $len = strlen($sList);
\r
1219 $this->aADL[$n] = '<ml l="1">'.$sList.'</ml>';
\r
1220 // NS: >>> BLP {id} BL
\r
1221 $this->ns_writeln("BLP $this->id BL");
\r
1222 foreach ($this->aADL as $str) {
\r
1223 $len = strlen($str);
\r
1224 // NS: >>> ADL {id} {size}
\r
1225 $this->ns_writeln("ADL $this->id $len");
\r
1226 $this->ns_writedata($str);
\r
1228 // NS: >>> PRP {id} MFN name
\r
1229 if ($this->alias == '') $this->alias = $user;
\r
1230 $aliasname = rawurlencode($this->alias);
\r
1231 $this->ns_writeln("PRP $this->id MFN $aliasname");
\r
1233 //$MsnObj=$this->PhotoStckObj();
\r
1234 // NS: >>> CHG {id} {status} {clientid} {msnobj}
\r
1235 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
1236 if ($this->PhotoStickerFile !== false)
\r
1237 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
1238 // NS: >>> UUX {id} length
\r
1239 $str = '<Data><PSM>'.htmlspecialchars($this->psm).'</PSM><CurrentMedia></CurrentMedia><MachineGuid></MachineGuid></Data>';
\r
1240 $len = strlen($str);
\r
1241 $this->ns_writeln("UUX $this->id $len");
\r
1242 $this->ns_writedata($str);
\r
1243 if (!self::socketcheck($this->NSfp)) {
\r
1244 $this->debug_message('*** Connected, waiting for commands');
\r
1247 $this->NSRetryWait($this->retry_wait);
\r
1253 * Called if there is an error during signon
\r
1255 * @param string $message Error message to log
\r
1257 private function signonFailure($message) {
\r
1258 $this->debug_message($message);
\r
1259 $this->callHandler('ConnectFailed');
\r
1260 $this->NSRetryWait($this->retry_wait);
\r
1263 function derive_key($key, $magic) {
\r
1264 $hash1 = mhash(MHASH_SHA1, $magic, $key);
\r
1265 $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
\r
1266 $hash3 = mhash(MHASH_SHA1, $hash1, $key);
\r
1267 $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
\r
1268 return $hash2.substr($hash4, 0, 4);
\r
1271 function generateLoginBLOB($key, $challenge) {
\r
1272 $key1 = base64_decode($key);
\r
1273 $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
\r
1274 $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
\r
1276 // get hash of challenge using key2
\r
1277 $hash = mhash(MHASH_SHA1, $challenge, $key2);
\r
1279 // get 8 bytes random data
\r
1280 $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
\r
1282 $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
\r
1284 $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
\r
1289 return base64_encode($blob);
\r
1293 * Get OIM mail data
\r
1295 * @return string mail data or false on failure
\r
1297 function getOIM_maildata() {
\r
1298 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1299 if (count($matches) == 0) {
\r
1300 $this->debug_message('*** No web ticket?');
\r
1303 $t = htmlspecialchars($matches[1]);
\r
1304 $p = htmlspecialchars($matches[2]);
\r
1305 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1306 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1307 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1308 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1310 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1316 <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
\r
1318 </soap:Envelope>';
\r
1320 $header_array = array(
\r
1321 'SOAPAction: '.$this->oim_maildata_soap,
\r
1322 'Content-Type: text/xml; charset=utf-8',
\r
1323 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1326 //$this->debug_message("*** URL: $this->oim_maildata_url");
\r
1327 //$this->debug_message("*** Sending SOAP:\n$XML");
\r
1328 $curl = curl_init();
\r
1329 curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
\r
1330 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1331 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1332 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1333 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1334 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1335 curl_setopt($curl, CURLOPT_POST, 1);
\r
1336 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1337 $data = curl_exec($curl);
\r
1338 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1339 curl_close($curl);
\r
1340 //$this->debug_message("*** Get Result:\n$data");
\r
1342 if ($http_code != 200) {
\r
1343 $this->debug_message("*** Could not get OIM maildata! http code: $http_code");
\r
1347 // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
\r
1348 preg_match('#<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
\r
1349 if (count($matches) == 0) {
\r
1350 $this->debug_message('*** Could not get OIM maildata');
\r
1353 return $matches[2];
\r
1357 * Fetch OIM message with given id
\r
1359 * @param string $msgid
\r
1360 * @return string Message or false on failure
\r
1362 function getOIM_message($msgid) {
\r
1363 preg_match('#t=(.*)&p=(.*)#', $this->ticket['web_ticket'], $matches);
\r
1364 if (count($matches) == 0) {
\r
1365 $this->debug_message('*** No web ticket?');
\r
1368 $t = htmlspecialchars($matches[1]);
\r
1369 $p = htmlspecialchars($matches[2]);
\r
1372 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1373 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1374 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1375 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1377 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1383 <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1384 <messageId>'.$msgid.'</messageId>
\r
1385 <alsoMarkAsRead>false</alsoMarkAsRead>
\r
1388 </soap:Envelope>';
\r
1390 $header_array = array(
\r
1391 'SOAPAction: '.$this->oim_read_soap,
\r
1392 'Content-Type: text/xml; charset=utf-8',
\r
1393 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1396 //$this->debug_message("*** URL: $this->oim_read_url");
\r
1397 //$this->debug_message("*** Sending SOAP:\n$XML");
\r
1398 $curl = curl_init();
\r
1399 curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
\r
1400 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1401 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1402 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1403 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1404 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1405 curl_setopt($curl, CURLOPT_POST, 1);
\r
1406 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1407 $data = curl_exec($curl);
\r
1408 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1409 curl_close($curl);
\r
1410 //$this->debug_message("*** Get Result:\n$data");
\r
1412 if ($http_code != 200) {
\r
1413 $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
\r
1417 // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
\r
1419 $start = strpos($data, '<GetMessageResult>');
\r
1420 $end = strpos($data, '</GetMessageResult>');
\r
1421 if ($start === false || $end === false || $start > $end) {
\r
1422 $this->debug_message("*** Can't get OIM: $msgid");
\r
1425 $lines = substr($data, $start + 18, $end - $start);
\r
1426 $aLines = @explode("\n", $lines);
\r
1430 foreach ($aLines as $line) {
\r
1431 $line = rtrim($line);
\r
1433 if ($line === '') {
\r
1439 // stop at empty lines
\r
1440 if ($line === '') break;
\r
1443 $sMsg = base64_decode($sOIM);
\r
1444 //$this->debug_message("*** we get OIM ($msgid): $sMsg");
\r
1447 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
1448 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
1449 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
1450 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
1452 <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1458 <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
\r
1460 <messageId>'.$msgid.'</messageId>
\r
1464 </soap:Envelope>';
\r
1466 $header_array = array(
\r
1467 'SOAPAction: '.$this->oim_del_soap,
\r
1468 'Content-Type: text/xml; charset=utf-8',
\r
1469 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
1472 //$this->debug_message("*** URL: $this->oim_del_url");
\r
1473 //$this->debug_message("*** Sending SOAP:\n$XML");
\r
1474 $curl = curl_init();
\r
1475 curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
\r
1476 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
1477 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1478 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
1479 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
1480 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1481 curl_setopt($curl, CURLOPT_POST, 1);
\r
1482 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
1483 $data = curl_exec($curl);
\r
1484 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
1485 curl_close($curl);
\r
1486 //$this->debug_message("*** Get Result:\n$data");
\r
1488 if ($http_code != 200)
\r
1489 $this->debug_message("*** Could not delete OIM: $msgid, http code = $http_code");
\r
1491 $this->debug_message("*** OIM ($msgid) deleted");
\r
1496 * Log out and close the NS connection
\r
1500 private function NSLogout() {
\r
1501 if (is_resource($this->NSfp) && !feof($this->NSfp)) {
\r
1504 $this->ns_writeln("OUT");
\r
1505 fclose($this->NSfp);
\r
1506 $this->NSfp = false;
\r
1507 $this->debug_message("*** Logged out");
\r
1512 * Sleep for the given number of seconds
\r
1514 * @param integer $wait Number of seconds to sleep for
\r
1516 private function NSRetryWait($wait) {
\r
1517 $this->debug_message("*** Sleeping for $wait seconds before retrying");
\r
1522 * Generate challenge response
\r
1524 * @param string $code
\r
1525 * @return string challenge response code
\r
1527 function getChallenge($code) {
\r
1529 // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
\r
1530 // Step 1: The MD5 Hash
\r
1531 $md5Hash = md5($code.$this->prod_key);
\r
1532 $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
1533 for ($i = 0; $i < 4; $i++) {
\r
1534 $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
\r
1535 $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
\r
1538 // Step 2: A new string
\r
1539 $chl_id = $code.$this->prod_id;
\r
1540 $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
\r
1542 $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
\r
1543 for ($i = 0; $i < count($aID); $i++) {
\r
1544 $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
\r
1545 $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
\r
1548 // Step 3: The 64 bit key
\r
1549 $magic_num = 0x0E79A9C1;
\r
1550 $str7f = 0x7FFFFFFF;
\r
1553 for ($i = 0; $i < count($aID); $i += 2) {
\r
1555 $temp = bcmod(bcmul($magic_num, $temp), $str7f);
\r
1556 $temp = bcadd($temp, $high);
\r
1557 $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
\r
1558 $temp = bcmod($temp, $str7f);
\r
1560 $high = $aID[$i+1];
\r
1561 $high = bcmod(bcadd($high, $temp), $str7f);
\r
1562 $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
\r
1563 $high = bcmod($high, $str7f);
\r
1565 $low = bcadd(bcadd($low, $high), $temp);
\r
1568 $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
\r
1569 $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
\r
1571 $new_high = bcmul($high & 0xFF, 0x1000000);
\r
1572 $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
\r
1573 $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
\r
1574 $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
\r
1575 // we need integer here
\r
1576 $high = 0+$new_high;
\r
1578 $new_low = bcmul($low & 0xFF, 0x1000000);
\r
1579 $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
\r
1580 $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
\r
1581 $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
\r
1582 // we need integer here
\r
1583 $low = 0+$new_low;
\r
1585 // we just use 32 bits integer, don't need the key, just high/low
\r
1586 // $key = bcadd(bcmul($high, 0x100000000), $low);
\r
1588 // Step 4: Using the key
\r
1589 $md5Hash = md5($code.$this->prod_key);
\r
1590 $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
\r
1593 $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
\r
1594 $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
\r
1595 $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
\r
1596 $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
\r
1602 * Generate the data to send a message
\r
1604 * @param string $sMessage Message
\r
1605 * @param integer $network Network
\r
1607 private function getMessage($sMessage, $network = 1) {
\r
1608 $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
1609 $msg_header_len = strlen($msg_header);
\r
1610 if ($network == 1)
\r
1611 $maxlen = $this->max_msn_message_len - $msg_header_len;
\r
1613 $maxlen = $this->max_yahoo_message_len - $msg_header_len;
\r
1614 $sMessage = str_replace("\r", '', $sMessage);
\r
1615 $msg = substr($sMessage, 0, $maxlen);
\r
1616 return $msg_header.$msg;
\r
1619 // read data for specified size
\r
1620 private function ns_readdata($size) {
\r
1623 while (!feof($this->NSfp)) {
\r
1624 $buf = @fread($this->NSfp, $size - $count);
\r
1626 $count += strlen($buf);
\r
1627 if ($count >= $size) break;
\r
1629 $this->debug_message("NS: data ($size/$count) <<<\n$data");
\r
1634 private function ns_readln() {
\r
1635 $data = @fgets($this->NSfp, 4096);
\r
1636 if ($data !== false) {
\r
1637 $data = trim($data);
\r
1638 $this->debug_message("NS: <<< $data");
\r
1643 // write to server, append \r\n, also increase id
\r
1644 private function ns_writeln($data) {
\r
1645 @fwrite($this->NSfp, $data."\r\n");
\r
1646 $this->debug_message("NS: >>> $data");
\r
1651 // write data to server
\r
1652 private function ns_writedata($data) {
\r
1653 @fwrite($this->NSfp, $data);
\r
1654 $this->debug_message("NS: >>> $data");
\r
1658 // read data for specified size for SB
\r
1659 private function sb_readdata($socket, $size) {
\r
1662 while (!feof($socket)) {
\r
1663 $buf = @fread($socket, $size - $count);
\r
1665 $count += strlen($buf);
\r
1666 if ($count >= $size) break;
\r
1668 $this->debug_message("SB: data ($size/$count) <<<\n$data");
\r
1672 // read one line for SB
\r
1673 private function sb_readln($socket) {
\r
1674 $data = @fgets($socket, 4096);
\r
1675 if ($data !== false) {
\r
1676 $data = trim($data);
\r
1677 $this->debug_message("SB: <<< $data");
\r
1682 // write to server for SB, append \r\n, also increase id
\r
1683 // switchboard server only accept \r\n, it will lost connection if just \n only
\r
1684 private function sb_writeln($socket, &$id, $data) {
\r
1685 @fwrite($socket, $data."\r\n");
\r
1686 $this->debug_message("SB: >>> $data");
\r
1691 // write data to server
\r
1692 private function sb_writedata($socket, $data) {
\r
1693 @fwrite($socket, $data);
\r
1694 $this->debug_message("SB: >>> $data");
\r
1698 // show debug information
\r
1699 function debug_message($str) {
\r
1700 if (!$this->debug) return;
\r
1701 if ($this->debug===STDOUT) echo $str."\n";
\r
1702 /*$fname=MSN_CLASS_LOG_DIR.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.debug';
\r
1703 $fp = fopen($fname, 'at');
\r
1705 fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.posix_getpid().'] '.$str."\n");
\r
1709 // still show debug information, if we can't open log_file
\r
1714 function dump_binary($str) {
\r
1718 $len = strlen($str);
\r
1719 for ($i = 0; $i < $len; $i++) {
\r
1720 if (($i % 16) == 0) {
\r
1721 if ($buf !== '') {
\r
1722 $buf .= "$h_str $a_str\n";
\r
1724 $buf .= sprintf("%04X:", $i);
\r
1728 $ch = ord($str[$i]);
\r
1732 $a_str .= chr($ch);
\r
1733 $h_str .= sprintf(" %02X", $ch);
\r
1735 if ($h_str !== '')
\r
1736 $buf .= "$h_str $a_str\n";
\r
1742 * @param $FilePath 圖檔路徑
\r
1743 * @param $Type 檔案類型 3=>大頭貼,2表情圖案
\r
1746 private function MsnObj($FilePath,$Type=3)
\r
1748 if (!($FileSize=filesize($FilePath))) return '';
\r
1749 $Location = md5($FilePath);
\r
1750 $Friendly = md5($FilePath.$Type);
\r
1751 if (isset($this->MsnObjMap[$Location])) return $this->MsnObjMap[$Location];
\r
1752 $sha1d = base64_encode(sha1(file_get_contents($FilePath), true));
\r
1753 $sha1c = base64_encode(sha1("Creator".$this->user."Size$FileSize"."Type$Type"."Location$Location"."Friendly".$Friendly."SHA1D$sha1d",true));
\r
1754 $this->MsnObjArray[$Location] = $FilePath;
\r
1755 $MsnObj = '<msnobj Creator="'.$this->user.'" Size="'.$FileSize.'" Type="'.$Type.'" Location="'.$Location.'" Friendly="'.$Friendly.'" SHA1D="'.$sha1d.'" SHA1C="'.$sha1c.'"/>';
\r
1756 $this->MsnObjMap[$Location] = $MsnObj;
\r
1757 $this->debug_message("*** p2p: addMsnObj $FilePath::$MsnObj\n");
\r
1761 private function linetoArray($lines) {
\r
1762 $lines = str_replace("\r", '', $lines);
\r
1763 $lines = explode("\n", $lines);
\r
1764 foreach ($lines as $line) {
\r
1765 if (!isset($line{3})) continue;
\r
1766 list($Key,$Val) = explode(':', $line);
\r
1767 $Data[trim($Key)] = trim($Val);
\r
1772 private function GetPictureFilePath($Context) {
\r
1773 $MsnObj = base64_decode($Context);
\r
1774 if (preg_match('/location="(.*?)"/i', $MsnObj, $Match))
\r
1775 $location = $Match[1];
\r
1776 $this->debug_message("*** p2p: PictureFile[$location] ::All".print_r($this->MsnObjArray,true)."\n");
\r
1777 if ($location && isset($this->MsnObjArray[$location]))
\r
1778 return $this->MsnObjArray[$location];
\r
1782 private function GetMsnObjDefine($Message) {
\r
1783 $DefineString = '';
\r
1784 if (is_array($this->Emotions))
\r
1785 foreach ($this->Emotions as $Pattern => $FilePath) {
\r
1786 if (strpos($Message, $Pattern)!==false)
\r
1787 $DefineString .= "$Pattern\t".$this->MsnObj($FilePath, 2)."\t";
\r
1789 return $DefineString;
\r
1793 * Read and handle incoming command from NS
\r
1797 private function nsReceive() {
\r
1798 // Sign in again if not signed in or socket failed
\r
1799 if (!is_resource($this->NSfp) || self::socketcheck($this->NSfp)) {
\r
1800 $this->callHandler('Reconnect');
\r
1801 $this->NSRetryWait($this->retry_wait);
\r
1806 $data = $this->ns_readln();
\r
1807 if ($data === false) {
\r
1808 // There was no data / an error when reading from the socket so reconnect
\r
1809 $this->callHandler('Reconnect');
\r
1810 $this->NSRetryWait($this->retry_wait);
\r
1814 switch (substr($data, 0, 3)) {
\r
1816 // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
\r
1817 // NS: <<< SBS 0 null
\r
1822 // NS: <<< RFS ???
\r
1823 // refresh ADL, so we re-send it again
\r
1824 if (is_array($this->aADL)) {
\r
1825 foreach ($this->aADL as $str) {
\r
1826 $len = strlen($str);
\r
1827 // NS: >>> ADL {id} {size}
\r
1828 $this->ns_writeln("ADL $this->id $len");
\r
1829 $this->ns_writedata($str);
\r
1835 // NS: <<< LST {email} {alias} 11 0
\r
1836 @list(/* LST */, $email, /* alias */,) = @explode(' ', $data);
\r
1837 @list($u_name, $u_domain) = @explode('@', $email);
\r
1838 if (!isset($this->aContactList[$u_domain][$u_name][1])) {
\r
1839 $this->aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
\r
1840 $this->debug_message("*** Added to contact list: $u_name@$u_domain");
\r
1845 // randomly, we get ADL command, someone add us to their contact list for MSNP15
\r
1846 // NS: <<< ADL 0 {size}
\r
1847 @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
\r
1848 if (is_numeric($size) && $size > 0) {
\r
1849 $data = $this->ns_readdata($size);
\r
1850 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1851 if (is_array($matches) && count($matches) > 0) {
\r
1852 $u_domain = $matches[1];
\r
1853 $u_name = $matches[2];
\r
1854 $network = $matches[4];
\r
1855 if (isset($this->aContactList[$u_domain][$u_name][$network]))
\r
1856 $this->debug_message("*** Someone (network: $network) added us to their list (but already in our list): $u_name@$u_domain");
\r
1858 $re_login = false;
\r
1860 foreach (array('Allow', 'Reverse') as $list) {
\r
1861 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1863 $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");
\r
1866 $aTickets = $this->get_passport_ticket();
\r
1867 if (!$aTickets || !is_array($aTickets)) {
\r
1868 // failed to login? ignore it
\r
1869 $this->debug_message("*** Could not re-login, something wrong here");
\r
1870 $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");
\r
1874 $this->ticket = $aTickets;
\r
1875 $this->debug_message("**** Got new ticket, trying again");
\r
1876 if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
\r
1877 $this->debug_message("*** Could not add $u_name@$u_domain (network: $network) to $list list");
\r
1881 $this->aContactList[$u_domain][$u_name][$network][$list] = false;
\r
1884 $this->debug_message("*** Someone (network: $network) added us to their list: $u_name@$u_domain");
\r
1886 $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
\r
1887 $len = strlen($str);
\r
1889 $this->callHandler('AddedToList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));
\r
1892 $this->debug_message("*** Someone added us to their list: $data");
\r
1897 // randomly, we get RML command, someome remove us to their contact list for MSNP15
\r
1898 // NS: <<< RML 0 {size}
\r
1899 @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
\r
1900 if (is_numeric($size) && $size > 0) {
\r
1901 $data = $this->ns_readdata($size);
\r
1902 preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
\r
1903 if (is_array($matches) && count($matches) > 0) {
\r
1904 $u_domain = $matches[1];
\r
1905 $u_name = $matches[2];
\r
1906 $network = $matches[4];
\r
1907 if (isset($this->aContactList[$u_domain][$u_name][$network])) {
\r
1908 $aData = $this->aContactList[$u_domain][$u_name][$network];
\r
1910 foreach ($aData as $list => $id)
\r
1911 $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
\r
1913 unset($this->aContactList[$u_domain][$u_name][$network]);
\r
1914 $this->debug_message("*** Someone (network: $network) removed us from their list: $u_name@$u_domain");
\r
1917 $this->debug_message("*** Someone (network: $network) removed us from their list (but not in our list): $u_name@$u_domain");
\r
1919 $this->callHandler('RemovedFromList', array('screenname' => $u_name.'@'.$u_domain, 'network' => $network));
\r
1922 $this->debug_message("*** Someone removed us from their list: $data");
\r
1927 // randomly, we get MSG notification from server
\r
1928 // NS: <<< MSG Hotmail Hotmail {size}
\r
1929 @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
\r
1930 if (is_numeric($size) && $size > 0) {
\r
1931 $data = $this->ns_readdata($size);
\r
1932 $aLines = @explode("\n", $data);
\r
1936 foreach ($aLines as $line) {
\r
1937 $line = rtrim($line);
\r
1939 if ($line === '') {
\r
1943 if (strncasecmp($line, 'Content-Type:', 13) == 0) {
\r
1944 if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false && strpos($line, 'text/x-msmsgsoimnotification') === false) {
\r
1945 // we just need text/x-msmsgsinitialmdatanotification
\r
1946 // or text/x-msmsgsoimnotification
\r
1953 if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
\r
1954 $maildata = trim(substr($line, 10));
\r
1959 $this->debug_message("*** Ignoring MSG for: $line");
\r
1962 if ($maildata == '') {
\r
1963 $this->debug_message("*** Ignoring MSG not for OIM");
\r
1966 $re_login = false;
\r
1967 if (strcasecmp($maildata, 'too-large') == 0) {
\r
1968 $this->debug_message("*** Large mail-data, need to get the data via SOAP");
\r
1969 $maildata = $this->getOIM_maildata();
\r
1970 if ($maildata === false) {
\r
1971 $this->debug_message("*** Could not get mail-data via SOAP");
\r
1973 // maybe we need to re-login again
\r
1974 $aTickets = $this->get_passport_ticket();
\r
1975 if (!$aTickets || !is_array($aTickets)) {
\r
1976 // failed to login? ignore it
\r
1977 $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");
\r
1981 $this->ticket = $aTickets;
\r
1982 $this->debug_message("*** Got new ticket, trying again");
\r
1983 $maildata = $this->getOIM_maildata();
\r
1984 if ($maildata === false) {
\r
1985 $this->debug_message("*** Could not get mail-data via SOAP, and re-login already attempted, ignoring this OIM");
\r
1990 // could be a lots of <M>...</M>, so we can't use preg_match here
\r
1994 $start = strpos($p, '<M>');
\r
1995 $end = strpos($p, '</M>');
\r
1996 if ($start === false || $end === false || $start > $end) break;
\r
1998 $sOIM = substr($p, $start, $end - $start);
\r
2000 $p = substr($p, $end);
\r
2002 if (count($aOIMs) == 0) {
\r
2003 $this->debug_message("*** Ignoring empty OIM");
\r
2006 foreach ($aOIMs as $maildata) {
\r
2007 // T: 11 for MSN, 13 for Yahoo
\r
2008 // S: 6 for MSN, 7 for Yahoo
\r
2009 // RT: the datetime received by server
\r
2010 // RS: already read or not
\r
2011 // SZ: size of message
\r
2014 // F: always 00000000-0000-0000-0000-000000000009
\r
2015 // N: sender alias
\r
2016 preg_match('#<T>(.*)</T>#', $maildata, $matches);
\r
2017 if (count($matches) == 0) {
\r
2018 $this->debug_message("*** Ignoring OIM maildata without <T>type</T>");
\r
2021 $oim_type = $matches[1];
\r
2022 if ($oim_type = 13)
\r
2026 preg_match('#<E>(.*)</E>#', $maildata, $matches);
\r
2027 if (count($matches) == 0) {
\r
2028 $this->debug_message("*** Ignoring OIM maildata without <E>sender</E>");
\r
2031 $oim_sender = $matches[1];
\r
2032 preg_match('#<I>(.*)</I>#', $maildata, $matches);
\r
2033 if (count($matches) == 0) {
\r
2034 $this->debug_message("*** Ignoring OIM maildata without <I>msgid</I>");
\r
2037 $oim_msgid = $matches[1];
\r
2038 preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
\r
2039 $oim_size = (count($matches) == 0) ? 0 : $matches[1];
\r
2040 preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
\r
2041 $oim_time = (count($matches) == 0) ? 0 : $matches[1];
\r
2042 $this->debug_message("*** OIM received from $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
\r
2043 $sMsg = $this->getOIM_message($oim_msgid);
\r
2044 if ($sMsg === false) {
\r
2045 $this->debug_message("*** Could not get OIM, msgid = $oim_msgid");
\r
2047 $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");
\r
2050 $aTickets = $this->get_passport_ticket();
\r
2051 if (!$aTickets || !is_array($aTickets)) {
\r
2052 // failed to login? ignore it
\r
2053 $this->debug_message("*** Could not re-login, something wrong here, ignoring this OIM");
\r
2057 $this->ticket = $aTickets;
\r
2058 $this->debug_message("*** get new ticket, try it again");
\r
2059 $sMsg = $this->getOIM_message($oim_msgid);
\r
2060 if ($sMsg === false) {
\r
2061 $this->debug_message("*** Could not get OIM via SOAP, and re-login already attempted, ignoring this OIM");
\r
2065 $this->debug_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
\r
2066 $this->callHandler('IMin', array('sender' => $oim_sender, 'message' => $sMsg, 'network' => $network, 'offline' => true));
\r
2072 // randomly, we get UBM, this is the message from other network, like Yahoo!
\r
2073 // NS: <<< UBM {email} $network $type {size}
\r
2074 @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
\r
2075 if (is_numeric($size) && $size > 0) {
\r
2076 $data = $this->ns_readdata($size);
\r
2077 $aLines = @explode("\n", $data);
\r
2081 foreach ($aLines as $line) {
\r
2082 $line = rtrim($line);
\r
2084 if ($line === '') {
\r
2088 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2094 $aSubLines = @explode("\r", $line);
\r
2095 foreach ($aSubLines as $str) {
\r
2102 $this->debug_message("*** Ignoring message from $from_email: $line");
\r
2105 $this->debug_message("*** MSG from $from_email (network: $network): $sMsg");
\r
2106 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
2111 // randomly, we get UBX notification from server
\r
2112 // NS: <<< UBX email {network} {size}
\r
2113 @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
\r
2114 // we don't need the notification data, so just ignore it
\r
2115 if (is_numeric($size) && $size > 0)
\r
2116 $this->ns_readdata($size);
\r
2120 // randomly, we'll get challenge from server
\r
2121 // NS: <<< CHL 0 {code}
\r
2122 @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
\r
2123 $fingerprint = $this->getChallenge($chl_code);
\r
2124 // NS: >>> QRY {id} {product_id} 32
\r
2125 // NS: >>> fingerprint
\r
2126 $this->ns_writeln("QRY $this->id $this->prod_id 32");
\r
2127 $this->ns_writedata($fingerprint);
\r
2128 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2129 if ($this->PhotoStickerFile !== false)
\r
2130 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2133 // NS: <<< CHG {id} {status} {code}
\r
2135 // change our status to online first
\r
2139 // sometimes, NS will redirect to another NS
\r
2141 // NS: <<< XFR {id} NS {server} 0 {server}
\r
2143 // NS: <<< XFR {id} NS {server} U D
\r
2144 // for normal switchboard XFR
\r
2145 // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
\r
2146 @list(/* XFR */, /* {id} */, $server_type, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
\r
2147 @list($ip, $port) = @explode(':', $server);
\r
2148 if ($server_type != 'SB') {
\r
2150 // this connection will close after XFR
\r
2151 $this->NSLogout();
\r
2155 $this->debug_message("NS: <<< XFR SB");
\r
2156 $user = array_shift($this->waitingForXFR);
\r
2157 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $User, $Message);
\r
2159 $bSBresult = $this->switchboard_control($ip, $port, $cki_code, $aMSNUsers[$nCurrentUser], $sMessage);
\r
2160 if ($bSBresult === false) {
\r
2161 // error for switchboard
\r
2162 $this->debug_message("!!! error for sending message to ".$aMSNUsers[$nCurrentUser]);
\r
2163 $aOfflineUsers[] = $aMSNUsers[$nCurrentUser];
\r
2167 // NS: <<< QNG {time}
\r
2168 @list(/* QNG */, $ping_wait) = @explode(' ', $data);
\r
2169 $this->callHandler('Pong', $ping_wait);
\r
2173 if ($this->PhotoStickerFile !== false)
\r
2174 $this->ns_writeln("CHG $this->id NLN $this->clientid ".rawurlencode($this->MsnObj($this->PhotoStickerFile)));
\r
2176 $this->ns_writeln("CHG $this->id NLN $this->clientid");
\r
2177 // someone is trying to talk to us
\r
2178 // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
\r
2179 $this->debug_message("NS: <<< RNG $data");
\r
2180 @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
\r
2181 @list($sb_ip, $sb_port) = @explode(':', $server);
\r
2182 $this->debug_message("*** RING from $email, $sb_ip:$sb_port");
\r
2183 $this->addContact($email, 1, $email, true);
\r
2184 $this->connectToSBSession('Passive', $sb_ip, $sb_port, $email, array('sid' => $sid, 'ticket' => $ticket));
\r
2187 // force logout from NS
\r
2188 // NS: <<< OUT xxx
\r
2189 $this->debug_message("*** LOGOUT from NS");
\r
2190 return $this->NsLogout();
\r
2193 $code = substr($data,0,3);
\r
2194 if (is_numeric($code)) {
\r
2195 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2196 $this->debug_message("*** NS: $this->error");
\r
2198 return $this->NsLogout();
\r
2206 * Read and handle incoming command/message from
\r
2207 * a switchboard session socket
\r
2209 private function sbReceive($socket) {
\r
2210 $intsocket = (int) $socket;
\r
2211 $session = &$this->switchBoardSessions[$intsocket];
\r
2213 if (feof($socket)) {
\r
2214 // Unset session lookup value
\r
2215 unset($this->switchBoardSessionLookup[$session['to']]);
\r
2217 // Unset session itself
\r
2218 unset($this->switchBoardSessions[$intsocket]);
\r
2222 $id = &$session['id'];
\r
2224 $data = $this->sb_readln($socket);
\r
2225 $code = substr($data, 0, 3);
\r
2228 // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
\r
2229 @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
\r
2230 $this->debug_message("*** $email joined session");
\r
2231 $session['joined'] = true;
\r
2234 $this->debug_message("*** Quit for BYE");
\r
2235 $this->endSBSession();
\r
2238 // SB: <<< USR {id} OK {user} {alias}
\r
2239 // we don't need the data, just ignore it
\r
2240 // request user to join this switchboard
\r
2241 // SB: >>> CAL {id} {user}
\r
2242 $this->sb_writeln($socket, $id, "CAL $this->id $user");
\r
2245 // SB: <<< CAL {id} RINGING {?}
\r
2246 // we don't need this, just ignore, and wait for other response
\r
2250 // SB: <<< JOI {user} {alias} {clientid?}
\r
2251 // someone join us
\r
2252 // we don't need the data, just ignore it
\r
2253 // no more user here
\r
2254 $session['joined'] = true;
\r
2257 // SB: <<< MSG {email} {alias} {len}
\r
2258 @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
\r
2259 $len = trim($len);
\r
2260 $data = $this->sb_readdata($socket, $len);
\r
2261 $aLines = @explode("\n", $data);
\r
2266 foreach ($aLines as $line) {
\r
2267 $line = rtrim($line);
\r
2269 if ($line === '') {
\r
2273 if (strncasecmp($line, 'TypingUser:', 11) == 0) {
\r
2274 // typing notification, just ignore
\r
2278 if (strncasecmp($line, 'Chunk:', 6) == 0) {
\r
2279 // we don't handle any split message, just ignore
\r
2283 if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
\r
2284 // p2p message, ignore it, but we need to send acknowledgement for it...
\r
2286 $p = strstr($data, "\n\n");
\r
2288 if ($p === false) {
\r
2289 $p = strstr($data, "\r\n\r\n");
\r
2291 $sMsg = substr($p, 4);
\r
2294 $sMsg = substr($p, 2);
\r
2297 if (strncasecmp($line, 'Content-Type: application/x-', 28) == 0) {
\r
2298 // ignore all application/x-... message
\r
2300 // application/x-ms-ink => ink message
\r
2304 if (strncasecmp($line, 'Content-Type: text/x-', 21) == 0) {
\r
2305 // ignore all text/x-... message
\r
2307 // text/x-msnmsgr-datacast => nudge, voice clip....
\r
2308 // text/x-mms-animemoticon => customized animemotion word
\r
2319 $this->debug_message("*** Ignoring SB data from $from_email: $line");
\r
2323 // we will ignore any p2p message after sending acknowledgement
\r
2325 $len = strlen($sMsg);
\r
2326 $this->debug_message("*** p2p message from $from_email, size $len");
\r
2327 // header = 48 bytes
\r
2328 // content >= 0 bytes
\r
2329 // footer = 4 bytes
\r
2330 // so it need to >= 52 bytes
\r
2331 /*if ($len < 52) {
\r
2332 $this->debug_message("*** p2p: size error, less than 52!");
\r
2335 $aDwords = @unpack("V12dword", $sMsg);
\r
2336 if (!is_array($aDwords)) {
\r
2337 $this->debug_message("*** p2p: header unpack error!");
\r
2340 $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
\r
2341 $hdr_SessionID = $aDwords['dword1'];
\r
2342 $hdr_Identifier = $aDwords['dword2'];
\r
2343 $hdr_DataOffsetLow = $aDwords['dword3'];
\r
2344 $hdr_DataOffsetHigh = $aDwords['dword4'];
\r
2345 $hdr_TotalDataSizeLow = $aDwords['dword5'];
\r
2346 $hdr_TotalDataSizeHigh = $aDwords['dword6'];
\r
2347 $hdr_MessageLength = $aDwords['dword7'];
\r
2348 $hdr_Flag = $aDwords['dword8'];
\r
2349 $hdr_AckID = $aDwords['dword9'];
\r
2350 $hdr_AckUID = $aDwords['dword10'];
\r
2351 $hdr_AckSizeLow = $aDwords['dword11'];
\r
2352 $hdr_AckSizeHigh = $aDwords['dword12'];
\r
2353 $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
\r
2354 $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
\r
2355 $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
\r
2356 $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
\r
2357 $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
\r
2358 $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
\r
2359 $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
\r
2360 $this->debug_message("*** p2p: header Flag = $hdr_Flag");
\r
2361 $this->debug_message("*** p2p: header AckID = $hdr_AckID");
\r
2362 $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
\r
2363 $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
\r
2364 $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
\r
2365 if ($hdr_Flag == 2) {
\r
2366 //This is an ACK from SB ignore....
\r
2367 $this->debug_message("*** p2p: //This is an ACK from SB ignore....:\n");
\r
2370 $MsgBody = $this->linetoArray(substr($sMsg, 48, -4));
\r
2371 $this->debug_message("*** p2p: body".print_r($MsgBody, true));
\r
2372 if (($MsgBody['EUF-GUID']=='{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}')&&($PictureFilePath=$this->GetPictureFilePath($MsgBody['Context']))) {
\r
2374 if ($this->sb_readln($socket) === false) break;
\r
2376 $this->debug_message("*** p2p: Inv hdr:\n".$this->dump_binary(substr($sMsg, 0, 48)));
\r
2377 preg_match('/{([0-9A-F\-]*)}/i', $MsgBody['Via'], $Matches);
\r
2378 $BranchGUID = $Matches[1];
\r
2379 //it's an invite to send a display picture.
\r
2380 $new_id = ~$hdr_Identifier;
\r
2382 "LLLLLLLLLLLL", $hdr_SessionID,
\r
2385 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2390 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh
\r
2392 $footer = pack("L", 0);
\r
2393 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2394 $len = strlen($message);
\r
2395 $this->sb_writeln($socket, $id, "MSG $this->id D $len");
\r
2396 $this->sb_writedata($socket, $message);
\r
2397 $this->debug_message("*** p2p: send display picture acknowledgement for $hdr_SessionID");
\r
2398 $this->debug_message("*** p2p: Invite ACK message:\n".$this->dump_binary($message));
\r
2399 $this->sb_readln($socket); // Read ACK;
\r
2400 $this->debug_message("*** p2p: Invite ACK Hdr:\n".$this->dump_binary($hdr));
\r
2402 //Send 200 OK message
\r
2403 $MessageContent="SessionID: ".$MsgBody['SessionID']."\r\n\r\n".pack("C", 0);
\r
2405 "MSNSLP/1.0 200 OK\r\n".
\r
2406 "To: <msnmsgr:".$from_email.">\r\n".
\r
2407 "From: <msnmsgr:".$this->user.">\r\n".
\r
2408 "Via: ".$MsgBody['Via']."\r\n".
\r
2409 "CSeq: ".($MsgBody['CSeq']+1)."\r\n".
\r
2410 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2411 "Max-Forwards: 0\r\n".
\r
2412 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n".
\r
2413 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".
\r
2415 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2416 $hdr_TotalDataSizeHigh=0;
\r
2418 "LLLLLLLLLLLL", $hdr_SessionID,
\r
2421 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2422 strlen($MessagePayload),
\r
2430 "MIME-Version: 1.0\r\n".
\r
2431 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2432 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2433 $this->sb_writeln($socket, $id, "MSG $this->id D ".strlen($message));
\r
2434 $this->sb_writedata($socket, $message);
\r
2435 $this->debug_message("*** p2p: dump 200 ok message:\n".$this->dump_binary($message));
\r
2436 $this->sb_readln($socket); // Read ACK;
\r
2438 $this->debug_message("*** p2p: 200 ok:\n".$this->dump_binary($hdr));
\r
2439 // send data preparation message
\r
2440 // send 4 null bytes as data
\r
2441 $hdr_TotalDataSizeLow = 4;
\r
2442 $hdr_TotalDataSizeHigh = 0 ;
\r
2446 $MsgBody['SessionID'],
\r
2449 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2450 $hdr_TotalDataSizeLow,
\r
2457 "MIME-Version: 1.0\r\n".
\r
2458 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2459 "P2P-Dest: $from_email\r\n\r\n$hdr".pack('L', 0)."$footer";
\r
2460 $this->sb_writeln($socket, $id, "MSG $this->id D ".strlen($message));
\r
2461 $this->sb_writedata($socket, $message);
\r
2462 $this->debug_message("*** p2p: dump send Data preparation message:\n".$this->dump_binary($message));
\r
2463 $this->debug_message("*** p2p: Data Prepare Hdr:\n".$this->dump_binary($hdr));
\r
2464 $this->sb_readln($socket); // Read ACK;
\r
2466 // send Data Content..
\r
2467 $footer=pack('N',1);
\r
2469 $FileSize=filesize($PictureFilePath);
\r
2470 if ($hTitle=fopen($PictureFilePath,'rb')) {
\r
2473 while (!feof($hTitle)) {
\r
2474 $FileContent = fread($hTitle, 1024);
\r
2475 $FileContentSize = strlen($FileContent);
\r
2478 $MsgBody['SessionID'],
\r
2489 "MIME-Version: 1.0\r\n".
\r
2490 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2491 "P2P-Dest: $from_email\r\n\r\n$hdr$FileContent$footer";
\r
2492 $this->sb_writeln($socket, $id, "MSG $this->id D ".strlen($message));
\r
2493 $this->sb_writedata($socket, $message);
\r
2494 $this->debug_message("*** p2p: dump send Data Content message $Offset / $FileSize :\n".$this->dump_binary($message));
\r
2495 $this->debug_message("*** p2p: Data Content Hdr:\n".$this->dump_binary($hdr));
\r
2496 //$this->SB_readln($socket);//Read ACK;
\r
2497 $Offset += $FileContentSize;
\r
2502 $MessageContent="\r\n".pack("C", 0);
\r
2504 "BYE MSNMSGR:MSNSLP/1.0\r\n".
\r
2505 "To: <msnmsgr:$from_email>\r\n".
\r
2506 "From: <msnmsgr:".$this->user.">\r\n".
\r
2507 "Via: MSNSLP/1.0/TLP ;branch={".$BranchGUID."}\r\n".
\r
2509 "Call-ID: ".$MsgBody['Call-ID']."\r\n".
\r
2510 "Max-Forwards: 0\r\n".
\r
2511 "Content-Type: application/x-msnmsgr-sessionclosebody\r\n".
\r
2512 "Content-Length: ".strlen($MessageContent)."\r\n\r\n".$MessageContent;
\r
2513 $footer=pack('N',0);
\r
2514 $hdr_TotalDataSizeLow=strlen($MessagePayload);
\r
2515 $hdr_TotalDataSizeHigh=0;
\r
2517 $hdr = pack("LLLLLLLLLLLL",
\r
2521 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2528 "MIME-Version: 1.0\r\n".
\r
2529 "Content-Type: application/x-msnmsgrp2p\r\n".
\r
2530 "P2P-Dest: $from_email\r\n\r\n$hdr$MessagePayload$footer";
\r
2531 $this->sb_writeln($socket, $id, "MSG $id D ".strlen($message));
\r
2533 $this->sb_writedata($socket, $message);
\r
2534 $this->debug_message("*** p2p: dump send BYE message :\n".$this->dump_binary($message));
\r
2539 //if ($hdr_Flag == 2) {
\r
2540 // just send ACK...
\r
2541 // $this->sb_writeln($socket, $id, "ACK $id");
\r
2544 if ($hdr_SessionID == 4) {
\r
2546 $this->debug_message("*** p2p: ignore flag 4");
\r
2549 $finished = false;
\r
2550 if ($hdr_TotalDataSizeHigh == 0) {
\r
2551 // only 32 bites size
\r
2552 if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
\r
2556 // we won't accept any file transfer
\r
2557 // so I think we won't get any message size need to use 64 bits
\r
2558 // 64 bits size here, can't count directly...
\r
2559 $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
\r
2560 $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
\r
2561 $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
\r
2562 $now_size = bcadd($dataoffset, $messagelength);
\r
2563 if (bccomp($now_size, $totalsize) >= 0)
\r
2567 // ignore not finished split packet
\r
2568 $this->debug_message("*** p2p: ignore split packet, not finished");
\r
2571 //$new_id = ~$hdr_Identifier;
\r
2574 $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
\r
2577 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh,
\r
2582 $hdr_TotalDataSizeLow, $hdr_TotalDataSizeHigh);
\r
2583 $footer = pack("L", 0);
\r
2584 $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
\r
2585 $len = strlen($message);
\r
2586 $this->sb_writeln($socket, $id, "MSG $id D $len");
\r
2588 $this->sb_writedata($socket, $message);
\r
2589 $this->debug_message("*** p2p: send acknowledgement for $hdr_SessionID");
\r
2590 $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
\r
2594 $this->debug_message("*** MSG from $from_email: $sMsg");
\r
2595 $this->callHandler('IMin', array('sender' => $from_email, 'message' => $sMsg, 'network' => $network, 'offline' => false));
\r
2598 $this->debug_message("*** User $user is offline. Trying OIM.");
\r
2599 $session['offline'] = true;
\r
2602 if (is_numeric($code)) {
\r
2603 $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
\r
2604 $this->debug_message("*** SB: $this->error");
\r
2612 * Called when we want to end a switchboard session
\r
2613 * or a switchboard session ends
\r
2615 * @param resource $socket Socket
\r
2616 * @param boolean $killsession Whether to delete the session
\r
2619 private function endSBSession($socket, $killsession = false) {
\r
2620 if (!self::socketcheck($socket)) {
\r
2621 $this->sb_writeln($socket, $fake = 0, 'OUT');
\r
2625 // Unset session lookup value
\r
2626 $intsocket = (int) $socket;
\r
2627 unset($this->switchBoardSessionLookup[$this->switchBoardSessions[$intsocket]['to']]);
\r
2629 // Unset session itself
\r
2630 unset($this->switchBoardSessions[$intsocket]);
\r
2634 * Checks for new data and calls appropriate methods
\r
2636 * This method is usually called in an infinite loop to keep checking for new data
\r
2640 public function receive() {
\r
2641 // First, get an array of sockets that have data that is ready to be read
\r
2643 $ready = $this->getSockets();
\r
2644 $numrdy = stream_select($ready, $w = NULL, $x = NULL, NULL);
\r
2646 // Now that we've waited for something, go through the $ready
\r
2647 // array and read appropriately
\r
2649 foreach ($ready as $socket) {
\r
2650 if ($socket == $this->NSfp) {
\r
2651 $this->nsReceive();
\r
2653 $this->sbReceive($socket);
\r
2659 * Send a request for a switchboard session
\r
2661 * @param string $to Target email for switchboard session
\r
2663 private function reqSBSession($to) {
\r
2664 $this->debug_message("*** Request SB for $to");
\r
2665 $this->ns_writeln("XFR $this->id SB");
\r
2667 // Add to the queue of those waiting for a switchboard session reponse
\r
2668 $this->switchBoardSessions[$to] = array(
\r
2672 'joined' => false,
\r
2673 'offline' => false,
\r
2674 'XFRReqTime' => time()
\r
2676 $this->waitingForXFR[] = &$this->switchBoardSessions[$to];
\r
2680 * Following an XFR or RNG, connect to the switchboard session
\r
2682 * @param string $mode Mode, either 'Active' (in the case of XFR) or 'Passive' (in the case of RNG)
\r
2683 * @param string $ip IP of Switchboard
\r
2684 * @param integer $port Port of Switchboard
\r
2685 * @param string $to User on other end of Switchboard
\r
2686 * @param array $param Array of parameters - 'cki', 'ticket', 'sid'
\r
2687 * @return boolean true if successful
\r
2689 private function connectToSBSession($mode, $ip, $port, $to, $param) {
\r
2690 $this->debug_message("*** SB: Trying to connect to switchboard server $ip:$port");
\r
2692 $socket = @fsockopen($ip, $port, $errno, $errstr, $this->timeout);
\r
2694 $this->debug_message("*** SB: Can't connect to $ip:$port, error => $errno, $errstr");
\r
2698 // Store the socket in the lookup array
\r
2699 $this->switchBoardSessionLookup[$to] = $socket;
\r
2701 // Store the socket in the sessions array
\r
2702 $intsocket = (int) $socket;
\r
2703 $this->switchBoardSessions[$to] = array(
\r
2705 'socket' => $socket,
\r
2707 'joined' => false,
\r
2708 'offline' => false,
\r
2709 'XFRReqTime' => time()
\r
2712 // Change the index of the session to the socket
\r
2713 $this->switchBoardSessions[$intsocket] = $this->switchBoardSessions[$to];
\r
2714 unset($this->switchBoardSessions[$to]);
\r
2716 $id = &$this->switchBoardSessions[$intsocket]['id'];
\r
2718 if ($mode == 'Active') {
\r
2719 $cki_code = $param['cki'];
\r
2721 // SB: >>> USR {id} {user} {cki}
\r
2722 $this->sb_writeln($socket, $id, "USR $id $this->user $cki_code");
\r
2725 $ticket = $param['ticket'];
\r
2726 $sid = $param['sid'];
\r
2728 // SB: >>> ANS {id} {user} {ticket} {session_id}
\r
2729 $this->sb_writeln($socket, $id, "ANS $id $this->user $ticket $sid");
\r
2734 * Send a message via an existing SB session
\r
2736 * @param string $to Recipient for message
\r
2737 * @param string $message Message
\r
2738 * @return boolean true on success
\r
2740 private function sendMessageViaSB($to, $message) {
\r
2741 $socket = $this->switchBoardSessionLookup[$to];
\r
2742 if (self::socketcheck($socket)) {
\r
2746 if (!$this->switchBoardSessions[$to]['joined']) {
\r
2747 // If our participant has not joined the session yet we can't message them!
\r
2751 $intsocket = (int) $socket;
\r
2752 $id = &$this->switchBoardSessions[$intsocket]['id'];
\r
2754 $aMessage = $this->getMessage($Message);
\r
2756 $MsnObjDefine=$this->GetMsnObjDefine($aMessage);
\r
2757 if ($MsnObjDefine !== '') {
\r
2758 $SendString="MIME-Version: 1.0\r\nContent-Type: text/x-mms-emoticon\r\n\r\n$MsnObjDefine";
\r
2759 $len = strlen($SendString);
\r
2760 // TODO handle failure during write to socket
\r
2761 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
2762 $this->sb_writedata($socket, $SendString);
\r
2764 $len = strlen($aMessage);
\r
2765 // TODO handle failure during write to socket
\r
2766 $this->sb_writeln($socket, $id, "MSG $id N $len");
\r
2767 $this->sb_writedata($socket, $aMessage);
\r
2769 // Don't close the SB session, we might as well leave it open
\r
2775 * Send offline message
\r
2777 * @param string $to Intended recipient
\r
2778 * @param string $sMessage Message
\r
2779 * @param string $lockkey Lock key
\r
2780 * @return mixed true on success or error data
\r
2782 private function sendOIM($to, $sMessage, $lockkey) {
\r
2783 $XML = '<?xml version="1.0" encoding="utf-8"?>
\r
2784 <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
\r
2785 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
\r
2786 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
\r
2788 <From memberName="'.$this->user.'"
\r
2789 friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
\r
2792 xmlns="http://messenger.msn.com/ws/2004/09/oim/"
\r
2793 msnpVer="'.$this->protocol.'"
\r
2794 buildVer="'.$this->buildver.'"/>
\r
2795 <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2796 <Ticket passport="'.htmlspecialchars($this->ticket['oim_ticket']).'"
\r
2797 appid="'.$this->prod_id.'"
\r
2798 lockkey="'.$lockkey.'"
\r
2799 xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
\r
2800 <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
\r
2801 <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
\r
2802 <MessageNumber>1</MessageNumber>
\r
2806 <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
\r
2807 <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
\r
2808 Content-Type: text/plain; charset=UTF-8
\r
2809 Content-Transfer-Encoding: base64
\r
2810 X-OIM-Message-Type: OfflineMessage
\r
2811 X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
\r
2812 X-OIM-Sequence-Num: 1
\r
2814 '.chunk_split(base64_encode($sMessage)).'
\r
2817 </soap:Envelope>';
\r
2819 $header_array = array(
\r
2820 'SOAPAction: '.$this->oim_send_soap,
\r
2821 'Content-Type: text/xml',
\r
2822 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
\r
2825 $this->debug_message("*** URL: $this->oim_send_url");
\r
2826 $this->debug_message("*** Sending SOAP:\n$XML");
\r
2827 $curl = curl_init();
\r
2828 curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
\r
2829 curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
\r
2830 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
2831 curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
\r
2832 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
\r
2833 if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
\r
2834 curl_setopt($curl, CURLOPT_POST, 1);
\r
2835 curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
\r
2836 $data = curl_exec($curl);
\r
2837 $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
\r
2838 curl_close($curl);
\r
2839 $this->debug_message("*** Get Result:\n$data");
\r
2841 if ($http_code == 200) {
\r
2842 $this->debug_message("*** OIM sent for $to");
\r
2846 $challenge = false;
\r
2847 $auth_policy = false;
\r
2848 // the lockkey is invalid, authenticated fail, we need challenge it again
\r
2849 // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
\r
2850 preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
\r
2851 if (count($matches) != 0) {
\r
2852 // yes, we get new LockKeyChallenge
\r
2853 $challenge = $matches[2];
\r
2854 $this->debug_message("*** OIM need new challenge ($challenge) for $to");
\r
2856 // auth policy error
\r
2857 // <RequiredAuthPolicy xmlns="http://messenger.msn.com/ws/2004/09/oim/">MBI_SSL</RequiredAuthPolicy>
\r
2858 preg_match("#<RequiredAuthPolicy (.*)>(.*)</RequiredAuthPolicy>#", $data, $matches);
\r
2859 if (count($matches) != 0) {
\r
2860 $auth_policy = $matches[2];
\r
2861 $this->debug_message("*** OIM need new auth policy ($auth_policy) for $to");
\r
2863 if ($auth_policy === false && $challenge === false) {
\r
2864 //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
\r
2865 preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
\r
2866 if (count($matches) == 0) {
\r
2867 // no error, we assume the OIM is sent
\r
2868 $this->debug_message("*** OIM sent for $to");
\r
2871 $err_code = $matches[2];
\r
2872 //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
\r
2873 preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
\r
2874 if (count($matches) > 0)
\r
2875 $err_msg = $matches[1];
\r
2878 $this->debug_message("*** OIM failed for $to");
\r
2879 $this->debug_message("*** OIM Error code: $err_code");
\r
2880 $this->debug_message("*** OIM Error Message: $err_msg");
\r
2883 return array('challenge' => $challenge, 'auth_policy' => $auth_policy);
\r
2887 * Send a message to a user on another network
\r
2889 * @param string $to Intended recipient
\r
2890 * @param string $message Message
\r
2891 * @param integer $network Network
\r
2894 private function sendOtherNetworkMessage($to, $message, $network) {
\r
2895 $message = $this->getMessage($message, $network);
\r
2896 $len = strlen($message);
\r
2897 // TODO Introduce error checking for message sending
\r
2898 $this->ns_writeln("UUM $this->id $to $network 1 $len");
\r
2899 $this->ns_writedata($Message);
\r
2900 $this->debug_message("*** Sent to $to (network: $network):\n$Message");
\r
2907 * @param string $to To address in form user@host.com(@network)
\r
2908 * where network is 1 for MSN, 32 for Yahoo
\r
2909 * and 'Offline' for offline messages
\r
2910 * @param string $message Message
\r
2912 public function sendMessage($to, $message) {
\r
2913 if ($message != '') {
\r
2914 list($name, $host, $network) = explode('@', $to);
\r
2915 $network = $network == '' ? 1 : $network;
\r
2916 $recipient = $name.$host;
\r
2918 if ($network === 1) {
\r
2919 if (!isset($this->switchBoardSessionLookup[$recipient]) && (!isset($this->switchBoardSessions[$recipient])
\r
2920 || time() - $this->switchBoardSessions[$recipient]['XFRReqTime'] > $this->XFRReqTimeout)) {
\r
2921 $this->debug_message("*** No existing SB session or request has timed out");
\r
2922 $this->reqSBSession($recipient);
\r
2925 $socket = $this->switchBoardSessionLookup[$to];
\r
2926 if ($this->switchBoardSessions[(int) $socket]['offline']) {
\r
2927 $this->debug_message("*** Contact ($recipient) offline, sending OIM");
\r
2928 $this->endSBSession($socket);
\r
2929 return $this->sendMessage($recipient.'@Offline', $message);
\r
2931 $this->debug_message("*** Attempting to send message to $recipient using existing SB session");
\r
2933 if ($this->sendMessageViaSB($recipient, $message)) {
\r
2934 $this->debug_message('*** Message sent successfully');
\r
2937 $this->debug_message('*** Message sending failed, requesting new SB session');
\r
2938 $this->reqSBSession($to);
\r
2943 } elseif ($network == 'Offline') {
\r
2945 //FIXME: 修正Send OIM
\r
2947 $re_login = false;
\r
2948 for ($i = 0; $i < $this->oim_try; $i++) {
\r
2949 if (($oim_result = $this->sendOIM($recipient, $message, $lockkey)) === true) break;
\r
2950 if (is_array($oim_result) && $oim_result['challenge'] !== false) {
\r
2951 // need challenge lockkey
\r
2952 $this->debug_message("*** Need challenge code for ".$oim_result['challenge']);
\r
2953 $lockkey = $this->getChallenge($oim_result['challenge']);
\r
2956 if ($oim_result === false || $oim_result['auth_policy'] !== false) {
\r
2958 $this->debug_message("*** Can't send OIM, but we already re-logged-in again, so returning false");
\r
2961 $this->debug_message("*** Can't send OIM, maybe ticket expired, trying to login again");
\r
2963 // Maybe we need to re-login again
\r
2964 if (!$this->get_passport_ticket()) {
\r
2965 $this->debug_message("*** Can't re-login, something went wrong here, returning false");
\r
2968 $this->debug_message("*** Getting new ticket and trying again");
\r
2974 return $this->sendOtherNetworkMessage($recipient, $message, $network);
\r
2981 * Sends a ping command
\r
2983 * Should be called about every 50 seconds
\r
2987 public function sendPing() {
\r
2989 $this->ns_writeln("PNG");
\r
2993 * Methods to return sockets / check socket status
\r
2997 * Get the NS socket
\r
2999 * @return resource NS socket
\r
3001 public function getNSSocket() {
\r
3002 return $this->NSfp;
\r
3006 * Get the Switchboard sockets currently in use
\r
3008 * @return array Array of Switchboard sockets
\r
3010 public function getSBSockets() {
\r
3011 return $this->switchBoardSessionLookup;
\r
3015 * Get all the sockets currently in use
\r
3017 * @return array Array of socket resources
\r
3019 public function getSockets() {
\r
3020 return array_merge(array($this->NSfp), $this->switchBoardSessionLookup);
\r
3024 * Checks socket for end of file
\r
3026 * @param resource $socket Socket to check
\r
3027 * @return boolean true if end of file (socket)
\r
3029 private static function socketcheck($socket){
\r
3030 $info = stream_get_meta_data($socket);
\r
3031 return $info['eof'];
\r
3035 * Methods to add / call callbacks
\r
3039 * Calls User Handler
\r
3041 * Calls registered handler for a specific event.
\r
3043 * @param string $event Command (event) name (Rvous etc)
\r
3044 * @param array $data Data
\r
3045 * @see registerHandler
\r
3048 private function callHandler($event, $data = NULL) {
\r
3049 if (isset($this->myEventHandlers[$event])) {
\r
3050 if ($data !== NULL) {
\r
3051 call_user_func($this->myEventHandlers[$event], $data);
\r
3053 call_user_func($this->myEventHandlers[$event]);
\r
3059 * Registers a user handler
\r
3062 * IMIn, Pong, ConnectFailed, Reconnect,
\r
3063 * AddedToList, RemovedFromList
\r
3065 * @param string $event Event name
\r
3066 * @param string $handler User function to call
\r
3067 * @see callHandler
\r
3068 * @return boolean true if successful
\r
3070 public function registerHandler($event, $handler) {
\r
3071 if (is_callable($handler)) {
\r
3072 $this->myEventHandlers[$event] = $handler;
\r