[pLog-svn] r5665 - in plugins/branches/lifetype-1.2/addcommentnotify: . class/action class/phpmsnclass class/view locale templates
mark at devel.lifetype.net
mark at devel.lifetype.net
Mon Jul 16 01:42:13 EDT 2007
Author: mark
Date: 2007-07-16 01:42:13 -0400 (Mon, 16 Jul 2007)
New Revision: 5665
Added:
plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/changelog.txt
Modified:
plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php
plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/config.php
plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msn.class.php
plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.php
plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnsendmsg.php
plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/sample.php
plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php
plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_en_UK.php
plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_zh_TW.php
plugins/branches/lifetype-1.2/addcommentnotify/pluginaddcommentnotify.class.php
plugins/branches/lifetype-1.2/addcommentnotify/readme.txt
plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template
Log:
Upgrade to version 20070716
Modified: plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -5,6 +5,7 @@
class PluginAddCommentNotifyUpdateConfigAction extends AdminAction
{
var $_pluginEnabled;
+ var $_notifyTrackback;
var $_useMSNClass;
var $_msnClassFile;
var $_msnUser;
@@ -23,6 +24,9 @@
$this->_pluginEnabled = $this->_request->getValue('pluginEnabled');
$this->_pluginEnabled = ($this->_pluginEnabled != '');
+ $this->_notifyTrackback = $this->_request->getValue('notifyTrackback');
+ $this->_notifyTrackback = ($this->_notifyTrackback != '');
+
$this->_useMSNClass = $this->_request->getValue('useMSNClass');
$this->_useMSNClass = ($this->_useMSNClass != '');
@@ -99,6 +103,7 @@
// Update the relevant values with the form inputs
$blogSettings->setValue('plugin_addcommentnotify_enabled', $this->_pluginEnabled);
+ $blogSettings->setValue('plugin_addcommentnotify_notifytrackback', $this->_notifyTrackback);
$blogSettings->setValue('addcommentnotify_usemsnclass', $this->_useMSNClass);
$blogSettings->setValue('addcommentnotify_msnclass_file', $this->_msnClassFile);
$blogSettings->setValue('addcommentnotify_msn_user', $this->_msnUser);
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/changelog.txt
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/changelog.txt (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/changelog.txt 2007-07-16 05:42:13 UTC (rev 5665)
@@ -0,0 +1,54 @@
+phpmsnclass changelog:
+
+1.7 2007/06/17
+ ! change client id to 0x7000800C, use forth parameter of MSN() to assign client id
+ + handle text/x-mms-animemoticon, just ignore
+ + handle application/x-ms-ink, just ignore
+ + handle application/x-msnmsgrp2p, sending acknowledgement then ignore it
+ ! if ignore message, don't show timeout error for SB.
+
+1.6 2007/06/07
+ ! fix login problem if login.live.com redirect to some URL (we'll get this even httpd code = 200).
+ + add clientid for CHG command
+ + if $use_ping is integer, we use send PNG for every $use_ping seconds
+ + make this class and msnbot.php work under windows (tested for php 5.2.3)
+ + try to remove from contact list for RML command
+
+1.5 2007/04/06
+ ! change default stream timeout from 10 seconds to 2 seconds (for both NS and SB), can modify it, just change $stream_timeout
+ ! alias should be encoded for PRP command.
+ ! fix the problem for sendMessage() can't send to yahoo user.
+
+1.4 2007/04/02
+ ! change the timeout detect, not for whole session, just for 2 command, reset $start_tm after each command.
+ ! only show header for debug mode
+ ! fix login problem if login.live.com redirect to some URL.
+ ! fix OIM issue, can't send OIM for first email after TO: (one extra space)
+ ! fix OIM problem when msnbot running over 1 day, the ticket will expire, we'll try to get new ticket again.
+ + yes, we can get contact list for MSNP15 now
+ + process ADD/REM and ADL/RML command, allow user define some function for this.
+ + add support for Yahoo! network in MSNP15
+ + try to send OIM if we can't send via SB (timeout? or some error in SB)
+ + also read OIM, and reply it.
+ + move .msn file to backup folder after sent the message
+
+1.3a 2007/03/29
+ ! fix the problem when we need to re-login again.
+ + also wait for $retry_wait seconds if we logout from sever.
+
+1.3 2007/03/29
+ ! fix the return code for switchboard_control(), it should be true for successful.
+ + add doLoop() to work like a sending message bot, get the data from spool directory and never logout.
+ + allow user define a function to process the message.
+ + add login for contacts.msn.com, but even we can login, we still can't send the correct SOAP to get address book.
+
+1.2 2007/03/13
+ ! fix the problem for 32bits platform can't get correct challenge code
+
+1.1 2007/03/13
+ + add timeout detect, make sure we can quit from loop when protocol changed or sometime wrong
+ + allow save the debug information to file
+
+1.0 2007/03/11
+ = public release
+
Modified: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/config.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/config.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/config.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -1,17 +1,17 @@
-<?php
-
-// your MSN login account
-$msn_acct = 'YOUR_MSN_ACCOUNT';
-// your MSN password
-$msn_password = 'YOUR_MSN_PASSWORD';
-// your alias name for MSN
-$msn_alias = 'YOUR_MSN_ALIAS';
-// notify list when someone add us to their list or remove us from their list
-// after domain, you can assign @n to specify the network of this email,
-// where @1 is for MSN (yes, if no @1, also for MSN)
-// @32 is for Yahoo
-$aNotifyUser = array('MSN_ACCOUNT1 at 1',
- 'MSN_ACCOUNT2',
- 'YAHOO_ACCOUNT at 32');
-
-?>
+<?php
+
+// your MSN login account
+$msn_acct = 'YOUR_MSN_ACCOUNT';
+// your MSN password
+$msn_password = 'YOUR_MSN_PASSWORD';
+// your alias name for MSN
+$msn_alias = 'YOUR_MSN_ALIAS';
+// notify list when someone add us to their list or remove us from their list
+// after domain, you can assign @n to specify the network of this email,
+// where @1 is for MSN (yes, if no @1, also for MSN)
+// @32 is for Yahoo
+$aNotifyUser = array('MSN_ACCOUNT1 at 1',
+ 'MSN_ACCOUNT2',
+ 'YAHOO_ACCOUNT at 32');
+
+?>
Modified: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msn.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msn.class.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msn.class.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -1,2735 +1,2922 @@
-<?php
-/*
-
-MSN class ver 1.5 by Tommy Wu
-License: GPL
-
-You can find MSN protocol from this site: http://msnpiki.msnfanatic.com/index.php/Main_Page
-
-This class support both MSNP15 and MSNP9 for send message. The PHP module needed:
-
- MSNP9: curl pcre
- MSNP15: curl pcre mhash mcrypt bcmath
-
-Usually, this class will try to use MSNP15 if your system can support it, if your system can't support it,
-it will switch to use MSNP9. But if you use MSNP9, it won't support OIM (Offline Messages).
-
-Sameple Code:
-
-$msn = new MSN;
-
-if (!$msn->connect('YOUR_MSN_ID', 'YOUR_MSN_PASSWORD')) {
- echo "Error for connect to MSN network\n";
- echo "$msn->error\n";
- exit;
-}
-
-$msn->sendMessage('Now: '.strftime('%D %T')."\nTesting\nSecond Line\n\n\n\nand Empty Line",
- array(
- 'somebody1 at hotmail.com',
- 'somebody2 at hotmail.com'
- )
- );
-echo "Done!\n";
-exit;
-
-changelog:
-
-1.5 2007/04/06
- ! change default stream timeout from 10 seconds to 2 seconds (for both NS and SB), can modify it, just change $stream_timeout
- ! alias should be encoded for PRP command.
- ! fix the problem for sendMessage() can't send to yahoo user.
-
-1.4 2007/04/02
- ! change the timeout detect, not for whole session, just for 2 command, reset $start_tm after each command.
- ! only show header for debug mode
- ! fix login problem if login.live.com redirect to some URL.
- ! fix OIM issue, can't send OIM for first email after TO: (one extra space)
- ! fix OIM problem when msnbot running over 1 day, the ticket will expire, we'll try to get new ticket again.
- + yes, we can get contact list for MSNP15 now
- + process ADD/REM and ADL/RML command, allow user define some function for this.
- + add support for Yahoo! network in MSNP15
- + try to send OIM if we can't send via SB (timeout? or some error in SB)
- + also read OIM, and reply it.
- + move .msn file to backup folder after sent the message
-
-1.3a 2007/03/29
- ! fix the problem when we need to re-login again.
- + also wait for $retry_wait seconds if we logout from sever.
-
-1.3 2007/03/29
- ! fix the return code for switchboard_control(), it should be true for successful.
- + add doLoop() to work like a sending message bot, get the data from spool directory and never logout.
- + allow user define a function to process the message.
- + add login for contacts.msn.com, but even we can login, we still can't send the correct SOAP to get address book.
-
-1.2 2007/03/13
- ! fix the problem for 32bits platform can't get correct challenge code
-
-1.1 2007/03/13
- + add timeout detect, make sure we can quit from loop when protocol changed or sometime wrong
- + allow save the debug information to file
-
-1.0 2007/03/11
- = public release
-
-*/
-
-class MSN
-{
- var $server = 'messenger.hotmail.com';
- var $port = 1863;
-
- var $passport_url = 'https://login.live.com/RST.srf';
- var $protocol = 'MSNP15';
- var $buildver = '8.1.0178';
- var $prod_key = 'PK}_A_0N_K%O?A9S';
- var $prod_id = 'PROD0114ES4Z%Q5W';
- var $login_method = 'SSO';
-
- var $oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';
- var $oim_sendsoap = 'http://messenger.live.com/ws/2006/09/oim/Store2';
- var $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
- var $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
- var $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
- var $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
- var $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
- var $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
-
- var $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
- var $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';
-
- var $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
- var $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';
-
- var $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
- var $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
-
- var $id;
- var $fp = false;
- var $error = '';
-
- var $authed = false;
- var $user = '';
- var $password = '';
-
- var $passport_policy = '';
- var $oim_try = 3;
- var $oim_ticket = '';
- var $contact_ticket = '';
-
- // FIXME: even we login for following site, but... we don't need that now.
- var $web_ticket = '';
- var $space_ticket = '';
- var $storage_ticket = '';
-
- var $debug = false;
- var $log_file = '';
- var $timeout = 15;
- var $stream_timeout = 2;
-
- var $log_path = false;
-
- var $sb;
- var $font_fn = 'Arial';
- var $font_co = '333333';
- var $font_ef = '';
-
- var $kill_me = false;
-
- function MSN($protocol = '', $debug = false, $timeout = 15)
- {
- if (is_string($debug) && $debug !== '') {
- $this->debug = true;
- $this->log_file = $debug;
- }
- else
- $this->debug = $debug;
- $this->timeout = $timeout;
- // check support
- if (!function_exists('curl_init')) die("We need curl module!\n");
- if (!function_exists('preg_match')) die("We need pcre module!\n");
-
- if ($protocol != 'MSNP9' && $protocol != 'MSNP15')
- $protocol = '';
-
- if ($protocol != 'MSNP9' && !function_exists('mhash')) {
- if ($protocol == 'MSNP15') die("We need mhash module for $protocol!\n");
- $protocol = 'MSNP9';
- }
- if ($protocol != 'MSNP9' && !function_exists('mcrypt_cbc')) {
- if ($protocol == 'MSNP15') die("We need mcrypt module for $protocol!\n");
- $protocol = 'MSNP9';
- }
- if ($protocol != 'MSNP9' && !function_exists('bcmod')) {
- if ($protocol == 'MSNP15') die("We need bcmath module for $protocol!\n");
- $protocol = 'MSNP9';
- }
- if ($protocol == 'MSNP9') {
- $this->protocol = 'MSNP9';
- $this->passport_url = 'https://nexus.passport.com/rdr/pprdr.asp';
- $this->buildver = '6.0.0602';
- $this->prod_key = 'Q1P7W2E4J9R8U3S5';
- $this->prod_id = 'msmsgs at msnmsgr.com';
- $this->login_method = 'TWN';
- }
- else {
- $this->protocol = 'MSNP15';
- $this->passport_url = 'https://login.live.com/RST.srf';
- $this->buildver = '8.1.0178';
- $this->prod_key = 'PK}_A_0N_K%O?A9S';
- $this->prod_id = 'PROD0114ES4Z%Q5W';
- $this->login_method = 'SSO';
-
- $this->oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';
- $this->oim_send_soap = 'http://messenger.live.com/ws/2006/09/oim/Store2';
- }
- return;
- }
-
- function get_passport_ticket($url = '')
- {
- $user = htmlspecialchars($this->user);
- $password = htmlspecialchars($this->password);
-
- if ($url === '')
- $passport_url = $this->passport_url;
- else
- $passport_url = $url;
-
- $XML = '<?xml version="1.0" encoding="UTF-8"?>
-<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
- xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
- xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
- xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
- xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
- xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
- xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
-<Header>
- <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
- <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
- <ps:BinaryVersion>4</ps:BinaryVersion>
- <ps:UIVersion>1</ps:UIVersion>
- <ps:Cookies></ps:Cookies>
- <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
- </ps:AuthInfo>
- <wsse:Security>
- <wsse:UsernameToken Id="user">
- <wsse:Username>'.$user.'</wsse:Username>
- <wsse:Password>'.$password.'</wsse:Password>
- </wsse:UsernameToken>
- </wsse:Security>
-</Header>
-<Body>
- <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
- <wst:RequestSecurityToken Id="RST0">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>http://Passport.NET/tb</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- </wst:RequestSecurityToken>
- <wst:RequestSecurityToken Id="RST1">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>messengerclear.live.com</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
- </wst:RequestSecurityToken>
- <wst:RequestSecurityToken Id="RST2">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>messenger.msn.com</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
- </wst:RequestSecurityToken>
- <wst:RequestSecurityToken Id="RST3">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>contacts.msn.com</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
- </wst:RequestSecurityToken>
- <wst:RequestSecurityToken Id="RST4">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>messengersecure.live.com</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
- </wst:RequestSecurityToken>
- <wst:RequestSecurityToken Id="RST5">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>spaces.live.com</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
- </wst:RequestSecurityToken>
- <wst:RequestSecurityToken Id="RST6">
- <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
- <wsp:AppliesTo>
- <wsa:EndpointReference>
- <wsa:Address>storage.msn.com</wsa:Address>
- </wsa:EndpointReference>
- </wsp:AppliesTo>
- <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
- </wst:RequestSecurityToken>
- </ps:RequestMultipleSecurityTokens>
-</Body>
-</Envelope>';
-
- $this->debug_message("*** URL: $passport_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $passport_url);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code != 200) {
- // sometimes, rediret to another URL
- // MSNP15
- //<faultcode>psf:Redirect</faultcode>
- //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
- //<faultstring>Authentication Failure</faultstring>
- if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
- $this->debug_message("*** Can't get passport ticket! http code = $http_code");
- return false;
- }
- preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
- if (count($matches) == 0) {
- $this->debug_message("*** redirect, but can't get redirect URL!");
- return false;
- }
- $redirect_url = $matches[1];
- if ($redirect_url == $passport_url) {
- $this->debug_message("*** redirect, but redirect to same URL!");
- return false;
- }
- return $this->get_passport_ticket($redirect_url);
- }
-
- // we need ticket and secret code
- // RST1: messengerclear.live.com
- // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
- // <wst:BinarySecret>binary secret</wst:BinarySecret>
- // RST2: messenger.msn.com
- // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
- // RST3: contacts.msn.com
- // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
- // RST4: messengersecure.live.com
- // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
- // RST5: spaces.live.com
- // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
- // RST6: storage.msn.com
- // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
- preg_match("#".
- "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
- "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
- "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
- "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
- "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
- "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
- "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
- "#",
- $data, $matches);
-
- // no ticket found!
- if (count($matches) == 0) {
- $this->debug_message("*** Can't get passport ticket!");
- return false;
- }
-
- //$this->debug_message(var_export($matches, true));
- // matches[0]: all data
- // matches[1]: RST1 (messengerclear.live.com) ticket
- // matches[2]: ...
- // matches[3]: RST1 (messengerclear.live.com) binary secret
- // matches[4]: ...
- // matches[5]: RST2 (messenger.msn.com) ticket
- // matches[6]: ...
- // matches[7]: RST3 (contacts.msn.com) ticket
- // matches[8]: ...
- // matches[9]: RST4 (messengersecure.live.com) ticket
- // matches[10]: ...
- // matches[11]: RST5 (spaces.live.com) ticket
- // matches[12]: ...
- // matches[13]: RST6 (storage.live.com) ticket
- // matches[14]: ...
-
- // so
- // ticket => $matches[1]
- // secret => $matches[3]
- // web_ticket => $matches[5]
- // contact_ticket => $matches[7]
- // oim_ticket => $matches[9]
- // space_ticket => $matches[11]
- // storage_ticket => $matches[13]
-
- // yes, we get ticket
- $aTickets = array(
- 'ticket' => html_entity_decode($matches[1]),
- 'secret' => html_entity_decode($matches[3]),
- 'web_ticket' => html_entity_decode($matches[5]),
- 'contact_ticket' => html_entity_decode($matches[7]),
- 'oim_ticket' => html_entity_decode($matches[9]),
- 'space_ticket' => html_entity_decode($matches[11]),
- 'storage_ticket' => html_entity_decode($matches[13])
- );
- //$this->debug_message(var_export($aTickets, true));
- return $aTickets;
- }
-
- function get_tweener_passport_ticket($nonce)
- {
- $this->debug_message("*** URL: $this->passport_url");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->passport_url);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_NOBODY, 1);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- // we need login URL
- // DALogin=xxx
- preg_match('/DALogin=(.*?),/', $data, $matches);
-
- // no URL found!
- if (count($matches) == 0) {
- $this->debug_message("*** Can't get passport's URL! http code = $http_code");
- return false;
- }
-
- $url = 'https://'.$matches[1];
-
- $this->debug_message("*** URL: $url");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, array(
- 'Authorization: Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in='.$this->user.',pwd='.$this->password.','.$nonce,
- 'Host: login.passport.com'
- ));
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_NOBODY, 1);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- // we need ticket
- // from-PP=xxx
- preg_match("/from-PP='(.*?)'/", $data, $matches);
-
- // no URL found!
- if (count($matches) == 0) {
- $this->debug_message("*** Can't get passport's ticket! http code = $http_code");
- return false;
- }
- return $matches[1];
- }
-
- function delMemberFromList($memberID, $email, $network, $list)
- {
- if ($network != 1 && $network != 32) return true;
- if ($memberID === false) return true;
- $user = htmlspecialchars($email);
- $ticket = htmlspecialchars($this->contact_ticket);
- if ($network == 1)
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
-<soap:Header>
- <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
- <IsMigration>false</IsMigration>
- <PartnerScenario>ContactMsgrAPI</PartnerScenario>
- </ABApplicationHeader>
- <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ManagedGroupRequest>false</ManagedGroupRequest>
- <TicketToken>'.$ticket.'</TicketToken>
- </ABAuthHeader>
-</soap:Header>
-<soap:Body>
- <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
- <serviceHandle>
- <Id>0</Id>
- <Type>Messenger</Type>
- <ForeignId></ForeignId>
- </serviceHandle>
- <memberships>
- <Membership>
- <MemberRole>'.$list.'</MemberRole>
- <Members>
- <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <Type>Passport</Type>
- <MembershipId>'.$memberID.'</MembershipId>
- <State>Accepted</State>
- </Member>
- </Members>
- </Membership>
- </memberships>
- </DeleteMember>
-</soap:Body>
-</soap:Envelope>';
- else
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
-<soap:Header>
- <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
- <IsMigration>false</IsMigration>
- <PartnerScenario>ContactMsgrAPI</PartnerScenario>
- </ABApplicationHeader>
- <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ManagedGroupRequest>false</ManagedGroupRequest>
- <TicketToken>'.$ticket.'</TicketToken>
- </ABAuthHeader>
-</soap:Header>
-<soap:Body>
- <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
- <serviceHandle>
- <Id>0</Id>
- <Type>Messenger</Type>
- <ForeignId></ForeignId>
- </serviceHandle>
- <memberships>
- <Membership>
- <MemberRole>'.$list.'</MemberRole>
- <Members>
- <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <Type>Email</Type>
- <MembershipId>'.$memberID.'</MembershipId>
- <State>Accepted</State>
- </Member>
- </Members>
- </Membership>
- </memberships>
- </DeleteMember>
-</soap:Body>
-</soap:Envelope>';
-
- $header_array = array(
- 'SOAPAction: '.$this->delmember_soap,
- 'Content-Type: text/xml; charset=utf-8',
- 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
- );
-
- $this->debug_message("*** URL: $this->delmember_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code != 200) {
- preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
- if (count($matches) == 0) {
- $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
- return false;
- }
- $faultcode = trim($matches[1]);
- $faultstring = trim($matches[2]);
- if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
- $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
- return false;
- }
- $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
- return true;
- }
- $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
- return true;
- }
-
- function addMemberToList($email, $network, $list)
- {
- if ($network != 1 && $network != 32) return true;
- $ticket = htmlspecialchars($this->contact_ticket);
- $user = htmlspecialchars($email);
-
- if ($network == 1)
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
-<soap:Header>
- <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
- <IsMigration>false</IsMigration>
- <PartnerScenario>ContactMsgrAPI</PartnerScenario>
- </ABApplicationHeader>
- <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ManagedGroupRequest>false</ManagedGroupRequest>
- <TicketToken>'.$ticket.'</TicketToken>
- </ABAuthHeader>
-</soap:Header>
-<soap:Body>
- <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
- <serviceHandle>
- <Id>0</Id>
- <Type>Messenger</Type>
- <ForeignId></ForeignId>
- </serviceHandle>
- <memberships>
- <Membership>
- <MemberRole>'.$list.'</MemberRole>
- <Members>
- <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <Type>Passport</Type>
- <State>Accepted</State>
- <PassportName>'.$user.'</PassportName>
- </Member>
- </Members>
- </Membership>
- </memberships>
- </AddMember>
-</soap:Body>
-</soap:Envelope>';
- else
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
-<soap:Header>
- <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
- <IsMigration>false</IsMigration>
- <PartnerScenario>ContactMsgrAPI</PartnerScenario>
- </ABApplicationHeader>
- <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ManagedGroupRequest>false</ManagedGroupRequest>
- <TicketToken>'.$ticket.'</TicketToken>
- </ABAuthHeader>
-</soap:Header>
-<soap:Body>
- <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
- <serviceHandle>
- <Id>0</Id>
- <Type>Messenger</Type>
- <ForeignId></ForeignId>
- </serviceHandle>
- <memberships>
- <Membership>
- <MemberRole>'.$list.'</MemberRole>
- <Members>
- <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <Type>Email</Type>
- <State>Accepted</State>
- <Email>'.$user.'</Email>
- <Annotations>
- <Annotation>
- <Name>MSN.IM.BuddyType</Name>
- <Value>32:YAHOO</Value>
- </Annotation>
- </Annotations>
- </Member>
- </Members>
- </Membership>
- </memberships>
- </AddMember>
-</soap:Body>
-</soap:Envelope>';
- $header_array = array(
- 'SOAPAction: '.$this->addmember_soap,
- 'Content-Type: text/xml; charset=utf-8',
- 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
- );
-
- $this->debug_message("*** URL: $this->addmember_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code != 200) {
- preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
- if (count($matches) == 0) {
- $this->log_message("*** can't add member (network: $network) $email to $list");
- return false;
- }
- $faultcode = trim($matches[1]);
- $faultstring = trim($matches[2]);
- if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
- $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
- return false;
- }
- $this->log_message("*** add member (network: $network) $email to $list, already exist!");
- return true;
- }
- $this->log_message("*** add member (network: $network) $email to $list");
- return true;
- }
-
- function getMembershipList()
- {
- $ticket = htmlspecialchars($this->contact_ticket);
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
-<soap:Header>
- <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
- <IsMigration>false</IsMigration>
- <PartnerScenario>Initial</PartnerScenario>
- </ABApplicationHeader>
- <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
- <ManagedGroupRequest>false</ManagedGroupRequest>
- <TicketToken>'.$ticket.'</TicketToken>
- </ABAuthHeader>
-</soap:Header>
-<soap:Body>
- <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
- <serviceFilter>
- <Types>
- <ServiceType>Messenger</ServiceType>
- <ServiceType>Invitation</ServiceType>
- <ServiceType>SocialNetwork</ServiceType>
- <ServiceType>Space</ServiceType>
- <ServiceType>Profile</ServiceType>
- </Types>
- </serviceFilter>
- </FindMembership>
-</soap:Body>
-</soap:Envelope>';
- $header_array = array(
- 'SOAPAction: '.$this->membership_soap,
- 'Content-Type: text/xml; charset=utf-8',
- 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
- );
- $this->debug_message("*** URL: $this->membership_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->membership_url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code != 200) return array();
- $p = $data;
- $aMemberships = array();
- while (1) {
- //$this->debug_message("search p = $p");
- $start = strpos($p, '<Membership>');
- $end = strpos($p, '</Membership>');
- if ($start === false || $end === false || $start > $end) break;
- //$this->debug_message("start = $start, end = $end");
- $end += 13;
- $sMembership = substr($p, $start, $end - $start);
- $aMemberships[] = $sMembership;
- //$this->debug_message("add sMembership = $sMembership");
- $p = substr($p, $end);
- }
- //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
-
- $aContactList = array();
- foreach ($aMemberships as $sMembership) {
- //$this->debug_message("sMembership = $sMembership");
- if (isset($matches)) unset($matches);
- preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
- if (count($matches) == 0) continue;
- $sMemberRole = $matches[1];
- //$this->debug_message("MemberRole = $sMemberRole");
- if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
- $p = $sMembership;
- if (isset($aMembers)) unset($aMembers);
- $aMembers = array();
- while (1) {
- //$this->debug_message("search p = $p");
- $start = strpos($p, '<Member xsi:type="');
- $end = strpos($p, '</Member>');
- if ($start === false || $end === false || $start > $end) break;
- //$this->debug_message("start = $start, end = $end");
- $end += 9;
- $sMember = substr($p, $start, $end - $start);
- $aMembers[] = $sMember;
- //$this->debug_message("add sMember = $sMember");
- $p = substr($p, $end);
- }
- //$this->debug_message("aMembers = ".var_export($aMembers, true));
- foreach ($aMembers as $sMember) {
- //$this->debug_message("sMember = $sMember");
- if (isset($matches)) unset($matches);
- preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
- if (count($matches) == 0) continue;
- $sMemberType = $matches[1];
- //$this->debug_message("MemberType = $sMemberType");
- $network = -1;
- preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
- if (count($matches) == 0) continue;
- $id = $matches[1];
- if ($sMemberType == 'PassportMember') {
- if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
- $network = 1;
- preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
- }
- else if ($sMemberType == 'EmailMember') {
- if (strpos($sMember, '<Type>Email</Type>') === false) continue;
- // Value is 32: or 32:YAHOO
- preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
- if (count($matches) == 0) continue;
- if ($matches[1] != 32) continue;
- $network = 32;
- preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
- }
- if ($network == -1) continue;
- if (count($matches) > 0) {
- $email = $matches[1];
- @list($u_name, $u_domain) = @explode('@', $email);
- if ($u_domain == NULL) continue;
- $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
- $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
- }
- }
- }
- return $aContactList;
- }
-
- function connect($user, $password)
- {
- $this->id = 1;
- $this->fp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
- if (!$this->fp) {
- $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
- return false;
- }
-
- stream_set_timeout($this->fp, $this->stream_timeout);
- $this->authed = false;
- // MSNP9
- // NS: >> VER {id} MSNP9 CVR0
- // MSNP15
- // NS: >>> VER {id} MSNP15 CVR0
- $this->writeln("VER $this->id $this->protocol CVR0");
-
- $start_tm = time();
- while (!feof($this->fp)) {
- $data = $this->readln();
- // no data?
- if ($data === false) {
- if ($this->timeout > 0) {
- $now_tm = time();
- $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
- if ($used_time > $this->timeout) {
- // logout now
- // NS: >>> OUT
- $this->writeln("OUT");
- fclose($this->fp);
- $this->error = 'Timeout, maybe protocol changed!';
- $this->debug_message("*** $this->error");
- return false;
- }
- }
- continue;
- }
- $code = substr($data, 0, 3);
- $start_tm = time();
- switch ($code) {
- case 'VER':
- // MSNP9
- // NS: <<< VER {id} MSNP9 CVR0
- // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
- // MSNP15
- // NS: <<< VER {id} MSNP15 CVR0
- // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
- $this->writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
- break;
-
- case 'CVR':
- // MSNP9
- // NS: <<< CVR {id} {ver_list} {download_serve} ....
- // NS: >>> USR {id} TWN I {user}
- // MSNP15
- // NS: <<< CVR {id} {ver_list} {download_serve} ....
- // NS: >>> USR {id} SSO I {user}
- $this->writeln("USR $this->id $this->login_method I $user");
- break;
-
- case 'USR':
- // already login for passport site, finish the login process now.
- // NS: <<< USR {id} OK {user} {verify} 0
- if ($this->authed) return true;
-
- $this->user = $user;
- $this->password = urlencode($password);
-
- if ($this->protocol == 'MSNP15') {
- // NS: <<< USR {id} SSO S {policy} {nonce}
- @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
-
- $this->passport_policy = $policy;
- $aTickets = $this->get_passport_ticket();
- if (!$aTickets || !is_array($aTickets)) {
- // logout now
- // NS: >>> OUT
- $this->writeln("OUT");
- fclose($this->fp);
- $this->error = 'Passport authenticated fail!';
- $this->debug_message("*** $this->error");
- return false;
- }
-
- $ticket = $aTickets['ticket'];
- $secret = $aTickets['secret'];
- $this->oim_ticket = $aTickets['oim_ticket'];
- $this->contact_ticket = $aTickets['contact_ticket'];
- $this->web_ticket = $aTickets['web_ticket'];
- $this->space_ticket = $aTickets['space_ticket'];
- $this->storage_ticket = $aTickets['storage_ticket'];
-
- $login_code = $this->generateLoginBLOB($secret, $nonce);
-
- // NS: >>> USR {id} SSO S {ticket} {login_code}
- $this->writeln("USR $this->id $this->login_method S $ticket $login_code");
- }
- else {
- // NS: <<< USR {id} TWN S {nonce}
- @list(/* USR */, /* id */, /* TWN */, /* S */, $nonce,) = @explode(' ', $data);
-
- $ticket = $this->get_tweener_passport_ticket($nonce);
- if (!$ticket) {
- // logout now
- // NS: >>> OUT
- $this->writeln("OUT");
- fclose($this->fp);
- $this->error = 'Passport authenticated fail!';
- $this->debug_message("*** $this->error");
- return false;
- }
-
- // NS: >>> USR {id} TWN S {ticket}
- $this->writeln("USR $this->id $this->login_method S $ticket");
- }
- $this->authed = true;
- break;
-
- case 'XFR':
- // main login server will redirect to anther NS after USR command
- // MSNP9
- // NS: <<< XFR {id} NS {server} 0 {server}
- // MSNP15
- // NS: <<< XFR {id} NS {server} U D
- @list(/* XFR */, /* id */, /* NS */, $server, /* ... */) = @explode(' ', $data);
- @list($ip, $port) = @explode(':', $server);
- // this connection will close after XFR
- fclose($this->fp);
-
- $this->fp = @fsockopen($ip, $port, $errno, $errstr, 5);
- if (!$this->fp) {
- $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
- $this->debug_message("*** $this->error");
- return false;
- }
-
- stream_set_timeout($this->fp, $this->stream_timeout);
- // MSNP9
- // NS: >> VER {id} MSNP9 CVR0
- // MSNP15
- // NS: >>> VER {id} MSNP15 CVR0
- $this->writeln("VER $this->id $this->protocol CVR0");
- break;
-
- case 'GCF':
- // return some policy data after 'USR {id} SSO I {user}' command
- // NS: <<< GCF 0 {size}
- @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
- // we don't need the data, just read it and drop
- if (is_numeric($size) && $size > 0)
- $this->readdata($size);
- break;
-
- default:
- // we'll quit if got any error
- if (is_numeric($code)) {
- // logout now
- // NS: >>> OUT
- $this->writeln("OUT");
- fclose($this->fp);
- $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
- $this->debug_message("*** $this->error");
- return false;
- }
- // unknown response from server, just ignore it
- break;
- }
- }
- // never goto here
- }
-
- function derive_key($key, $magic)
- {
- $hash1 = mhash(MHASH_SHA1, $magic, $key);
- $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
- $hash3 = mhash(MHASH_SHA1, $hash1, $key);
- $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
- return $hash2.substr($hash4, 0, 4);
- }
-
- function generateLoginBLOB($key, $challenge)
- {
- $key1 = base64_decode($key);
- $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
- $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
-
- // get hash of challenge using key2
- $hash = mhash(MHASH_SHA1, $challenge, $key2);
-
- // get 8 bytes random data
- $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
-
- $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
-
- $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
- $blob .= $iv;
- $blob .= $hash;
- $blob .= $cipher;
-
- return base64_encode($blob);
- }
-
- function getOIM_maildata()
- {
- preg_match('#t=(.*)&p=(.*)#', $this->web_ticket, $matches);
- if (count($matches) == 0) {
- $this->debug_message('*** no web ticket?');
- return false;
- }
- $t = htmlspecialchars($matches[1]);
- $p = htmlspecialchars($matches[2]);
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
-<soap:Header>
- <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
- <t>'.$t.'</t>
- <p>'.$p.'</p>
- </PassportCookie>
-</soap:Header>
-<soap:Body>
- <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
-</soap:Body>
-</soap:Envelope>';
-
- $header_array = array(
- 'SOAPAction: '.$this->oim_maildata_soap,
- 'Content-Type: text/xml; charset=utf-8',
- 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
- );
-
- $this->debug_message("*** URL: $this->oim_maildata_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code != 200) {
- $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
- return false;
- }
-
- // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
- preg_match('<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
- if (count($matches) == 0) {
- $this->debug_message("*** Can't get OIM maildata");
- return '';
- }
- return $matches[2];
- }
-
- function getOIM_message($msgid)
- {
- preg_match('#t=(.*)&p=(.*)#', $this->web_ticket, $matches);
- if (count($matches) == 0) {
- $this->debug_message('*** no web ticket?');
- return false;
- }
- $t = htmlspecialchars($matches[1]);
- $p = htmlspecialchars($matches[2]);
-
- // read OIM
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
-<soap:Header>
- <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
- <t>'.$t.'</t>
- <p>'.$p.'</p>
- </PassportCookie>
-</soap:Header>
-<soap:Body>
- <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
- <messageId>'.$msgid.'</messageId>
- <alsoMarkAsRead>false</alsoMarkAsRead>
- </GetMessage>
-</soap:Body>
-</soap:Envelope>';
-
- $header_array = array(
- 'SOAPAction: '.$this->oim_read_soap,
- 'Content-Type: text/xml; charset=utf-8',
- 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
- );
-
- $this->debug_message("*** URL: $this->oim_read_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code != 200) {
- $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
- return false;
- }
-
- // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
- // multi-lines?
- $start = strpos($data, '<GetMessageResult>');
- $end = strpos($data, '</GetMessageResult>');
- if ($start === false || $end === false || $start > $end) {
- $this->debug_message("*** Can't get OIM: $msgid");
- return false;
- }
- $lines = substr($data, $start + 18, $end - $start);
- $aLines = @explode("\n", $lines);
- $header = true;
- $ignore = false;
- $sOIM = '';
- foreach ($aLines as $line) {
- $line = trim($line);
- if ($header) {
- if ($line === '') {
- $header = false;
- continue;
- }
- continue;
- }
- // stop at empty lines
- if ($line === '') break;
- $sOIM .= $line;
- }
- $sMsg = base64_decode($sOIM);
- $this->debug_message("*** we get OIM ($msgid): $sMsg");
-
- // delete OIM
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
-<soap:Header>
- <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
- <t>'.$t.'</t>
- <p>'.$p.'</p>
- </PassportCookie>
-</soap:Header>
-<soap:Body>
- <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
- <messageIds>
- <messageId>'.$msgid.'</messageId>
- </messageIds>
- </DeleteMessages>
-</soap:Body>
-</soap:Envelope>';
-
- $header_array = array(
- 'SOAPAction: '.$this->oim_del_soap,
- 'Content-Type: text/xml; charset=utf-8',
- 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
- );
-
- $this->debug_message("*** URL: $this->oim_del_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code != 200)
- $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
- else
- $this->debug_message("*** OIM ($msgid) deleted");
- return $sMsg;
- }
-
- function doLoop($user, $password, $alias = '',
- $my_function = false, $my_add_function = false, $my_rem_function = false,
- $use_ping = false, $retry_wait = 30, $backup_file = true, $update_pending = true)
- {
- if ($this->kill_me) return;
- $this->log_message("*** startup ***");
- $process_file = false;
- $sent = false;
- $online = false;
- $aADL = array();
- $ping_wait = 50;
- $aContactList = array();
- $first = true;
- while (1) {
- if ($this->kill_me) {
- if (is_resource($this->fp) && !feof($this->fp)) {
- // logout now
- // NS: >>> OUT
- $this->writeln("OUT");
- fclose($this->fp);
- $this->fp = false;
- $this->log_message("*** logout now!");
- }
- $this->log_message("*** Okay, kill me now!");
- break;
- }
- if (!is_resource($this->fp) || feof($this->fp)) {
- if ($first)
- $first = false;
- else {
- $this->log_message("*** wait for $retry_wait seconds");
- sleep($retry_wait);
- }
- if ($this->kill_me) continue;
- $this->log_message("*** try to connect to MSN network");
- if (!$this->connect($user, $password)) {
- $this->log_message("!!! Can't connect to server: $this->error");
- continue;
- }
- if ($this->protocol == 'MSNP9') {
- // we need to send SYN command for MSNP9
- // NS: >>> SYN {id} 0
- $this->writeln("SYN $this->id 0");
- }
- $this->log_message("*** connected, wait for command");
- $start_tm = time();
- $ping_tm = time();
- stream_set_timeout($this->fp, $this->stream_timeout);
- }
- $data = $this->readln();
- if ($data === false) {
- if ($this->kill_me) continue;
- if ($process_file !== false && $sent !== true) {
- // some error for sending message
- $precess_file = false;
- }
- // check here, do we have any message need to send?
- $aFiles = glob(dirname($_SERVER['argv'][0]).'/spool/*.msn');
- if (!is_array($aFiles)) continue;
- clearstatcache();
- foreach ($aFiles as $filename) {
- if (fileperms($filename) != (0x8000 | 00666)) continue;
- $fp = fopen($filename, 'rt');
- if (!$fp) continue;
- $aTo = array();
- $sMessage = '';
- $buf = trim(fgets($fp));
- if (substr($buf, 0, 3) == 'TO:') {
- $to_str = trim(substr($buf, 3));
- $aTo = @explode(',', $to_str);
- while (!feof($fp)) {
- $buf = trim(fgets($fp));
- $sMessage .= $buf;
- $sMessage .= "\r\n";
- }
- }
- fclose($fp);
- if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '') {
- $this->log_message("!!! message format error? delete $filename");
- if ($backup_file) {
- $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
- if (!file_exists($backup_dir))
- @mkdir($backup_dir);
- $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($filename);
- if (@rename($filename, $backup_name))
- $this->log_message("*** move file to $backup_name");
- }
- @unlink($filename);
- continue;
- }
- // assign process_file
- $process_file = $filename;
- break;
- }
-
- if ($process_file === false) {
- if ($online && $use_ping) {
- $now = time();
- if ($now < $ping_tm)
- $len = $now;
- else
- $len = $now - $ping_tm;
- if ($len > $ping_wait) {
- // NS: >>> PNG
- $this->writeln("PNG");
- $ping_tm = time();
- }
- }
- continue;
- }
- $sent = false;
-
- $this->log_message("*** try to send message from $process_file");
- $this->log_message("*** TO: $to_str");
- $this->log_message("*** MSG: $sMessage");
-
- // okay, try to ask a switchboard (SB) for sending message
- // NS: >>> XFR {id} SB
- $this->writeln("XFR $this->id SB");
- continue;
- }
- $code = substr($data, 0, 3);
-
- switch ($code) {
- case 'SBS':
- // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
- // NS: <<< SBS 0 null
- $aContactList = $this->getMembershipList();
- if ($update_pending) {
- if (is_array($aContactList)) {
- $pending = 'Pending';
- foreach ($aContactList as $u_domain => $aUserList) {
- foreach ($aUserList as $u_name => $aNetworks) {
- foreach ($aNetworks as $network => $aData) {
- if (isset($aData[$pending])) {
- // pending list
- $cnt = 0;
- foreach (array('Allow', 'Reverse') as $list) {
- if (isset($aData[$list]))
- $cnt++;
- else {
- if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
- $aContactList[$u_domain][$u_name][$network][$list] = false;
- $cnt++;
- }
- }
- }
- if ($cnt >= 2) {
- $id = $aData[$pending];
- // we can delete it from pending now
- if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
- unset($aContactList[$u_domain][$u_name][$network][$pending]);
- }
- }
- else {
- // sync list
- foreach (array('Allow', 'Reverse') as $list) {
- if (!isset($aData[$list])) {
- if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
- $aContactList[$u_domain][$u_name][$network][$list] = false;
- }
- }
- }
- }
- }
- }
- }
- }
- $n = 0;
- $sList = '';
- $len = 0;
- if (is_array($aContactList)) {
- foreach ($aContactList as $u_domain => $aUserList) {
- $str = '<d n="'.$u_domain.'">';
- $len += strlen($str);
- if ($len > 7400) {
- $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
- $n++;
- $sList = '';
- $len = strlen($str);
- }
- $sList .= $str;
- foreach ($aUserList as $u_name => $aNetworks) {
- foreach ($aNetworks as $network => $status) {
- $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
- $len += strlen($str);
- // max: 7500, but <ml l="1"></d></ml> is 19,
- // so we use 7475
- if ($len > 7475) {
- $sList .= '</d>';
- $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
- $n++;
- $sList = '<d n="'.$u_domain.'">'.$str;
- $len = strlen($sList);
- }
- else
- $sList .= $str;
- }
- }
- $sList .= '</d>';
- }
- }
- $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
- // NS: >>> BLP {id} BL
- $this->writeln("BLP $this->id BL");
- foreach ($aADL as $str) {
- $len = strlen($str);
- // NS: >>> ADL {id} {size}
- $this->writeln("ADL $this->id $len");
- $this->writedata($str);
- }
- // NS: >>> PRP {id} MFN name
- if ($alias == '') $alias = $user;
- $aliasname = rawurlencode($alias);
- $this->writeln("PRP $this->id MFN $aliasname");
- // NS: >>> CHG {id} {status} {clientid} {msnobj}
- $this->writeln("CHG $this->id NLN");
- $online = true;
- break;
-
- case 'RFS':
- // FIXME:
- // NS: <<< RFS ???
- // refresh ADL, so we re-send it again
- if (is_array($aADL)) {
- foreach ($aADL as $str) {
- $len = strlen($str);
- // NS: >>> ADL {id} {size}
- $this->writeln("ADL $this->id $len");
- $this->writedata($str);
- }
- }
- break;
-
- case 'LST':
- // NS: <<< LST {email} {alias} 11 0
- @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
- @list($u_name, $u_domain) = @explode('@', $email);
- if (!isset($aContactList[$u_domain][$u_name][1])) {
- $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
- $this->log_message("*** add to our contact list: $u_name@$u_domain");
- }
- break;
-
- case 'ADD':
- // randomly, we get ADD command, someome add us to their contact list for MSNP9
- // NS: <<< ADD 0 {list} {0} {email} {alias}
- @list(/* ADD */, /* 0 */, $u_list, /* 0 */, $u_email, /* alias */) = @explode(' ', $data);
- @list($u_name, $u_domain) = @explode('@', $u_email);
- if (isset($aContactList[$u_domain][$u_name][1]['Allow']))
- $this->log_message("*** someone add us to their list (but already in our list): $u_name@$u_domain");
- else {
- $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
- $this->log_message("*** someone add us to their list: $u_name@$u_domain");
- }
- if ($my_add_function && function_exists($my_add_function) && is_callable($my_add_function))
- $my_add_function($u_email);
- break;
-
- case 'REM':
- // randomly, we get REM command, someome remove us from their contact list for MSNP9
- // NS: <<< REM 0 {list} {0} {email}
- @list(/* REM */, /* 0 */, $u_list, /* 0 */, $u_email,) = @explode(' ', $data);
- @list($u_name, $u_domain) = @explode('@', $u_email);
- if (isset($aContactList[$u_domain][$u_name][1])) {
- unset($aContactList[$u_domain][$u_name][1]);
- $this->log_message("*** someone remove us from their list: $u_name@$u_domain");
- }
- else
- $this->log_message("*** someone remove us from their list (but not in our list): $u_name@$u_domain");
- if ($my_rem_function && function_exists($my_rem_function) && is_callable($my_rem_function))
- $my_rem_function($u_email);
- break;
-
- case 'ADL':
- // randomly, we get ADL command, someome add us to their contact list for MSNP15
- // NS: <<< ADL 0 {size}
- @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
- if (is_numeric($size) && $size > 0) {
- $data = $this->readdata($size);
- preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
- if (is_array($matches) && count($matches) > 0) {
- $u_domain = $matches[1];
- $u_name = $matches[2];
- $network = $matches[4];
- if (isset($aContactList[$u_domain][$u_name][$network]))
- $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
- else {
- $re_login = false;
- $cnt = 0;
- foreach (array('Allow', 'Reverse') as $list) {
- if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
- if ($re_login) {
- $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
- continue;
- }
- $aTickets = $this->get_passport_ticket();
- if (!$aTickets || !is_array($aTickets)) {
- // failed to login? ignore it
- $this->log_message("*** can't re-login, something wrong here");
- $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
- continue;
- }
- $re_login = true;
- $this->oim_ticket = $aTickets['oim_ticket'];
- $this->contact_ticket = $aTickets['contact_ticket'];
- $this->web_ticket = $aTickets['web_ticket'];
- $this->space_ticket = $aTickets['space_ticket'];
- $this->storage_ticket = $aTickets['storage_ticket'];
- $this->log_message("**** get new ticket, try it again");
- if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
- $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
- continue;
- }
- }
- $aContactList[$u_domain][$u_name][$network][$list] = false;
- $cnt++;
- }
- $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
- }
- $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
- $len = strlen($str);
- // NS: >>> ADL {id} {size}
- $this->writeln("ADL $this->id $len");
- $this->writedata($str);
- if ($my_add_function && function_exists($my_add_function) && is_callable($my_add_function))
- $my_add_function($u_name.'@'.$u_domain, $network);
- }
- else {
- $this->log_message("*** someone add us to their list: $data");
- }
- }
- break;
-
- case 'RML':
- // randomly, we get RML command, someome remove us to their contact list for MSNP15
- // NS: <<< RML 0 {size}
- @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
- if (is_numeric($size) && $size > 0) {
- $data = $this->readdata($size);
- preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
- if (is_array($matches) && count($matches) > 0) {
- $u_domain = $matches[1];
- $u_name = $matches[2];
- $network = $matches[4];
- if (isset($aContactList[$u_domain][$u_name][$network])) {
- unset($aContactList[$u_domain][$u_name][$network]);
- $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
- }
- else
- $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
- if ($my_rem_function && function_exists($my_rem_function) && is_callable($my_rem_function))
- $my_rem_function($u_name.'@'.$u_domain, $network);
- }
- else {
- $this->log_message("*** someone remove us from their list: $data");
- }
- }
- break;
-
- case 'MSG':
- // randomly, we get MSG notification from server
- // NS: <<< MSG Hotmail Hotmail {size}
- @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
- if (is_numeric($size) && $size > 0) {
- $data = $this->readdata($size);
- $aLines = @explode("\n", $data);
- $header = true;
- $ignore = false;
- $maildata = '';
- foreach ($aLines as $line) {
- $line = trim($line);
- if ($header) {
- if ($line === '') {
- $header = false;
- continue;
- }
- if (strncasecmp($line, 'Content-Type:', 13) == 0) {
- if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
- strpos($line, 'text/x-msmsgsoimnotification') === false) {
- // we just need text/x-msmsgsinitialmdatanotification
- // or text/x-msmsgsoimnotification
- $ignore = true;
- break;
- }
- }
- continue;
- }
- if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
- $maildata = trim(substr($line, 10));
- break;
- }
- }
- if ($ignore) {
- $this->log_message("*** ingnore MSG for: $line");
- break;
- }
- if ($maildata == '') {
- $this->log_message("*** ingnore MSG not for OIM");
- break;
- }
- $re_login = false;
- if (strcasecmp($maildata, 'too-large') == 0) {
- $this->log_message("*** large mail-data, need to get the data via SOAP");
- $maildata = $this->getOIM_maildata();
- if ($maildata === false) {
- $this->log_message("*** can't get mail-data via SOAP");
- // maybe we need to re-login again
- $aTickets = $this->get_passport_ticket();
- if (!$aTickets || !is_array($aTickets)) {
- // failed to login? ignore it
- $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
- break;
- }
- $re_login = true;
- $this->oim_ticket = $aTickets['oim_ticket'];
- $this->contact_ticket = $aTickets['contact_ticket'];
- $this->web_ticket = $aTickets['web_ticket'];
- $this->space_ticket = $aTickets['space_ticket'];
- $this->storage_ticket = $aTickets['storage_ticket'];
- $this->log_message("**** get new ticket, try it again");
- $maildata = $this->getOIM_maildata();
- if ($maildata === false) {
- $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
- break;
- }
- }
- }
- // could be a lots of <M>...</M>, so we can't use preg_match here
- $p = $maildata;
- $aOIMs = array();
- while (1) {
- $start = strpos($p, '<M>');
- $end = strpos($p, '</M>');
- if ($start === false || $end === false || $start > $end) break;
- $end += 4;
- $sOIM = substr($p, $start, $end - $start);
- $aOIMs[] = $sOIM;
- $p = substr($p, $end);
- }
- if (count($aOIMs) == 0) {
- $this->log_message("*** ingnore empty OIM");
- break;
- }
- foreach ($aOIMs as $maildata) {
- // T: 11 for MSN, 13 for Yahoo
- // S: 6 for MSN, 7 for Yahoo
- // RT: the datetime received by server
- // RS: already read or not
- // SZ: size of message
- // E: sender
- // I: msgid
- // F: always 00000000-0000-0000-0000-000000000009
- // N: sender alias
- preg_match('#<T>(.*)</T>#', $maildata, $matches);
- if (count($matches) == 0) {
- $this->log_message("*** ingnore OIM maildata without <T>type</T>");
- continue;
- }
- $oim_type = $matches[1];
- if ($oim_type = 13)
- $network = 32;
- else
- $network = 1;
- preg_match('#<E>(.*)</E>#', $maildata, $matches);
- if (count($matches) == 0) {
- $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
- continue;
- }
- $oim_sender = $matches[1];
- preg_match('#<I>(.*)</I>#', $maildata, $matches);
- if (count($matches) == 0) {
- $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
- continue;
- }
- $oim_msgid = $matches[1];
- preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
- $oim_size = (count($matches) == 0) ? 0 : $matches[1];
- preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
- $oim_time = (count($matches) == 0) ? 0 : $matches[1];
- $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
- $sMsg = $this->getOIM_message($oim_msgid);
- if ($sMsg === false) {
- $this->log_message("*** can't get OIM, msgid = $oim_msgid");
- if ($re_login) {
- $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
- continue;
- }
- $aTickets = $this->get_passport_ticket();
- if (!$aTickets || !is_array($aTickets)) {
- // failed to login? ignore it
- $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
- continue;
- }
- $re_login = true;
- $this->oim_ticket = $aTickets['oim_ticket'];
- $this->contact_ticket = $aTickets['contact_ticket'];
- $this->web_ticket = $aTickets['web_ticket'];
- $this->space_ticket = $aTickets['space_ticket'];
- $this->storage_ticket = $aTickets['storage_ticket'];
- $this->log_message("**** get new ticket, try it again");
- $sMsg = $this->getOIM_message($oim_msgid);
- if ($sMsg === false) {
- $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
- continue;
- }
- }
- $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
- if ($my_function && function_exists($my_function) && is_callable($my_function)) {
- $sMessage = $my_function($oim_sender, $sMsg, $network);
- if ($sMessage !== '') {
- $now = strftime('%D %T');
- $fname = dirname($_SERVER['argv'][0]).'/spool/msn_'.posix_getpid().'_'.md5('offline'.rand(1,1000).$now).'.msn';
- $fp = fopen($fname, 'wt');
- if ($fp) {
- fputs($fp, "TO: $oim_sender@$network\n");
- fputs($fp, $sMessage);
- fclose($fp);
- chmod($fname, 0666);
- $this->log_message("Response to $oim_sender (Offline, network: $network): $fname");
- }
- }
- }
- }
- }
- break;
-
- case 'UBM':
- // randomly, we get UBM, this is the message from other network, like Yahoo!
- // NS: <<< UBM {email} $network $type {size}
- @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
- if (is_numeric($size) && $size > 0) {
- $data = $this->readdata($size);
- $aLines = @explode("\n", $data);
- $header = true;
- $ignore = false;
- $sMsg = '';
- foreach ($aLines as $line) {
- $line = trim($line);
- if ($header) {
- if ($line === '') {
- $header = false;
- continue;
- }
- if (strncasecmp($line, 'TypingUser:', 11) == 0) {
- $ignore = true;
- break;
- }
- continue;
- }
- if ($sMsg !== '')
- $sMsg .= "\r\n";
- $sMsg .= $line;
- }
- if ($ignore) {
- $this->log_message("*** ingnore from $from_email: $line");
- break;
- }
- $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
- if ($my_function && function_exists($my_function) && is_callable($my_function)) {
- $msg = $my_function($from_email, $sMsg, $network);
- if ($msg !== '') {
- /* typing?
- $message = "MIME-Version: 1.0\r\nContent-Type: text/x-msmsgscontrol\r\nTypingUser: $user\r\n\r\n\r\n";
- $len = strlen($message);
- $this->writeln("UUM $this->id $from_email $network 2 $len");
- $this->writedata($message);
- */
- $message = "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$msg";
- $len = strlen($message);
- $this->writeln("UUM $this->id $from_email $network 1 $len");
- $this->writedata($message);
- $this->log_message("Response to $from_email (network: $network):\n$msg");
- }
- }
- }
- break;
-
- case 'UBX':
- // randomly, we get UBX notification from server
- // NS: <<< UBX email {network} {size}
- @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
- // we don't need the notification data, so just ignore it
- if (is_numeric($size) && $size > 0)
- $this->readdata($size);
- break;
-
- case 'CHL':
- // randomly, we'll get challenge from server
- // NS: <<< CHL 0 {code}
- @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
- $fingerprint = $this->getChallenge($chl_code);
- // NS: >>> QRY {id} {product_id} 32
- // NS: >>> fingerprint
- $this->writeln("QRY $this->id $this->prod_id 32");
- $this->writedata($fingerprint);
- break;
-
- case 'SYN':
- if ($this->protocol != 'MSNP9') break;
- // NS: <<< SYN 8 1 2 1
- // ignore it
- // change our status to online first
- // NS: >>> CHG {id} {status}
- $this->writeln("CHG $this->id NLN");
- $online = true;
- break;
-
- case 'CHG':
- // NS: <<< CHG {id} {status} {code}
- // ignore it
- // change our status to online first
- break;
-
- case 'XFR':
- // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
- @list(/* XFR */, /* {id} */, /* SB */, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
- @list($ip, $port) = @explode(':', $server);
- $this->error = '';
- $aSBresult = $this->switchboard_control($ip, $port, $cki_code, $aTo, $sMessage);
- if ($aSBresult === false) {
- // error for switchboard, quit
- $this->log_message("!!! error for sending message: $this->error");
- $process_file = false;
- $sent = false;
- break;
- }
- if ($aSBresult === true) {
- $this->log_message("*** already send message from $process_file");
- if ($backup_file) {
- $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
- if (!file_exists($backup_dir))
- @mkdir($backup_dir);
- $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($process_file);
- if (@rename($process_file, $backup_name))
- $this->log_message("*** move file to $backup_name");
- }
- @unlink($process_file);
- $process_file = false;
- $sent = true;
- break;
- }
- // okay, process offline user or other networks
- if ($this->protocol == 'MSNP9') {
- // MSNP9 don't support OIM
- $this->debug_message("*** MSNP9 don't support OIM, so we ignore offline user and other networks!");
- $this->log_message("*** already send message from $process_file");
- if ($backup_file) {
- $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
- if (!file_exists($backup_dir))
- @mkdir($backup_dir);
- $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($process_file);
- if (@rename($process_file, $backup_name))
- $this->log_message("*** move file to $backup_name");
- }
- @unlink($process_file);
- $process_file = false;
- $sent = true;
- break;
- }
- // offline user first
- $lockkey = '';
- $re_login = false;
- foreach ($aSBresult['offline'] as $to) {
- for ($i = 0; $i < $this->oim_try; $i++) {
- $chl_code = $this->sendOIM($to, $sMessage, $lockkey);
- if ($chl_code === true) {
- // finished
- break;
- }
- if ($chl_code === false) {
- if ($re_login) {
- $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
- break;
- }
- $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
- // maybe we need to re-login again
- $aTickets = $this->get_passport_ticket();
- if (!$aTickets || !is_array($aTickets)) {
- // failed to login? ignore it
- $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
- break;
- }
- $re_login = true;
- $this->oim_ticket = $aTickets['oim_ticket'];
- $this->contact_ticket = $aTickets['contact_ticket'];
- $this->web_ticket = $aTickets['web_ticket'];
- $this->space_ticket = $aTickets['space_ticket'];
- $this->storage_ticket = $aTickets['storage_ticket'];
- $this->log_message("**** get new ticket, try it again");
- $i--;
- continue;
- }
- // need challenge lockkey
- $this->log_message("*** we need a new challenge code for $chl_code");
- $lockkey = $this->getChallenge($chl_code);
- }
- }
- // other networks
- foreach ($aSBresult['others'] as $network => $aNetUsers) {
- foreach ($aNetUsers as $to) {
- $message = "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$sMessage";
- $len = strlen($message);
- $this->writeln("UUM $this->id $to $network 1 $len");
- $this->writedata($message);
- $this->log_message("*** sent to $to (network: $network):\n$sMessage");
- }
- }
- $this->log_message("*** already send message from $process_file");
- if ($backup_file) {
- $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
- if (!file_exists($backup_dir))
- @mkdir($backup_dir);
- $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.posix_getpid().'_'.basename($process_file);
- if (@rename($process_file, $backup_name))
- $this->log_message("*** move file to $backup_name");
- }
- @unlink($process_file);
- $process_file = false;
- $sent = true;
- break;
-
- case 'QNG':
- // NS: <<< QNG {time}
- @list(/* QNG */, $ping_wait) = @explode(' ', $data);
- if ($ping_wait == 0) $ping_wait = 50;
- break;
-
- case 'RNG':
- // someone is trying to talk to us
- // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
- @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
- @list($sb_ip, $sb_port) = @explode(':', $server);
- $this->log_message("*** RING from $email, $sb_ip:$sb_port");
- $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket, $my_function);
- break;
-
- case 'OUT':
- // force logout from NS
- // NS: <<< OUT xxx
- fclose($this->fp);
- $this->log_message("*** LOGOUT from NS");
- break;
-
- default:
- if (is_numeric($code)) {
- $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
- $this->debug_message("*** NS: $this->error");
- if ($process_file !== false) {
- // try login again...
- // logout now
- // NS: >>> OUT
- $this->writeln("OUT");
- fclose($this->fp);
- $this->fp = false;
- $this->log_message("!!! logout");
- }
- }
- break;
- }
- }
- }
-
- function sendMessage($sMessage, $aTo)
- {
- if ($this->protocol == 'MSNP9') {
- // we need to send SYN command for MSNP9
- // NS: >>> SYN {id} 0
- $this->writeln("SYN $this->id 0");
- }
- stream_set_timeout($this->fp, $this->stream_timeout);
- $quit = false;
- $online_cnt = 0;
- $offline_cnt = 0;
- $other_cnt = 0;
- $start_tm = time();
- while (!feof($this->fp)) {
- if ($quit) break;
- $data = $this->readln();
- // no data ?
- if ($data === false) {
- if ($this->timeout > 0) {
- $now_tm = time();
- $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
- if ($used_time > $this->timeout) {
- $this->error = 'Timeout, maybe protocol changed!';
- $this->debug_message("*** $this->error");
- break;
- }
- }
- continue;
- }
- $code = substr($data, 0, 3);
- $start_tm = time();
- switch ($code) {
- case 'SBS':
- // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
- // NS: <<< SBS 0 null
- // we don't need profile data, so just ignore it
- // change our status to online first
- // NS: >>> CHG {id} {status}
- $this->writeln("CHG $this->id NLN");
- break;
-
- case 'MSG':
- // randomly, we get MSG notification from server
- // NS: <<< MSG Hotmail Hotmail {size}
- @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
- // we don't need the notification data, so just ignore it
- if (is_numeric($size) && $size > 0)
- $this->readdata($size);
- break;
-
- case 'CHL':
- // randomly, we'll get challenge from server
- // NS: <<< CHL 0 {code}
- @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
- $fingerprint = $this->getChallenge($chl_code);
- // NS: >>> QRY {id} {product_id} 32
- // NS: >>> fingerprint
- $this->writeln("QRY $this->id $this->prod_id 32");
- $this->writedata($fingerprint);
- break;
-
- case 'SYN':
- if ($this->protocol != 'MSNP9') break;
- // NS: <<< SYN 8 1 2 1
- // ignore it
- // change our status to online first
- // NS: >>> CHG {id} {status}
- $this->writeln("CHG $this->id NLN");
- break;
-
- case 'CHG':
- // NS: <<< CHG {id} {status} {code}
- // ignore it
-
- // if message is empty or To list is empty, just quit
- if (count($aTo) == 0 || $sMessage === '') {
- $quit = true;
- break;
- }
- // okay, try to ask a switchboard (SB) for sending message
- // NS: >>> XFR {id} SB
- $this->writeln("XFR $this->id SB");
- break;
-
- case 'XFR':
- // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
- @list(/* XFR */, /* {id} */, /* SB */, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
- @list($ip, $port) = @explode(':', $server);
- $aSBresult = $this->switchboard_control($ip, $port, $cki_code, $aTo, $sMessage);
- if ($aSBresult === false) {
- // error for switchboard, quit
- $quit = true;
- break;
- }
- if ($aSBresult === true) {
- // we're done switchboard, quit
- $quit = true;
- break;
- }
- // okay, process offline user
- if ($this->protocol == 'MSNP9') {
- // MSNP9 don't support OIM
- $this->debug_message("*** MSNP9 don't support OIM, so we ignore offline user!");
- $quit = true;
- break;
- }
- $lockkey = '';
- foreach ($aSBresult['offline'] as $to) {
- for ($i = 0; $i < $this->oim_try; $i++) {
- $chl_code = $this->sendOIM($to, $sMessage, $lockkey);
- if ($chl_code === true) {
- // finished
- break;
- }
- if ($chl_code === false) {
- if ($re_login) {
- $this->debug_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
- break;
- }
- $this->debug_message("*** can't send OIM, maybe ticket expired, try to login again");
- // maybe we need to re-login again
- $aTickets = $this->get_passport_ticket();
- if (!$aTickets || !is_array($aTickets)) {
- // failed to login? ignore it
- $this->debug_message("*** can't re-login, something wrong here, ignore this OIM");
- break;
- }
- $re_login = true;
- $this->oim_ticket = $aTickets['oim_ticket'];
- $this->contact_ticket = $aTickets['contact_ticket'];
- $this->web_ticket = $aTickets['web_ticket'];
- $this->space_ticket = $aTickets['space_ticket'];
- $this->storage_ticket = $aTickets['storage_ticket'];
- $this->debug_message("**** get new ticket, try it again");
- $i--;
- continue;
- }
- // need challenge lockkey
- $this->debug_message("*** we need a new challenge code for $chl_code");
- $lockkey = $this->getChallenge($chl_code);
- }
- }
- // other networks
- foreach ($aSBresult['others'] as $network => $aNetUsers) {
- foreach ($aNetUsers as $to) {
- $other_cnt++;
- $message = "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$sMessage";
- $len = strlen($message);
- $this->writeln("UUM $this->id $to $network 1 $len");
- $this->writedata($message);
- $this->debug_message("*** sent to $to (network: $network):\n$sMessage");
- }
- }
- $quit = true;
- break;
-
- default:
- if (is_numeric($code)) {
- $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
- $this->debug_message("*** NS: $this->error");
- }
- break;
- }
- }
- // logout now
- // NS: >>> OUT
- $this->writeln("OUT");
- fclose($this->fp);
- return array(
- 'online' => $online_cnt,
- 'offline' => $offline_cnt,
- 'others' => $other_cnt
- );
- }
-
- function getChallenge($code)
- {
- if ($this->protocol == 'MSNP9') {
- // simple challenge for MSNP9
- return md5($code.$this->prod_key);
- }
- // MSNP15
- // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
- // Step 1: The MD5 Hash
- $md5Hash = md5($code.$this->prod_key);
- $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
- for ($i = 0; $i < 4; $i++) {
- $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
- $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
- }
-
- // Step 2: A new string
- $chl_id = $code.$this->prod_id;
- $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
-
- $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
- for ($i = 0; $i < count($aID); $i++) {
- $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
- $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
- }
-
- // Step 3: The 64 bit key
- $magic_num = 0x0E79A9C1;
- $str7f = 0x7FFFFFFF;
- $high = 0;
- $low = 0;
- for ($i = 0; $i < count($aID); $i += 2) {
- $temp = $aID[$i];
- $temp = bcmod(bcmul($magic_num, $temp), $str7f);
- $temp = bcadd($temp, $high);
- $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
- $temp = bcmod($temp, $str7f);
-
- $high = $aID[$i+1];
- $high = bcmod(bcadd($high, $temp), $str7f);
- $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
- $high = bcmod($high, $str7f);
-
- $low = bcadd(bcadd($low, $high), $temp);
- }
-
- $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
- $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
-
- $new_high = bcmul($high & 0xFF, 0x1000000);
- $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
- $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
- $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
- // we need integer here
- $high = 0+$new_high;
-
- $new_low = bcmul($low & 0xFF, 0x1000000);
- $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
- $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
- $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
- // we need integer here
- $low = 0+$new_low;
-
- // we just use 32 bits integer, don't need the key, just high/low
- // $key = bcadd(bcmul($high, 0x100000000), $low);
-
- // Step 4: Using the key
- $md5Hash = md5($code.$this->prod_key);
- $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
-
- $hash = '';
- $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
- $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
- $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
- $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
-
- return $hash;
- }
-
- function switchboard_control($ip, $port, $cki_code, $aTo, $sMessage)
- {
- if (is_array($aTo))
- $aUsers = $aTo;
- else
- $aUsers[0] = $aTo;
-
- $aMSNUsers = array();
- $aOtherUsers = array();
- foreach ($aUsers as $user) {
- @list($u_user, $u_domain, $u_network) = @explode('@', $user);
- if ($u_network === '' || $u_network == NULL)
- $u_network = 1;
- $to_email = trim($u_user.'@'.$u_domain);
- if ($u_network == 1)
- $aMSNUsers[] = $to_email;
- else
- $aOtherUsers[$u_network][] = $to_email;
- }
-
- $aOfflineUsers = array();
- $total_cnt = count($aMSNUsers);
-
- if ($total_cnt == 0) {
- $this->sb_writeln("OUT");
- @fclose($this->sb);
- if (count($aOtherUsers) > 0) {
- return array('msn' => true,
- 'offline' => $aOfflineUsers,
- 'others' => $aOtherUsers);
- }
- return true;
- }
-
- $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
- $this->sb = @fsockopen($ip, $port, $errno, $errstr, 5);
- if (!$this->sb) {
- $this->error = "SB: Can't connect to $ip:$port, error => $errno, $errstr";
- $this->debug_message("*** $this->error");
- return false;
- }
-
- stream_set_timeout($this->sb, $this->stream_timeout);
- // SB: >>> USR {id} {user} {cki}
- $this->sb_writeln("USR $this->id $this->user $cki_code");
-
- $sent = false;
- $cal_cnt = 0;
- $other_cnt = 0;
- $offline_cnt = 0;
- $start_tm = time();
- $got_error = false;
- while (!feof($this->sb)) {
- if ($sent) break;
- $data = $this->sb_readln();
- if ($data === false) {
- if ($this->timeout > 0) {
- $now_tm = time();
- $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
- if ($used_time > $this->timeout) {
- $this->error = 'Timeout, maybe protocol changed!';
- $this->debug_message("*** $this->error");
- break;
- }
- }
- continue;
- }
- $code = substr($data, 0, 3);
- $start_tm = time();
- switch($code) {
- case 'USR':
- // SB: <<< USR {id} OK {user} {alias}
- // we don't need the data, just ignore it
- $user = $aMSNUsers[$cal_cnt++];
- // request user to join this switchboard
- // SB: >>> CAL {id} {user}
- $this->sb_writeln("CAL $this->id $user");
- break;
-
- case 'CAL':
- // SB: <<< CAL {id} RINGING {?}
- // we don't need this, just ignore, and wait for other response
- break;
-
- case '217':
- // SB: <<< 217 {id}
- // if user isn't online or no such user, we will get 217.
- // switchboard can't send message if he isn't online
- $aOfflineUsers[$offline_cnt++] = $user;
- // continue process with JOI here
- case 'JOI':
- // SB: <<< JOI {user} {alias} {clientid?}
- // someone join us
- // we don't need the data, just ignore it
- // try to call others if we've
- if ($cal_cnt < $total_cnt) {
- $user = $aMSNUsers[$cal_cnt++];
- // request user to join this switchboard
- // SB: >>> CAL {id} {user}
- $this->sb_writeln("CAL $this->id $user");
- break;
- }
- // no more user here
- // check, all users are offline
- if ($cal_cnt == $offline_cnt) {
- $this->debug_message("*** SB: no any use online! skip to send message!");
- $sent = true;
- break;
- }
- $message = "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$sMessage";
- $len = strlen($message);
- $this->sb_writeln("MSG 20 N $len");
- $this->sb_writedata($message);
- $sent = true;
- break;
-
- default:
- if (is_numeric($code)) {
- $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
- $this->debug_message("*** SB: $this->error");
- $got_error = true;
- }
- break;
- }
- }
- $this->sb_writeln("OUT");
- @fclose($this->sb);
- if ($sent) {
- if (count($aOfflineUsers) > 0 || count($aOtherUsers) > 0) {
- return array('msn' => !$got_error,
- 'offline' => $aOfflineUsers,
- 'others' => $aOtherUsers);
- }
- return !$got_error;
- }
- // if not sent, try to sending via OIM
- return array('msn' => !$got_error,
- 'offline' => $aMSNUsers,
- 'others' => $aOtherUsers);
- }
-
- function switchboard_ring($ip, $port, $sid, $ticket, $my_function)
- {
- $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
- $this->sb = @fsockopen($ip, $port, $errno, $errstr, 5);
- if (!$this->sb) {
- $this->error = "SB: Can't connect to $ip:$port, error => $errno, $errstr";
- $this->debug_message("*** $this->error");
- $this->log_message("!!! $this->error");
- return false;
- }
-
- stream_set_timeout($this->sb, $this->stream_timeout);
- // SB: >>> ANS {id} {user} {ticket} {session_id}
- $this->sb_writeln("ANS $this->id $this->user $ticket $sid");
-
- $start_tm = time();
- while (!feof($this->sb)) {
- $data = $this->sb_readln();
- if ($data === false) {
- if ($this->timeout > 0) {
- $now_tm = time();
- $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
- if ($used_time > $this->timeout) {
- $this->error = 'Timeout, maybe protocol changed!';
- $this->debug_message("*** $this->error");
- $this->log_message("!!! $this->error");
- $this->sb_writeln("OUT");
- fclose($this->sb);
- $this->sb = false;
- return false;
- }
- }
- continue;
- }
- $code = substr($data, 0, 3);
- $start_tm = time();
- switch($code) {
- case 'IRO':
- // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
- @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
- $this->log_message("*** $email join us");
- break;
-
- case 'ANS':
- // SB: <<< ANS {id} OK
- // ignore this
- break;
-
- case 'BYE':
- $this->log_message("*** Quit for BYE");
- $this->sb_writeln("OUT");
- fclose($this->sb);
- $this->sb = false;
- return true;
-
- case 'MSG':
- // SB: <<< MSG {email} {alias} {len}
- @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
- $len = trim($len);
- $data = $this->sb_readdata($len);
- $aLines = @explode("\n", $data);
- $header = true;
- $ignore = false;
- $sMsg = '';
- foreach ($aLines as $line) {
- $line = trim($line);
- if ($header) {
- if ($line === '') {
- $header = false;
- continue;
- }
- if (strncasecmp($line, 'TypingUser:', 11) == 0) {
- $ignore = true;
- break;
- }
- continue;
- }
- if ($sMsg !== '')
- $sMsg .= "\r\n";
- $sMsg .= $line;
- }
- if ($ignore) {
- $this->log_message("*** ingnore from $from_email: $line");
- break;
- }
- $this->log_message("*** MSG from $from_email: $sMsg");
- if ($my_function && function_exists($my_function) && is_callable($my_function)) {
- $msg = $my_function($from_email, $sMsg);
- if ($msg !== '') {
- /* typing
- $message = "MIME-Version: 1.0\r\nContent-Type: text/x-msmsgscontrol\r\nTypingUser: $this->user\r\n\r\n\r\n";
- $len = strlen($message);
- $this->sb_writeln("MSG 20 N $len");
- $this->sb_writedata($message);
- */
- $message = "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$msg";
- $len = strlen($message);
- $this->sb_writeln("MSG 20 N $len");
- $this->sb_writedata($message);
- $this->log_message("Response to $from_email:\n$msg");
- }
- }
- $this->sb_writeln("OUT");
- fclose($this->sb);
- $this->sb = false;
- return true;
-
- default:
- if (is_numeric($code)) {
- $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
- $this->debug_message("*** SB: $this->error");
- }
- break;
- }
- }
- return true;
- }
-
- function sendOIM($to, $sMessage, $lockkey)
- {
- $XML = '<?xml version="1.0" encoding="utf-8"?>
-<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
-<soap:Header>
- <From memberName="'.$this->user.'"
- friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
- xml:lang="zh-TW"
- proxy="MSNMSGR"
- xmlns="http://messenger.msn.com/ws/2004/09/oim/"
- msnpVer="'.$this->protocol.'"
- buildVer="'.$this->buildver.'"/>
- <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
- <Ticket passport="'.htmlspecialchars($this->oim_ticket).'"
- appid="'.$this->prod_id.'"
- lockkey="'.$lockkey.'"
- xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
- <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
- <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
- <MessageNumber>1</MessageNumber>
- </Sequence>
-</soap:Header>
-<soap:Body>
- <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
- <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: base64
-X-OIM-Message-Type: OfflineMessage
-X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
-X-OIM-Sequence-Num: 1
-
-'.chunk_split(base64_encode($sMessage)).'
- </Content>
-</soap:Body>
-</soap:Envelope>';
-
- $header_array = array(
- 'SOAPAction: '.$this->oim_send_soap,
- 'Content-Type: text/xml',
- 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
- );
-
- $this->debug_message("*** URL: $this->oim_send_url");
- $this->debug_message("*** Sending SOAP:\n$XML");
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
- if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
- $data = curl_exec($curl);
- $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $this->debug_message("*** Get Result:\n$data");
-
- if ($http_code == 200) {
- $this->debug_message("*** OIM sent for $to");
- return true;
- }
-
- // the lockkey is invalid, authenticated fail, we need challenge it again
- // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
- preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
- // no challenge, assume it work
- if (count($matches) == 0) {
- //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
- preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
- if (count($matches) == 0) {
- // no error, we assume the OIM is sent
- $this->debug_message("*** OIM sent for $to");
- return true;
- }
- $err_code = $matches[2];
- //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
- preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
- if (count($matches) > 0)
- $err_msg = $matches[1];
- else
- $err_msg = '';
- $this->debug_message("*** OIM failed for $to");
- $this->debug_message("*** OIM Error code: $err_code");
- $this->debug_message("*** OIM Error Message: $err_msg");
- return false;
- }
- // yes, we get new LockKeyChallenge
- $challenge = $matches[2];
- $this->debug_message("*** OIM need new challenge ($challenge) for $to");
- return $challenge;
- }
-
- // read data for specified size
- function readdata($size)
- {
- $data = '';
- $count = 0;
- while (!feof($this->fp)) {
- $buf = @fread($this->fp, $size - $count);
- $data .= $buf;
- $count += strlen($buf);
- if ($count >= $size) break;
- }
- $this->debug_message("NS: data ($size/$count) <<<\n$data");
- return $data;
- }
-
- // read one line
- function readln()
- {
- $data = @fgets($this->fp, 4096);
- if ($data !== false) {
- $data = trim($data);
- $this->debug_message("NS: <<< $data");
- }
- return $data;
- }
-
- // write to server, append \r\n, also increase id
- function writeln($data)
- {
- @fwrite($this->fp, $data."\r\n");
- $this->debug_message("NS: >>> $data");
- $this->id++;
- return;
- }
-
- // write data to server
- function writedata($data)
- {
- @fwrite($this->fp, $data);
- $this->debug_message("NS: >>> $data");
- return;
- }
-
- // read data for specified size for SB
- function sb_readdata($size)
- {
- $data = '';
- $count = 0;
- while (!feof($this->sb)) {
- $buf = @fread($this->sb, $size - $count);
- $data .= $buf;
- $count += strlen($buf);
- if ($count >= $size) break;
- }
- $this->debug_message("SB: data ($size/$count) <<<\n$data");
- return $data;
- }
-
- // read one line for SB
- function sb_readln()
- {
- $data = @fgets($this->sb, 4096);
- if ($data !== false) {
- $data = trim($data);
- $this->debug_message("SB: <<< $data");
- }
- return $data;
- }
-
- // write to server for SB, append \r\n, also increase id
- // switchboard server only accept \r\n, it will lost connection if just \n only
- function sb_writeln($data)
- {
- @fwrite($this->sb, $data."\r\n");
- $this->debug_message("SB: >>> $data");
- $this->id++;
- return;
- }
-
- // write data to server
- function sb_writedata($data)
- {
- @fwrite($this->sb, $data);
- $this->debug_message("SB: >>> $data");
- return;
- }
-
- // show debug information
- function debug_message($str)
- {
- if (!$this->debug) return;
- if ($this->log_file !== '') {
- $fp = fopen($this->log_file, 'at');
- if ($fp) {
- fputs($fp, strftime('%D %T').' ['.posix_getpid().'] '.$str."\n");
- fclose($fp);
- return;
- }
- // still show debug information, if we can't open log_file
- }
- echo $str."\n";
- return;
- }
-
- // write log
- function log_message($str)
- {
- if (!$this->log_path)
- $this->log_path = dirname($_SERVER['argv'][0]);
- $fname = $this->log_path.'/log/msn_'.strftime('%Y%m%d').'.log';
- $fp = fopen($fname, 'at');
- if ($fp) {
- fputs($fp, strftime('%D %T').' ['.posix_getpid().'] '.$str."\n");
- fclose($fp);
- }
- $this->debug_message($str);
- return;
- }
-}
-
-?>
+<?php
+/*
+
+MSN class ver 1.7 by Tommy Wu
+License: GPL
+
+You can find MSN protocol from this site: http://msnpiki.msnfanatic.com/index.php/Main_Page
+
+This class support both MSNP15 and MSNP9 for send message. The PHP module needed:
+
+ MSNP9: curl pcre
+ MSNP15: curl pcre mhash mcrypt bcmath
+
+Usually, this class will try to use MSNP15 if your system can support it, if your system can't support it,
+it will switch to use MSNP9. But if you use MSNP9, it won't support OIM (Offline Messages).
+
+Sameple Code:
+
+$msn = new MSN;
+
+if (!$msn->connect('YOUR_MSN_ID', 'YOUR_MSN_PASSWORD')) {
+ echo "Error for connect to MSN network\n";
+ echo "$msn->error\n";
+ exit;
+}
+
+$msn->sendMessage('Now: '.strftime('%m/%d/%y %H:%M:%S')."\nTesting\nSecond Line\n\n\n\nand Empty Line",
+ array(
+ 'somebody1 at hotmail.com',
+ 'somebody2 at hotmail.com'
+ )
+ );
+echo "Done!\n";
+exit;
+
+*/
+
+class MSN
+{
+ var $server = 'messenger.hotmail.com';
+ var $port = 1863;
+
+ var $passport_url = 'https://login.live.com/RST.srf';
+ var $protocol = 'MSNP15';
+ var $buildver = '8.1.0178';
+ var $prod_key = 'PK}_A_0N_K%O?A9S';
+ var $prod_id = 'PROD0114ES4Z%Q5W';
+ var $login_method = 'SSO';
+
+ var $clientid = '';
+
+ var $oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';
+ var $oim_sendsoap = 'http://messenger.live.com/ws/2006/09/oim/Store2';
+ var $oim_maildata_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
+ var $oim_maildata_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata';
+ var $oim_read_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
+ var $oim_read_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage';
+ var $oim_del_url = 'https://rsi.hotmail.com/rsi/rsi.asmx';
+ var $oim_del_soap = 'http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages';
+
+ var $membership_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
+ var $membership_soap = 'http://www.msn.com/webservices/AddressBook/FindMembership';
+
+ var $addmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
+ var $addmember_soap = 'http://www.msn.com/webservices/AddressBook/AddMember';
+
+ var $delmember_url = 'https://contacts.msn.com/abservice/SharingService.asmx';
+ var $delmember_soap = 'http://www.msn.com/webservices/AddressBook/DeleteMember';
+
+ var $id;
+ var $fp = false;
+ var $error = '';
+
+ var $authed = false;
+ var $user = '';
+ var $password = '';
+
+ var $passport_policy = '';
+ var $oim_try = 3;
+ var $oim_ticket = '';
+ var $contact_ticket = '';
+
+ // FIXME: even we login for following site, but... we don't need that now.
+ var $web_ticket = '';
+ var $space_ticket = '';
+ var $storage_ticket = '';
+
+ var $debug = false;
+ var $log_file = '';
+ var $timeout = 15;
+ var $stream_timeout = 2;
+
+ var $log_path = false;
+
+ var $sb;
+ var $font_fn = 'Arial';
+ var $font_co = '333333';
+ var $font_ef = '';
+
+ var $windows = false;
+ var $kill_me = false;
+
+ function MSN($protocol = '', $debug = false, $timeout = 15, $client_id = 0x7000800C)
+ {
+ if (is_string($debug) && $debug !== '') {
+ $this->debug = true;
+ $this->log_file = $debug;
+ }
+ else
+ $this->debug = $debug;
+ $this->timeout = $timeout;
+ // check support
+ if (!function_exists('curl_init')) die("We need curl module!\n");
+ if (!function_exists('preg_match')) die("We need pcre module!\n");
+
+ if ($protocol != 'MSNP9' && $protocol != 'MSNP15')
+ $protocol = '';
+
+ if ($protocol != 'MSNP9' && !function_exists('mhash')) {
+ if ($protocol == 'MSNP15') die("We need mhash module for $protocol!\n");
+ $protocol = 'MSNP9';
+ }
+ if ($protocol != 'MSNP9' && !function_exists('mcrypt_cbc')) {
+ if ($protocol == 'MSNP15') die("We need mcrypt module for $protocol!\n");
+ $protocol = 'MSNP9';
+ }
+ if ($protocol != 'MSNP9' && !function_exists('bcmod')) {
+ if ($protocol == 'MSNP15') die("We need bcmath module for $protocol!\n");
+ $protocol = 'MSNP9';
+ }
+ if ($protocol == 'MSNP9') {
+ $this->protocol = 'MSNP9';
+ $this->passport_url = 'https://nexus.passport.com/rdr/pprdr.asp';
+ $this->buildver = '6.0.0602';
+ $this->prod_key = 'Q1P7W2E4J9R8U3S5';
+ $this->prod_id = 'msmsgs at msnmsgr.com';
+ $this->login_method = 'TWN';
+ }
+ else {
+ $this->protocol = 'MSNP15';
+ $this->passport_url = 'https://login.live.com/RST.srf';
+ $this->buildver = '8.1.0178';
+ $this->prod_key = 'PK}_A_0N_K%O?A9S';
+ $this->prod_id = 'PROD0114ES4Z%Q5W';
+ $this->login_method = 'SSO';
+
+ $this->oim_send_url = 'https://ows.messenger.msn.com/OimWS/oim.asmx';
+ $this->oim_send_soap = 'http://messenger.live.com/ws/2006/09/oim/Store2';
+
+/*
+ http://msnpiki.msnfanatic.com/index.php/Client_ID
+ Client ID for MSN:
+ normal MSN 8.1 clientid is:
+ 01110110 01001100 11000000 00101100
+ = 0x764CC02C
+
+f we just use following:
+ * 0x04: Your client can send/receive Ink (GIF format)
+ * 0x08: Your client can send/recieve Ink (ISF format)
+ * 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')
+ * 0x70000000: This is the value for MSNC7 (WL Msgr 8.1)
+ = 0x7000800C;
+*/
+ $this->clientid = $client_id;
+ }
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
+ $this->windows = true;
+ else
+ $this->windows = false;
+ return;
+ }
+
+ function get_passport_ticket($url = '')
+ {
+ $user = htmlspecialchars($this->user);
+ $password = htmlspecialchars($this->password);
+
+ if ($url === '')
+ $passport_url = $this->passport_url;
+ else
+ $passport_url = $url;
+
+ $XML = '<?xml version="1.0" encoding="UTF-8"?>
+<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext"
+ xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"
+ xmlns:wsp="http://schemas.xmlsoap.org/ws/2002/12/policy"
+ xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
+ xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
+ xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc"
+ xmlns:wst="http://schemas.xmlsoap.org/ws/2004/04/trust">
+<Header>
+ <ps:AuthInfo xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="PPAuthInfo">
+ <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>
+ <ps:BinaryVersion>4</ps:BinaryVersion>
+ <ps:UIVersion>1</ps:UIVersion>
+ <ps:Cookies></ps:Cookies>
+ <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>
+ </ps:AuthInfo>
+ <wsse:Security>
+ <wsse:UsernameToken Id="user">
+ <wsse:Username>'.$user.'</wsse:Username>
+ <wsse:Password>'.$password.'</wsse:Password>
+ </wsse:UsernameToken>
+ </wsse:Security>
+</Header>
+<Body>
+ <ps:RequestMultipleSecurityTokens xmlns:ps="http://schemas.microsoft.com/Passport/SoapServices/PPCRL" Id="RSTS">
+ <wst:RequestSecurityToken Id="RST0">
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
+ <wsp:AppliesTo>
+ <wsa:EndpointReference>
+ <wsa:Address>http://Passport.NET/tb</wsa:Address>
+ </wsa:EndpointReference>
+ </wsp:AppliesTo>
+ </wst:RequestSecurityToken>
+ <wst:RequestSecurityToken Id="RST1">
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
+ <wsp:AppliesTo>
+ <wsa:EndpointReference>
+ <wsa:Address>messengerclear.live.com</wsa:Address>
+ </wsa:EndpointReference>
+ </wsp:AppliesTo>
+ <wsse:PolicyReference URI="'.$this->passport_policy.'"></wsse:PolicyReference>
+ </wst:RequestSecurityToken>
+ <wst:RequestSecurityToken Id="RST2">
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
+ <wsp:AppliesTo>
+ <wsa:EndpointReference>
+ <wsa:Address>messenger.msn.com</wsa:Address>
+ </wsa:EndpointReference>
+ </wsp:AppliesTo>
+ <wsse:PolicyReference URI="?id=507"></wsse:PolicyReference>
+ </wst:RequestSecurityToken>
+ <wst:RequestSecurityToken Id="RST3">
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
+ <wsp:AppliesTo>
+ <wsa:EndpointReference>
+ <wsa:Address>contacts.msn.com</wsa:Address>
+ </wsa:EndpointReference>
+ </wsp:AppliesTo>
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
+ </wst:RequestSecurityToken>
+ <wst:RequestSecurityToken Id="RST4">
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
+ <wsp:AppliesTo>
+ <wsa:EndpointReference>
+ <wsa:Address>messengersecure.live.com</wsa:Address>
+ </wsa:EndpointReference>
+ </wsp:AppliesTo>
+ <wsse:PolicyReference URI="MBI_SSL"></wsse:PolicyReference>
+ </wst:RequestSecurityToken>
+ <wst:RequestSecurityToken Id="RST5">
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
+ <wsp:AppliesTo>
+ <wsa:EndpointReference>
+ <wsa:Address>spaces.live.com</wsa:Address>
+ </wsa:EndpointReference>
+ </wsp:AppliesTo>
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
+ </wst:RequestSecurityToken>
+ <wst:RequestSecurityToken Id="RST6">
+ <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>
+ <wsp:AppliesTo>
+ <wsa:EndpointReference>
+ <wsa:Address>storage.msn.com</wsa:Address>
+ </wsa:EndpointReference>
+ </wsp:AppliesTo>
+ <wsse:PolicyReference URI="MBI"></wsse:PolicyReference>
+ </wst:RequestSecurityToken>
+ </ps:RequestMultipleSecurityTokens>
+</Body>
+</Envelope>';
+
+ $this->debug_message("*** URL: $passport_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $passport_url);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code != 200) {
+ // sometimes, rediret to another URL
+ // MSNP15
+ //<faultcode>psf:Redirect</faultcode>
+ //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
+ //<faultstring>Authentication Failure</faultstring>
+ if (strpos($data, '<faultcode>psf:Redirect</faultcode>') === false) {
+ $this->debug_message("*** Can't get passport ticket! http code = $http_code");
+ return false;
+ }
+ preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
+ if (count($matches) == 0) {
+ $this->debug_message("*** redirect, but can't get redirect URL!");
+ return false;
+ }
+ $redirect_url = $matches[1];
+ if ($redirect_url == $passport_url) {
+ $this->debug_message("*** redirect, but redirect to same URL!");
+ return false;
+ }
+ $this->debug_message("*** redirect to $redirect_url");
+ return $this->get_passport_ticket($redirect_url);
+ }
+
+ // sometimes, rediret to another URL, also return 200
+ // MSNP15
+ //<faultcode>psf:Redirect</faultcode>
+ //<psf:redirectUrl>https://msnia.login.live.com/pp450/RST.srf</psf:redirectUrl>
+ //<faultstring>Authentication Failure</faultstring>
+ if (strpos($data, '<faultcode>psf:Redirect</faultcode>') !== false) {
+ preg_match("#<psf\:redirectUrl>(.*)</psf\:redirectUrl>#", $data, $matches);
+ if (count($matches) != 0) {
+ $redirect_url = $matches[1];
+ if ($redirect_url == $passport_url) {
+ $this->debug_message("*** redirect, but redirect to same URL!");
+ return false;
+ }
+ $this->debug_message("*** redirect to $redirect_url");
+ return $this->get_passport_ticket($redirect_url);
+ }
+ }
+
+ // no Redurect faultcode or URL
+ // we should get the ticket here
+
+ // we need ticket and secret code
+ // RST1: messengerclear.live.com
+ // <wsse:BinarySecurityToken Id="Compact1">t=tick&p=</wsse:BinarySecurityToken>
+ // <wst:BinarySecret>binary secret</wst:BinarySecret>
+ // RST2: messenger.msn.com
+ // <wsse:BinarySecurityToken Id="PPToken2">t=tick</wsse:BinarySecurityToken>
+ // RST3: contacts.msn.com
+ // <wsse:BinarySecurityToken Id="Compact3">t=tick&p=</wsse:BinarySecurityToken>
+ // RST4: messengersecure.live.com
+ // <wsse:BinarySecurityToken Id="Compact4">t=tick&p=</wsse:BinarySecurityToken>
+ // RST5: spaces.live.com
+ // <wsse:BinarySecurityToken Id="Compact5">t=tick&p=</wsse:BinarySecurityToken>
+ // RST6: storage.msn.com
+ // <wsse:BinarySecurityToken Id="Compact6">t=tick&p=</wsse:BinarySecurityToken>
+ preg_match("#".
+ "<wsse\:BinarySecurityToken Id=\"Compact1\">(.*)</wsse\:BinarySecurityToken>(.*)".
+ "<wst\:BinarySecret>(.*)</wst\:BinarySecret>(.*)".
+ "<wsse\:BinarySecurityToken Id=\"PPToken2\">(.*)</wsse\:BinarySecurityToken>(.*)".
+ "<wsse\:BinarySecurityToken Id=\"Compact3\">(.*)</wsse\:BinarySecurityToken>(.*)".
+ "<wsse\:BinarySecurityToken Id=\"Compact4\">(.*)</wsse\:BinarySecurityToken>(.*)".
+ "<wsse\:BinarySecurityToken Id=\"Compact5\">(.*)</wsse\:BinarySecurityToken>(.*)".
+ "<wsse\:BinarySecurityToken Id=\"Compact6\">(.*)</wsse\:BinarySecurityToken>(.*)".
+ "#",
+ $data, $matches);
+
+ // no ticket found!
+ if (count($matches) == 0) {
+ $this->debug_message("*** Can't get passport ticket!");
+ return false;
+ }
+
+ //$this->debug_message(var_export($matches, true));
+ // matches[0]: all data
+ // matches[1]: RST1 (messengerclear.live.com) ticket
+ // matches[2]: ...
+ // matches[3]: RST1 (messengerclear.live.com) binary secret
+ // matches[4]: ...
+ // matches[5]: RST2 (messenger.msn.com) ticket
+ // matches[6]: ...
+ // matches[7]: RST3 (contacts.msn.com) ticket
+ // matches[8]: ...
+ // matches[9]: RST4 (messengersecure.live.com) ticket
+ // matches[10]: ...
+ // matches[11]: RST5 (spaces.live.com) ticket
+ // matches[12]: ...
+ // matches[13]: RST6 (storage.live.com) ticket
+ // matches[14]: ...
+
+ // so
+ // ticket => $matches[1]
+ // secret => $matches[3]
+ // web_ticket => $matches[5]
+ // contact_ticket => $matches[7]
+ // oim_ticket => $matches[9]
+ // space_ticket => $matches[11]
+ // storage_ticket => $matches[13]
+
+ // yes, we get ticket
+ $aTickets = array(
+ 'ticket' => html_entity_decode($matches[1]),
+ 'secret' => html_entity_decode($matches[3]),
+ 'web_ticket' => html_entity_decode($matches[5]),
+ 'contact_ticket' => html_entity_decode($matches[7]),
+ 'oim_ticket' => html_entity_decode($matches[9]),
+ 'space_ticket' => html_entity_decode($matches[11]),
+ 'storage_ticket' => html_entity_decode($matches[13])
+ );
+ //$this->debug_message(var_export($aTickets, true));
+ return $aTickets;
+ }
+
+ function get_tweener_passport_ticket($nonce)
+ {
+ $this->debug_message("*** URL: $this->passport_url");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->passport_url);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_NOBODY, 1);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ // we need login URL
+ // DALogin=xxx
+ preg_match('/DALogin=(.*?),/', $data, $matches);
+
+ // no URL found!
+ if (count($matches) == 0) {
+ $this->debug_message("*** Can't get passport's URL! http code = $http_code");
+ return false;
+ }
+
+ $url = 'https://'.$matches[1];
+
+ $this->debug_message("*** URL: $url");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array(
+ 'Authorization: Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in='.$this->user.',pwd='.$this->password.','.$nonce,
+ 'Host: login.passport.com'
+ ));
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_NOBODY, 1);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ // we need ticket
+ // from-PP=xxx
+ preg_match("/from-PP='(.*?)'/", $data, $matches);
+
+ // no URL found!
+ if (count($matches) == 0) {
+ $this->debug_message("*** Can't get passport's ticket! http code = $http_code");
+ return false;
+ }
+ return $matches[1];
+ }
+
+ function delMemberFromList($memberID, $email, $network, $list)
+ {
+ if ($network != 1 && $network != 32) return true;
+ if ($memberID === false) return true;
+ $user = htmlspecialchars($email);
+ $ticket = htmlspecialchars($this->contact_ticket);
+ if ($network == 1)
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+<soap:Header>
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
+ <IsMigration>false</IsMigration>
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>
+ </ABApplicationHeader>
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ManagedGroupRequest>false</ManagedGroupRequest>
+ <TicketToken>'.$ticket.'</TicketToken>
+ </ABAuthHeader>
+</soap:Header>
+<soap:Body>
+ <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
+ <serviceHandle>
+ <Id>0</Id>
+ <Type>Messenger</Type>
+ <ForeignId></ForeignId>
+ </serviceHandle>
+ <memberships>
+ <Membership>
+ <MemberRole>'.$list.'</MemberRole>
+ <Members>
+ <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <Type>Passport</Type>
+ <MembershipId>'.$memberID.'</MembershipId>
+ <State>Accepted</State>
+ </Member>
+ </Members>
+ </Membership>
+ </memberships>
+ </DeleteMember>
+</soap:Body>
+</soap:Envelope>';
+ else
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+<soap:Header>
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
+ <IsMigration>false</IsMigration>
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>
+ </ABApplicationHeader>
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ManagedGroupRequest>false</ManagedGroupRequest>
+ <TicketToken>'.$ticket.'</TicketToken>
+ </ABAuthHeader>
+</soap:Header>
+<soap:Body>
+ <DeleteMember xmlns="http://www.msn.com/webservices/AddressBook">
+ <serviceHandle>
+ <Id>0</Id>
+ <Type>Messenger</Type>
+ <ForeignId></ForeignId>
+ </serviceHandle>
+ <memberships>
+ <Membership>
+ <MemberRole>'.$list.'</MemberRole>
+ <Members>
+ <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <Type>Email</Type>
+ <MembershipId>'.$memberID.'</MembershipId>
+ <State>Accepted</State>
+ </Member>
+ </Members>
+ </Membership>
+ </memberships>
+ </DeleteMember>
+</soap:Body>
+</soap:Envelope>';
+
+ $header_array = array(
+ 'SOAPAction: '.$this->delmember_soap,
+ 'Content-Type: text/xml; charset=utf-8',
+ 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
+ );
+
+ $this->debug_message("*** URL: $this->delmember_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->delmember_url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code != 200) {
+ preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
+ if (count($matches) == 0) {
+ $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list");
+ return false;
+ }
+ $faultcode = trim($matches[1]);
+ $faultstring = trim($matches[2]);
+ if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member does not exist') === false) {
+ $this->log_message("*** can't delete member (network: $network) $email ($memberID) to $list, error code: $faultcode, $faultstring");
+ return false;
+ }
+ $this->log_message("*** delete member (network: $network) $email ($memberID) from $list, not exist");
+ return true;
+ }
+ $this->log_message("*** delete member (network: $network) $email ($memberID) from $list");
+ return true;
+ }
+
+ function addMemberToList($email, $network, $list)
+ {
+ if ($network != 1 && $network != 32) return true;
+ $ticket = htmlspecialchars($this->contact_ticket);
+ $user = htmlspecialchars($email);
+
+ if ($network == 1)
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+<soap:Header>
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
+ <IsMigration>false</IsMigration>
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>
+ </ABApplicationHeader>
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ManagedGroupRequest>false</ManagedGroupRequest>
+ <TicketToken>'.$ticket.'</TicketToken>
+ </ABAuthHeader>
+</soap:Header>
+<soap:Body>
+ <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
+ <serviceHandle>
+ <Id>0</Id>
+ <Type>Messenger</Type>
+ <ForeignId></ForeignId>
+ </serviceHandle>
+ <memberships>
+ <Membership>
+ <MemberRole>'.$list.'</MemberRole>
+ <Members>
+ <Member xsi:type="PassportMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <Type>Passport</Type>
+ <State>Accepted</State>
+ <PassportName>'.$user.'</PassportName>
+ </Member>
+ </Members>
+ </Membership>
+ </memberships>
+ </AddMember>
+</soap:Body>
+</soap:Envelope>';
+ else
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+<soap:Header>
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
+ <IsMigration>false</IsMigration>
+ <PartnerScenario>ContactMsgrAPI</PartnerScenario>
+ </ABApplicationHeader>
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ManagedGroupRequest>false</ManagedGroupRequest>
+ <TicketToken>'.$ticket.'</TicketToken>
+ </ABAuthHeader>
+</soap:Header>
+<soap:Body>
+ <AddMember xmlns="http://www.msn.com/webservices/AddressBook">
+ <serviceHandle>
+ <Id>0</Id>
+ <Type>Messenger</Type>
+ <ForeignId></ForeignId>
+ </serviceHandle>
+ <memberships>
+ <Membership>
+ <MemberRole>'.$list.'</MemberRole>
+ <Members>
+ <Member xsi:type="EmailMember" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <Type>Email</Type>
+ <State>Accepted</State>
+ <Email>'.$user.'</Email>
+ <Annotations>
+ <Annotation>
+ <Name>MSN.IM.BuddyType</Name>
+ <Value>32:YAHOO</Value>
+ </Annotation>
+ </Annotations>
+ </Member>
+ </Members>
+ </Membership>
+ </memberships>
+ </AddMember>
+</soap:Body>
+</soap:Envelope>';
+ $header_array = array(
+ 'SOAPAction: '.$this->addmember_soap,
+ 'Content-Type: text/xml; charset=utf-8',
+ 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
+ );
+
+ $this->debug_message("*** URL: $this->addmember_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->addmember_url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code != 200) {
+ preg_match('#<faultcode>(.*)</faultcode><faultstring>(.*)</faultstring>#', $data, $matches);
+ if (count($matches) == 0) {
+ $this->log_message("*** can't add member (network: $network) $email to $list");
+ return false;
+ }
+ $faultcode = trim($matches[1]);
+ $faultstring = trim($matches[2]);
+ if (strcasecmp($faultcode, 'soap:Client') || stripos($faultstring, 'Member already exists') === false) {
+ $this->log_message("*** can't add member (network: $network) $email to $list, error code: $faultcode, $faultstring");
+ return false;
+ }
+ $this->log_message("*** add member (network: $network) $email to $list, already exist!");
+ return true;
+ }
+ $this->log_message("*** add member (network: $network) $email to $list");
+ return true;
+ }
+
+ function getMembershipList()
+ {
+ $ticket = htmlspecialchars($this->contact_ticket);
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
+<soap:Header>
+ <ABApplicationHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ApplicationId>996CDE1E-AA53-4477-B943-2BE802EA6166</ApplicationId>
+ <IsMigration>false</IsMigration>
+ <PartnerScenario>Initial</PartnerScenario>
+ </ABApplicationHeader>
+ <ABAuthHeader xmlns="http://www.msn.com/webservices/AddressBook">
+ <ManagedGroupRequest>false</ManagedGroupRequest>
+ <TicketToken>'.$ticket.'</TicketToken>
+ </ABAuthHeader>
+</soap:Header>
+<soap:Body>
+ <FindMembership xmlns="http://www.msn.com/webservices/AddressBook">
+ <serviceFilter>
+ <Types>
+ <ServiceType>Messenger</ServiceType>
+ <ServiceType>Invitation</ServiceType>
+ <ServiceType>SocialNetwork</ServiceType>
+ <ServiceType>Space</ServiceType>
+ <ServiceType>Profile</ServiceType>
+ </Types>
+ </serviceFilter>
+ </FindMembership>
+</soap:Body>
+</soap:Envelope>';
+ $header_array = array(
+ 'SOAPAction: '.$this->membership_soap,
+ 'Content-Type: text/xml; charset=utf-8',
+ 'User-Agent: MSN Explorer/9.0 (MSN 8.0; TmstmpExt)'
+ );
+ $this->debug_message("*** URL: $this->membership_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->membership_url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code != 200) return array();
+ $p = $data;
+ $aMemberships = array();
+ while (1) {
+ //$this->debug_message("search p = $p");
+ $start = strpos($p, '<Membership>');
+ $end = strpos($p, '</Membership>');
+ if ($start === false || $end === false || $start > $end) break;
+ //$this->debug_message("start = $start, end = $end");
+ $end += 13;
+ $sMembership = substr($p, $start, $end - $start);
+ $aMemberships[] = $sMembership;
+ //$this->debug_message("add sMembership = $sMembership");
+ $p = substr($p, $end);
+ }
+ //$this->debug_message("aMemberships = ".var_export($aMemberships, true));
+
+ $aContactList = array();
+ foreach ($aMemberships as $sMembership) {
+ //$this->debug_message("sMembership = $sMembership");
+ if (isset($matches)) unset($matches);
+ preg_match('#<MemberRole>(.*)</MemberRole>#', $sMembership, $matches);
+ if (count($matches) == 0) continue;
+ $sMemberRole = $matches[1];
+ //$this->debug_message("MemberRole = $sMemberRole");
+ if ($sMemberRole != 'Allow' && $sMemberRole != 'Reverse' && $sMemberRole != 'Pending') continue;
+ $p = $sMembership;
+ if (isset($aMembers)) unset($aMembers);
+ $aMembers = array();
+ while (1) {
+ //$this->debug_message("search p = $p");
+ $start = strpos($p, '<Member xsi:type="');
+ $end = strpos($p, '</Member>');
+ if ($start === false || $end === false || $start > $end) break;
+ //$this->debug_message("start = $start, end = $end");
+ $end += 9;
+ $sMember = substr($p, $start, $end - $start);
+ $aMembers[] = $sMember;
+ //$this->debug_message("add sMember = $sMember");
+ $p = substr($p, $end);
+ }
+ //$this->debug_message("aMembers = ".var_export($aMembers, true));
+ foreach ($aMembers as $sMember) {
+ //$this->debug_message("sMember = $sMember");
+ if (isset($matches)) unset($matches);
+ preg_match('#<Member xsi\:type="([^"]*)">#', $sMember, $matches);
+ if (count($matches) == 0) continue;
+ $sMemberType = $matches[1];
+ //$this->debug_message("MemberType = $sMemberType");
+ $network = -1;
+ preg_match('#<MembershipId>(.*)</MembershipId>#', $sMember, $matches);
+ if (count($matches) == 0) continue;
+ $id = $matches[1];
+ if ($sMemberType == 'PassportMember') {
+ if (strpos($sMember, '<Type>Passport</Type>') === false) continue;
+ $network = 1;
+ preg_match('#<PassportName>(.*)</PassportName>#', $sMember, $matches);
+ }
+ else if ($sMemberType == 'EmailMember') {
+ if (strpos($sMember, '<Type>Email</Type>') === false) continue;
+ // Value is 32: or 32:YAHOO
+ preg_match('#<Annotation><Name>MSN.IM.BuddyType</Name><Value>(.*):(.*)</Value></Annotation>#', $sMember, $matches);
+ if (count($matches) == 0) continue;
+ if ($matches[1] != 32) continue;
+ $network = 32;
+ preg_match('#<Email>(.*)</Email>#', $sMember, $matches);
+ }
+ if ($network == -1) continue;
+ if (count($matches) > 0) {
+ $email = $matches[1];
+ @list($u_name, $u_domain) = @explode('@', $email);
+ if ($u_domain == NULL) continue;
+ $aContactList[$u_domain][$u_name][$network][$sMemberRole] = $id;
+ $this->log_message("*** add new contact (network: $network, status: $sMemberRole): $u_name@$u_domain ($id)");
+ }
+ }
+ }
+ return $aContactList;
+ }
+
+ function connect($user, $password)
+ {
+ $this->id = 1;
+ $this->fp = @fsockopen($this->server, $this->port, $errno, $errstr, 5);
+ if (!$this->fp) {
+ $this->error = "Can't connect to $this->server:$this->port, error => $errno, $errstr";
+ return false;
+ }
+
+ stream_set_timeout($this->fp, $this->stream_timeout);
+ $this->authed = false;
+ // MSNP9
+ // NS: >> VER {id} MSNP9 CVR0
+ // MSNP15
+ // NS: >>> VER {id} MSNP15 CVR0
+ $this->writeln("VER $this->id $this->protocol CVR0");
+
+ $start_tm = time();
+ while (!feof($this->fp)) {
+ $data = $this->readln();
+ // no data?
+ if ($data === false) {
+ if ($this->timeout > 0) {
+ $now_tm = time();
+ $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
+ if ($used_time > $this->timeout) {
+ // logout now
+ // NS: >>> OUT
+ $this->writeln("OUT");
+ fclose($this->fp);
+ $this->error = 'Timeout, maybe protocol changed!';
+ $this->debug_message("*** $this->error");
+ return false;
+ }
+ }
+ continue;
+ }
+ $code = substr($data, 0, 3);
+ $start_tm = time();
+ switch ($code) {
+ case 'VER':
+ // MSNP9
+ // NS: <<< VER {id} MSNP9 CVR0
+ // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 6.0.0602 msmsgs {user}
+ // MSNP15
+ // NS: <<< VER {id} MSNP15 CVR0
+ // NS: >>> CVR {id} 0x0409 winnt 5.1 i386 MSMSGS 8.1.0178 msmsgs {user}
+ $this->writeln("CVR $this->id 0x0409 winnt 5.1 i386 MSMSGS $this->buildver msmsgs $user");
+ break;
+
+ case 'CVR':
+ // MSNP9
+ // NS: <<< CVR {id} {ver_list} {download_serve} ....
+ // NS: >>> USR {id} TWN I {user}
+ // MSNP15
+ // NS: <<< CVR {id} {ver_list} {download_serve} ....
+ // NS: >>> USR {id} SSO I {user}
+ $this->writeln("USR $this->id $this->login_method I $user");
+ break;
+
+ case 'USR':
+ // already login for passport site, finish the login process now.
+ // NS: <<< USR {id} OK {user} {verify} 0
+ if ($this->authed) return true;
+
+ $this->user = $user;
+ $this->password = urlencode($password);
+
+ if ($this->protocol == 'MSNP15') {
+ // NS: <<< USR {id} SSO S {policy} {nonce}
+ @list(/* USR */, /* id */, /* SSO */, /* S */, $policy, $nonce,) = @explode(' ', $data);
+
+ $this->passport_policy = $policy;
+ $aTickets = $this->get_passport_ticket();
+ if (!$aTickets || !is_array($aTickets)) {
+ // logout now
+ // NS: >>> OUT
+ $this->writeln("OUT");
+ fclose($this->fp);
+ $this->error = 'Passport authenticated fail!';
+ $this->debug_message("*** $this->error");
+ return false;
+ }
+
+ $ticket = $aTickets['ticket'];
+ $secret = $aTickets['secret'];
+ $this->oim_ticket = $aTickets['oim_ticket'];
+ $this->contact_ticket = $aTickets['contact_ticket'];
+ $this->web_ticket = $aTickets['web_ticket'];
+ $this->space_ticket = $aTickets['space_ticket'];
+ $this->storage_ticket = $aTickets['storage_ticket'];
+
+ $login_code = $this->generateLoginBLOB($secret, $nonce);
+
+ // NS: >>> USR {id} SSO S {ticket} {login_code}
+ $this->writeln("USR $this->id $this->login_method S $ticket $login_code");
+ }
+ else {
+ // NS: <<< USR {id} TWN S {nonce}
+ @list(/* USR */, /* id */, /* TWN */, /* S */, $nonce,) = @explode(' ', $data);
+
+ $ticket = $this->get_tweener_passport_ticket($nonce);
+ if (!$ticket) {
+ // logout now
+ // NS: >>> OUT
+ $this->writeln("OUT");
+ fclose($this->fp);
+ $this->error = 'Passport authenticated fail!';
+ $this->debug_message("*** $this->error");
+ return false;
+ }
+
+ // NS: >>> USR {id} TWN S {ticket}
+ $this->writeln("USR $this->id $this->login_method S $ticket");
+ }
+ $this->authed = true;
+ break;
+
+ case 'XFR':
+ // main login server will redirect to anther NS after USR command
+ // MSNP9
+ // NS: <<< XFR {id} NS {server} 0 {server}
+ // MSNP15
+ // NS: <<< XFR {id} NS {server} U D
+ @list(/* XFR */, /* id */, /* NS */, $server, /* ... */) = @explode(' ', $data);
+ @list($ip, $port) = @explode(':', $server);
+ // this connection will close after XFR
+ fclose($this->fp);
+
+ $this->fp = @fsockopen($ip, $port, $errno, $errstr, 5);
+ if (!$this->fp) {
+ $this->error = "Can't connect to $ip:$port, error => $errno, $errstr";
+ $this->debug_message("*** $this->error");
+ return false;
+ }
+
+ stream_set_timeout($this->fp, $this->stream_timeout);
+ // MSNP9
+ // NS: >> VER {id} MSNP9 CVR0
+ // MSNP15
+ // NS: >>> VER {id} MSNP15 CVR0
+ $this->writeln("VER $this->id $this->protocol CVR0");
+ break;
+
+ case 'GCF':
+ // return some policy data after 'USR {id} SSO I {user}' command
+ // NS: <<< GCF 0 {size}
+ @list(/* GCF */, /* 0 */, $size,) = @explode(' ', $data);
+ // we don't need the data, just read it and drop
+ if (is_numeric($size) && $size > 0)
+ $this->readdata($size);
+ break;
+
+ default:
+ // we'll quit if got any error
+ if (is_numeric($code)) {
+ // logout now
+ // NS: >>> OUT
+ $this->writeln("OUT");
+ fclose($this->fp);
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
+ $this->debug_message("*** $this->error");
+ return false;
+ }
+ // unknown response from server, just ignore it
+ break;
+ }
+ }
+ // never goto here
+ }
+
+ function derive_key($key, $magic)
+ {
+ $hash1 = mhash(MHASH_SHA1, $magic, $key);
+ $hash2 = mhash(MHASH_SHA1, $hash1.$magic, $key);
+ $hash3 = mhash(MHASH_SHA1, $hash1, $key);
+ $hash4 = mhash(MHASH_SHA1, $hash3.$magic, $key);
+ return $hash2.substr($hash4, 0, 4);
+ }
+
+ function generateLoginBLOB($key, $challenge)
+ {
+ $key1 = base64_decode($key);
+ $key2 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY HASH');
+ $key3 = $this->derive_key($key1, 'WS-SecureConversationSESSION KEY ENCRYPTION');
+
+ // get hash of challenge using key2
+ $hash = mhash(MHASH_SHA1, $challenge, $key2);
+
+ // get 8 bytes random data
+ $iv = substr(base64_encode(rand(1000,9999).rand(1000,9999)), 2, 8);
+
+ $cipher = mcrypt_cbc(MCRYPT_3DES, $key3, $challenge."\x08\x08\x08\x08\x08\x08\x08\x08", MCRYPT_ENCRYPT, $iv);
+
+ $blob = pack('LLLLLLL', 28, 1, 0x6603, 0x8004, 8, 20, 72);
+ $blob .= $iv;
+ $blob .= $hash;
+ $blob .= $cipher;
+
+ return base64_encode($blob);
+ }
+
+ function getOIM_maildata()
+ {
+ preg_match('#t=(.*)&p=(.*)#', $this->web_ticket, $matches);
+ if (count($matches) == 0) {
+ $this->debug_message('*** no web ticket?');
+ return false;
+ }
+ $t = htmlspecialchars($matches[1]);
+ $p = htmlspecialchars($matches[2]);
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+<soap:Header>
+ <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
+ <t>'.$t.'</t>
+ <p>'.$p.'</p>
+ </PassportCookie>
+</soap:Header>
+<soap:Body>
+ <GetMetadata xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi" />
+</soap:Body>
+</soap:Envelope>';
+
+ $header_array = array(
+ 'SOAPAction: '.$this->oim_maildata_soap,
+ 'Content-Type: text/xml; charset=utf-8',
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
+ );
+
+ $this->debug_message("*** URL: $this->oim_maildata_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->oim_maildata_url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code != 200) {
+ $this->debug_message("*** Can't get OIM maildata! http code: $http_code");
+ return false;
+ }
+
+ // <GetMetadataResponse xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">See #XML_Data</GetMetadataResponse>
+ preg_match('<GetMetadataResponse([^>]*)>(.*)</GetMetadataResponse>#', $data, $matches);
+ if (count($matches) == 0) {
+ $this->debug_message("*** Can't get OIM maildata");
+ return '';
+ }
+ return $matches[2];
+ }
+
+ function getOIM_message($msgid)
+ {
+ preg_match('#t=(.*)&p=(.*)#', $this->web_ticket, $matches);
+ if (count($matches) == 0) {
+ $this->debug_message('*** no web ticket?');
+ return false;
+ }
+ $t = htmlspecialchars($matches[1]);
+ $p = htmlspecialchars($matches[2]);
+
+ // read OIM
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+<soap:Header>
+ <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
+ <t>'.$t.'</t>
+ <p>'.$p.'</p>
+ </PassportCookie>
+</soap:Header>
+<soap:Body>
+ <GetMessage xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
+ <messageId>'.$msgid.'</messageId>
+ <alsoMarkAsRead>false</alsoMarkAsRead>
+ </GetMessage>
+</soap:Body>
+</soap:Envelope>';
+
+ $header_array = array(
+ 'SOAPAction: '.$this->oim_read_soap,
+ 'Content-Type: text/xml; charset=utf-8',
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
+ );
+
+ $this->debug_message("*** URL: $this->oim_read_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->oim_read_url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code != 200) {
+ $this->debug_message("*** Can't get OIM: $msgid, http code = $http_code");
+ return false;
+ }
+
+ // why can't use preg_match('#<GetMessageResult>(.*)</GetMessageResult>#', $data, $matches)?
+ // multi-lines?
+ $start = strpos($data, '<GetMessageResult>');
+ $end = strpos($data, '</GetMessageResult>');
+ if ($start === false || $end === false || $start > $end) {
+ $this->debug_message("*** Can't get OIM: $msgid");
+ return false;
+ }
+ $lines = substr($data, $start + 18, $end - $start);
+ $aLines = @explode("\n", $lines);
+ $header = true;
+ $ignore = false;
+ $sOIM = '';
+ foreach ($aLines as $line) {
+ $line = trim($line);
+ if ($header) {
+ if ($line === '') {
+ $header = false;
+ continue;
+ }
+ continue;
+ }
+ // stop at empty lines
+ if ($line === '') break;
+ $sOIM .= $line;
+ }
+ $sMsg = base64_decode($sOIM);
+ $this->debug_message("*** we get OIM ($msgid): $sMsg");
+
+ // delete OIM
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+<soap:Header>
+ <PassportCookie xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
+ <t>'.$t.'</t>
+ <p>'.$p.'</p>
+ </PassportCookie>
+</soap:Header>
+<soap:Body>
+ <DeleteMessages xmlns="http://www.hotmail.msn.com/ws/2004/09/oim/rsi">
+ <messageIds>
+ <messageId>'.$msgid.'</messageId>
+ </messageIds>
+ </DeleteMessages>
+</soap:Body>
+</soap:Envelope>';
+
+ $header_array = array(
+ 'SOAPAction: '.$this->oim_del_soap,
+ 'Content-Type: text/xml; charset=utf-8',
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
+ );
+
+ $this->debug_message("*** URL: $this->oim_del_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->oim_del_url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code != 200)
+ $this->debug_message("*** Can't delete OIM: $msgid, http code = $http_code");
+ else
+ $this->debug_message("*** OIM ($msgid) deleted");
+ return $sMsg;
+ }
+
+ function doLoop($user, $password, $alias = '',
+ $my_function = false, $my_add_function = false, $my_rem_function = false,
+ $use_ping = false, $retry_wait = 30, $backup_file = true, $update_pending = true)
+ {
+ if ($this->kill_me) return;
+ $this->log_message("*** startup ***");
+ $process_file = false;
+ $sent = false;
+ $online = false;
+ $aADL = array();
+ if (is_int($use_ping) && $use_ping > 0)
+ $ping_wait = $use_ping;
+ else
+ $ping_wait = 50;
+ $aContactList = array();
+ $first = true;
+ while (1) {
+ if ($this->kill_me) {
+ if (is_resource($this->fp) && !feof($this->fp)) {
+ // logout now
+ // NS: >>> OUT
+ $this->writeln("OUT");
+ fclose($this->fp);
+ $this->fp = false;
+ $this->log_message("*** logout now!");
+ }
+ $this->log_message("*** Okay, kill me now!");
+ break;
+ }
+ if (!is_resource($this->fp) || feof($this->fp)) {
+ if ($first)
+ $first = false;
+ else {
+ $this->log_message("*** wait for $retry_wait seconds");
+ sleep($retry_wait);
+ }
+ if ($this->kill_me) continue;
+ $this->log_message("*** try to connect to MSN network");
+ if (!$this->connect($user, $password)) {
+ $this->log_message("!!! Can't connect to server: $this->error");
+ continue;
+ }
+ if ($this->protocol == 'MSNP9') {
+ // we need to send SYN command for MSNP9
+ // NS: >>> SYN {id} 0
+ $this->writeln("SYN $this->id 0");
+ }
+ $this->log_message("*** connected, wait for command");
+ $start_tm = time();
+ $ping_tm = time();
+ stream_set_timeout($this->fp, $this->stream_timeout);
+ }
+ $data = $this->readln();
+ if ($data === false) {
+ if ($this->kill_me) continue;
+ if ($process_file !== false && $sent !== true) {
+ // some error for sending message
+ $precess_file = false;
+ }
+ // check here, do we have any message need to send?
+ $aFiles = glob(dirname($_SERVER['argv'][0]).DIRECTORY_SEPARATOR.'spool'.DIRECTORY_SEPARATOR.'*.msn');
+ if (!is_array($aFiles)) continue;
+ clearstatcache();
+ foreach ($aFiles as $filename) {
+ if (fileperms($filename) != (0x8000 | 00666)) continue;
+ $fp = fopen($filename, 'rt');
+ if (!$fp) continue;
+ $aTo = array();
+ $sMessage = '';
+ $buf = trim(fgets($fp));
+ if (substr($buf, 0, 3) == 'TO:') {
+ $to_str = trim(substr($buf, 3));
+ $aTo = @explode(',', $to_str);
+ while (!feof($fp)) {
+ $buf = trim(fgets($fp));
+ $sMessage .= $buf;
+ $sMessage .= "\r\n";
+ }
+ }
+ fclose($fp);
+ if (!is_array($aTo) || count($aTo) == 0 || $sMessage == '') {
+ $this->log_message("!!! message format error? delete $filename");
+ if ($backup_file) {
+ $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
+ if (!file_exists($backup_dir))
+ @mkdir($backup_dir);
+ $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.$this->getpid().'_'.basename($filename);
+ if (@rename($filename, $backup_name))
+ $this->log_message("*** move file to $backup_name");
+ }
+ @unlink($filename);
+ continue;
+ }
+ // assign process_file
+ $process_file = $filename;
+ break;
+ }
+
+ if ($process_file === false) {
+ if ($online && $use_ping) {
+ $now = time();
+ if ($now < $ping_tm)
+ $len = $now;
+ else
+ $len = $now - $ping_tm;
+ if ($len > $ping_wait) {
+ // NS: >>> PNG
+ $this->writeln("PNG");
+ $ping_tm = time();
+ }
+ }
+ continue;
+ }
+ $sent = false;
+
+ $this->log_message("*** try to send message from $process_file");
+ $this->log_message("*** TO: $to_str");
+ $this->log_message("*** MSG: $sMessage");
+
+ // okay, try to ask a switchboard (SB) for sending message
+ // NS: >>> XFR {id} SB
+ $this->writeln("XFR $this->id SB");
+ continue;
+ }
+ $code = substr($data, 0, 3);
+
+ switch ($code) {
+ case 'SBS':
+ // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
+ // NS: <<< SBS 0 null
+ $aContactList = $this->getMembershipList();
+ if ($update_pending) {
+ if (is_array($aContactList)) {
+ $pending = 'Pending';
+ foreach ($aContactList as $u_domain => $aUserList) {
+ foreach ($aUserList as $u_name => $aNetworks) {
+ foreach ($aNetworks as $network => $aData) {
+ if (isset($aData[$pending])) {
+ // pending list
+ $cnt = 0;
+ foreach (array('Allow', 'Reverse') as $list) {
+ if (isset($aData[$list]))
+ $cnt++;
+ else {
+ if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
+ $aContactList[$u_domain][$u_name][$network][$list] = false;
+ $cnt++;
+ }
+ }
+ }
+ if ($cnt >= 2) {
+ $id = $aData[$pending];
+ // we can delete it from pending now
+ if ($this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $pending))
+ unset($aContactList[$u_domain][$u_name][$network][$pending]);
+ }
+ }
+ else {
+ // sync list
+ foreach (array('Allow', 'Reverse') as $list) {
+ if (!isset($aData[$list])) {
+ if ($this->addMemberToList($u_name.'@'.$u_domain, $network, $list))
+ $aContactList[$u_domain][$u_name][$network][$list] = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ $n = 0;
+ $sList = '';
+ $len = 0;
+ if (is_array($aContactList)) {
+ foreach ($aContactList as $u_domain => $aUserList) {
+ $str = '<d n="'.$u_domain.'">';
+ $len += strlen($str);
+ if ($len > 7400) {
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
+ $n++;
+ $sList = '';
+ $len = strlen($str);
+ }
+ $sList .= $str;
+ foreach ($aUserList as $u_name => $aNetworks) {
+ foreach ($aNetworks as $network => $status) {
+ $str = '<c n="'.$u_name.'" l="3" t="'.$network.'" />';
+ $len += strlen($str);
+ // max: 7500, but <ml l="1"></d></ml> is 19,
+ // so we use 7475
+ if ($len > 7475) {
+ $sList .= '</d>';
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
+ $n++;
+ $sList = '<d n="'.$u_domain.'">'.$str;
+ $len = strlen($sList);
+ }
+ else
+ $sList .= $str;
+ }
+ }
+ $sList .= '</d>';
+ }
+ }
+ $aADL[$n] = '<ml l="1">'.$sList.'</ml>';
+ // NS: >>> BLP {id} BL
+ $this->writeln("BLP $this->id BL");
+ foreach ($aADL as $str) {
+ $len = strlen($str);
+ // NS: >>> ADL {id} {size}
+ $this->writeln("ADL $this->id $len");
+ $this->writedata($str);
+ }
+ // NS: >>> PRP {id} MFN name
+ if ($alias == '') $alias = $user;
+ $aliasname = rawurlencode($alias);
+ $this->writeln("PRP $this->id MFN $aliasname");
+ // NS: >>> CHG {id} {status} {clientid} {msnobj}
+ $this->writeln("CHG $this->id NLN $this->clientid");
+ $online = true;
+ break;
+
+ case 'RFS':
+ // FIXME:
+ // NS: <<< RFS ???
+ // refresh ADL, so we re-send it again
+ if (is_array($aADL)) {
+ foreach ($aADL as $str) {
+ $len = strlen($str);
+ // NS: >>> ADL {id} {size}
+ $this->writeln("ADL $this->id $len");
+ $this->writedata($str);
+ }
+ }
+ break;
+
+ case 'LST':
+ // NS: <<< LST {email} {alias} 11 0
+ @list(/* LST */, $email, /* alias */, ) = @explode(' ', $data);
+ @list($u_name, $u_domain) = @explode('@', $email);
+ if (!isset($aContactList[$u_domain][$u_name][1])) {
+ $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
+ $this->log_message("*** add to our contact list: $u_name@$u_domain");
+ }
+ break;
+
+ case 'ADD':
+ // randomly, we get ADD command, someome add us to their contact list for MSNP9
+ // NS: <<< ADD 0 {list} {0} {email} {alias}
+ @list(/* ADD */, /* 0 */, $u_list, /* 0 */, $u_email, /* alias */) = @explode(' ', $data);
+ @list($u_name, $u_domain) = @explode('@', $u_email);
+ if (isset($aContactList[$u_domain][$u_name][1]['Allow']))
+ $this->log_message("*** someone add us to their list (but already in our list): $u_name@$u_domain");
+ else {
+ $aContactList[$u_domain][$u_name][1]['Allow'] = 'Allow';
+ $this->log_message("*** someone add us to their list: $u_name@$u_domain");
+ }
+ if ($my_add_function && function_exists($my_add_function) && is_callable($my_add_function))
+ $my_add_function($u_email);
+ break;
+
+ case 'REM':
+ // randomly, we get REM command, someome remove us from their contact list for MSNP9
+ // NS: <<< REM 0 {list} {0} {email}
+ @list(/* REM */, /* 0 */, $u_list, /* 0 */, $u_email,) = @explode(' ', $data);
+ @list($u_name, $u_domain) = @explode('@', $u_email);
+ if (isset($aContactList[$u_domain][$u_name][1])) {
+ unset($aContactList[$u_domain][$u_name][1]);
+ $this->log_message("*** someone remove us from their list: $u_name@$u_domain");
+ }
+ else
+ $this->log_message("*** someone remove us from their list (but not in our list): $u_name@$u_domain");
+ if ($my_rem_function && function_exists($my_rem_function) && is_callable($my_rem_function))
+ $my_rem_function($u_email);
+ break;
+
+ case 'ADL':
+ // randomly, we get ADL command, someome add us to their contact list for MSNP15
+ // NS: <<< ADL 0 {size}
+ @list(/* ADL */, /* 0 */, $size,) = @explode(' ', $data);
+ if (is_numeric($size) && $size > 0) {
+ $data = $this->readdata($size);
+ preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
+ if (is_array($matches) && count($matches) > 0) {
+ $u_domain = $matches[1];
+ $u_name = $matches[2];
+ $network = $matches[4];
+ if (isset($aContactList[$u_domain][$u_name][$network]))
+ $this->log_message("*** someone (network: $network) add us to their list (but already in our list): $u_name@$u_domain");
+ else {
+ $re_login = false;
+ $cnt = 0;
+ foreach (array('Allow', 'Reverse') as $list) {
+ if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
+ if ($re_login) {
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
+ continue;
+ }
+ $aTickets = $this->get_passport_ticket();
+ if (!$aTickets || !is_array($aTickets)) {
+ // failed to login? ignore it
+ $this->log_message("*** can't re-login, something wrong here");
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
+ continue;
+ }
+ $re_login = true;
+ $this->oim_ticket = $aTickets['oim_ticket'];
+ $this->contact_ticket = $aTickets['contact_ticket'];
+ $this->web_ticket = $aTickets['web_ticket'];
+ $this->space_ticket = $aTickets['space_ticket'];
+ $this->storage_ticket = $aTickets['storage_ticket'];
+ $this->log_message("**** get new ticket, try it again");
+ if (!$this->addMemberToList($u_name.'@'.$u_domain, $network, $list)) {
+ $this->log_message("*** can't add $u_name@$u_domain (network: $network) to $list");
+ continue;
+ }
+ }
+ $aContactList[$u_domain][$u_name][$network][$list] = false;
+ $cnt++;
+ }
+ $this->log_message("*** someone (network: $network) add us to their list: $u_name@$u_domain");
+ }
+ $str = '<ml l="1"><d n="'.$u_domain.'"><c n="'.$u_name.'" l="3" t="'.$network.'" /></d></ml>';
+ $len = strlen($str);
+ // NS: >>> ADL {id} {size}
+ $this->writeln("ADL $this->id $len");
+ $this->writedata($str);
+ if ($my_add_function && function_exists($my_add_function) && is_callable($my_add_function))
+ $my_add_function($u_name.'@'.$u_domain, $network);
+ }
+ else {
+ $this->log_message("*** someone add us to their list: $data");
+ }
+ }
+ break;
+
+ case 'RML':
+ // randomly, we get RML command, someome remove us to their contact list for MSNP15
+ // NS: <<< RML 0 {size}
+ @list(/* RML */, /* 0 */, $size,) = @explode(' ', $data);
+ if (is_numeric($size) && $size > 0) {
+ $data = $this->readdata($size);
+ preg_match('#<ml><d n="([^"]+)"><c n="([^"]+)"(.*) t="(\d*)"(.*) /></d></ml>#', $data, $matches);
+ if (is_array($matches) && count($matches) > 0) {
+ $u_domain = $matches[1];
+ $u_name = $matches[2];
+ $network = $matches[4];
+ if (isset($aContactList[$u_domain][$u_name][$network])) {
+ $aData = $aContactList[$u_domain][$u_name][$network];
+ foreach ($aData as $list => $id)
+ $this->delMemberFromList($id, $u_name.'@'.$u_domain, $network, $list);
+ unset($aContactList[$u_domain][$u_name][$network]);
+ $this->log_message("*** someone (network: $network) remove us from their list: $u_name@$u_domain");
+ }
+ else
+ $this->log_message("*** someone (network: $network) remove us from their list (but not in our list): $u_name@$u_domain");
+ if ($my_rem_function && function_exists($my_rem_function) && is_callable($my_rem_function))
+ $my_rem_function($u_name.'@'.$u_domain, $network);
+ }
+ else {
+ $this->log_message("*** someone remove us from their list: $data");
+ }
+ }
+ break;
+
+ case 'MSG':
+ // randomly, we get MSG notification from server
+ // NS: <<< MSG Hotmail Hotmail {size}
+ @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
+ if (is_numeric($size) && $size > 0) {
+ $data = $this->readdata($size);
+ $aLines = @explode("\n", $data);
+ $header = true;
+ $ignore = false;
+ $maildata = '';
+ foreach ($aLines as $line) {
+ $line = trim($line);
+ if ($header) {
+ if ($line === '') {
+ $header = false;
+ continue;
+ }
+ if (strncasecmp($line, 'Content-Type:', 13) == 0) {
+ if (strpos($line, 'text/x-msmsgsinitialmdatanotification') === false &&
+ strpos($line, 'text/x-msmsgsoimnotification') === false) {
+ // we just need text/x-msmsgsinitialmdatanotification
+ // or text/x-msmsgsoimnotification
+ $ignore = true;
+ break;
+ }
+ }
+ continue;
+ }
+ if (strncasecmp($line, 'Mail-Data:', 10) == 0) {
+ $maildata = trim(substr($line, 10));
+ break;
+ }
+ }
+ if ($ignore) {
+ $this->log_message("*** ingnore MSG for: $line");
+ break;
+ }
+ if ($maildata == '') {
+ $this->log_message("*** ingnore MSG not for OIM");
+ break;
+ }
+ $re_login = false;
+ if (strcasecmp($maildata, 'too-large') == 0) {
+ $this->log_message("*** large mail-data, need to get the data via SOAP");
+ $maildata = $this->getOIM_maildata();
+ if ($maildata === false) {
+ $this->log_message("*** can't get mail-data via SOAP");
+ // maybe we need to re-login again
+ $aTickets = $this->get_passport_ticket();
+ if (!$aTickets || !is_array($aTickets)) {
+ // failed to login? ignore it
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
+ break;
+ }
+ $re_login = true;
+ $this->oim_ticket = $aTickets['oim_ticket'];
+ $this->contact_ticket = $aTickets['contact_ticket'];
+ $this->web_ticket = $aTickets['web_ticket'];
+ $this->space_ticket = $aTickets['space_ticket'];
+ $this->storage_ticket = $aTickets['storage_ticket'];
+ $this->log_message("**** get new ticket, try it again");
+ $maildata = $this->getOIM_maildata();
+ if ($maildata === false) {
+ $this->log_message("*** can't get mail-data via SOAP, and we already re-login again, so ignore this OIM");
+ break;
+ }
+ }
+ }
+ // could be a lots of <M>...</M>, so we can't use preg_match here
+ $p = $maildata;
+ $aOIMs = array();
+ while (1) {
+ $start = strpos($p, '<M>');
+ $end = strpos($p, '</M>');
+ if ($start === false || $end === false || $start > $end) break;
+ $end += 4;
+ $sOIM = substr($p, $start, $end - $start);
+ $aOIMs[] = $sOIM;
+ $p = substr($p, $end);
+ }
+ if (count($aOIMs) == 0) {
+ $this->log_message("*** ingnore empty OIM");
+ break;
+ }
+ foreach ($aOIMs as $maildata) {
+ // T: 11 for MSN, 13 for Yahoo
+ // S: 6 for MSN, 7 for Yahoo
+ // RT: the datetime received by server
+ // RS: already read or not
+ // SZ: size of message
+ // E: sender
+ // I: msgid
+ // F: always 00000000-0000-0000-0000-000000000009
+ // N: sender alias
+ preg_match('#<T>(.*)</T>#', $maildata, $matches);
+ if (count($matches) == 0) {
+ $this->log_message("*** ingnore OIM maildata without <T>type</T>");
+ continue;
+ }
+ $oim_type = $matches[1];
+ if ($oim_type = 13)
+ $network = 32;
+ else
+ $network = 1;
+ preg_match('#<E>(.*)</E>#', $maildata, $matches);
+ if (count($matches) == 0) {
+ $this->log_message("*** ingnore OIM maildata without <E>sender</E>");
+ continue;
+ }
+ $oim_sender = $matches[1];
+ preg_match('#<I>(.*)</I>#', $maildata, $matches);
+ if (count($matches) == 0) {
+ $this->log_message("*** ingnore OIM maildata without <I>msgid</I>");
+ continue;
+ }
+ $oim_msgid = $matches[1];
+ preg_match('#<SZ>(.*)</SZ>#', $maildata, $matches);
+ $oim_size = (count($matches) == 0) ? 0 : $matches[1];
+ preg_match('#<RT>(.*)</RT>#', $maildata, $matches);
+ $oim_time = (count($matches) == 0) ? 0 : $matches[1];
+ $this->log_message("*** You've OIM sent by $oim_sender, Time: $oim_time, MSGID: $oim_msgid, size: $oim_size");
+ $sMsg = $this->getOIM_message($oim_msgid);
+ if ($sMsg === false) {
+ $this->log_message("*** can't get OIM, msgid = $oim_msgid");
+ if ($re_login) {
+ $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
+ continue;
+ }
+ $aTickets = $this->get_passport_ticket();
+ if (!$aTickets || !is_array($aTickets)) {
+ // failed to login? ignore it
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
+ continue;
+ }
+ $re_login = true;
+ $this->oim_ticket = $aTickets['oim_ticket'];
+ $this->contact_ticket = $aTickets['contact_ticket'];
+ $this->web_ticket = $aTickets['web_ticket'];
+ $this->space_ticket = $aTickets['space_ticket'];
+ $this->storage_ticket = $aTickets['storage_ticket'];
+ $this->log_message("**** get new ticket, try it again");
+ $sMsg = $this->getOIM_message($oim_msgid);
+ if ($sMsg === false) {
+ $this->log_message("*** can't get OIM via SOAP, and we already re-login again, so ignore this OIM");
+ continue;
+ }
+ }
+ $this->log_message("*** MSG (Offline) from $oim_sender (network: $network): $sMsg");
+ if ($my_function && function_exists($my_function) && is_callable($my_function)) {
+ $sMessage = $my_function($oim_sender, $sMsg, $network);
+ if ($sMessage !== '') {
+ $now = strftime('%m/%d/%y %H:%M:%S');
+ $fname = dirname($_SERVER['argv'][0]).DIRECTORY_SEPARATOR.'spool'.DIRECTORY_SEPARATOR.'msn_'.$this->getpid().'_'.md5('offline'.rand(1,1000).$now).'.msn';
+ $fp = fopen($fname, 'wt');
+ if ($fp) {
+ fputs($fp, "TO: $oim_sender@$network\n");
+ fputs($fp, $sMessage);
+ fclose($fp);
+ chmod($fname, 0666);
+ $this->log_message("Response to $oim_sender (Offline, network: $network): $fname");
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 'UBM':
+ // randomly, we get UBM, this is the message from other network, like Yahoo!
+ // NS: <<< UBM {email} $network $type {size}
+ @list(/* UBM */, $from_email, $network, $type, $size,) = @explode(' ', $data);
+ if (is_numeric($size) && $size > 0) {
+ $data = $this->readdata($size);
+ $aLines = @explode("\n", $data);
+ $header = true;
+ $ignore = false;
+ $sMsg = '';
+ foreach ($aLines as $line) {
+ $line = trim($line);
+ if ($header) {
+ if ($line === '') {
+ $header = false;
+ continue;
+ }
+ if (strncasecmp($line, 'TypingUser:', 11) == 0) {
+ $ignore = true;
+ break;
+ }
+ continue;
+ }
+ if ($sMsg !== '')
+ $sMsg .= "\r\n";
+ $sMsg .= $line;
+ }
+ if ($ignore) {
+ $this->log_message("*** ingnore from $from_email: $line");
+ break;
+ }
+ $this->log_message("*** MSG from $from_email (network: $network): $sMsg");
+ if ($my_function && function_exists($my_function) && is_callable($my_function)) {
+ $msg = $my_function($from_email, $sMsg, $network);
+ if ($msg !== '') {
+ /* typing?
+ $message = "MIME-Version: 1.0\r\nContent-Type: text/x-msmsgscontrol\r\nTypingUser: $user\r\n\r\n\r\n";
+ $len = strlen($message);
+ $this->writeln("UUM $this->id $from_email $network 2 $len");
+ $this->writedata($message);
+ */
+ $message = "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$msg";
+ $len = strlen($message);
+ $this->writeln("UUM $this->id $from_email $network 1 $len");
+ $this->writedata($message);
+ $this->log_message("Response to $from_email (network: $network):\n$msg");
+ }
+ }
+ }
+ break;
+
+ case 'UBX':
+ // randomly, we get UBX notification from server
+ // NS: <<< UBX email {network} {size}
+ @list(/* UBX */, /* email */, /* network */, $size,) = @explode(' ', $data);
+ // we don't need the notification data, so just ignore it
+ if (is_numeric($size) && $size > 0)
+ $this->readdata($size);
+ break;
+
+ case 'CHL':
+ // randomly, we'll get challenge from server
+ // NS: <<< CHL 0 {code}
+ @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
+ $fingerprint = $this->getChallenge($chl_code);
+ // NS: >>> QRY {id} {product_id} 32
+ // NS: >>> fingerprint
+ $this->writeln("QRY $this->id $this->prod_id 32");
+ $this->writedata($fingerprint);
+ break;
+
+ case 'SYN':
+ if ($this->protocol != 'MSNP9') break;
+ // NS: <<< SYN 8 1 2 1
+ // ignore it
+ // change our status to online first
+ // NS: >>> CHG {id} {status}
+ //$this->writeln("CHG $this->id NLN");
+ $this->writeln("CHG $this->id NLN $this->clientid");
+ $online = true;
+ break;
+
+ case 'CHG':
+ // NS: <<< CHG {id} {status} {code}
+ // ignore it
+ // change our status to online first
+ break;
+
+ case 'XFR':
+ // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
+ @list(/* XFR */, /* {id} */, /* SB */, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
+ @list($ip, $port) = @explode(':', $server);
+ $this->error = '';
+ $aSBresult = $this->switchboard_control($ip, $port, $cki_code, $aTo, $sMessage);
+ if ($aSBresult === false) {
+ // error for switchboard, quit
+ $this->log_message("!!! error for sending message: $this->error");
+ $process_file = false;
+ $sent = false;
+ break;
+ }
+ if ($aSBresult === true) {
+ $this->log_message("*** already send message from $process_file");
+ if ($backup_file) {
+ $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
+ if (!file_exists($backup_dir))
+ @mkdir($backup_dir);
+ $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.$this->getpid().'_'.basename($process_file);
+ if (@rename($process_file, $backup_name))
+ $this->log_message("*** move file to $backup_name");
+ }
+ @unlink($process_file);
+ $process_file = false;
+ $sent = true;
+ break;
+ }
+ // okay, process offline user or other networks
+ if ($this->protocol == 'MSNP9') {
+ // MSNP9 don't support OIM
+ $this->debug_message("*** MSNP9 don't support OIM, so we ignore offline user and other networks!");
+ $this->log_message("*** already send message from $process_file");
+ if ($backup_file) {
+ $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
+ if (!file_exists($backup_dir))
+ @mkdir($backup_dir);
+ $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.$this->getpid().'_'.basename($process_file);
+ if (@rename($process_file, $backup_name))
+ $this->log_message("*** move file to $backup_name");
+ }
+ @unlink($process_file);
+ $process_file = false;
+ $sent = true;
+ break;
+ }
+ // offline user first
+ $lockkey = '';
+ $re_login = false;
+ foreach ($aSBresult['offline'] as $to) {
+ for ($i = 0; $i < $this->oim_try; $i++) {
+ $chl_code = $this->sendOIM($to, $sMessage, $lockkey);
+ if ($chl_code === true) {
+ // finished
+ break;
+ }
+ if ($chl_code === false) {
+ if ($re_login) {
+ $this->log_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
+ break;
+ }
+ $this->log_message("*** can't send OIM, maybe ticket expired, try to login again");
+ // maybe we need to re-login again
+ $aTickets = $this->get_passport_ticket();
+ if (!$aTickets || !is_array($aTickets)) {
+ // failed to login? ignore it
+ $this->log_message("*** can't re-login, something wrong here, ignore this OIM");
+ break;
+ }
+ $re_login = true;
+ $this->oim_ticket = $aTickets['oim_ticket'];
+ $this->contact_ticket = $aTickets['contact_ticket'];
+ $this->web_ticket = $aTickets['web_ticket'];
+ $this->space_ticket = $aTickets['space_ticket'];
+ $this->storage_ticket = $aTickets['storage_ticket'];
+ $this->log_message("**** get new ticket, try it again");
+ $i--;
+ continue;
+ }
+ // need challenge lockkey
+ $this->log_message("*** we need a new challenge code for $chl_code");
+ $lockkey = $this->getChallenge($chl_code);
+ }
+ }
+ // other networks
+ foreach ($aSBresult['others'] as $network => $aNetUsers) {
+ foreach ($aNetUsers as $to) {
+ $message = "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$sMessage";
+ $len = strlen($message);
+ $this->writeln("UUM $this->id $to $network 1 $len");
+ $this->writedata($message);
+ $this->log_message("*** sent to $to (network: $network):\n$sMessage");
+ }
+ }
+ $this->log_message("*** already send message from $process_file");
+ if ($backup_file) {
+ $backup_dir = dirname($_SERVER['argv'][0]).'/backup';
+ if (!file_exists($backup_dir))
+ @mkdir($backup_dir);
+ $backup_name = $backup_dir.'/'.strftime('%Y%m%d%H%M%S').'_'.$this->getpid().'_'.basename($process_file);
+ if (@rename($process_file, $backup_name))
+ $this->log_message("*** move file to $backup_name");
+ }
+ @unlink($process_file);
+ $process_file = false;
+ $sent = true;
+ break;
+
+ case 'QNG':
+ // NS: <<< QNG {time}
+ @list(/* QNG */, $ping_wait) = @explode(' ', $data);
+ if ($ping_wait == 0) $ping_wait = 50;
+ if (is_int($use_ping) && $use_ping > 0) $ping_wait = $use_ping;
+ break;
+
+ case 'RNG':
+ // someone is trying to talk to us
+ // NS: <<< RNG {session_id} {server} {auth_type} {ticket} {email} {alias} U {client} 0
+ @list(/* RNG */, $sid, $server, /* auth_type */, $ticket, $email, $name, ) = @explode(' ', $data);
+ @list($sb_ip, $sb_port) = @explode(':', $server);
+ $this->log_message("*** RING from $email, $sb_ip:$sb_port");
+ $this->switchboard_ring($sb_ip, $sb_port, $sid, $ticket, $my_function);
+ break;
+
+ case 'OUT':
+ // force logout from NS
+ // NS: <<< OUT xxx
+ fclose($this->fp);
+ $this->log_message("*** LOGOUT from NS");
+ break;
+
+ default:
+ if (is_numeric($code)) {
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
+ $this->debug_message("*** NS: $this->error");
+ if ($process_file !== false) {
+ // try login again...
+ // logout now
+ // NS: >>> OUT
+ $this->writeln("OUT");
+ fclose($this->fp);
+ $this->fp = false;
+ $this->log_message("!!! logout");
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ function sendMessage($sMessage, $aTo)
+ {
+ if ($this->protocol == 'MSNP9') {
+ // we need to send SYN command for MSNP9
+ // NS: >>> SYN {id} 0
+ $this->writeln("SYN $this->id 0");
+ }
+ stream_set_timeout($this->fp, $this->stream_timeout);
+ $quit = false;
+ $online_cnt = 0;
+ $offline_cnt = 0;
+ $other_cnt = 0;
+ $start_tm = time();
+ while (!feof($this->fp)) {
+ if ($quit) break;
+ $data = $this->readln();
+ // no data ?
+ if ($data === false) {
+ if ($this->timeout > 0) {
+ $now_tm = time();
+ $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
+ if ($used_time > $this->timeout) {
+ $this->error = 'Timeout, maybe protocol changed!';
+ $this->debug_message("*** $this->error");
+ break;
+ }
+ }
+ continue;
+ }
+ $code = substr($data, 0, 3);
+ $start_tm = time();
+ switch ($code) {
+ case 'SBS':
+ // after 'USR {id} OK {user} {verify} 0' response, the server will send SBS and profile to us
+ // NS: <<< SBS 0 null
+ // we don't need profile data, so just ignore it
+ // change our status to online first
+ // NS: >>> CHG {id} {status}
+ $this->writeln("CHG $this->id NLN");
+ break;
+
+ case 'MSG':
+ // randomly, we get MSG notification from server
+ // NS: <<< MSG Hotmail Hotmail {size}
+ @list(/* MSG */, /* Hotmail */, /* Hotmail */, $size,) = @explode(' ', $data);
+ // we don't need the notification data, so just ignore it
+ if (is_numeric($size) && $size > 0)
+ $this->readdata($size);
+ break;
+
+ case 'CHL':
+ // randomly, we'll get challenge from server
+ // NS: <<< CHL 0 {code}
+ @list(/* CHL */, /* 0 */, $chl_code,) = @explode(' ', $data);
+ $fingerprint = $this->getChallenge($chl_code);
+ // NS: >>> QRY {id} {product_id} 32
+ // NS: >>> fingerprint
+ $this->writeln("QRY $this->id $this->prod_id 32");
+ $this->writedata($fingerprint);
+ break;
+
+ case 'SYN':
+ if ($this->protocol != 'MSNP9') break;
+ // NS: <<< SYN 8 1 2 1
+ // ignore it
+ // change our status to online first
+ // NS: >>> CHG {id} {status}
+ $this->writeln("CHG $this->id NLN");
+ break;
+
+ case 'CHG':
+ // NS: <<< CHG {id} {status} {code}
+ // ignore it
+
+ // if message is empty or To list is empty, just quit
+ if (count($aTo) == 0 || $sMessage === '') {
+ $quit = true;
+ break;
+ }
+ // okay, try to ask a switchboard (SB) for sending message
+ // NS: >>> XFR {id} SB
+ $this->writeln("XFR $this->id SB");
+ break;
+
+ case 'XFR':
+ // NS: <<< XFR {id} SB {server} CKI {cki} U messenger.msn.com 0
+ @list(/* XFR */, /* {id} */, /* SB */, $server, /* CKI */, $cki_code, /* ... */) = @explode(' ', $data);
+ @list($ip, $port) = @explode(':', $server);
+ $aSBresult = $this->switchboard_control($ip, $port, $cki_code, $aTo, $sMessage);
+ if ($aSBresult === false) {
+ // error for switchboard, quit
+ $quit = true;
+ break;
+ }
+ if ($aSBresult === true) {
+ // we're done switchboard, quit
+ $quit = true;
+ break;
+ }
+ // okay, process offline user
+ if ($this->protocol == 'MSNP9') {
+ // MSNP9 don't support OIM
+ $this->debug_message("*** MSNP9 don't support OIM, so we ignore offline user!");
+ $quit = true;
+ break;
+ }
+ $lockkey = '';
+ foreach ($aSBresult['offline'] as $to) {
+ for ($i = 0; $i < $this->oim_try; $i++) {
+ $chl_code = $this->sendOIM($to, $sMessage, $lockkey);
+ if ($chl_code === true) {
+ // finished
+ break;
+ }
+ if ($chl_code === false) {
+ if ($re_login) {
+ $this->debug_message("*** can't send OIM, but we already re-login again, so ignore this OIM");
+ break;
+ }
+ $this->debug_message("*** can't send OIM, maybe ticket expired, try to login again");
+ // maybe we need to re-login again
+ $aTickets = $this->get_passport_ticket();
+ if (!$aTickets || !is_array($aTickets)) {
+ // failed to login? ignore it
+ $this->debug_message("*** can't re-login, something wrong here, ignore this OIM");
+ break;
+ }
+ $re_login = true;
+ $this->oim_ticket = $aTickets['oim_ticket'];
+ $this->contact_ticket = $aTickets['contact_ticket'];
+ $this->web_ticket = $aTickets['web_ticket'];
+ $this->space_ticket = $aTickets['space_ticket'];
+ $this->storage_ticket = $aTickets['storage_ticket'];
+ $this->debug_message("**** get new ticket, try it again");
+ $i--;
+ continue;
+ }
+ // need challenge lockkey
+ $this->debug_message("*** we need a new challenge code for $chl_code");
+ $lockkey = $this->getChallenge($chl_code);
+ }
+ }
+ // other networks
+ foreach ($aSBresult['others'] as $network => $aNetUsers) {
+ foreach ($aNetUsers as $to) {
+ $other_cnt++;
+ $message = "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$sMessage";
+ $len = strlen($message);
+ $this->writeln("UUM $this->id $to $network 1 $len");
+ $this->writedata($message);
+ $this->debug_message("*** sent to $to (network: $network):\n$sMessage");
+ }
+ }
+ $quit = true;
+ break;
+
+ default:
+ if (is_numeric($code)) {
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
+ $this->debug_message("*** NS: $this->error");
+ }
+ break;
+ }
+ }
+ // logout now
+ // NS: >>> OUT
+ $this->writeln("OUT");
+ fclose($this->fp);
+ return array(
+ 'online' => $online_cnt,
+ 'offline' => $offline_cnt,
+ 'others' => $other_cnt
+ );
+ }
+
+ function getChallenge($code)
+ {
+ if ($this->protocol == 'MSNP9') {
+ // simple challenge for MSNP9
+ return md5($code.$this->prod_key);
+ }
+ // MSNP15
+ // http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
+ // Step 1: The MD5 Hash
+ $md5Hash = md5($code.$this->prod_key);
+ $aMD5 = @explode("\0", chunk_split($md5Hash, 8, "\0"));
+ for ($i = 0; $i < 4; $i++) {
+ $aMD5[$i] = implode('', array_reverse(@explode("\0", chunk_split($aMD5[$i], 2, "\0"))));
+ $aMD5[$i] = (0 + base_convert($aMD5[$i], 16, 10)) & 0x7FFFFFFF;
+ }
+
+ // Step 2: A new string
+ $chl_id = $code.$this->prod_id;
+ $chl_id .= str_repeat('0', 8 - (strlen($chl_id) % 8));
+
+ $aID = @explode("\0", substr(chunk_split($chl_id, 4, "\0"), 0, -1));
+ for ($i = 0; $i < count($aID); $i++) {
+ $aID[$i] = implode('', array_reverse(@explode("\0", chunk_split($aID[$i], 1, "\0"))));
+ $aID[$i] = 0 + base_convert(bin2hex($aID[$i]), 16, 10);
+ }
+
+ // Step 3: The 64 bit key
+ $magic_num = 0x0E79A9C1;
+ $str7f = 0x7FFFFFFF;
+ $high = 0;
+ $low = 0;
+ for ($i = 0; $i < count($aID); $i += 2) {
+ $temp = $aID[$i];
+ $temp = bcmod(bcmul($magic_num, $temp), $str7f);
+ $temp = bcadd($temp, $high);
+ $temp = bcadd(bcmul($aMD5[0], $temp), $aMD5[1]);
+ $temp = bcmod($temp, $str7f);
+
+ $high = $aID[$i+1];
+ $high = bcmod(bcadd($high, $temp), $str7f);
+ $high = bcadd(bcmul($aMD5[2], $high), $aMD5[3]);
+ $high = bcmod($high, $str7f);
+
+ $low = bcadd(bcadd($low, $high), $temp);
+ }
+
+ $high = bcmod(bcadd($high, $aMD5[1]), $str7f);
+ $low = bcmod(bcadd($low, $aMD5[3]), $str7f);
+
+ $new_high = bcmul($high & 0xFF, 0x1000000);
+ $new_high = bcadd($new_high, bcmul($high & 0xFF00, 0x100));
+ $new_high = bcadd($new_high, bcdiv($high & 0xFF0000, 0x100));
+ $new_high = bcadd($new_high, bcdiv($high & 0xFF000000, 0x1000000));
+ // we need integer here
+ $high = 0+$new_high;
+
+ $new_low = bcmul($low & 0xFF, 0x1000000);
+ $new_low = bcadd($new_low, bcmul($low & 0xFF00, 0x100));
+ $new_low = bcadd($new_low, bcdiv($low & 0xFF0000, 0x100));
+ $new_low = bcadd($new_low, bcdiv($low & 0xFF000000, 0x1000000));
+ // we need integer here
+ $low = 0+$new_low;
+
+ // we just use 32 bits integer, don't need the key, just high/low
+ // $key = bcadd(bcmul($high, 0x100000000), $low);
+
+ // Step 4: Using the key
+ $md5Hash = md5($code.$this->prod_key);
+ $aHash = @explode("\0", chunk_split($md5Hash, 8, "\0"));
+
+ $hash = '';
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[0], 16, 10)) ^ $high);
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[1], 16, 10)) ^ $low);
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[2], 16, 10)) ^ $high);
+ $hash .= sprintf("%08x", (0 + base_convert($aHash[3], 16, 10)) ^ $low);
+
+ return $hash;
+ }
+
+ function switchboard_control($ip, $port, $cki_code, $aTo, $sMessage)
+ {
+ if (is_array($aTo))
+ $aUsers = $aTo;
+ else
+ $aUsers[0] = $aTo;
+
+ $aMSNUsers = array();
+ $aOtherUsers = array();
+ foreach ($aUsers as $user) {
+ @list($u_user, $u_domain, $u_network) = @explode('@', $user);
+ if ($u_network === '' || $u_network == NULL)
+ $u_network = 1;
+ $to_email = trim($u_user.'@'.$u_domain);
+ if ($u_network == 1)
+ $aMSNUsers[] = $to_email;
+ else
+ $aOtherUsers[$u_network][] = $to_email;
+ }
+
+ $aOfflineUsers = array();
+ $total_cnt = count($aMSNUsers);
+
+ if ($total_cnt == 0) {
+ $this->sb_writeln("OUT");
+ @fclose($this->sb);
+ if (count($aOtherUsers) > 0) {
+ return array('msn' => true,
+ 'offline' => $aOfflineUsers,
+ 'others' => $aOtherUsers);
+ }
+ return true;
+ }
+
+ $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
+ $this->sb = @fsockopen($ip, $port, $errno, $errstr, 5);
+ if (!$this->sb) {
+ $this->error = "SB: Can't connect to $ip:$port, error => $errno, $errstr";
+ $this->debug_message("*** $this->error");
+ return false;
+ }
+
+ stream_set_timeout($this->sb, $this->stream_timeout);
+ // SB: >>> USR {id} {user} {cki}
+ $this->sb_writeln("USR $this->id $this->user $cki_code");
+
+ $sent = false;
+ $cal_cnt = 0;
+ $other_cnt = 0;
+ $offline_cnt = 0;
+ $start_tm = time();
+ $got_error = false;
+ while (!feof($this->sb)) {
+ if ($sent) break;
+ $data = $this->sb_readln();
+ if ($data === false) {
+ if ($this->timeout > 0) {
+ $now_tm = time();
+ $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
+ if ($used_time > $this->timeout) {
+ $this->error = 'Timeout, maybe protocol changed!';
+ $this->debug_message("*** $this->error");
+ break;
+ }
+ }
+ continue;
+ }
+ $code = substr($data, 0, 3);
+ $start_tm = time();
+ switch($code) {
+ case 'USR':
+ // SB: <<< USR {id} OK {user} {alias}
+ // we don't need the data, just ignore it
+ $user = $aMSNUsers[$cal_cnt++];
+ // request user to join this switchboard
+ // SB: >>> CAL {id} {user}
+ $this->sb_writeln("CAL $this->id $user");
+ break;
+
+ case 'CAL':
+ // SB: <<< CAL {id} RINGING {?}
+ // we don't need this, just ignore, and wait for other response
+ break;
+
+ case '217':
+ // SB: <<< 217 {id}
+ // if user isn't online or no such user, we will get 217.
+ // switchboard can't send message if he isn't online
+ $aOfflineUsers[$offline_cnt++] = $user;
+ // continue process with JOI here
+ case 'JOI':
+ // SB: <<< JOI {user} {alias} {clientid?}
+ // someone join us
+ // we don't need the data, just ignore it
+ // try to call others if we've
+ if ($cal_cnt < $total_cnt) {
+ $user = $aMSNUsers[$cal_cnt++];
+ // request user to join this switchboard
+ // SB: >>> CAL {id} {user}
+ $this->sb_writeln("CAL $this->id $user");
+ break;
+ }
+ // no more user here
+ // check, all users are offline
+ if ($cal_cnt == $offline_cnt) {
+ $this->debug_message("*** SB: no any use online! skip to send message!");
+ $sent = true;
+ break;
+ }
+ $message = "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$sMessage";
+ $len = strlen($message);
+ $this->sb_writeln("MSG 20 N $len");
+ $this->sb_writedata($message);
+ $sent = true;
+ break;
+
+ default:
+ if (is_numeric($code)) {
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
+ $this->debug_message("*** SB: $this->error");
+ $got_error = true;
+ }
+ break;
+ }
+ }
+ $this->sb_writeln("OUT");
+ @fclose($this->sb);
+ if ($sent) {
+ if (count($aOfflineUsers) > 0 || count($aOtherUsers) > 0) {
+ return array('msn' => !$got_error,
+ 'offline' => $aOfflineUsers,
+ 'others' => $aOtherUsers);
+ }
+ return !$got_error;
+ }
+ // if not sent, try to sending via OIM
+ return array('msn' => !$got_error,
+ 'offline' => $aMSNUsers,
+ 'others' => $aOtherUsers);
+ }
+
+ function switchboard_ring($ip, $port, $sid, $ticket, $my_function)
+ {
+ $this->debug_message("*** SB: try to connect to switchboard server $ip:$port");
+ $this->sb = @fsockopen($ip, $port, $errno, $errstr, 5);
+ if (!$this->sb) {
+ $this->error = "SB: Can't connect to $ip:$port, error => $errno, $errstr";
+ $this->debug_message("*** $this->error");
+ $this->log_message("!!! $this->error");
+ return false;
+ }
+
+ stream_set_timeout($this->sb, $this->stream_timeout);
+ // SB: >>> ANS {id} {user} {ticket} {session_id}
+ $this->sb_writeln("ANS $this->id $this->user $ticket $sid");
+
+ $ignore = false;
+ $start_tm = time();
+ while (!feof($this->sb)) {
+ $data = $this->sb_readln();
+ if ($data === false) {
+ if ($this->timeout > 0) {
+ $now_tm = time();
+ $used_time = ($now_tm >= $start_tm) ? $now_tm - $start_tm : $now_tm;
+ if ($used_time > $this->timeout) {
+ if ($ignore == false) {
+ $this->error = 'Timeout, maybe protocol changed!';
+ $this->debug_message("*** $this->error");
+ $this->log_message("!!! $this->error");
+ }
+ $this->sb_writeln("OUT");
+ fclose($this->sb);
+ $this->sb = false;
+ return false;
+ }
+ }
+ continue;
+ }
+ $code = substr($data, 0, 3);
+ $start_tm = time();
+ switch($code) {
+ case 'IRO':
+ // SB: <<< IRO {id} {rooster} {roostercount} {email} {alias} {clientid}
+ @list(/* IRO */, /* id */, $cur_num, $total, $email, $alias, $clientid) = @explode(' ', $data);
+ $this->log_message("*** $email join us");
+ break;
+
+ case 'ANS':
+ // SB: <<< ANS {id} OK
+ // ignore this
+ break;
+
+ case 'BYE':
+ $this->log_message("*** Quit for BYE");
+ $this->sb_writeln("OUT");
+ fclose($this->sb);
+ $this->sb = false;
+ return true;
+
+ case 'MSG':
+ // SB: <<< MSG {email} {alias} {len}
+ @list(/* MSG */, $from_email, /* alias */, $len, ) = @explode(' ', $data);
+ $len = trim($len);
+ $data = $this->sb_readdata($len);
+ $aLines = @explode("\n", $data);
+ $header = true;
+ $ignore = false;
+ $is_p2p = false;
+ $sMsg = '';
+ foreach ($aLines as $line) {
+ $line = trim($line);
+ if ($header) {
+ if ($line === '') {
+ $header = false;
+ continue;
+ }
+ if (strncasecmp($line, 'TypingUser:', 11) == 0) {
+ // typing notification, just ignore
+ $ignore = true;
+ break;
+ }
+ if (strncasecmp($line, 'Content-Type: text/x-msnmsgr-datacast', 37) == 0) {
+ // nudge, voice clip...., just ignore
+ $ignore = true;
+ break;
+ }
+ if (strncasecmp($line, 'Content-Type: text/x-mms-animemoticon', 37) == 0) {
+ // customized animemotion word, just ignore
+ $ignore = true;
+ break;
+ }
+ if (strncasecmp($line, 'Content-Type: application/x-ms-ink', 34) == 0) {
+ // ink message, just ignore
+ $ignore = true;
+ break;
+ }
+ if (strncasecmp($line, 'Chunk:', 6) == 0) {
+ // we don't handle any split message, just ignore
+ $ignore = true;
+ break;
+ }
+ if (strncasecmp($line, 'Content-Type: application/x-msnmsgrp2p', 38) == 0) {
+ // p2p message, ignore it, but we need to send acknowledgement for it...
+ $is_p2p = true;
+ $p = strstr($data, "\n\n");
+ $sMsg = '';
+ if ($p === false) {
+ $p = strstr($data, "\r\n\r\n");
+ if ($p !== false)
+ $sMsg = substr($p, 4);
+ }
+ else
+ $sMsg = substr($p, 2);
+ break;
+ }
+ continue;
+ }
+ if ($is_p2p) {
+ if ($sMsg !== '')
+ $sMsg .= "\n";
+ }
+ else {
+ if ($sMsg !== '')
+ $sMsg .= "\r\n";
+ }
+ $sMsg .= $line;
+ }
+ if ($ignore) {
+ $this->log_message("*** ingnore from $from_email: $line");
+ break;
+ }
+ if ($is_p2p) {
+ // we will ignore any p2p message after sending acknowledgement
+ $ignore = true;
+ $len = strlen($sMsg);
+ $this->log_message("*** p2p message from $from_email, size $len");
+ // header = 48 bytes
+ // content >= 0 bytes
+ // footer = 4 bytes
+ // so it need to >= 52 bytes
+ if ($len < 52) {
+ $this->log_message("*** p2p: size error, less than 52!");
+ break;
+ }
+ $aDwords = @unpack("V12dword", $sMsg);
+ if (!is_array($aDwords)) {
+ $this->log_message("*** p2p: header unpack error!");
+ break;
+ }
+ $this->debug_message("*** p2p: dump received message:\n".$this->dump_binary($sMsg));
+ $hdr_SessionID = $aDwords['dword1'];
+ $hdr_Identifier = $aDwords['dword2'];
+ $hdr_DataOffsetLow = $aDwords['dword3'];
+ $hdr_DataOffsetHigh = $aDwords['dword4'];
+ $hdr_TotalDataSizeLow = $aDwords['dword5'];
+ $hdr_TotalDataSizeHigh = $aDwords['dword6'];
+ $hdr_MessageLength = $aDwords['dword7'];
+ $hdr_Flag = $aDwords['dword8'];
+ $hdr_AckID = $aDwords['dword9'];
+ $hdr_AckUID = $aDwords['dword10'];
+ $hdr_AckSizeLow = $aDwords['dword11'];
+ $hdr_AckSizeHigh = $aDwords['dword12'];
+ $this->debug_message("*** p2p: header SessionID = $hdr_SessionID");
+ $this->debug_message("*** p2p: header Inentifier = $hdr_Identifier");
+ $this->debug_message("*** p2p: header Data Offset Low = $hdr_DataOffsetLow");
+ $this->debug_message("*** p2p: header Data Offset High = $hdr_DataOffsetHigh");
+ $this->debug_message("*** p2p: header Total Data Size Low = $hdr_TotalDataSizeLow");
+ $this->debug_message("*** p2p: header Total Data Size High = $hdr_TotalDataSizeHigh");
+ $this->debug_message("*** p2p: header MessageLength = $hdr_MessageLength");
+ $this->debug_message("*** p2p: header Flag = $hdr_Flag");
+ $this->debug_message("*** p2p: header AckID = $hdr_AckID");
+ $this->debug_message("*** p2p: header AckUID = $hdr_AckUID");
+ $this->debug_message("*** p2p: header AckSize Low = $hdr_AckSizeLow");
+ $this->debug_message("*** p2p: header AckSize High = $hdr_AckSizeHigh");
+
+ if ($hdr_Flag == 2) {
+ // just send ACK...
+ $this->sb_writeln("ACK $this->id");
+ break;
+ }
+ if ($hdr_SessionID == 4 && $hdr_Flag == 2) {
+ // ignore?
+ $this->debug_message("*** p2p: ignore flag 4");
+ break;
+ }
+ $finished = false;
+ if ($hdr_TotalDataSizeHigh == 0) {
+ // only 32 bites size
+ if (($hdr_MessageLength + $hdr_DataOffsetLow) == $hdr_TotalDataSizeLow)
+ $finished = true;
+ }
+ else {
+ // we won't accept any file transfer
+ // so I think we won't get any message size need to use 64 bits
+ // 64 bits size here, can't count directly...
+ $totalsize = base_convert(sprintf("%X%08X", $hdr_TotalDataSizeHigh, $hdr_TotalDataSizeLow), 16, 10);
+ $dataoffset = base_convert(sprintf("%X%08X", $hdr_DataOffsetHigh, $hdr_DataOffsetLow), 16, 10);
+ $messagelength = base_convert(sprintf("%X", $hdr_MessageLength), 16, 10);
+ $now_size = bcadd($dataoffset, $messagelength);
+ if (bccomp($now_size, $totalsize) >= 0)
+ $finished = true;
+ }
+ if (!$finished) {
+ // ignore not finished split packet
+ $this->debug_message("*** p2p: ignore split packet, not finished");
+ break;
+ }
+ $new_id = ~$hdr_Identifier;
+ $hdr = pack("LLLLLLLLLLLL", $hdr_SessionID,
+ $new_id,
+ 0, 0,
+ $hdr_TotalDataSize1, $hdr_TotalDataSize2,
+ 0,
+ 2,
+ $hdr_Identifier,
+ $hdr_AckID,
+ $hdr_TotalDataSize1, $hdr_TotalDataSize2);
+ $footer = pack("L", 0);
+ $message = "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $from_email\r\n\r\n$hdr$footer";
+ $len = strlen($message);
+ $this->sb_writeln("MSG 1 D $len");
+ $this->sb_writedata($message);
+ $this->log_message("*** p2p: send acknowledgement for $hdr_SessionID");
+ $this->debug_message("*** p2p: dump sent message:\n".$this->dump_binary($hdr.$footer));
+ break;
+ }
+ $this->log_message("*** MSG from $from_email: $sMsg");
+ if ($my_function && function_exists($my_function) && is_callable($my_function)) {
+ $msg = $my_function($from_email, $sMsg);
+ if ($msg !== '') {
+ /* typing
+ $message = "MIME-Version: 1.0\r\nContent-Type: text/x-msmsgscontrol\r\nTypingUser: $this->user\r\n\r\n\r\n";
+ $len = strlen($message);
+ $this->sb_writeln("MSG 20 N $len");
+ $this->sb_writedata($message);
+ */
+ $message = "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$msg";
+ $len = strlen($message);
+ $this->sb_writeln("MSG 20 N $len");
+ $this->sb_writedata($message);
+ $this->log_message("Response to $from_email:\n$msg");
+ }
+ }
+ $this->sb_writeln("OUT");
+ fclose($this->sb);
+ $this->sb = false;
+ return true;
+
+ default:
+ if (is_numeric($code)) {
+ $this->error = "Error code: $code, please check the detail information from: http://msnpiki.msnfanatic.com/index.php/Reference:Error_List";
+ $this->debug_message("*** SB: $this->error");
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ function sendOIM($to, $sMessage, $lockkey)
+ {
+ $XML = '<?xml version="1.0" encoding="utf-8"?>
+<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+<soap:Header>
+ <From memberName="'.$this->user.'"
+ friendlyName="=?utf-8?B?'.base64_encode($this->user).'?="
+ xml:lang="zh-TW"
+ proxy="MSNMSGR"
+ xmlns="http://messenger.msn.com/ws/2004/09/oim/"
+ msnpVer="'.$this->protocol.'"
+ buildVer="'.$this->buildver.'"/>
+ <To memberName="'.$to.'" xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
+ <Ticket passport="'.htmlspecialchars($this->oim_ticket).'"
+ appid="'.$this->prod_id.'"
+ lockkey="'.$lockkey.'"
+ xmlns="http://messenger.msn.com/ws/2004/09/oim/"/>
+ <Sequence xmlns="http://schemas.xmlsoap.org/ws/2003/03/rm">
+ <Identifier xmlns="http://schemas.xmlsoap.org/ws/2002/07/utility">http://messenger.msn.com</Identifier>
+ <MessageNumber>1</MessageNumber>
+ </Sequence>
+</soap:Header>
+<soap:Body>
+ <MessageType xmlns="http://messenger.msn.com/ws/2004/09/oim/">text</MessageType>
+ <Content xmlns="http://messenger.msn.com/ws/2004/09/oim/">MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: base64
+X-OIM-Message-Type: OfflineMessage
+X-OIM-Run-Id: {DAB68CFA-38C9-449B-945E-38AFA51E50A7}
+X-OIM-Sequence-Num: 1
+
+'.chunk_split(base64_encode($sMessage)).'
+ </Content>
+</soap:Body>
+</soap:Envelope>';
+
+ $header_array = array(
+ 'SOAPAction: '.$this->oim_send_soap,
+ 'Content-Type: text/xml',
+ 'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Messenger '.$this->buildver.')'
+ );
+
+ $this->debug_message("*** URL: $this->oim_send_url");
+ $this->debug_message("*** Sending SOAP:\n$XML");
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $this->oim_send_url);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $header_array);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
+ if ($this->debug) curl_setopt($curl, CURLOPT_HEADER, 1);
+ curl_setopt($curl, CURLOPT_POST, 1);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $XML);
+ $data = curl_exec($curl);
+ $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+ $this->debug_message("*** Get Result:\n$data");
+
+ if ($http_code == 200) {
+ $this->debug_message("*** OIM sent for $to");
+ return true;
+ }
+
+ // the lockkey is invalid, authenticated fail, we need challenge it again
+ // <LockKeyChallenge xmlns="http://messenger.msn.com/ws/2004/09/oim/">364763969</LockKeyChallenge>
+ preg_match("#<LockKeyChallenge (.*)>(.*)</LockKeyChallenge>#", $data, $matches);
+ // no challenge, assume it work
+ if (count($matches) == 0) {
+ //<faultcode xmlns:q0="http://messenger.msn.com/ws/2004/09/oim/">q0:AuthenticationFailed</faultcode>
+ preg_match("#<faultcode (.*)>(.*)</faultcode>#", $data, $matches);
+ if (count($matches) == 0) {
+ // no error, we assume the OIM is sent
+ $this->debug_message("*** OIM sent for $to");
+ return true;
+ }
+ $err_code = $matches[2];
+ //<faultstring>Exception of type 'System.Web.Services.Protocols.SoapException' was thrown.</faultstring>
+ preg_match("#<faultstring>(.*)</faultstring>#", $data, $matches);
+ if (count($matches) > 0)
+ $err_msg = $matches[1];
+ else
+ $err_msg = '';
+ $this->debug_message("*** OIM failed for $to");
+ $this->debug_message("*** OIM Error code: $err_code");
+ $this->debug_message("*** OIM Error Message: $err_msg");
+ return false;
+ }
+ // yes, we get new LockKeyChallenge
+ $challenge = $matches[2];
+ $this->debug_message("*** OIM need new challenge ($challenge) for $to");
+ return $challenge;
+ }
+
+ // read data for specified size
+ function readdata($size)
+ {
+ $data = '';
+ $count = 0;
+ while (!feof($this->fp)) {
+ $buf = @fread($this->fp, $size - $count);
+ $data .= $buf;
+ $count += strlen($buf);
+ if ($count >= $size) break;
+ }
+ $this->debug_message("NS: data ($size/$count) <<<\n$data");
+ return $data;
+ }
+
+ // read one line
+ function readln()
+ {
+ $data = @fgets($this->fp, 4096);
+ if ($data !== false) {
+ $data = trim($data);
+ $this->debug_message("NS: <<< $data");
+ }
+ return $data;
+ }
+
+ // write to server, append \r\n, also increase id
+ function writeln($data)
+ {
+ @fwrite($this->fp, $data."\r\n");
+ $this->debug_message("NS: >>> $data");
+ $this->id++;
+ return;
+ }
+
+ // write data to server
+ function writedata($data)
+ {
+ @fwrite($this->fp, $data);
+ $this->debug_message("NS: >>> $data");
+ return;
+ }
+
+ // read data for specified size for SB
+ function sb_readdata($size)
+ {
+ $data = '';
+ $count = 0;
+ while (!feof($this->sb)) {
+ $buf = @fread($this->sb, $size - $count);
+ $data .= $buf;
+ $count += strlen($buf);
+ if ($count >= $size) break;
+ }
+ $this->debug_message("SB: data ($size/$count) <<<\n$data");
+ return $data;
+ }
+
+ // read one line for SB
+ function sb_readln()
+ {
+ $data = @fgets($this->sb, 4096);
+ if ($data !== false) {
+ $data = trim($data);
+ $this->debug_message("SB: <<< $data");
+ }
+ return $data;
+ }
+
+ // write to server for SB, append \r\n, also increase id
+ // switchboard server only accept \r\n, it will lost connection if just \n only
+ function sb_writeln($data)
+ {
+ @fwrite($this->sb, $data."\r\n");
+ $this->debug_message("SB: >>> $data");
+ $this->id++;
+ return;
+ }
+
+ // write data to server
+ function sb_writedata($data)
+ {
+ @fwrite($this->sb, $data);
+ $this->debug_message("SB: >>> $data");
+ return;
+ }
+
+ // show debug information
+ function debug_message($str)
+ {
+ if (!$this->debug) return;
+ if ($this->log_file !== '') {
+ $fp = fopen($this->log_file, 'at');
+ if ($fp) {
+ fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.$this->getpid().'] '.$str."\n");
+ fclose($fp);
+ return;
+ }
+ // still show debug information, if we can't open log_file
+ }
+ echo $str."\n";
+ return;
+ }
+
+ function dump_binary($str)
+ {
+ $buf = '';
+ $a_str = '';
+ $h_str = '';
+ $len = strlen($str);
+ for ($i = 0; $i < $len; $i++) {
+ if (($i % 16) == 0) {
+ if ($buf !== '') {
+ $buf .= "$h_str $a_str\n";
+ }
+ $buf .= sprintf("%04X:", $i);
+ $a_str = '';
+ $h_str = '';
+ }
+ $ch = ord($str[$i]);
+ if ($ch < 32)
+ $a_str .= '.';
+ else
+ $a_str .= chr($ch);
+ $h_str .= sprintf(" %02X", $ch);
+ }
+ if ($h_str !== '')
+ $buf .= "$h_str $a_str\n";
+ return $buf;
+ }
+
+ // write log
+ function log_message($str)
+ {
+ if (!$this->log_path)
+ $this->log_path = dirname($_SERVER['argv'][0]);
+ $fname = $this->log_path.DIRECTORY_SEPARATOR.'log'.DIRECTORY_SEPARATOR.'msn_'.strftime('%Y%m%d').'.log';
+ $fp = fopen($fname, 'at');
+ if ($fp) {
+ fputs($fp, strftime('%m/%d/%y %H:%M:%S').' ['.$this->getpid().'] '.$str."\n");
+ fclose($fp);
+ }
+ $this->debug_message($str);
+ return;
+ }
+
+ // get current process id
+ function getpid()
+ {
+ if ($this->windows) return 'nopid';
+ return posix_getpid();
+ }
+}
+
+?>
Modified: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -1,181 +1,221 @@
-#!/usr/bin/php -Cq
-<?php
-/*
-INSTALL:
-
-1. Create some folders like:
- mkdir /var/spool/msnbot
- mkdir /var/spool/msnbot/log
- mkdir /var/spool/msnbot/spool
-
-2. Change the attribute for spool folder:
- chmod 777 /var/spool/msnbot/spool
- chmod o+t /var/spool/msnbot/spool
-
-3. Put msnbot.php and msn.class.php to /var/spool/msnbot/, and make msnbot.php executable:
- chmod +x /var/spool/msnbot/msnbot.php
-
-4. Use msnbot.sh as your startup script to execute msnbot after system boot.
-
-5. Change processMsg function in msnbot.php to do whatever you want.
-
-6. If you need to send message to someone, just create a file under /var/spool/msnbot/spool, the filename like '*.msn',
- and the format like test.msn, first line is TO: email1,email2, and the other lines is the message.
- After create the file, just change the attribute to 0666, then msnbot will try to send it.
-
-*/
-
-function sig_handler($signal)
-{
- global $msn;
-
- if (is_object($msn)) {
- $msn->log_message("*** someone kill me ***");
- $msn->kill_me = true;
- }
- return;
-}
-
-// network:
-// 1: WLM/MSN
-// 2: LCS
-// 4: Mobile Phones
-// 32: Yahoo!
-function getNetworkName($network)
-{
- switch ($network) {
- case 1:
- return 'WLM/MSN';
- case 2:
- return 'LCS';
- case 4:
- return 'Mobile Phones';
- case 32:
- return 'Yahoo!';
- }
- return "Unknown ($network)";
-}
-
-// your function to process message from someone
-function processMsg($from, $msg, $network = 1)
-{
- global $msn_acct;
-
- // from myself? ignore it
- if ($from == $msn_acct) return '';
- // also ignore other bot
- if ($from == 'msnbot at hotmail.com') return '';
- $nw_name = getNetworkName($network);
- return "message from $from (network: $nw_name):\r\n$msg";
-}
-
-// your function when someone add us to his contact list
-function addContact($from, $network = 1)
-{
- global $aNotifyUser;
-
- if (is_array($aNotifyUser) && count($aNotifyUser) > 0) {
- $nw_name = getNetworkName($network);
- $now = strftime('%D %T');
- $fname = dirname($_SERVER['argv'][0]).'/spool/msn_'.posix_getpid().'_'.md5('add'.rand(1,1000).$now).'.msn';
- $fp = fopen($fname, 'wt');
- if ($fp) {
- $list = '';
- foreach ($aNotifyUser as $user) {
- if ($list === '')
- $list = $user;
- else
- $list .= ','.$user;
- }
- fputs($fp, "TO: $list\n");
- fputs($fp, "Now: $now\n$from (network: $nw_name) add me to his contact list!");
- fclose($fp);
- chmod($fname, 0666);
- }
- }
- return;
-}
-
-// your function when someone remove us from his contact list
-function removeContact($from, $network = 1)
-{
- global $aNotifyUser;
-
- if (is_array($aNotifyUser) && count($aNotifyUser) > 0) {
- $nw_name = getNetworkName($network);
- $now = strftime('%D %T');
- $fname = dirname($_SERVER['argv'][0]).'/spool/msn_'.posix_getpid().'_'.md5('delete'.rand(1,1000).$now).'.msn';
- $fp = fopen($fname, 'wt');
- if ($fp) {
- $list = '';
- foreach ($aNotifyUser as $user) {
- if ($list === '')
- $list = $user;
- else
- $list .= ','.$user;
- }
- fputs($fp, "TO: $list\n");
- fputs($fp, "Now: $now\n$from (network: $nw_name) remove me from his contact list!");
- fclose($fp);
- chmod($fname, 0666);
- }
- }
- return;
-}
-
-// tick use required as of PHP 4.3.0
-declare (ticks = 1);
-
-error_reporting(E_ALL);
-
-$pid = pcntl_fork();
-if ($pid) exit;
-
-require('config.php');
-include_once('msn.class.php');
-
-$aContact = false;
-$msn = new MSN('', dirname($_SERVER['argv'][0]).'/log/debug.log');
-
-pcntl_signal(SIGTERM, 'sig_handler');
-pcntl_signal(SIGHUP, 'sig_handler');
-
-$fp = fopen(dirname($_SERVER['argv'][0]).'/log/msnbot.pid', 'wt');
-if ($fp) {
- fputs($fp, posix_getpid());
- fclose($fp);
-}
-/*
-
-function doLoop($user,
- $password,
- $alias = '',
- $my_function = false,
- $my_add_function = false,
- $my_rem_function = false,
- $use_ping = false,
- $retry_wait = 30,
- $backup_file = true,
- $update_pending = true
- );
-
-$user: the MSN account
-$password: password of the MSN account
-$alias: the MSN alias, if empty, the program will use $user
-$my_function: your message process function, if empty, msnbot will ignore any message. like: processMsg($from, $msg, $network = 1)
-$my_add_function: your function when someone add us to his contact list. like: addContact($from, $network = 1)
-$my_rem_function: your function when someone remove us from his contact list. like removeContact($from, $network = 1)
-$use_ping: if true, msnbot will send PNG to server (0-50 seconds) to keep alive, but... even without this, we're still online.
-$retry_wait: if we lost connection, how long we should wait before we try again.
-$backup_file: move .msn file to backup folder after processed
-$update_pending: try to add pending list member to avail/reverse list, and delete it from pending list
-
-*/
-$msn->doLoop($msn_acct, $msn_password, $msn_alias, 'processMsg', 'addContact', 'removeContact');
-
-$msn->log_message("done!");
- at unlink(dirname($_SERVER['argv'][0]).'/log/msnbot.pid');
-
-exit;
-
-?>
+#!/usr/bin/php -Cq
+<?php
+/*
+UNIX INSTALL:
+
+1. Create some folders like:
+ mkdir /var/spool/msnbot
+ mkdir /var/spool/msnbot/log
+ mkdir /var/spool/msnbot/spool
+
+2. Change the attribute for spool folder:
+ chmod 777 /var/spool/msnbot/spool
+ chmod o+t /var/spool/msnbot/spool
+
+3. Put msnbot.php, config.php and msn.class.php to /var/spool/msnbot/, and make msnbot.php executable:
+ chmod +x /var/spool/msnbot/msnbot.php
+
+4. change the setting in config.php
+
+5. Use msnbot.sh as your startup script to execute msnbot after system boot.
+
+6. Change processMsg function in msnbot.php to do whatever you want.
+
+7. If you need to send message to someone, just create a file under /var/spool/msnbot/spool, the filename like '*.msn',
+ and the format like test.msn, first line is TO: email1,email2, and the other lines is the message.
+ After create the file, just change the attribute to 0666, then msnbot will try to send it.
+ NOTICE: file encoding should be UTF-8 if included non-English word.
+
+WINDOWS INSTALL:
+
+1. Create some folders like:
+ md c:\msnbot
+ md c:\msnbot\log
+ md c:\msnbot\spool
+
+2. Put msnbot.php, config.php and msn.class.php to c:\msnbot
+
+3. change the setting in config.php
+
+4. Change processMsg function in msnbot.php to do whatever you want.
+
+5. execute php.exe -Cq c:\msnbot\msnbot.php
+
+6. If you need to send message to someone, just create a file under c:\msnbot\spool, the filename like '*.msn',
+ and the format like test.msn, first line is TO: email1,email2, and the other lines is the message.
+ NOTICE: file encoding should be UTF-8 if included non-English word.
+
+*/
+
+function sig_handler($signal)
+{
+ global $msn;
+
+ if (is_object($msn)) {
+ $msn->log_message("*** someone kill me ***");
+ $msn->kill_me = true;
+ }
+ return;
+}
+
+// network:
+// 1: WLM/MSN
+// 2: LCS
+// 4: Mobile Phones
+// 32: Yahoo!
+function getNetworkName($network)
+{
+ switch ($network) {
+ case 1:
+ return 'WLM/MSN';
+ case 2:
+ return 'LCS';
+ case 4:
+ return 'Mobile Phones';
+ case 32:
+ return 'Yahoo!';
+ }
+ return "Unknown ($network)";
+}
+
+// your function to process message from someone
+function processMsg($from, $msg, $network = 1)
+{
+ global $msn_acct;
+
+ // from myself? ignore it
+ if ($from == $msn_acct) return '';
+ // also ignore other bot
+ if ($from == 'msnbot at hotmail.com') return '';
+ $nw_name = getNetworkName($network);
+ return "message from $from (network: $nw_name):\r\n$msg";
+}
+
+// your function when someone add us to his contact list
+function addContact($from, $network = 1)
+{
+ global $aNotifyUser;
+
+ if (is_array($aNotifyUser) && count($aNotifyUser) > 0) {
+ $nw_name = getNetworkName($network);
+ $now = strftime('%D %T');
+ $fname = dirname($_SERVER['argv'][0]).DIRECTORY_SEPARATOR.'spool'.DIRECTORY_SEPARATOR.'msn_'.getpid().'_'.md5('add'.rand(1,1000).$now).'.msn';
+ $fp = fopen($fname, 'wt');
+ if ($fp) {
+ $list = '';
+ foreach ($aNotifyUser as $user) {
+ if ($list === '')
+ $list = $user;
+ else
+ $list .= ','.$user;
+ }
+ fputs($fp, "TO: $list\n");
+ fputs($fp, "Now: $now\n$from (network: $nw_name) add me to his contact list!");
+ fclose($fp);
+ chmod($fname, 0666);
+ }
+ }
+ return;
+}
+
+// your function when someone remove us from his contact list
+function removeContact($from, $network = 1)
+{
+ global $aNotifyUser;
+
+ if (is_array($aNotifyUser) && count($aNotifyUser) > 0) {
+ $nw_name = getNetworkName($network);
+ $now = strftime('%D %T');
+ $fname = dirname($_SERVER['argv'][0]).DIRECTORY_SEPARATOR.'spool'.DIRECTORY_SEPARATOR.'msn_'.getpid().'_'.md5('delete'.rand(1,1000).$now).'.msn';
+ $fp = fopen($fname, 'wt');
+ if ($fp) {
+ $list = '';
+ foreach ($aNotifyUser as $user) {
+ if ($list === '')
+ $list = $user;
+ else
+ $list .= ','.$user;
+ }
+ fputs($fp, "TO: $list\n");
+ fputs($fp, "Now: $now\n$from (network: $nw_name) remove me from his contact list!");
+ fclose($fp);
+ chmod($fname, 0666);
+ }
+ }
+ return;
+}
+
+// tick use required as of PHP 4.3.0
+declare (ticks = 1);
+
+error_reporting(E_ALL);
+
+if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
+ $windows = true;
+else
+ $windows = false;
+
+if (!$windows) {
+ $pid = pcntl_fork();
+ if ($pid) exit;
+}
+
+require('config.php');
+include_once('msn.class.php');
+
+$msn = new MSN('', dirname($_SERVER['argv'][0]).DIRECTORY_SEPARATOR.'log'.DIRECTORY_SEPARATOR.'debug.log');
+
+if (!$windows) {
+ pcntl_signal(SIGTERM, 'sig_handler');
+ pcntl_signal(SIGHUP, 'sig_handler');
+}
+
+$fp = fopen(dirname($_SERVER['argv'][0]).DIRECTORY_SEPARATOR.'log'.DIRECTORY_SEPARATOR.'msnbot.pid', 'wt');
+if ($fp) {
+ fputs($fp, getpid());
+ fclose($fp);
+}
+/*
+
+function doLoop($user,
+ $password,
+ $alias = '',
+ $my_function = false,
+ $my_add_function = false,
+ $my_rem_function = false,
+ $use_ping = false,
+ $retry_wait = 30,
+ $backup_file = true,
+ $update_pending = true
+ );
+
+$user: the MSN account
+$password: password of the MSN account
+$alias: the MSN alias, if empty, the program will use $user
+$my_function: your message process function, if empty, msnbot will ignore any message. like: processMsg($from, $msg, $network = 1)
+$my_add_function: your function when someone add us to his contact list. like: addContact($from, $network = 1)
+$my_rem_function: your function when someone remove us from his contact list. like removeContact($from, $network = 1)
+$use_ping: if true, msnbot will send PNG to server (0-50 seconds) to keep alive, but... even without this, we're still online.
+ if this variable is a non-zero integer, will send PNG command every $use_ping seconds.
+$retry_wait: if we lost connection, how long we should wait before we try again.
+$backup_file: move .msn file to backup folder after processed
+$update_pending: try to add pending list member to avail/reverse list, and delete it from pending list
+
+*/
+$msn->doLoop($msn_acct, $msn_password, $msn_alias, 'processMsg', 'addContact', 'removeContact', 600);
+
+$msn->log_message("done!");
+ at unlink(dirname($_SERVER['argv'][0]).DIRECTORY_SEPARATOR.'log'.DIRECTORY_SEPARATOR.'msnbot.pid');
+
+exit;
+
+function getpid()
+{
+ global $windows;
+
+ if ($windows) return 'nopid';
+ return posix_getpid();
+}
+
+
+?>
Modified: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnsendmsg.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnsendmsg.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnsendmsg.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -1,56 +1,56 @@
-#!/usr/bin/php -Cq
-<?php
-
-error_reporting(0);
-
-if (!isset($argc)) $argc = $_SERVER['argc'];
-if (!isset($argv)) $argv = $_SERVER['argv'];
-
-if ($argc != 3) {
- echo "Syntax: $argv[0] to msg\n";
- exit;
-}
-/*
-// send it via MSN object directly.
-$aTo = explode(',', $argv[1]);
-$sMsg = $argv[2];
-
-$msn_acct = 'YOUR_MSN_ACCOUNT';
-$msn_password = 'YOUR_MSN_PASSWORD';
-
-include('msn.class.php');
-
-$msn = new MSN();
-
-if (!$msn->connect($msn_acct, $msn_password)) {
- echo "Error for connect to MSN network\n";
- echo "$msn->error\n";
- exit;
-}
-
-$msn->sendMessage($sMsg, $aTo);
-if ($msn->error != '')
- echo "Error: $msn->error\n";
-
-*/
-
-// write the file to use msnbot
-$sTo = $argv[1];
-$sMsg = $argv[2];
-
-$fname = '/var/spool/msnbot/spool/msn_'.posix_getpid().'_'.md5(strftime('%D %T')).'.msn';
-
-$fp = fopen($fname, 'wt');
-if (!$fp) {
- echo "Can't write to $fname\n";
- exit;
-}
-
-fputs($fp, "TO: $sTo\n$sMsg");
-fclose($fp);
-chmod($fname, 0666);
-
-exit;
-
-?>
-
+#!/usr/bin/php -Cq
+<?php
+
+error_reporting(0);
+
+if (!isset($argc)) $argc = $_SERVER['argc'];
+if (!isset($argv)) $argv = $_SERVER['argv'];
+
+if ($argc != 3) {
+ echo "Syntax: $argv[0] to msg\n";
+ exit;
+}
+/*
+// send it via MSN object directly.
+$aTo = explode(',', $argv[1]);
+$sMsg = $argv[2];
+
+$msn_acct = 'YOUR_MSN_ACCOUNT';
+$msn_password = 'YOUR_MSN_PASSWORD';
+
+include_once('msn.class.php');
+
+$msn = new MSN();
+
+if (!$msn->connect($msn_acct, $msn_password)) {
+ echo "Error for connect to MSN network\n";
+ echo "$msn->error\n";
+ exit;
+}
+
+$msn->sendMessage($sMsg, $aTo);
+if ($msn->error != '')
+ echo "Error: $msn->error\n";
+
+*/
+
+// write the file to use msnbot
+$sTo = $argv[1];
+$sMsg = $argv[2];
+
+$fname = '/var/spool/msnbot/spool/msn_'.posix_getpid().'_'.md5(strftime('%D %T')).'.msn';
+
+$fp = fopen($fname, 'wt');
+if (!$fp) {
+ echo "Can't write to $fname\n";
+ exit;
+}
+
+fputs($fp, "TO: $sTo\n$sMsg");
+fclose($fp);
+chmod($fname, 0666);
+
+exit;
+
+?>
+
Modified: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/sample.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/sample.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/sample.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -1,41 +1,41 @@
-#!/usr/bin/php -Cq
-<?php
-
-error_reporting(E_ALL);
-include('msn.class.php');
-
-// force to use MSNP9, without debug information
-// $msn = new MSN('MSNP9');
-
-// force to use MSNP9, with debug information
-// $msn = new MSN('MSNP9', true);
-
-// force to use MSNP15, without debug information
-// $msn = new MSN('MSNP15');
-
-// force to use MSNP15, with debug information
-// $msn = new MSN('MSNP15', true);
-
-// auto detect MSN protocol, without debug information
-// $msn = new MSN;
-
-// auto detect MSN protocol, with debug information
-$msn = new MSN('', true);
-
-if (!$msn->connect('YOUR_ID', 'YOUR_PASSWORD')) {
- echo "Error for connect to MSN network\n";
- echo "$msn->error\n";
- exit;
-}
-
-$msn->sendMessage('Now: '.strftime('%D %T')."\nTesting\nSecond Line\n\n\n\nand Empty Line",
- array(
- 'somebody1 at hotmail.com',
- 'somebody2 at hotmail.com'
- )
- );
-echo "Done!\n";
-exit;
-
-?>
-
+#!/usr/bin/php -Cq
+<?php
+
+error_reporting(E_ALL);
+include_once('msn.class.php');
+
+// force to use MSNP9, without debug information
+// $msn = new MSN('MSNP9');
+
+// force to use MSNP9, with debug information
+// $msn = new MSN('MSNP9', true);
+
+// force to use MSNP15, without debug information
+// $msn = new MSN('MSNP15');
+
+// force to use MSNP15, with debug information
+// $msn = new MSN('MSNP15', true);
+
+// auto detect MSN protocol, without debug information
+// $msn = new MSN;
+
+// auto detect MSN protocol, with debug information
+$msn = new MSN('', true);
+
+if (!$msn->connect('YOUR_ID', 'YOUR_PASSWORD')) {
+ echo "Error for connect to MSN network\n";
+ echo "$msn->error\n";
+ exit;
+}
+
+$msn->sendMessage('Now: '.strftime('%D %T')."\nTesting\nSecond Line\n\n\n\nand Empty Line",
+ array(
+ 'somebody1 at hotmail.com',
+ 'somebody2 at hotmail.com'
+ )
+ );
+echo "Done!\n";
+exit;
+
+?>
+
Modified: plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -13,6 +13,7 @@
{
$blogSettings = $this->_blogInfo->getSettings();
$pluginEnabled = $blogSettings->getValue('plugin_addcommentnotify_enabled');
+ $notifyTrackback = $blogSettings->getValue('plugin_addcommentnotify_notifytrackback');
$usemsnclass = $blogSettings->getValue('addcommentnotify_usemsnclass');
$msnclass_file = $blogSettings->getValue('addcommentnotify_msnclass_file');
$msn_user = $blogSettings->getValue('addcommentnotify_msn_user');
@@ -22,6 +23,7 @@
// Export the settings to the template
$this->setValue('pluginEnabled', $pluginEnabled);
+ $this->setValue('notifyTrackback', $notifyTrackback);
$this->setValue('useMSNClass', $usemsnclass);
$this->setValue('msnClassFile', $msnclass_file);
$this->setValue('msnUser', $msn_user);
Modified: plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_en_UK.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_en_UK.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_en_UK.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -11,6 +11,7 @@
$messages['label_configuration'] = 'Configuration';
$messages['label_addcommentnotify_enable'] = 'Enable this plugin';
+$messages['label_addcommentnotify_notifytrackback'] = 'Enable this plugin for trackback';
$messages['label_addcommentnotify_usemsnclass'] = 'Using msn.class.php';
$messages['label_addcommentnotify_msnclass_file'] = 'msn.class.php location';
$messages['label_addcommentnotify_msn_user'] = 'MSN login account';
@@ -19,6 +20,7 @@
$messages['label_addcommentnotify_tolist'] = 'MSN/Yahoo list';
$messages['help_addcommentnotify_enable'] = 'Enable Add Comment Notify plugin';
+$messages['help_addcommentnotify_notifytrackback'] = 'Enable Add Comment Notify plugin for trackback';
$messages['help_addcommentnotify_usemsnclass'] = 'Using msn.class.php';
$messages['help_addcommentnotify_msnclass_file'] = 'msn.class.php location (If you choose to use msn.class.php)';
$messages['help_addcommentnotify_msn_user'] = 'MSN login account (If you choose to use msn.class.php)';
Modified: plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_zh_TW.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_zh_TW.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_zh_TW.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -11,6 +11,7 @@
$messages['label_configuration'] = '設定';
$messages['label_addcommentnotify_enable'] = '啟動這個外掛模組';
+$messages['label_addcommentnotify_notifytrackback'] = '啟動 Trackback 的新增通知';
$messages['label_addcommentnotify_usemsnclass'] = '使用 msn.class.php 來傳送';
$messages['label_addcommentnotify_msnclass_file'] = 'msn.class.php 的檔案路徑';
$messages['label_addcommentnotify_msn_user'] = 'MSN 的登入帳號';
@@ -19,6 +20,7 @@
$messages['label_addcommentnotify_tolist'] = '收件人名單';
$messages['help_addcommentnotify_enable'] = '啟動 Add Comment Notify 外掛模組';
+$messages['help_addcommentnotify_notifytrackback'] = '啟動 Add Comment Notify 外掛模組對於 Trackback 的處理';
$messages['help_addcommentnotify_usemsnclass'] = '使用 msn.class.php 來傳送訊息';
$messages['help_addcommentnotify_msnclass_file'] = 'msn.class.php 的檔案路徑 (使用 msn.class.php 時必須設定)';
$messages['help_addcommentnotify_msn_user'] = 'MSN 的登入帳號 (使用 msn.class.php 時必須設定)';
Modified: plugins/branches/lifetype-1.2/addcommentnotify/pluginaddcommentnotify.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/pluginaddcommentnotify.class.php 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/pluginaddcommentnotify.class.php 2007-07-16 05:42:13 UTC (rev 5665)
@@ -4,6 +4,7 @@
class PluginAddCommentNotify extends PluginBase
{
var $_pluginEnabled;
+ var $_notifyTrackback;
var $_useMSNClass;
var $_msnClassFile;
var $_msnUser;
@@ -19,13 +20,14 @@
$this->id = 'addcommentnotify';
$this->author = 'twu2';
$this->desc = 'send MSN via msnbot after add comment.';
- $this->version = '20070407';
+ $this->version = '20070716';
// Setup the locale
$this->locales = array('en_UK', 'zh_TW');
if ($source == 'admin')
$this->initAdmin();
+ $this->registerNotification(EVENT_POST_TRACKBACK_ADD);
$this->registerNotification(EVENT_POST_COMMENT_ADD);
}
@@ -43,6 +45,7 @@
{
$blogSettings = $this->blogInfo->getSettings();
$this->_pluginEnabled = $blogSettings->getValue('plugin_addcommentnotify_enabled');
+ $this->_notifyTrackback = $blogSettings->getValue('plugin_addcommentnotify_notifytrackback');
$this->_useMSNClass = $blogSettings->getValue('addcommentnotify_usemsnclass');
$this->_msnClassFile = $blogSettings->getValue('addcommentnotify_msnclass_file');
$this->_msnUser = $blogSettings->getValue('addcommentnotify_msn_user');
@@ -59,12 +62,10 @@
function process($eventType, $params)
{
- if (!isset($params['comment'])) return true;
- $comment = $params['comment'];
$this->register();
if (!$this->_pluginEnabled) return true;
if (!$this->_useMSNClass)
- if (!is_dir($this->_msnbot_spool)) return true;
+ if (!is_dir($this->_msnbot_spool)) return true;
$aTo = @explode(',', $this->_tolist);
if (count($aTo) == 0) return true;
$sTo = '';
@@ -84,20 +85,43 @@
}
if ($sTo === '') return true;
$url = $this->blogInfo->getBlogRequestGenerator();
- $permalink = $url->postPermalink($comment->getArticle()).'#'.$comment->getId();
- $sText = "Date: ".$comment->getDate()."\n".
- "Article: $permalink\n".
- "IP: ".$comment->getClientIp()."\n".
- "Name: ".$comment->getUserName()."\n".
- "Email: ".$comment->getUserEmail()."\n".
- "URL: ".$comment->getUserUrl()."\n".
- "Topic: ".$comment->getNormalizedTopic()."\n".
- "----------------------------------------\n".
- $comment->getNormalizedText();
+ if ($eventType == EVENT_POST_TRACKBACK_ADD) {
+ if (!$this->_notifyTrackback) return true;
+ if (!isset($params['trackback'])) return true;
+ $trackback = $params['trackback'];
+ $permalink = $url->postPermalink($trackback->getArticle());
+ $sText = "Article: $permalink\n".
+ "Name: ".$trackback->getUserName()."\n".
+ "IP: ".$trackback->getClientIp()."\n".
+ "Trackback URL: ".$trackback->getUserUrl()."\n".
+ "Topic: ".$trackback->getNormalizedTopic()."\n".
+ "----------------------------------------\n".
+ $trackback->getNormalizedText();
+ }
+ else {
+ if (!isset($params['comment'])) return true;
+ $comment = $params['comment'];
+ $permalink = $url->postPermalink($comment->getArticle()).'#'.$comment->getId();
+/*
+ $sText = "Date: ".$comment->getDate()."\n".
+ "Article: $permalink\n".
+ "IP: ".$comment->getClientIp()."\n".
+ "Name: ".$comment->getUserName()."\n".
+ "Email: ".$comment->getUserEmail()."\n".
+ "URL: ".$comment->getUserUrl()."\n".
+ "Topic: ".$comment->getNormalizedTopic()."\n".
+ "----------------------------------------\n".
+ $comment->getNormalizedText();
+*/
+ $sText = "Article: $permalink\n".
+ "Name: ".$comment->getUserName()."\n".
+ "Topic: ".$comment->getNormalizedTopic()."\n".
+ "----------------------------------------\n".
+ $comment->getNormalizedText();
+ }
if ($this->_useMSNClass) {
require_once($this->_msnClassFile);
$msn = new MSN;
- echo "$$$$";
if ($msn->connect($this->_msnUser, $this->_msnPassword)) {
$aTo = @explode(',', $sTo);
$msn->sendMessage($sText, $aTo);
Modified: plugins/branches/lifetype-1.2/addcommentnotify/readme.txt
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/readme.txt 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/readme.txt 2007-07-16 05:42:13 UTC (rev 5665)
@@ -1,3 +1,8 @@
+AddCommentNotify Plugin v20070716
+
+ + also send notification for add trackback
+ + update phpmsnclass to version 1.7
+
AddCommentNotify Plugin v20070406
You need msnbot or msn.class.php:
Modified: plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template 2007-07-15 22:07:46 UTC (rev 5664)
+++ plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template 2007-07-16 05:42:13 UTC (rev 5665)
@@ -13,6 +13,13 @@
</div>
</div>
<div class="field">
+ <label for="notifyTrackback">{$locale->tr("label_addcommentnotify_notifytrackback")}</label>
+ <span class="required"></span>
+ <div class="formHelp">
+ <input class="checkbox" type="checkbox" name="notifyTrackback" id="notifyTrackback" {if $notifyTrackback} checked="checked" {/if} value="1" />{$locale->tr("help_addcommentnotify_notifytrackback")}
+ </div>
+ </div>
+ <div class="field">
<label for="useMSNClass">{$locale->tr("label_addcommentnotify_usemsnclass")}</label>
<span class="required"></span>
<div class="formHelp">
More information about the pLog-svn
mailing list