jdkchat.h 1/4
[top][prev][next]
/*
Copyright (C) 2003 Aditya Godbole (urwithaditya@gmx.net)
jdkchat version 1.4 - Simple chat daemon usable via telnet.
Copyright (C) 1995 J.D.Koftinoff Software, Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __JDKCHAT_H
#define __JDKCHAT_H
class JDKChatUser;
class JDKChatRoom;
#define MAX_CHAT_USERS 20
class JDKChatUser
{
public:
JDKChatUser( JDKChatRoom *current_room);
~JDKChatUser();
int IsUserAccepted();
int GetFD();
const char *GetName();
int ManageUser(); // -1 means connection ended.
int Send( const char *from, const char *text );
protected:
void ClearEntryLine();
void RedisplayEntryLine();
void ClearBuffer();
private:
int HandleNoConnection();
int HandleSendHello();
int HandleGetName();
int HandleGetLine();
enum State
{
STATE_NO_CONNECTION,
STATE_GET_NAME,
STATE_GET_LINE
} state;
int ManageInputBuffer();
int HandleUserCommand();
char buffer[256];
int cur_pos;
char name[256];
char prompt[256];
int local_mode;
struct sockaddr_in user_address;
int user_fd;
int user_accepted;
JDKChatRoom *room;
static const char *welcome;
};
class JDKChatRoom
{
public:
JDKChatRoom( int port, FILE *log_file_=0 );
~JDKChatRoom();
void BindSocket();
int Make_fdset( fd_set *r, fd_set *w, fd_set *e );
int ManageRoom( fd_set *r, fd_set *w, fd_set *e );
void LookForNewUsers();
void BroadCast( const char *from, const char *c );
void Announce( const char *c );
void ListUsers( JDKChatUser *u );
int GetServerFD();
struct sockaddr_in *GetServerAddress();
JDKChatUser *GetUser(int which);
private:
int port;
int server_fd;
struct sockaddr_in server_address;
JDKChatUser *user[MAX_CHAT_USERS];
FILE *log_file;
};
#endif
world.h 2/4
[top][prev][next]
#ifndef __WORLD_H
#define __WORLD_H
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>
#include <ctype.h>
#include <errno.h>
#ifdef __APPLE__
#define ACCEPT_SIGNED 1
#else
#define ACCEPT_SIGNED 0
#endif
#endif
jdkchat.cpp 3/4
[top][prev][next]
/*
Copyright (C) 2003 Aditya Godbole (urwithaditya@gmx.net).
jdkchat version 1.5 - Simple chat daemon usable via telnet.
Copyright (C) 1995 J.D.Koftinoff Software, Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "world.h"
#include "jdkchat.h"
//#define DBG(a) printf a
#define DBG(a)
static char telnet_init[] =
{
IAC, DONT, TELOPT_LINEMODE,
IAC, WILL, TELOPT_SGA,
0
};
static char telnet_local_on[] =
{
IAC, WONT, TELOPT_ECHO,
0
};
static char telnet_local_off[] =
{
IAC, WILL, TELOPT_ECHO,
0
};
const char *JDKChatUser::welcome =
"Welcome To jdkchat v1.5 by J.D. Koftinoff Software, Ltd.\r\nhttp://www.jdkoftinoff.com/\r\n"
"and modified by Aditya Godbole (urwithaditya@gmx.net)\r\n"
"Commands available:\r\n"
" /who -- (list all users along with their connection numbers)\r\n"
" /exit -- (exit chat room)\r\n"
" /local -- (toggle local mode for your telnet session)\r\n"
" /[connection number] message -- (send private message to user at \r\n"
" specified connection number) \r\n"
"\r\n"
"\r\n";
// write entire buffer to socket fd
int lpwrite( int fd, const void *buf, int sz )
{
const char *b=(const char *)buf;
int todo=sz;
int done=0;
while( todo>0 )
{
int cnt=write( fd, b, sz );
if( cnt<0 && errno!=EINTR && errno!=EAGAIN)
{
done=-1; // error!
break;
}
if( cnt>0 )
{
todo-=cnt;
done+=cnt;
b+=cnt;
}
}
return done;
}
//------------------------------------------------------
JDKChatRoom::JDKChatRoom( int _port, FILE *_log_file )
{
port=_port;
log_file=_log_file;
for( int i=0; i<MAX_CHAT_USERS; ++i )
{
user[i] = new JDKChatUser( this );
}
server_fd=-1;
}
JDKChatRoom::~JDKChatRoom()
{
if( server_fd!=-1 )
{
close( server_fd );
}
}
void JDKChatRoom::BindSocket()
{
int len;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
len = sizeof(server_address);
bzero((char*) &server_address, len);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(port);
if (bind(server_fd, (sockaddr *)&server_address, len) != 0)
{
fprintf( stderr, "Error binding to port %d\r\n", port );
exit(1);
}
listen(server_fd, MAX_CHAT_USERS);
}
int JDKChatRoom::Make_fdset( fd_set *r, fd_set *w, fd_set *e )
{
int num=1;
FD_SET( server_fd, r );
FD_SET( server_fd, e );
for( int i=0; i<MAX_CHAT_USERS; ++i )
{
int fd=user[i]->GetFD();
if( fd!=-1 )
{
FD_SET( fd, r );
++num;
}
}
return num;
}
int JDKChatRoom::ManageRoom( fd_set *r, fd_set *w, fd_set *e )
{
if( FD_ISSET( server_fd, r ) )
{
LookForNewUsers();
}
for( int i=0; i<MAX_CHAT_USERS; ++i )
{
int fd=user[i]->GetFD();
if( fd!=-1 )
{
int left=0;
if( FD_ISSET( fd, r ) )
{
if( user[i]->ManageUser()==-1 )
{
left=1;
}
}
if( left )
{
char buf[256];
sprintf( buf, "%s has left", user[i]->GetName() );
Announce( buf );
}
}
}
return 0;
}
void JDKChatRoom::LookForNewUsers()
{
for( int i=0; i<MAX_CHAT_USERS; ++i )
{
int fd=user[i]->GetFD();
if( fd==-1 )
{
user[i]->ManageUser();
break;
}
}
}
void JDKChatRoom::BroadCast( const char *from, const char *c )
{
if( log_file )
{
fprintf( log_file, "%10s: %s\n", from, c );
fflush(log_file);
}
for( int i=0; i<MAX_CHAT_USERS; ++i )
{
if( user[i]->GetFD()!=-1 && user[i]->IsUserAccepted() )
{
user[i]->Send( from, c );
}
}
}
void JDKChatRoom::Announce( const char *c )
{
BroadCast( "JDKCHAT", c );
}
void JDKChatRoom::ListUsers( JDKChatUser *u )
{
char list[512];
char num[5];
strcpy( list, "Users = " );
for( int i=0; i<MAX_CHAT_USERS; ++i )
{
if( user[i]->GetFD()!=-1 && user[i]->IsUserAccepted() )
{
strcat( list, user[i]->GetName() );
strcat( list, ":" );
sprintf(num,"%d",i);
strcat(list,num);
strcat( list, " " );
}
}
u->Send( "JDKCHAT", list );
}
int JDKChatRoom::GetServerFD()
{
return server_fd;
}
struct sockaddr_in *JDKChatRoom::GetServerAddress()
{
return &server_address;
}
// 31 Dec 2003, Aditya Godbole - Added this function for private messaging
JDKChatUser *JDKChatRoom::GetUser(int which)
{
if (which >= MAX_CHAT_USERS)
return NULL;
if( user[which]->GetFD()!=-1 && user[which]->IsUserAccepted() )
return user[which];
return NULL;
}
//------------------------------------------------------
JDKChatUser::JDKChatUser( JDKChatRoom *current_room )
{
room= current_room;
*buffer='\0';
cur_pos=0;
*name='\0';
user_fd=-1;
user_accepted=0;
local_mode=0;
state = STATE_NO_CONNECTION;
}
JDKChatUser::~JDKChatUser()
{
if( user_fd!=-1 )
{
close( user_fd );
}
}
int JDKChatUser::IsUserAccepted()
{
return user_accepted;
}
int JDKChatUser::GetFD() // returns file descriptor, or -1 if none
{
return user_fd;
}
const char *JDKChatUser::GetName()
{
return name;
}
int JDKChatUser::ManageUser() // -1 means user left
{
int ret=0;
switch( state )
{
case STATE_NO_CONNECTION:
ret=HandleNoConnection();
break;
case STATE_GET_NAME:
ret=HandleGetName();
break;
case STATE_GET_LINE:
ret=HandleGetLine();
break;
};
if( ret==-1 )
{
if( state==STATE_GET_NAME )
ret=0;
close( user_fd );
user_fd=-1;
state = STATE_NO_CONNECTION;
user_accepted=0;
ClearBuffer();
}
return ret;
}
int JDKChatUser::Send( const char *from, const char *text )
{
if( user_fd!=-1 )
{
char buf[384];
sprintf( buf, "%10s: %s\r\n", from, text );
int len=strlen(buf);
ClearEntryLine();
if( lpwrite( user_fd, buf, len )!=len )
{
return -1;
}
else
{
RedisplayEntryLine();
return len;
}
}
else
{
return 0;
}
}
void JDKChatUser::ClearEntryLine()
{
char *buf = "\r\033[K\r";
if( user_fd!=-1 )
{
lpwrite( user_fd, buf, strlen(buf) );
}
}
void JDKChatUser::RedisplayEntryLine()
{
if( user_fd!=-1)
{
ClearEntryLine();
lpwrite( user_fd, prompt, strlen(prompt) );
lpwrite( user_fd, buffer, strlen(buffer) );
}
}
void JDKChatUser::ClearBuffer()
{
*buffer='\0';
cur_pos=0;
}
int JDKChatUser::HandleNoConnection()
{
#if ACCEPT_SIGNED
int len;
#else
unsigned int len;
#endif
len=sizeof( user_address );
user_fd = accept( room->GetServerFD(), (sockaddr *)&user_address, &len );
// note: accept returns -1 if there is no connection or if error.
if( user_fd!=-1 )
{
local_mode=0;
strcpy( prompt, "Your Name:" );
lpwrite( user_fd, telnet_init, strlen(telnet_init) );
if( local_mode )
lpwrite( user_fd, telnet_local_on, strlen(telnet_local_on) );
else
lpwrite( user_fd, telnet_local_off, strlen(telnet_local_off) );
lpwrite( user_fd, welcome, strlen(welcome) );
state=STATE_GET_NAME;
ClearBuffer();
RedisplayEntryLine();
}
return 0;
}
int JDKChatUser::HandleGetName()
{
int ret=ManageInputBuffer();
if( ret==1 )
{
if( strlen( buffer )<2 )
{
ClearBuffer();
RedisplayEntryLine();
}
else
{
user_accepted=1;
char s[256];
strcpy( name, buffer );
strcpy( s, name );
strcat( s, " just entered the room.\007" );
strcpy( prompt, name ); /*Changes here */
strcat( prompt, " >" );
room->Announce( s );
state=STATE_GET_LINE;
ClearBuffer();
room->ListUsers( this );
}
}
if( ret<0 )
{
return -1;
}
return 0;
}
int JDKChatUser::HandleGetLine()
{
int ret=ManageInputBuffer();
if( ret==1 || cur_pos>=69 )
{
if( strlen( buffer )<1 )
{
ClearBuffer();
ClearEntryLine();
RedisplayEntryLine();
}
else
{
if( *buffer=='/' ) // 31 Dec 2003, Aditya Godbole -
// Changed command indicator to '/'. Its easier to type.
{
ret = HandleUserCommand();
ClearBuffer();
state=STATE_GET_LINE;
RedisplayEntryLine();
}
else
{
char tmp[256];
strcpy( tmp, buffer );
ClearBuffer();
room->BroadCast( name, tmp );
state=STATE_GET_LINE;
}
}
}
if( ret<0 )
{
return -1;
}
return 0;
}
int JDKChatUser::ManageInputBuffer()
{
static char *backspace="\010 \010";
// return -1 on error
// return 0 if no full line received yet
// return 1 if a full line received.. Remove any return or line feeds.
// always maintain a null terminated string.
// handle backspace, delete.
char c;
int ret = read( user_fd, &c, 1 ); // try read one char
if( ret==-1 )
{
if( errno==EAGAIN || errno==EINTR )
{
// no data yet!
return 0;
}
else
{
return -1; // error reading - close socket
}
}
if( ret==0 )
{
// end of file - socket must be closed.
return -1;
}
if( ret==1 )
{
DBG(( "Got Char %02x\r\n", ((int)c)&0xff ));
if( (unsigned char)c>=0x80 )
return 0;
// if it is CR then return 1
if( c=='\r' )
{
return 1;
}
// if it is backspace or delete then do backspace sequence.
if( c==8 || c==0x7f )
{
if( cur_pos>0 )
{
cur_pos--;
buffer[cur_pos]='\0';
if( !local_mode )
{
lpwrite( user_fd, backspace, strlen(backspace) );
}
}
if( local_mode )
{
RedisplayEntryLine();
}
return 0;
}
if( state==STATE_GET_NAME )
{
if( !isalpha(c) )
{
return 0;
}
}
// if it any non-printing char, then ignore it.
if( !isprint( c ) )
{
return 0;
}
// if it is anything else then put it in buffer and echo it
if( cur_pos<70 )
{
buffer[cur_pos++]=c;
buffer[cur_pos]='\0';
if( !local_mode )
lpwrite( user_fd, &c, 1 );
DBG( ("Buffer=%s\r\n", buffer ) );
return 0;
}
else
{
// beep to the user. Only one line allowed!
c=7;
lpwrite( user_fd, &c, 1 );
if( local_mode )
{
RedisplayEntryLine();
}
}
}
return 0;
}
int JDKChatUser::HandleUserCommand()
{
int which,val;
if( strncmp( buffer+1, "who", 3 )==0 )
{
room->ListUsers( this );
}
else if( strncmp( buffer+1, "local", 4 )==0 )
{
local_mode=!local_mode;
if( local_mode )
{
lpwrite( user_fd, telnet_local_on, strlen(telnet_local_on) );
}
else
{
lpwrite( user_fd, telnet_local_off, strlen(telnet_local_off) );
}
}
else if( strncmp( buffer+1, "quit", 4 )==0 ||
strncmp( buffer+1, "exit", 4 )==0 )
{
close( user_fd );
user_fd=-1;
state = STATE_NO_CONNECTION;
user_accepted=0;
ClearBuffer();
return -1;
}
// 31 Dec 2003, Aditya Godbole - This is the part that takes care of private messages
else if (sscanf(buffer + 1,"%d",&which) == 1) {
JDKChatUser *other;
if (other = room->GetUser(which)) {
int count;
char frombuff[100];
strcpy(frombuff,name);
strcat(frombuff,"**:");
for (count = 0;count < 5 && !isspace(buffer[count]);count++)
;
other->Send(frombuff,buffer + count);
strcpy(frombuff,"\t");
strcpy(frombuff,name);
strcat(frombuff," -> ");
strcat(frombuff,other->GetName());
strcat(frombuff,"*:");
Send(frombuff,buffer + count);
}
}
return 0;
}
//------------------------------------------------------
jdkchatmain.cpp 4/4
[top][prev][next]
/*
Copyright (C) 2003 Aditya Godbole (urwithaditya@gmx.net)
jdkchat version 1.5 - Simple chat daemon usable via telnet.
Copyright (C) 1995 J.D.Koftinoff Software, Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "world.h"
#include "jdkchat.h"
void print_hello()
{
fprintf( stdout,
" Copyright (C) 2003 Aditya Godbole (urwithaditya@gmx.net)\r\n"
" jdkchat version 1.5,\r\n"
" Copyright (C) 1995 J.D. Koftinoff Software Ltd.\r\n"
" jdkchat comes with ABSOLUTELY NO WARRANTY.\r\n"
" This is free software, and you are welcome to redistribute it\r\n"
" under the terms of the Free Software Foundation's GNU Public\r\n"
" License.\r\n"
);
}
void manage_room( JDKChatRoom *room )
{
int num=0;
fd_set rd,wr,ex;
struct timeval t;
int i;
t.tv_sec=2;
t.tv_usec=0;
FD_ZERO( &rd );
FD_ZERO( &wr );
FD_ZERO( &ex );
room->Make_fdset( &rd, &wr, &ex );
num=select( FD_SETSIZE, &rd, &wr, &ex, &t );
if( num>0 )
{
room->ManageRoom( &rd, &wr, &ex );
}
}
int main( int argc, char **argv )
{
FILE *log_file=0;
signal(SIGPIPE, SIG_IGN);
if( argc<2 || (argc>1 && *argv[1]=='-' ) )
{
print_hello();
fprintf( stderr, "usage:\n\tjdkchat [port] [log_file]\nPlease give desired port #\n" );
exit(1);
}
int port=atoi(argv[1]);
fprintf( stdout, "jdkchat: using port %d", port );
if( argc>2 )
{
log_file = fopen( argv[2], "at" );
if( log_file )
{
fprintf( stdout, ", logging to '%s'", argv[2] );
}
}
fprintf( stdout, "\n" );
JDKChatRoom r(port,log_file);
r.BindSocket();
int f=fork();
if( f==0 )
{
close( STDIN_FILENO );
close( STDOUT_FILENO );
close( STDERR_FILENO );
// in child
while(1)
manage_room( &r );
}
if( f==-1 )
{
perror( "error forking processes" );
return 1;
}
return 0;
}
Generated by GNU enscript 1.6.1.