PHP: Read from socket or STDIN - php

I am learning socket programming in PHP and so I am trying a simple echo-chat server.
I wrote a server and it works. I can connect two netcats to it and when I write in the one netcat, I recive it at the other. Now, I want to implement what NC does in PHP
I want to use stream_select to see if I have data on STDIN or on the socket to either send the message from STDIN to the server or reading the incoming message from the server.
Unfortunately the example at the php manual doesn't give me any clue how to do that.
I tried to simply $line = fgets(STDIN) and socket_write($socket, $line) but it doesnt work. So I started to go down and just want stream_select to act up when the user typed the message.
$read = array(STDIN);
$write = NULL;
$exept = NULL;
while(1){
if(stream_select($read, $write, $exept, 0) > 0)
echo 'read';
}
Gives
PHP Warning: stream_select(): No stream arrays were passed in
/home/user/client.php on line 18
But when I var_dump($read) it tells me, that it is an array with a stream.
array(1) {
[0]=>
resource(1) of type (stream)
}
How do I get stream_select to work?
PS: In Python I can do something like
r,w,e = select.select([sys.stdin, sock.fd], [],[])
for input in r:
if input == sys.stdin:
#having input on stdin, we can read it now
if input == sock.fd
#there is input on socket, lets read it
I need the same in PHP

I found a solution. It seems to work, when I use:
$stdin = fopen('php://stdin', 'r');
$read = array($sock, $stdin);
$write = NULL;
$exept = NULL;
Instead of just STDIN. Despite php.net says, STDIN is already open and saves using
$stdin = fopen('php://stdin', 'r');
It seems not, if you want to pass it into stream_select.
Also, the socket to the server should be created with $sock = fsockopen($host); instead of using socket_create on the client side... gotta love this language and it's reasonability and clear manual...
Here a working example of a client that connects to an echo server using select.
<?php
$ip = '127.0.0.1';
$port = 1234;
$sock = fsockopen($ip, $port, $errno) or die(
"(EE) Couldn't connect to $ip:$port ".socket_strerror($errno)."\n");
if($sock)
$connected = TRUE;
$stdin = fopen('php://stdin', 'r'); //open STDIN for reading
while($connected){ //continuous loop monitoring the input streams
$read = array($sock, $stdin);
$write = NULL;
$exept = NULL;
if (stream_select($read, $write, $exept, 0) > 0){
//something happened on our monitors. let's see what it is
foreach ($read as $input => $fd){
if ($fd == $stdin){ //was it on STDIN?
$line = fgets($stdin); //then read the line and send it to socket
fwrite($sock, $line);
} else { //else was the socket itself, we got something from server
$line = fgets($sock); //lets read it
echo $line;
}
}
}
}

Related

How to check if bash input is being redirect to PHP script?

I'm writing a PHP script and I would like to be able to optionally use a file as the script input. This way:
$ php script.php < file.txt
I'm, actually, able to do that using file_get_contents:
$data = file_get_contents('php://stdin');
However, if I don't pass a file to the input, the scripts hangs indefinetelly, waiting for an input.
I tried the following, but it didn't work:
$data = '';
$in = fopen('php://stdin', 'r');
do {
$bytes = fread($in, 4096);
// Maybe the input will be empty here?! But no, it's not :(
if (empty($bytes)) {
break;
}
$data .= $bytes;
} while (!feof($in));
The script waits for fread to return a value, but it never returns. I guess it waits for some input the same way file_get_contents does.
Another attempt was made by replacing the do { ... } while loop by a while { ... }, checking for the EOF before than trying to read the input. But that also didn't work.
Any ideas on how can I achieve that?
You can set STDIN to be non-blocking via the stream_set_blocking() function.
function stdin()
{
$stdin = '';
$fh = fopen('php://stdin', 'r');
stream_set_blocking($fh, false);
while (($line = fgets($fh)) !== false) {
$stdin .= $line;
}
return $stdin;
}
$stdin = stdin(); // returns the contents of STDIN or empty string if nothing is ready
Obviously, you can change the use of line-at-a-time fgets() to hunk-at-a-time fread() as per your needs.

Using UDP fsockopen to get info from file on gameserver

I got a file on a gameserver called "current_map.tmp".
This file contains a number depending on the current map.
What I need is to read that number.
This is what I got so far:
<?php
$server_ip = '213.239.207.85';
$server_port = 27960;
$server_timeout = 2;
$server_addr = "udp://" . $server_ip;
$fp = fsockopen($server_addr, $server_port, $errno, $errstr, $server_timeout);
socket_set_timeout ($fp, $server_timeout);
if (!$fp) {
echo "ERROR: $errno - $errstr<br />\n";
} else {
$File = "current_map.tmp";
$filesize = filesize($File);
$handle = fopen($File, "r");
$map_id = fread($handle, $filesize);
fclose($handle);
}
fclose($fp);
?>
$fp returns "Resource id #2".
So that works.
Then there is nothing.
1) How do I know wich folder I connected to with $fp?
2) How can I read the content of this file?
$fp returns "Resource id #2". So that works.
No; this doesn't actually mean anything! Since UDP sockets are connectionless, there is no such thing as a UDP "connection"; calling fsockopen() only initializes sockets to prepare to send packets.
In any case, sending and receiving UDP packets does not allow you to access files on a remote server, unless that server has implemented a protocol to allow you to do so, and you are making use of that protocol. It certainly will not allow you to use fopen() to access remote files — this code is essentially just nonsense.

How to do an IMAP search based on "Delivered-To" header (or how to use the gmail imap extension?)

I need to search gmail messages (google apps for work) via imap/php. However, imap_search criterias are not enough to catch the messages in question.
The code I use looks like this:
$imap = imap_open("{imap.gmail.com:993/imap/ssl}Label1/label2", $user_email, $user_passwd);
$msg_list = imap_search($imap, 'TEXT "Delivered-To: username+label#gmail.com"');
imap_close($imap);
The imap_search call didn't return anything.
I did some research, it seems I can filter messages based on "Delivered-To" header field via the gmail search syntax X-GM-RAW, but I just couldn't achieve this, I tried all these calls (and more):
$msg_list = imap_search($imap, 'UID SEARCH X-GM-RAW "deliveredto:username+label#gmail.com"');
$msg_list = imap_search($imap, 'SEARCH X-GM-RAW "deliveredto:username+label#gmail.com"');
$msg_list = imap_search($imap, 'X-GM-RAW "deliveredto:username+label#gmail.com"');
But it didn't work, anyone knows what's wrong with my code?
OK, either I don't know how to ask questions, SOers are busy, or I'm asking difficult questions.
Anyway, now I know the imap_* functions built-into PHP don't handle direct IMAP commands, so I had to either use the zend framework (too heavy for my needs), or directly connect to imap via sockets.
I chose the second option, the code is as follow (code stolen from here and adapted for my own needs), in case someone needs it:
<?php
// Open a socket
if (!($fp = fsockopen('ssl://imap.gmail.com', 993, $errno, $errstr, 15))) die("Could not connect to host");
// Set timout to 1 second
if (!stream_set_timeout($fp, 1)) die("Could not set timeout");
// Fetch first line of response and echo it
echo fgets($fp);
// =========================================
fwrite($fp, "0001 LOGIN user.name#gmail.com YOUR_PASSWORD_HERE_WITHOUT_QUOTES\r\n");
// ie. fwrite($fp, "0001 LOGIN super.dude#gmail.com pass123\r\n");
// Keep fetching lines until response code is correct
while ($line = trim(fgets($fp)))
{
echo "Line = [$line]\n";
$line = preg_split('/\s+/', $line, 0, PREG_SPLIT_NO_EMPTY);
$code = $line[0];
if (strtoupper($code) == '0001') {
break;
}
}
// =========================================
fwrite($fp, "0002 SELECT Inbox\r\n");
// Keep fetching lines until response code is correct
while ($line = trim(fgets($fp)))
{
echo "Line = [$line]\n";
$line = preg_split('/\s+/', $line, 0, PREG_SPLIT_NO_EMPTY);
$code = $line[0];
if (strtoupper($code) == '0002') {
break;
}
}
// =========================================
fwrite($fp, "0003 SEARCH X-GM-RAW \"deliveredto:user.name+someLabel#gmail.com\"\r\n");
// Keep fetching lines until response code is correct
while ($line = fgets($fp))
{
echo "Line = [$line]\n";
$line = preg_split('/\s+/', $line, 0, PREG_SPLIT_NO_EMPTY);
$code = $line[0];
if (strtoupper($code) == '0003') {
break;
}
}
fclose($fp);
echo "I've finished!";
?>
Voila! Just copy and paste and now you have access to the gmail syntax right from PHP! (Hey vote if you like :p)

receiving VLC generated MP4 with PhP is a little messy

Im using VCL to broadcast to my localhost, 127.0.0.1 with UDP (legacy) method. To catch the traffic, I use this code:
$address = '127.0.0.1';
$port = 1234;
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($sock, $address, $port) or die('Could not bind to address');
$f = fopen ('output', 'w');
fclose ($f);
$sock = stream_socket_server('udp://127.0.0.1:1234', $errno, $errstr, STREAM_SERVER_BIND);
while(1)
{
$a = stream_socket_recvfrom($sock, 65536);
$f = fopen('output', 'a');
fwrite ($f, $a);
fclose ($f);
#ob_flush();
}
this logs the packets and saves, I rename it to .MP4 and open - well, the result is a little messy. I can recognize the output, the top screen is visible, the lower part is not good. I tried to capture it with another VCL player, and there were no problem.
Here is your code with a lot of useless stuff removed and a few efficiency improvements. Try it out and see what happens. It may or may not fix the problem, but report back with what happens and we'll take it from there.
// Settings
$address = '127.0.0.1';
$port = 1234;
$outfile = "output.mp4";
// Open pointers
if (!$ofp = fopen($outfile, 'w'))
exit("Could not open output file for writing");
if (!$ifp = stream_socket_server("udp://$address:$port", $errno, $errstr, STREAM_SERVER_BIND))
exit("Could not create listen socket ($errno: $errstr)");
// Loop and fetch data
// This method of looping is flawed and will cause problems because you are using
// UDP. The socket will never be "closed", so the loop will never exit. But you
// were looping infinitely before, so this is no different - we can address this
// later
while (!feof($ifp)) {
if (!strlen($chunk = fread($ifp, 8192))) continue;
fwrite($ofp, $chunk);
}
// Close file pointers
fclose($ofp);
#fclose($ifp);

PHP Socket and proc_open

I am trying to create a inetd-like service for Windows in PHP for future use with my other application.
So all I can think of is to use Steam Server and proc_open to pipe the stream directly to the process (like inetd). Because on Windows there is no pcntl_fork(), and PHP doesn't support threading.
So far, here is my code. The inetdtest program is a simple program with single printf (written in C). But the problem is that when I connected to my server (via netcat), I got no response message.
<?php
define ('SERVICE_COMMAND', 'inetdtest');
define ('SERVICE_PORT', 35123);
function main() {
echo "Simple inetd starting...\n";
$socket = stream_socket_server('tcp://0.0.0.0:' . SERVICE_PORT, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN);
if ($socket === false) {
echo "Can't bind to service port.\n";
echo "[$errno] $errstr";
die(1);
}
$processes = array();
while (true) {
$current = #stream_socket_accept($socket, 5, $host);
if ($current !== false) {
echo 'Incomming connection from client ' . $host . "\n";
echo "Lunching child process... ";
$io = array(
0 => $current,
1 => $current,
2 => array('file', 'stderr.log', 'a')
);
$proc = proc_open(SERVICE_COMMAND, $io, $pipes, NULL, NULL, array('bypass_shell'));
$status = proc_get_status($proc);
echo " DONE! PID : {$status['pid']}\n";
$processes[] = array($current, $proc);
}
foreach ($processes as $k=>$v) {
$status = proc_get_status($v[1]);
if (false === $status['running']) {
echo "Finalizing process {$status['pid']}... ";
fflush($v[0]);
fclose($v[0]);
proc_close($v[1]);
unset($processes[$k]);
echo "DONE!\n";
}
}
}
}
main();
The code justs works as it stands here (using cat as program and on linux), so the problem lies somewhere in the windows side of things.
For one thing, the option you are passing, to bypass the shell, should be given as
array('bypass_shell'=>true)
This may fix things already. The tricky part with these things, is that you're passing a socket fd to a process, which may or may not be expected to handle that properly. I don't know how these things are done in windows, but cutting cmd out of the equation can only help.
If it still doesn't work, you should create a loop which waits for data (either from network or child processes) and sends data from the network socket to the process pipe, and vice versa.

Categories