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.