Contents |
I wrote this WinBinder Application because of this thread:
http://www.winbinder.com/forum/viewtopic.php?t=293
r0swell asked if it would be possible to run applications which have an infinite loop for projects like an IRC-Client. I gave him a quick answer, but to be honest: without knowing if it was really all that simple.
I had to find out so here you go.
You need the Sockets Extension to use the real Sockets which PHP can provide. They are far better. The WinBinder Installation package i downloaded contained PHP 5.0.5. You have to download the Win32 Binaries from php.net with the same Version Number.
Go to the "ext" folder of your php zip-file and find the "php_sockets.dll". Put it into your WinBinder installation folder and add an "extension" line to your php.ini.
; PHP extensions extension_dir = "ext/" ; Directory in which the loadable extensions (modules) reside. extension=php_winbinder.dll extension=php_sockets.dll ; PHP_SOCKETS.DLL
Binary Package 5.0.5 from php.net
http://www.php.net/get/php-5.0.5-Win32.zip/from/a/mirror
Note: check you current PHP version. If it's different then 5.0.5 you have to adjust the version numbers within the download URL.
\binaries\php5\php.exe -version
Link to the PHP Sockets Refrence
http://de.php.net/manual/de/ref.sockets.php
Link to the IRC RFC.
http://www.irchelp.org/irchelp/rfc/rfc.html
<?php
########################################################################
# Simple IRC Client with real Sockets
# by Kjell Bublitz ( www.m3nt0r.de )
# written: 18.03.2006
########################################################################
error_reporting(E_ALL ^ E_NOTICE);
// Winbinder
//--------------------------------------------------------------------
define("PATH_INC", "../include/");
include PATH_INC . "winbinder.php";
//--------- CONSTANTS
define("APPNAME", "Simple IRC Client");
define("ID_TIMER", 100);
define("ID_CHAT", 101);
define("ID_CHATWIN", 102);
define("ID_CHATTXT", 106);
define("ID_SERVER", 103);
define("ID_CONNECT", 104);
define("ID_CHANNEL", 105);
define("ID_JOIN", 107);
define("ID_STATUS", 108);
define("ID_QUIT", 109);
//-------- DEFAULT VARS
$connected = false; // general status check
$joined = false; // i just wanted to avoid any output until user is in a channel.
$_socket = NULL; // just a placeholder
$server = NULL; // just a placeholder
$nickname = "Testing1"; // you might want to make editboxes out of this.
$username = "test";
$realname = "test";
$usermode = 0; // = 8 means invisible. read RFC..
//-------- WINDOW LAYOUT
$mainwin = wb_create_window(NULL, AppWindow, APPNAME, WBC_CENTER, WBC_CENTER, 620, 440, WBC_TOP);
wb_create_control($mainwin, StatusBar, APPNAME, null,null,null,null, ID_STATUS);
// Options-------------------------------
wb_create_control($mainwin, Frame, "IRC Options", 5, 5, 590, 45, 0);
wb_create_control($mainwin, EditBox, "irc.freenode.de:6667", 10, 20, 120, 20, ID_SERVER);
wb_create_control($mainwin, PushButton, "Connect", 140, 20, 90, 20, ID_CONNECT);
wb_create_control($mainwin, EditBox, "#test", 250, 20, 120, 20, ID_CHANNEL);
wb_create_control($mainwin, PushButton, "Join", 380, 20, 90, 20, ID_JOIN);
wb_create_control($mainwin, PushButton, "Quit", 490, 20, 90, 20, ID_QUIT);
// Chat----------------------------------
wb_create_control($mainwin, EditBox, "", 5, 55, 490, 290, ID_CHATWIN, WBC_MULTILINE | WBC_READONLY);
wb_create_control($mainwin, EditBox, "", 5, 350, 390, 20, ID_CHATTXT);
wb_create_control($mainwin, PushButton, "Say it", 400, 350, 90, 20, ID_CHAT);
// Run-----------------------------------
wb_create_timer($mainwin, ID_TIMER, 500);
wb_set_handler($mainwin, "process_main");
wb_main_loop();
//--------------------------------------------------------------------
// PROCESS HANDLER: process_main
//--------------------------------------------------------------------
function process_main($window, $id, $ctrl)
{
global $_socket, $connected, $server, $joined;
global $nickname, $username, $realname, $usermode;
switch($id)
{
case ID_TIMER:
if($connected == true)
{
$sread = array($_socket);
$result = @socket_select($sread, $w = null, $e = null, 0, 2000);
wb_set_text(wb_get_control($window,ID_STATUS), APPNAME." - Connected to ".$server."!");
// If new Data
if ($result == 1)
{
// Read Old Window and New String from Socket
$old_text = wb_get_text(wb_get_control($window,ID_CHATWIN));
$new_text = socket_read($_socket, 10240);
// JOIN
if(preg_match('/join/i',$new_text))
{
$new_text = convert_joinmsg($new_text);
$joined = true;
}
if($joined != false) // do nothing until we joined.
{
// PRIVMSG
if(preg_match('/privmsg/i',$new_text))
{
$new_text = convert_privmsg($new_text, $channel);
}
// MODE
if(preg_match('/mode/i',$new_text))
{
$new_text = convert_modemsg($new_text);
}
// Combine Text and Output
$text = $new_text.$old_text;
wb_set_text(wb_get_control($window,ID_CHATWIN), $text);
}
}
} else {
wb_set_text(wb_get_control($window,ID_STATUS), APPNAME." - Not connected...");
}
break;
case ID_CONNECT:
$server = wb_get_text(wb_get_control($window,ID_SERVER));
$serv_port = explode(":",$server);
wb_set_text(wb_get_control($window,ID_STATUS), APPNAME." - Connecting to ".$server." ...");
$_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = @socket_connect($_socket, $serv_port[0], $serv_port[1]);
if($result === false) // couldn't connect
{
$connected = false;
wb_set_text($statusbar, socket_strerror(socket_last_error($_socket)));
}
else // connected
{
$connected = true;
// login user
send($_socket, "NICK ".$nickname);
send($_socket, "USER ".$username." ".$username." ".$usermode." :".$realname);
}
break;
case ID_JOIN:
$channel = wb_get_text(wb_get_control($window,ID_CHANNEL));
if(!empty($channel))
{
send($_socket, "JOIN ".$channel);
}
break;
case ID_CHAT:
$channel = wb_get_text(wb_get_control($window,ID_CHANNEL));
$text = wb_get_text(wb_get_control($window,ID_CHATTXT));
if(!empty($channel) && !empty($text))
{
send($_socket, "PRIVMSG ".$channel." :".$text);
// write own chat to window.
$old_text = wb_get_text(wb_get_control($window,ID_CHATWIN));
$new_text = "<".$nickname."> ".$text."\n";
wb_set_text(wb_get_control($window,ID_CHATWIN), $new_text.$old_text);
}
break;
case ID_QUIT:
$connected = false;
@send($_socket, "QUIT");
@socket_shutdown($_socket);
@socket_close($_socket);
break;
case IDCLOSE:
@send($_socket, "QUIT");
@socket_shutdown($_socket);
@socket_close($_socket);
wb_destroy_window($window);
break;
}
}
// -------------- CUSTOM STUFF --------------
function send($_socket, $msg)
{
$msgbuffer = $msg."\n";
$bufferlen = strlen($msgbuffer);
@socket_write($_socket, $msgbuffer, $bufferlen);
return;
}
function convert_privmsg($string, $channel)
{
$server_msg1 = explode($channel." :", $string);
$server_msg2 = explode("!", $server_msg1[0]);
$chat_nick = substr($server_msg2[0], 1);
$chat_msg = $server_msg1[1];
$return = "<".$chat_nick."> ".$chat_msg;
return $return;
}
function convert_joinmsg($string)
{
$server_msg1 = explode("JOIN :", $string);
$server_msg2 = explode(" ", $server_msg1[1]);
$server_msg3 = explode(":", $server_msg2[0]);
$return = "**** Now talking in ".str_replace("\n","",$server_msg3[0])." ****\n\n\n\n";
return $return;
}
function convert_modemsg($string)
{
$server_msg1 = explode(" ",$string);
$server_msg2 = explode("!", $server_msg1[0]);
$chat_nick = substr($server_msg2[0], 1);
$return = "** User ".$chat_nick." sets mode '".$server_msg1[3]."' ".$server_msg1[4]." in ".$server_msg1[2]." **\n";
return $return;
}
?>
The custom functions are just simple helpers.
With this function you can write IRC commands (according to RFC) to the current socket.
Use this functions for any client-server communication like MODE, WHOIS, NOTICE, LIST, etc..
send (resource socket, string message)
This function returns: nothing
The convert functions are pretty dirty but working. ;)
Note: This function needs the channel to be given for fetching the correct line.
If you translate the script to work with multiple channels you need to work this function over maybe.
convert_privmsg (string text, string channel)
This function returns: formatted text
Beautifies the JOIN #channel message sent by the server.
convert_joinmsg (string text)
This function returns: formatted text
Beautifies the MODE messages sent by the server.
Note: the MOTD (message of the day) contains MODE alot! Needs a fix.
convert_modemsg (string text)
This function returns: formatted text