I've been trying to implement a basic IVR that interacts with our database using PHP. With some help on IRC, I've created an Event Socket interface for PHP. It is rudimentary at this point, and not ready for prime-time, but it is working -- FreeSwitch connects to my process when a call comes in, sends the basic context info, and I can issue calls. After a call to "event plain ALL", I start receiving events. Mainly HEARTBEAT events, with a few others thrown in. What I'm NOT seeing is any DTMF events. Now, I know that the DTMF codes are recognized by other parts of FreeSwitch, such as the IVR, and I've ensure that my SIP client is sending them in-audio, SIP INFO and RFC2833. However, I'm not getting anything when I connect and press buttons. I have some other issues with the Event Socket, that I'd love to get through, but without the DTMF, most of the applications we're working on won't go very far. Is there anything I have to do to be able to get these Events to show up? Thanks Bill PS: I've attached the EventSocketListener PHP class. You can run it from the command line and it will listen on port 9090 -- obviously, it's not clean nor done, but it is working #!/usr/local/bin/php <?php /** * Based loosely on the NET_Server code in PEAR. * This is only an example - considerable additional work is needed * Specifically, the code in the handleConnection method should be * handled in a subclass * * */ class Message { var $properties = array(); var $content = null; } class EventSocketListener { var $host; var $port; var $sock; var $is_parent = true; var $clientInfo; var $clientFD; var $connectionContext = array(); function &create($port, $host = "localhost") { $esl = new EventSocketListener; $esl->port = $port; $esl->host = $host; if (!function_exists('socket_create')) { return PEAR::raiseError('Sockets extension not available.'); } return $esl; } function start() { if (($this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) { echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n"; } if (!socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1)) { echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL; } if (socket_bind($this->sock, $this->host, $this->port) === false) { echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($this->sock)) . "\n"; } if (socket_listen($this->sock, 5) === false) { echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($this->sock)) . "\n"; } // Dear children, please do not become zombies pcntl_signal(SIGCHLD, SIG_IGN); // wait for incmoning connections while (true) { // new connection if(($fd = socket_accept($this->sock))) { $pid = pcntl_fork(); if($pid == -1) { return PEAR::raiseError('Could not fork child process.'); } // This is the child => handle the request elseif($pid == 0) { // this is not the parent $this->_isParent = false; // store the new file descriptor $this->clientFD = $fd; $peer_host = ""; $peer_port = ""; socket_getpeername($this->clientFD, $peer_host, $peer_port); $this->clientInfo = array( "host" => $peer_host, "port" => $peer_port, "connectOn" => time() ); $this->handleConnection(); socket_shutdown($this->clientFD, 2); socket_close($this->clientFD); } else /* Parent does nothing */ { } } } } function handleConnection() { $fd = $this->clientFD; //first, read headers & setup a state for this connection $line = ""; socket_write($fd, "CONNECT\n\n"); do { $line = socket_read($fd, 2048, PHP_NORMAL_READ); if (trim($line) == "") break; //we got a header, we need to add it to the list($key, $value) = explode(":", $line); $key = trim($key); $value = trim(urldecode($value)); $this->connectionContext[$key] = $value; } while ($line != ""); // print_r($this->connectionContext); $this->callConnected(); exit(); } function processMessages($returnOnReply = false) { $fd = $this->clientFD; $result = new Message(); $props = array(); while (true) { do { $line = @socket_read($fd, 2048, PHP_NORMAL_READ); if (socket_last_error($fd) == 104) return null; if ($line == null || $line == FALSE || trim($line) == "") break; //we got a header, we need to add it to the message list($key, $value) = explode(":", $line); $key = trim($key); $value = trim(urldecode($value)); $props[$key] = $value; } while ($line != ""); $result->properties = $props; if (isset($props['Content-Length'])) { $length = $props['Content-Length']; print("Reading content - $length\n"); $data = socket_read($fd, $length); $result->content = $data; } if (isset($props['Content-Type'])) { $type = $props['Content-Type']; if ($returnOnReply && ($type == "command/reply" || $type == "api/response")) { return $result; } else if ($type == "text/event-plain") //only plain events for now { $this->handleEvent($result); } } else { print("UNKNOWN MESSAGE: \n"); print_r($result); } } } function invokeCommand($command) { //Send the command print("Invoking: $command\n"); $this->sendCommand($command); // Wait for the response $result = $this->processMessages(true); return $result; } function sendCommand($command) { $fd = $this->clientFD; socket_write($fd, trim($command) . "\n\n"); } /*-----------------------------------------------------*/ /* Abstract Methods - should move to subclass*/ function callConnected() { print_r($this->connectionContext); print("----------------\n"); $result = $this->invokeCommand("log DEBUG"); print_r($result); $result = $this->invokeCommand("event plain ALL"); print_r($result); $this->processMessages(false); print("DONE PROCESSING MESSAGES"); print_r($this->connectionContext); } function handleCommandResponse($response) { print("Recieved Unhandled Response:\n"); print_r($response); } function handleEvent($event) { print("Recieved Unhandled Event:\n"); print_r($event); } } // create a server that forks new processes $server = &EventSocketListener::create(9090); // start the server $server->start(); ?>