[pLog-svn] r5332 - in plugins/branches/lifetype-1.2: . addcommentnotify addcommentnotify/class addcommentnotify/class/action addcommentnotify/class/phpmsnclass addcommentnotify/class/view addcommentnotify/locale addcommentnotify/templates
mark at devel.lifetype.net
mark at devel.lifetype.net
Sun Apr 22 13:59:17 EDT 2007
Author: mark
Date: 2007-04-22 13:59:17 -0400 (Sun, 22 Apr 2007)
New Revision: 5332
Added:
plugins/branches/lifetype-1.2/addcommentnotify/
plugins/branches/lifetype-1.2/addcommentnotify/class/
plugins/branches/lifetype-1.2/addcommentnotify/class/action/
plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyconfigaction.class.php
plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php
plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/
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/msnbot.sh
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/phpmsnclass/test.msn
plugins/branches/lifetype-1.2/addcommentnotify/class/view/
plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php
plugins/branches/lifetype-1.2/addcommentnotify/locale/
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/
plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template
Log:
Add addcommentnotify plugin contributed by twu2.
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyconfigaction.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyconfigaction.class.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyconfigaction.class.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,20 @@
+<?php
+lt_include(PLOG_CLASS_PATH.'class/action/admin/adminaction.class.php');
+lt_include(PLOG_CLASS_PATH.'plugins/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php');
+
+class PluginAddCommentNotifyConfigAction extends AdminAction
+{
+ function PluginAddCommentNotifyConfigAction($actionInfo, $request)
+ {
+ $this->AdminAction($actionInfo, $request);
+ return;
+ }
+
+ function perform()
+ {
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->setCommonData();
+ return true;
+ }
+}
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/action/pluginaddcommentnotifyupdateconfigaction.class.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,136 @@
+<?php
+lt_include(PLOG_CLASS_PATH.'class/action/admin/adminaction.class.php');
+lt_include( PLOG_CLASS_PATH.'plugins/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php');
+
+class PluginAddCommentNotifyUpdateConfigAction extends AdminAction
+{
+ var $_pluginEnabled;
+ var $_useMSNClass;
+ var $_msnClassFile;
+ var $_msnUser;
+ var $_msnPassword;
+ var $_msnbot_spool;
+ var $_tolist;
+
+ function PluginAddCommentNotifyUpdateConfigAction($actionInfo, $request)
+ {
+ $this->AdminAction($actionInfo, $request);
+ return;
+ }
+
+ function validate()
+ {
+ $this->_pluginEnabled = $this->_request->getValue('pluginEnabled');
+ $this->_pluginEnabled = ($this->_pluginEnabled != '');
+
+ $this->_useMSNClass = $this->_request->getValue('useMSNClass');
+ $this->_useMSNClass = ($this->_useMSNClass != '');
+
+ $this->_msnClassFile = trim($this->_request->getValue('msnClassFile'));
+ $this->_msnUser = trim($this->_request->getValue('msnUser'));
+ $this->_msnPassword = trim($this->_request->getValue('msnPassword'));
+ $this->_msnbot_spool = rtrim(trim($this->_request->getValue('msnbotSpool')), '/');
+ $this->_tolist = trim($this->_request->getValue('toList'));
+
+ if (($this->_useMSNClass && ($this->_msnClassFile === '' || $this->_msnUser === '' || $this->_msnPassword === '')) ||
+ (!$this->_useMSNClass && ($this->_msnbot_spool === '')) ||
+ $this->_tolist === '') {
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->_view->setErrorMessage($this->_locale->tr('error_required_missing'));
+ $this->setCommonData();
+ return false;
+ }
+
+ if ($this->_useMSNClass) {
+ if (!is_file($this->_msnClassFile)) {
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->_view->setErrorMessage($this->_locale->tr('error_addcommentnotify_msnclass_file'));
+ $this->setCommonData();
+ return false;
+ }
+ @list($name, $domain, $network) = @explode('@', $this->_msnUser);
+ if ($domain == null || $network != null) {
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->_view->setErrorMessage($this->_locale->tr('error_addcommentnotify_msn_user'));
+ $this->setCommonData();
+ return false;
+ }
+ }
+ else {
+ if (!is_dir($this->_msnbot_spool)) {
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->_view->setErrorMessage($this->_locale->tr('error_addcommentnotify_msnbot_spool'));
+ $this->setCommonData();
+ return false;
+ }
+ }
+
+ $to_list_error = false;
+ $aList = @explode(',', $this->_tolist);
+ if (count($aList) == 0)
+ $to_list_error = true;
+ else {
+ foreach ($aList as $to) {
+ @list($name, $domain, $network) = @explode('@', $to);
+ if ($network != NULL && $network != 1 && $network != 32) {
+ $to_list_error = true;
+ break;
+ }
+ if ($domain == NULL) {
+ $to_list_error = true;
+ break;
+ }
+ }
+ }
+
+ if ($to_list_error) {
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->_view->setErrorMessage($this->_locale->tr('error_addcommentnotify_tolist'));
+ $this->setCommonData();
+ return false;
+ }
+ return true;
+ }
+
+ function perform()
+ {
+ // Get the blog settings
+ $blogSettings = $this->_blogInfo->getSettings();
+
+ // Update the relevant values with the form inputs
+ $blogSettings->setValue('plugin_addcommentnotify_enabled', $this->_pluginEnabled);
+ $blogSettings->setValue('addcommentnotify_usemsnclass', $this->_useMSNClass);
+ $blogSettings->setValue('addcommentnotify_msnclass_file', $this->_msnClassFile);
+ $blogSettings->setValue('addcommentnotify_msn_user', $this->_msnUser);
+ $blogSettings->setValue('addcommentnotify_msn_password', $this->_msnPassword);
+ $blogSettings->setValue('addcommentnotify_msnbot_spool', $this->_msnbot_spool);
+ $blogSettings->setValue('addcommentnotify_tolist', $this->_tolist);
+
+ // Set and save the new blog settings
+ $this->_blogInfo->setSettings($blogSettings);
+ $blogs = new Blogs();
+
+ // Display an error message if there was a problem with the update
+ if( !$blogs->updateBlog($this->_blogInfo)) {
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->_view->setErrorMessage($this->_locale->tr('error_updating_settings'));
+ $this->setCommonData();
+ return false;
+ }
+
+ // If things went well, save the session...
+ $this->_blogInfo->setSettings($blogSettings);
+ $this->_session->setValue('blogInfo', $this->_blogInfo);
+ $this->saveSession();
+
+ // and show the success message
+ $this->_view = new PluginAddCommentNotifyConfigView($this->_blogInfo);
+ $this->_view->setSuccessMessage($this->_locale->tr('gallery_settings_saved_ok'));
+ $this->setCommonData();
+
+ // clear the cache
+ CacheControl::resetBlogCache($this->_blogInfo->getId());
+ return true;
+ }
+}
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/config.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/config.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/config.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +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');
+
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msn.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msn.class.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msn.class.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,2735 @@
+<?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;
+ }
+}
+
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,181 @@
+#!/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;
+
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.sh
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.sh (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnbot.sh 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,35 @@
+#! /bin/sh
+#
+# MSN bot
+#
+
+NAME=msnbot
+DESC="MSN bot"
+
+set -e
+
+case "$1" in
+ start)
+ echo -n "Starting $DESC: $NAME"
+ /var/spool/msnbot/msnbot.php
+ echo "."
+ ;;
+ stop)
+ echo -n "Stopping $DESC: $NAME"
+ MSNPID=`cat /var/spool/msnbot/log/msnbot.pid`
+ kill $MSNPID
+ echo "."
+ ;;
+ restart|force-reload)
+ $0 stop
+ sleep 1s
+ $0 start
+ ;;
+ *)
+ N=/etc/init.d/$NAME
+ echo "Usage: $N {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnsendmsg.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnsendmsg.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/msnsendmsg.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +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_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;
+
+?>
+
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/sample.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/sample.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/sample.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,41 @@
+#!/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;
+
+?>
+
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/test.msn
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/test.msn (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/phpmsnclass/test.msn 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,5 @@
+TO: sombody1 at hotmail.com,sombody2 at hotmail.com@1,yahoo_acct at yahoo.com@32
+test測試中文
+123
+
+345
Added: plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/class/view/pluginaddcommentnotifyconfigview.class.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,36 @@
+<?php
+lt_include(PLOG_CLASS_PATH.'class/view/admin/adminplugintemplatedview.class.php');
+
+class PluginAddCommentNotifyConfigView extends AdminPluginTemplatedView
+{
+ function PluginAddCommentNotifyConfigView($blogInfo)
+ {
+ $this->AdminPluginTemplatedView($blogInfo, 'addcommentnotify', 'addcommentnotify');
+ return;
+ }
+
+ function render()
+ {
+ $blogSettings = $this->_blogInfo->getSettings();
+ $pluginEnabled = $blogSettings->getValue('plugin_addcommentnotify_enabled');
+ $usemsnclass = $blogSettings->getValue('addcommentnotify_usemsnclass');
+ $msnclass_file = $blogSettings->getValue('addcommentnotify_msnclass_file');
+ $msn_user = $blogSettings->getValue('addcommentnotify_msn_user');
+ $msn_password = $blogSettings->getValue('addcommentnotify_msn_password');
+ $msnbot_spool = $blogSettings->getValue('addcommentnotify_msnbot_spool');
+ $tolist = $blogSettings->getValue('addcommentnotify_tolist');
+
+ // Export the settings to the template
+ $this->setValue('pluginEnabled', $pluginEnabled);
+ $this->setValue('useMSNClass', $usemsnclass);
+ $this->setValue('msnClassFile', $msnclass_file);
+ $this->setValue('msnUser', $msn_user);
+ $this->setValue('msnPassword', $msn_password);
+ $this->setValue('msnbotSpool', $msnbot_spool);
+ $this->setValue('toList', $tolist);
+ parent::render();
+
+ return true;
+ }
+}
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_en_UK.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_en_UK.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_en_UK.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,28 @@
+<?php
+$messages['AddCommentNotify'] = 'Add Comment Notify';
+$messages['addcommentnotify_plugin'] = 'Add Comment Notify Plugin';
+
+$messages['error_required_missing'] = 'Please fill in all required values.';
+$messages['error_addcommentnotify_msnclass_file'] = 'Cannot find msn.class.php.';
+$messages['error_addcommentnotify_msn_user'] = 'MSN login account not correct.';
+$messages['error_addcommentnotify_msnbot_spool'] = 'msnbot spool folder does not seem to be correct.';
+$messages['error_addcommentnotify_tolist'] = 'MSN/Yahoo list is not correct.';
+$messages['addcommentnotify_settings_saved_ok'] = 'Add Comment Notify settings saved successfully!';
+
+$messages['label_configuration'] = 'Configuration';
+$messages['label_addcommentnotify_enable'] = 'Enable this plugin';
+$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';
+$messages['label_addcommentnotify_msn_password'] = 'MSN login password';
+$messages['label_addcommentnotify_msnbot_spool'] = 'msnbot spool folder';
+$messages['label_addcommentnotify_tolist'] = 'MSN/Yahoo list';
+
+$messages['help_addcommentnotify_enable'] = 'Enable Add Comment Notify plugin';
+$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)';
+$messages['help_addcommentnotify_msn_password'] = 'MSN login password (If you choose to use msn.class.php)';
+$messages['help_addcommentnotify_msnbot_spool'] = 'msnbot spool folder';
+$messages['help_addcommentnotify_tolist'] = 'MSN/Yahoo list.';
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_zh_TW.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_zh_TW.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/locale/locale_zh_TW.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,28 @@
+<?php
+$messages['AddCommentNotify'] = '新增迴響通知';
+$messages['addcommentnotify_plugin'] = '新增迴響通知 外掛設定';
+
+$messages['error_required_missing'] = '請輸入所有必要欄位.';
+$messages['error_addcommentnotify_msnclass_file'] = 'msn.class.php 的檔案路徑不正確.';
+$messages['error_addcommentnotify_msn_user'] = 'MSN 登入帳號不正確.';
+$messages['error_addcommentnotify_msnbot_spool'] = 'msnbot spool 的路徑不正確.';
+$messages['error_addcommentnotify_tolist'] = '收件人名單不正確.';
+$messages['addcommentnotify_settings_saved_ok'] = 'Add Comment Notify 外掛模組設定成功儲存!';
+
+$messages['label_configuration'] = '設定';
+$messages['label_addcommentnotify_enable'] = '啟動這個外掛模組';
+$messages['label_addcommentnotify_usemsnclass'] = '使用 msn.class.php 來傳送';
+$messages['label_addcommentnotify_msnclass_file'] = 'msn.class.php 的檔案路徑';
+$messages['label_addcommentnotify_msn_user'] = 'MSN 的登入帳號';
+$messages['label_addcommentnotify_msn_password'] = 'MSN 的登入密碼';
+$messages['label_addcommentnotify_msnbot_spool'] = 'msnbot spool 所在路徑';
+$messages['label_addcommentnotify_tolist'] = '收件人名單';
+
+$messages['help_addcommentnotify_enable'] = '啟動 Add Comment Notify 外掛模組';
+$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 時必須設定)';
+$messages['help_addcommentnotify_msn_password'] = 'MSN 的登入密碼 (使用 msn.class.php 時必須設定)';
+$messages['help_addcommentnotify_msnbot_spool'] = 'msnbot 的 spool 路徑 (不使用 msn.class.php 時必須設定)';
+$messages['help_addcommentnotify_tolist'] = 'MSN/Yahoo 收件人名單, 以逗號分隔.';
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/pluginaddcommentnotify.class.php
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/pluginaddcommentnotify.class.php (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/pluginaddcommentnotify.class.php 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,119 @@
+<?php
+lt_include(PLOG_CLASS_PATH.'class/plugin/pluginbase.class.php');
+
+class PluginAddCommentNotify extends PluginBase
+{
+ var $_pluginEnabled;
+ var $_useMSNClass;
+ var $_msnClassFile;
+ var $_msnUser;
+ var $_msnPassword;
+ var $_msnbot_spool;
+ var $_tolist;
+
+ function PluginAddCommentNotify($source = "")
+ {
+ $this->PluginBase($source);
+
+ // Setup the plugin information
+ $this->id = 'addcommentnotify';
+ $this->author = 'twu2';
+ $this->desc = 'send MSN via msnbot after add comment.';
+ $this->version = '20070407';
+
+ // Setup the locale
+ $this->locales = array('en_UK', 'zh_TW');
+
+ if ($source == 'admin')
+ $this->initAdmin();
+ $this->registerNotification(EVENT_POST_COMMENT_ADD);
+ }
+
+ function initAdmin()
+ {
+ // Register the actions
+ $this->registerAdminAction('addcommentnotify', 'PluginAddCommentNotifyConfigAction');
+ $this->registerAdminAction('updateaddcommentnotify', 'PluginAddCommentNotifyUpdateConfigAction');
+
+ // Set up the Admin menu options
+ $this->addMenuEntry('/menu/controlCenter/manageSettings', 'AddCommentNotify', '?op=addcommentnotify');
+ }
+
+ function register()
+ {
+ $blogSettings = $this->blogInfo->getSettings();
+ $this->_pluginEnabled = $blogSettings->getValue('plugin_addcommentnotify_enabled');
+ $this->_useMSNClass = $blogSettings->getValue('addcommentnotify_usemsnclass');
+ $this->_msnClassFile = $blogSettings->getValue('addcommentnotify_msnclass_file');
+ $this->_msnUser = $blogSettings->getValue('addcommentnotify_msn_user');
+ $this->_msnPassword = $blogSettings->getValue('addcommentnotify_msn_password');
+ $this->_msnbot_spool = $blogSettings->getValue('addcommentnotify_msnbot_spool');
+ $this->_tolist = $blogSettings->getValue('addcommentnotify_tolist');
+ return true;
+ }
+
+ function isEnabled()
+ {
+ return $this->_pluginEnabled;
+ }
+
+ 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;
+ $aTo = @explode(',', $this->_tolist);
+ if (count($aTo) == 0) return true;
+ $sTo = '';
+ foreach ($aTo as $to) {
+ @list($name, $domain, $network) = @explode('@', $to);
+ if ($domain == null) continue;
+ if ($network == null) $network = 1;
+ if ($network != 1 && $network != 32) continue;
+ $name = trim($name);
+ $domain = trim($domain);
+ if ($name === '' || $domain === '') continue;
+ $user = $name.'@'.$domain.'@'.$network;
+ if ($sTo === '')
+ $sTo = $user;
+ else
+ $sTo .= ','.$user;
+ }
+ 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 ($this->_useMSNClass) {
+ require_once($this->_msnClassFile);
+ $msn = new MSN;
+ echo "$$$$";
+ if ($msn->connect($this->_msnUser, $this->_msnPassword)) {
+ $aTo = @explode(',', $sTo);
+ $msn->sendMessage($sText, $aTo);
+ }
+ unset($msn);
+ }
+ else {
+ $fname = $this->_msnbot_spool.'/addcomment_'.md5(strftime('%D $T').rand(1,100)).'_'.posix_getpid().'.msn';
+ $fp = @fopen($fname, 'wt');
+ if ($fp) {
+ fputs($fp, "TO: $sTo\n$sText");
+ fclose($fp);
+ chmod($fname, 0666);
+ }
+ }
+ return true;
+ }
+}
+?>
Added: plugins/branches/lifetype-1.2/addcommentnotify/readme.txt
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/readme.txt (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/readme.txt 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,10 @@
+AddCommentNotify Plugin v20070406
+
+You need msnbot or msn.class.php:
+http://blog.teatime.com.tw/1/post/227
+http://blog.teatime.com.tw/1/post/220
+http://blog.teatime.com.tw/1/post/200
+
+When use msn.class.php, it will try to login to MSN and send the message.
+If not use msn.class.php, it will write the file to msnbot's spool folder, then msnbot will send the message to you MSN/Yahoo list.
+
Added: plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template
===================================================================
--- plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template (rev 0)
+++ plugins/branches/lifetype-1.2/addcommentnotify/templates/addcommentnotify.template 2007-04-22 17:59:17 UTC (rev 5332)
@@ -0,0 +1,60 @@
+{include file="$admintemplatepath/header.template"}
+{include file="$admintemplatepath/navigation.template" showOpt=AddCommentNotify title=$locale->tr("addcommentnotify_plugin")}
+<form name="addcommentnotifyPluginConfig" method="post">
+ <fieldset class="inputField">
+ <legend>{$locale->tr("label_configuration")}</legend>
+ {include file="$admintemplatepath/successmessage.template"}
+ {include file="$admintemplatepath/errormessage.template"}
+ <div class="field">
+ <label for="pluginEnabled">{$locale->tr("label_addcommentnotify_enable")}</label>
+ <span class="required"></span>
+ <div class="formHelp">
+ <input class="checkbox" type="checkbox" name="pluginEnabled" id="pluginEnabled" {if $pluginEnabled} checked="checked" {/if} value="1" />{$locale->tr("help_addcommentnotify_enable")}
+ </div>
+ </div>
+ <div class="field">
+ <label for="useMSNClass">{$locale->tr("label_addcommentnotify_usemsnclass")}</label>
+ <span class="required"></span>
+ <div class="formHelp">
+ <input class="checkbox" type="checkbox" name="useMSNClass" id="useMSNClass" {if $useMSNClass} checked="checked" {/if} value="1" />{$locale->tr("help_addcommentnotify_usemsnclass")}
+ </div>
+ </div>
+ <div class="field">
+ <label for="msnClassFile">{$locale->tr("label_addcommentnotify_msnclass_file")}</label>
+ <span class="required">*</span>
+ <div class="formHelp">{$locale->tr("help_addcommentnotify_msnclass_file")}</div>
+ <input class="text" type="text" name="msnClassFile" id="msnClassFile" value="{$msnClassFile}" width="40" />
+ </div>
+ <div class="field">
+ <label for="msnUser">{$locale->tr("label_addcommentnotify_msn_user")}</label>
+ <span class="required">*</span>
+ <div class="formHelp">{$locale->tr("help_addcommentnotify_msn_user")}</div>
+ <input class="text" type="text" name="msnUser" id="msnUser" value="{$msnUser}" width="40" />
+ </div>
+ <div class="field">
+ <label for="msnPassword">{$locale->tr("label_addcommentnotify_msn_password")}</label>
+ <span class="required">*</span>
+ <div class="formHelp">{$locale->tr("help_addcommentnotify_msn_password")}</div>
+ <input class="text" type="password" name="msnPassword" id="msnPassword" value="{$msnPassword}" width="40" />
+ </div>
+ <div class="field">
+ <label for="msnbotSpool">{$locale->tr("label_addcommentnotify_msnbot_spool")}</label>
+ <span class="required">*</span>
+ <div class="formHelp">{$locale->tr("help_addcommentnotify_msnbot_spool")}</div>
+ <input class="text" type="text" name="msnbotSpool" id="msnbotSpool" value="{$msnbotSpool}" width="40" />
+ </div>
+ <div class="field">
+ <label for="toList">{$locale->tr("label_addcommentnotify_tolist")}</label>
+ <span class="required">*</span>
+ <div class="formHelp">{$locale->tr("help_addcommentnotify_tolist")}</div>
+ <input class="text" type="text" name="toList" id="toList" value="{$toList}" width="40" />
+ </div>
+ </div>
+ <div class="buttons">
+ <input type="hidden" name="op" value="updateaddcommentnotify" />
+ <input type="reset" name="{$locale->tr("reset")}" />
+ <input type="submit" name="{$locale->tr("update_settings")}" value="{$locale->tr("update")}" />
+ </div>
+</form>
+{include file="$admintemplatepath/footernavigation.template"}
+{include file="$admintemplatepath/footer.template"}
More information about the pLog-svn
mailing list