Well, just to show that it is in fact possible to write a POP3 server in PHP, here it is. The server does no authentication--or pretty much anything else. It just keep sending the same message over and over. But it works. Thunderbird was able to retrieve messages from it. Totally useless, but sort of cool.
My setup is Apache 2 on Windows with PHP 5.2.
<?php
// echo something so fopen() would return
header("Content-type: text/plain");
echo "OK\n";
flush();
// listen for incoming connection
$listen_socket = socket_create_listen(110, 1);
$r = $w = $e = array($listen_socket);
$n = socket_select($r, $w, $e, 120);
$client_socket = ($n == 1) ? socket_accept($listen_socket) : null;
socket_close($listen_socket);
// spawn copy of myself
$internal_url = "http://{$_SERVER['HTTP_HOST']}:{$_SERVER['SERVER_PORT']}{$_SERVER['SCRIPT_NAME']}";
$stream_context_options = array (
'http' => array (
'method' => 'GET',
'timeout' => 1
)
);
$context = stream_context_create($stream_context_options);
if($f = fopen($internal_url, "rb", 0, $context)) {
fclose($f);
}
if(!$client_socket) {
// timed out
exit;
}
// start handling the session
$read_buffer = "";
$write_buffer = "+OK POP3 server ready\r\n";
$active = true;
$messages = array(
"From: [email protected]\r\nSubject: This is a test\r\n\r\nHello world!\r\n"
);
$idle_start = time();
while(true) {
$r = $w = $e = array($client_socket);
$n = socket_select($r, $w, $e, 60);
if($n) {
if($r) {
// read from the socket
$read_buffer .= socket_read($client_socket, 128);
$idle_start = time();
}
if($w) {
if($write_buffer) {
// write to the socket
$written = socket_write($client_socket, $write_buffer);
$write_buffer = substr($write_buffer, $written);
$idle_start = time();
} else if($active) {
$now = time();
$idle_time = $now - $idle_start;
if($idle_time > 10) {
// exit if nothing happened for 10 seconds
break;
} else if($idle_time > 2) {
// start napping when the client is too slow
sleep(1);
}
} else {
break;
}
}
if($e) {
break;
}
if($read_buffer) {
if(preg_match('/(.*?)(?:\s+(.*?))?[\r\n]+/', $read_buffer, $matches)) {
$read_buffer = substr($read_buffer, strlen($matches[0]));
$command = $matches[1];
$argument = $matches[2];
switch($command) {
case 'USER':
$username = $argument;
$write_buffer .= "+OK $username is welcome here\r\n";
break;
case 'PASS':
$message_count = count($messages);
$write_buffer .= "+OK mailbox has $message_count message(s)\r\n";
break;
case 'QUIT':
$write_buffer .= "+OK POP3 server signing off\r\n";
$active = false;
break;
case 'STAT':
$message_count = count($messages);
$mailbox_size = 0;
foreach($messages as $message) {
$mailbox_size += strlen($message);
}
$write_buffer .= "+OK $message_count $mailbox_size\r\n";
break;
case 'LIST':
$start_index = (int) $argument;
$message_count = count($messages) - $start_index;
$total_size = 0;
for($i = $start_index; $i < count($messages); $i++) {
$total_size += strlen($messages[$i]);
}
$write_buffer .= "+OK $message_count messages ($total_size octets)\r\n";
for($i = $start_index; $i < count($messages); $i++) {
$message_id = $i + 1;
$message_size = strlen($messages[$i]);
$write_buffer .= "$message_id $message_size\r\n";
}
$write_buffer .= ".\r\n";
break;
case 'RETR':
$message_id = (int) $argument;
$message = $messages[$message_id - 1];
$message_size = strlen($message);
$write_buffer .= "+OK $message_size octets\r\n";
$write_buffer .= "$message\r\n";
$write_buffer .= ".\r\n";
break;
case 'DELE':
$write_buffer .= "+OK\r\n";
break;
case 'NOOP':
$write_buffer .= "+OK\r\n";
break;
case 'LAST':
$message_count = count($messages) - $start_index;
$write_buffer .= "+OK $message_count\r\n";
break;
case 'RSET':
$write_buffer .= "+OK\r\n";
break;
default:
$write_buffer .= "-ERR Unknown command '$command'\r\n";
}
}
}
} else {
break;
}
}
?>
my app uses PHP
is particularly relevant here - the data storage layer is (should be) abstracted from the application layer and what you are talking about is a daemon/service which would exist separately from the rest of your app(s) anyway. – Bossismexec()
family of functions (or messing about in COM) and none of these mechanisms allow you to pass resource variables between the processes likepcntl_fork()
does because they are created in a completely separate memory space. So you can access the client socket in the new process - which for this specific task makes it basically useless. Unless you know something I don't - I'm not infallible by any stretch of the imagination. – Bossism