tags:

views:

242

answers:

2

Hi all!

I'm making an MSN client in PHP, which makes a persistent socket connection to one of Microsoft's servers:

//Connect to second server
$server2 = explode(":", $xfr[3]);
$socket2 = pfsockopen($server2[0], (int)$server2[1], $errno, $errstr, 999999);
$_SESSION["socket"] = $server2;
echo '<b>Connected to 2nd server.</b><br />';

This works fine, but PHP closes the connection after about a minute, and Microsoft's server signs me out of MSN. Here is the entire page chat.php:

<?php
session_start();

//Check username and password
if(!isset($_POST["username"]) || !isset($_POST["password"]) || !isset($_POST["status"]))
{
    die("Bad request");
}
$wronglogin = false;
//Yep, it's set, so let's connect to MSN

$socket = fsockopen("messenger.hotmail.com", 1863);
echo '<b>Connected to 1st server.</b><br />';
//Send MSNP version
fputs($socket, "VER 0 MSNP10 CVR0\r\n");
echo fread($socket, 5000) . '<br />';
//Send user-agent
fputs($socket, "CVR 1 0x0409 php ".phpversion()." i386 MSNMSGR 7.0.0000 MSMSGS ".$_POST["username"]."\r\n");
echo fread($socket, 5000) . '<br />';
//Send username
fputs($socket, "USR 2 TWN I ".$_POST["username"]."\r\n");
//Read XFR
$xfr = fread($socket, 5000);
echo $xfr . '<br />';
$xfr = explode(" ", $xfr);

//Connect to second server
$server2 = explode(":", $xfr[3]);
$socket2 = pfsockopen($server2[0], (int)$server2[1], $errno, $errstr, 999999);
$_SESSION["socket"] = $server2;
echo '<b>Connected to 2nd server.</b><br />';
//Send MSNP version
fputs($socket2, "VER 0 MSNP10 CVR0\r\n");
echo fread($socket2, 5000) . '<br />';
//Send user-agent
fputs($socket2, "CVR 1 0x0409 php ".phpversion()." i386 MSNMSGR 7.0.0000 MSMSGS ".$_POST["username"]."\r\n");
echo fread($socket2, 5000) . '<br />';
//Send username
fputs($socket2, "USR 2 TWN I ".$_POST["username"]."\r\n");
//Read USR
$usr = fread($socket2, 5000);
echo $usr . '<br />';
$usr = explode(" ", $usr);//This is for later usage.


//Connect to Nexus
$nexus = fsockopen("ssl://nexus.passport.com", 443);
$request_nexus = "GET /rdr/pprdr.asp HTTP/1.1\r\n";
$request_nexus .= "Host:nexus.passport.com\r\n";
$request_nexus .= "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1\r\n";
$request_nexus .= "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
$request_nexus .= "Accept-Language:en-us,en;q=0.5\r\n";
$request_nexus .= "Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n";
$request_nexus .= "Keep-Alive:300\r\n";
$request_nexus .= "Connection:keep-alive\r\n";
$request_nexus .= "Cache-Control:max-age=0\r\n\r\n";
fputs($nexus, $request_nexus);
//Receive nexus response
$response_nexus = fread($nexus, 5000);
echo str_replace("\n", "\n<br />", $response_nexus);
//Get passport urls
foreach(explode("\r\n", $response_nexus) as $line)
{
    if(substr($line, 0, strlen('PassportURLs:')) == 'PassportURLs:')
    {
     $PassportURLs = substr($line, strlen('PassportURLs:') + 1);
    }
}
echo $PassportURLs . '<br />';
//Get login server URL
foreach(explode(",", $PassportURLs) as $item)
{
    if(substr($item, 0, strlen('DALogin=')) == 'DALogin=')
    {
     $loginurl = substr($item, strlen('DALogin='));
    }
}
echo $loginurl . '<br />';
//Connect to login server
$loginurl_arr = explode("/", $loginurl);
$loginserver = fsockopen("ssl://" . $loginurl_arr[0], 443);
$request_login = "GET ".substr($loginurl, strlen($loginurl_arr[0]))." HTTP/1.1\r\n";
$request_login .= "Authorization: Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in=";
    $request_login .= urlencode($_POST["username"]) . ",pwd=" . urlencode($_POST["password"]) . ",";
    $request_login .= $usr[4];
$request_login .= "Host:".$loginurl_arr[0]."\r\n";
$request_login .= "User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1\r\n";
$request_login .= "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
$request_login .= "Accept-Language:en-us,en;q=0.5\r\n";
$request_login .= "Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n";
$request_login .= "Keep-Alive:300\r\n";
$request_login .= "Connection:keep-alive\r\n";
$request_login .= "Cache-Control:max-age=0\r\n\r\n";
fputs($loginserver, $request_login, 5000);
$response_login = fread($loginserver, 5000);
echo str_replace("\n", "\n<br />", $response_login);
//Get login ticket
foreach(explode("\r\n", $response_login) as $line)
{
    if(substr($line, 0, strlen('Authentication-Info:')) == 'Authentication-Info:')
    {
     $ticket_exp = substr($line, strlen('Authentication-Info:') + 1);
     $ticket_exp = explode(",", $ticket_exp);
     foreach($ticket_exp as $item)
     {
      if(substr($item, 0, strlen('from-PP=')) == 'from-PP=')
      {
       $ticket = str_replace("'", "", substr($item, strlen('from-PP=')));
      }
     }
    }
}
echo $ticket . '<br />';
if(!isset($ticket))
{
    $wronglogin = true;
}
else
{
    //Send USR
    fputs($socket2, "USR 3 TWN S ".$ticket."\r\n");
    //Read USR
    $usr = fread($socket2, 10000);
    echo $usr . '<br />';
    echo str_replace("\n", "\n<br />", fread($socket2, 5000));
    //Sync
    fputs($socket2, "SYN 4 0 0\r\n", 5000);
    echo fread($socket2, 5000) . '<br />';
    //Set status to available
    fputs($socket2, "CHG 5 ".$_POST["status"]."\r\n", 5000);
    echo fread($socket2, 5000) . '<br />';

    //Check login
    if(!substr($usr, 0, strlen("USR 3 OK")) == "USR 3 OK")//Login not succeeded
    {
     $wronglogin = true;
    }
}






if($wronglogin)
{
    fclose($socket);
    session_destroy();
    header("Location: index.php");
}
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
    <title>MSN.php</title>
    <script type="text/javascript" src="jquery-1.3.2.min.js"></script>
    <script type="text/javascript" src="main.js"></script>
</head>
<body>
    <div id="header">
        <input type="text" id="nickname" />
        <select id="status">
            <option value="NLN">Available</option>
            <option value="BSY">Busy</option>
            <option value="IDL">Idle</option>
            <option value="BRB">Be right back</option>
            <option value="AWY">Away</option>
            <option value="PHN">On the phone</option>
            <option value="LUN">Out to lunch</option>
            <option value="HDN">Show offline</option>
        </select>
    </div>
</body>
</html>

Here is main.js:

$(document).ready(function()
{
    //Change status
    $("#status option").click(function()
    {
     $.get("AJAX/status.php?s=" + $(this).attr("value"));
    });
});

And here is AJAX/status.php:

<?php
session_start();

$socket2 = pfsockopen($_SESSION["socket"][0], (int)$_SESSION["socket"][1]);
fputs($socket2, "CHG 0 ".$_GET["s"]." \r\n");
echo fread($socket2, 5000) . '<br />';

Everything works except that either PHP or Microsoft closes the connection. Can anyone help me with this problem? Thanks in advance.

A: 

PHP stops the script because of the time limit.

Put this on the top of your script (chat.php):

set_time_limit(0);

That will enable the script to run forever (which is what you want).

Pedro Cunha
Also, Microsoft logs you out, because it looses the connection. And Microsoft looses the connection because the script stops and PHP closes the sockets. More help on set_time_limit(): http://www.php.net/set-time-limit
Pedro Cunha
Sorry, does not work :(. It still logs me off.
Time Machine
Have you tried using the script without using the AJAX status changer?See how long it takes to log off.
Pedro Cunha
Actually, I am pretty sure that is the status changer that's causing another connection to be open to MSN's servers, causing the other connection to shutdown. It was answered on your previous question, read it: <code>pfsockopen() returns a resource. You cannot store resources in the Session as they are just handles to external resources which may not be there later.</code> (http://stackoverflow.com/questions/1213464/php-pfsockopen-in-a-session)
Pedro Cunha
Excactly 1 minute
Time Machine
The status changer actually works. I store the server's IP and the port in a session and open the connection again in the status changer. That worked well.
Time Machine
Could you give me a dump of the debug info (don't forget to censor password and stuff like that). Paste it here: http://pastebin.com/
Pedro Cunha
http://pastebin.com/m448f5e74
Time Machine
Seriously, I can't understand where your code fails...
Pedro Cunha
+2  A: 

PHP's socket streams have a default timeout of 60 seconds; you may be running into this.

Another thing to keep in mind with persistent sockets, is that the persistent socket is keyed by the connection details, so if you're always connecting to the same server for a multi-user application, you are going to end up with different users sharing connections with each other... and in the case of a messaging application, you're going to bleed from user to another. I'd strongly recommend against the use of pfsockopen here.

I think you'd be better off implementing the long-lived portion of this application as something more standalone (perhaps a PHP script that implemented a daemon process?)

Chat gateways are difficult to implement purely in stateless PHP because they tend to require a good deal of state, especially to maintain presence information.

Wez Furlong
Well, i make this for myself, so I'm the only one using it. I'm going to make this in Cocoa or adobe air I think.
Time Machine