// Filename:   chat.C
// Contents:   The chat object methods
// Author: Gregory Shaw
// Created:    4/10/95

/*
This file 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, or (at your option) any
later version.

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file with other programs, and to distribute
those programs without any restriction coming from the use of this
file.  (The General Public License restrictions do apply in other
respects; for example, they cover modification of the file, and
distribution when not linked into another program.)

This file 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; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#ifndef _CHAT_C_
#define _CHAT_C_



#include "bbshdr.h"

extern User   user;             // alias and other information

#define DEBUG

// Method: constructor
// Purpose:    initialize the chat object
// Input:  none
// Output: (object is initialized)
// Author: Gregory Shaw
// Created:    4/10/95

Chat::Chat()
{
	int x;
	int room;
	FILE *namefile;             // file of room names
	char tmpstr[255];
	char tmpstr2[255];
	char c, *ptr;


	chat_room = -1;
	for (x=0; x<MAX_CHAT_KILL_USERS; x++)
		killusers[x] = NULL;
	broadcast_message[0]=0;
	for (x=0; x<MAX_CHAT_ROOMS; x++)
		roomnames[x].room = -1;
	room_names = 0;
	sprintf(tmpstr,"%s/config/roomnames",getenv("BBSDIR"));
	if (namefile = fopen(tmpstr,"r"), namefile != NULL)
	{
		while (!feof(namefile))
		{
			ptr = tmpstr2;
			while (c = fgetc(namefile), c != '\n' && c != '\r' && !feof(namefile))
				*ptr++ = c;
			*ptr = 0;
			if (feof(namefile))
				continue;
			if (sscanf(tmpstr2,"%d %s",&room,tmpstr) == 2)
			{
                                // too long?
				if (strlen(tmpstr) > MAX_ROOM_NAME)
					tmpstr[MAX_ROOM_NAME] = 0;
				strcpy(roomnames[room_names].name,tmpstr);
				roomnames[room_names++].room = room;
			}
		}
		fclose(namefile);
	}
	// read the kill file
	read_kill_file();
}


// Method: destructor
// Purpose:    clean up the chat object
// Input:  none
// Output: all data storage is freed
// Author: Gregory Shaw
// Created:    4/10/95

Chat::~Chat()
{
	int x;

	disconnect();
	x = 0;
	while (killusers[x] != NULL)
		free(killusers[x++]);
}


// Method: admin_info
// Purpose:    get administration information from a server
// Input:  server - the type of server to contact
//     0 - chat server
//     1 - private chat server
//     2 - broadcast server
// Output: if the connect is successful, the admin information is displayed
// Author: Gregory Shaw
// Created:    4/10/95

void Chat::admin_info(int server)
{
	char tmpstr[255];
	int master = 0;

	clear_scr();
	switch(server)
	{
		case 0:
			master = CHAT_ADMIN_MASTER_PORT;
			break;
		case 1:
			master = CHAT_PRIVATE_ADMIN_MASTER_PORT;
			break;
		case 2:
			master = CHAT_BROADCAST_ADMIN_MASTER_PORT;
	}
	if (chatipc.open_sock(chathost(),master) == 0)
	{
		while (chatipc.receive(tmpstr) >= 0)
		{
			sstr(tmpstr);
		}
		chatipc.close_sock(0);
	}
	else
	{
		sstrcrl("UNABLESERVER");
	}
	waitcr();
}


// Method: broadcast
// Purpose:    send a broadcast to all users
// Input:  msg - the message to send.  NULL to enter message
// Output: connect to the server, send message
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::broadcast(int oneuser)
{
                                // string + alias
	char tmpstr[255+MAX_ALIAS_LENGTH+7];
	char tmpstr2[255];          // tmp
	char login[MAX_ALIAS_LENGTH];


	clear_scr();
	sstrcrl("RETURNTOEXIT");
	cr();
	cr();
	if (oneuser)
	{
		sstrl("WOULDALIAS");
		if (yesno())
		{
			sprintf(tmpstr,gstrl("MESSAGEFROM"),user.user_alias());
		}
		else
		{
			sprintf(tmpstr,gstrl("MESSAGEFROM"),user.logname());
		}
		sstrl("TOLOGIN");
		gstr(login,MAX_LOGIN_LENGTH-1);
		sprintf(tmpstr2,gstrl("MESSAGETO"),login);
		sstr(tmpstr2);
		gstr(tmpstr2,250);
	}
	else
	{
		sprintf(tmpstr,gstrl("MESSAGEFROM"),user.logname());
		sstrl("MESSAGETOALL");
		gstr(tmpstr2,250);
		strcpy(login,"all");    // send to all
	}
	if (strlen(tmpstr2) == 0)
	{
		sstrcrl("BROADABORT");
		waitcr();
		return(0);
	}
	if (broadipc.open_sock(chathost(),CHAT_BROADCAST_PORT) == 0)
	{
		broadipc.send(login);
		strcat(tmpstr,tmpstr2);
		broadipc.send(tmpstr);
		broadipc.close_sock(0);
	}
	else
	{
		sstrcrl("UNABLESERVER");
		waitcr();
	}
	return(0);
}


// Method: broadcast_direct
// Purpose:    send a broadcast to all users
// Input:  msg - the message to send.  NULL to enter message
// Output: connect to the server, send message
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::broadcast_direct(char *login, char *msg)
{
	if (broadipc.open_sock(chathost(),CHAT_BROADCAST_PORT) == 0)
	{
		broadipc.send(login);
		broadipc.send(msg);
		broadipc.close_sock(0);
	}
	else
		return(-1);
	return(0);
}


// Method: change_club_password
// Purpose:    change the password for a particular club
// Input:  new password
// Output: clublist is updated
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::change_club_password(int club)
{
	char *ptr,*ptr2; 
	char tmpstr[255];
	char msg[255];
	char newline[300];
	char *acllist[MAX_ACL_LIST];	// max names in an alias
	FILE *infile,*outfile;
	int names;
	int done;
	int pw=0;	// password system?
	int x,y;
	int changed;	// any changes?
	char c;
	int club_id;


	clear_scr();
	changed = 0;
	if (ptr = club_line(club), ptr != NULL)
	{
		if (strchr(ptr,'"') != NULL)
		{	// Password
			pw=1;
			sstrcrl("RETURNTOEXIT");
			cr();
			sstrl("CURRENTCLUBPW");
			gstr_starsecho(msg,24);
			if (strlen(msg) == 0)
				return(0);
			while (ptr2 = strchr(ptr,'"'), ptr2 != NULL)
				*ptr2 = ' ';	// eliminate quotes
			sscanf(ptr,"%s",tmpstr);
			if (strcmp(msg,tmpstr))
			{
				sstrcrl("INVALIDPW");
				waitcr();
				return(0);
			}
			done=0;
			while(!done)
			{
				changed++;
				cr();
				cr();
				sstrl("WHATISNEWPW");
				gstr(newline,24);
				cr();
				sstrl("ENTERNEWPW");
				gstr(tmpstr,24);
				if (strcmp(newline,tmpstr))
				{	// match failed
					sstrcrl("PWNOMATCH");
					sstrcrl("PLEASETRYAGAIN");
					cr();
					waitcr();
				}
				else // match
					done++;
			}
		}
		else
		{	// ACL list
			// parse list
			ptr2 = ptr;
			names = 0;		// empty list
			while (ptr2 = strchr(ptr,','), ptr2 != NULL)
			{
				*ptr2 = 0;	
				strcpy(tmpstr,ptr);
				acllist[names] = (char *)alloca(strlen(tmpstr)+1);
				if (acllist[names] == NULL)
				{
					ap_log("chat: out of memory!");
					return(0);
				}
				strcpy(acllist[names++],tmpstr);
				ptr = ptr2+1;
			}
			// handle the last guy in the line
			strcpy(tmpstr,ptr);
			acllist[names] = (char *)alloca(strlen(tmpstr)+1);
			if (acllist[names] == NULL)
			{
				ap_log("chat: out of memory!");
				return(0);
			}
			strcpy(acllist[names++],tmpstr);
			if (names == MAX_ACL_LIST)
			{
				sprintf(tmpstr,"chat: change_club_pw - too many names in list for club %d",club);
				ap_log(tmpstr);
				ap_log("Please increase MAX_ACL_LIST and recompile.");
				return(0);
			}
			for (x=names; x< MAX_ACL_LIST; x++)
				acllist[x] = NULL;	// clear all remaining entries
			// Ok.  We have a list.  let's edit!
			done = 0;
			while (!done)
			{
				sstrcrl("CURRENTCLUBACL");
				names = 0;
				for (x=0; x< MAX_ACL_LIST; x++)
				{
					if (acllist[x] != NULL)
					{
						sprintf(tmpstr,"%d %s ",names++,acllist[x]);
						sstrcr(tmpstr);
						if (!x%20 && x != 0)
							waitcr();
					}
				}
				sstrl("ADCQUIT");
				while (c = tolower(gch(1)), c != 'a' && c != 'd' && c !=
					'c' && c != 'q' && c != '\r');
				cr();
				cr();
				switch(c)
				{
				case '\r':
				case 'q':
					done++;
					break;
				case 'a':           // add
					sstrcrl("RETURNTOEXIT");
					cr();
					sstrl("ADDWHATLOGIN");
					gstr(tmpstr,MAX_LOGIN_LENGTH-1);
					if (strlen(tmpstr) == 0)
						continue;
					else
					{
						x = 0;
						while (acllist[x] != NULL && x < MAX_ACL_LIST)
							x++;
						if (x == MAX_ACL_LIST)
						{
							sprintf(tmpstr,gstrl("TOOMANYUSERS"),MAX_ACL_LIST);
							waitcr();
							continue;
						}
						// got one.  save it.
						acllist[x] = (char *) alloca(strlen(tmpstr + 1));
						if (acllist[x] == NULL)
						{
							ap_log("edit_club_list: out of memory!");
							return(0);
						}
						strcpy(acllist[x],tmpstr);
						changed++;
					}
					break;
				case 'd':           // delete
					sstrcrl("RETURNTOEXIT");
					cr();
					sstrl("DELETEWHICH");
					gstr(tmpstr,2);
					if (strlen(tmpstr) == 0)
						continue;
					else
					{
						sscanf(tmpstr,"%d",&y);
						if (y<0 || y > names)
							continue;
						x = 0;
						while (x != y)
							if (acllist[x] != NULL)
								x++;
						// mark it empty
						acllist[x] = NULL;
						changed++;
					}
					break;
				case 'c':           // change
					sstrcrl("RETURNTOEXIT");
					cr();
					sstrl("CHANGEWHICH");
					gstr(tmpstr,2);
					if (strlen(tmpstr) == 0)
						continue;
					else
					{
						sscanf(tmpstr,"%d",&y);
						if (y<0 || y > names)
							continue;
						x = 0;
						while (x != y)
							if (acllist[x] != NULL)
								x++;
						sstrl("CHANGETO");
						gstr(tmpstr,MAX_LOGIN_LENGTH-1);
						if (strlen(tmpstr) == 0)
							continue;
						acllist[x] = (char *) alloca(strlen(tmpstr + 1));
						if (acllist[x] == NULL)
						{
							ap_log("edit_club_list: out of memory!");
							return(1);;
						}
						strcpy(acllist[x],tmpstr);
						changed++;
					}
					break;
				}
			}
			// build replacement line
			tmpstr[0] = 0;
			for (x=0; x<MAX_ACL_LIST; x++)
				if (acllist[x] != NULL)
				{	// this should really check the length of the string.
					strcat(tmpstr,acllist[x]);
					strcat(tmpstr,",");
				}
			tmpstr[strlen(tmpstr)-1] = 0;	// chop last comma
			strcpy(newline,tmpstr);	// copy back
		}
		if (changed)
		{	// save it back into the file
			if (changed)
			{	// ask
				sstrl("SAVECHANGES");
				if (!yesno())
					return(0);
			}
			// now update the club line
			sprintf(tmpstr,"%s/admin/clubfile",getenv("BBSDIR"));
			if (infile = fopen(tmpstr,"r"), infile == NULL)
			{
				ap_log("Unable to open clubfile for read.");
				return(0);
			}
			strcat(tmpstr,".new");
			if (outfile = fopen(tmpstr,"w"), infile == NULL)
			{
				ap_log("Unable to open clubfile.new for write.");
				return(0);
			}
			while (!feof(infile))
			{
				ptr = tmpstr;
				while (c = fgetc(infile), c != '\n' && c != '\r' && !feof(infile))
					*ptr++ = c;
				*ptr = 0;               // add null
				if (feof(infile)) 
					continue;
				if (tmpstr[0] == '#')
				{
					fprintf(outfile,"%s\n",tmpstr);
					continue;
				}
				if (sscanf(tmpstr,"%d %s",&club_id,msg) != 2)
					fprintf(outfile,"%s\n",tmpstr);	// dump out weirdness
				else if (club_id == club)	// here?
					if (pw)
						fprintf(outfile,"%d \"%s\"\n",club,newline);
					else
						fprintf(outfile,"%d %s\n",club,newline);
				else	// club, but wrong one
					fprintf(outfile,"%s\n",tmpstr);
			}
			fclose(infile);
			// now rename the files to allow changes to take effect
			sprintf(tmpstr,"%s/admin/clubfile",getenv("BBSDIR"));
			sprintf(msg,"%s/admin/clubfile.old",getenv("BBSDIR"));
			if (rename(tmpstr,msg))
			{
				ap_log("change_club_password: unable to rename clubfile to clubfile.old");
				ap_log("change_club_password: check permissions");
				return(0);
			}
			sprintf(tmpstr,"%s/admin/clubfile.new",getenv("BBSDIR"));
			sprintf(msg,"%s/admin/clubfile",getenv("BBSDIR"));
			if (rename(tmpstr,msg))
			{
				ap_log("change_club_password: unable to rename clubfile.new to clubfile");
				ap_log("change_club_password: check permissions");
				return(0);
			}
		}
	}
	waitcr();
	return(0);
}


// Method: check_private
// Purpose:    check a message for a private chat request
// Input:  msg - the message to check
// Output: 0 if chat not found (and message is normal)
//     1 if chat found and entered
//     2 if chat found and not entered
// Author: Gregory Shaw
// Created:    4/22/95

int Chat::check_private(char *msg, int in_curses)
{
	char login[MAX_LOGIN_LENGTH];
	char tmpstr[255];
	int chat;

	chat = 0;
	if (strstr(msg,SPECIAL_BROADCAST) != NULL)
	{
		ap_log(msg);
		if (strstr(msg,"SysOp") != NULL)	// from sysop?
		{
			sscanf(msg,"%*s %*s %s",login);

			login[strlen(login)-1] = 0;
			ap_log(login);
			chat++;	// then jump right in!
		}
		else
		{
			clear_scr();
			sscanf(msg,"%*s %s",login);
					// chop off ':'
			login[strlen(login)-1] = 0;
			sprintf(tmpstr,gstrl("REQUESTPCHAT"),login);
			sstrcr(tmpstr);
			sstrl("WOULDCHAT");
			if (yesno())
				chat++;
			cr();
		}
		if (chat)
		{
			if (in_curses)
			{
				disconnect();   // exit chat
				endwin();       // get out of old curses for new
			}
			broadcast_direct(login,PRIVATE_YES);
                                // chat away
			private_room_connect(login);
			return(1);
		}
		else
		{                       // no
			broadcast_direct(login,PRIVATE_NO);
			return(2);
		}
	}
	return(0);
}


// Method: chatting
// Purpose:    invoke the user interface for the chat subsystem
// Input:  none
// Output: n/a
// Author: Gregory Shaw
// Created:    4/12/95

int Chat::chatting(void)
{
	char *tmp;
	char *bcastmsg;             // broadcast
	char c;
	char tmpstr[MAX_CHAT_MSG];  // buffer
	char instr[MAX_CHAT_MSG];   // buffer
	char fromname[100];	    // name of person sending message
	int  x,t;
	struct timeval waittime;
                                // buffer
	char chatmsg[MAX_CHAT_MSG+MAX_ALIAS_LENGTH+3];
	time_t now;                 // timeouts
	WINDOW *others, *typing;    // two windows

	waittime.tv_usec = 250;     // 100msec
	waittime.tv_sec = 0;

	initscr();
	cbreak();                   // set cbreak (no wait) mode

                                // create new window
	if (others = newwin(LINES-2, COLS, 1, 0), others == NULL)
	{
		exit(0);
	}
                                // create new window
	if (typing = newwin(1, COLS, LINES-1, 0 ), typing == NULL)
	{
		exit(0);
	}
	// Ok.  We've got windows.  Now set their modes
	scrollok(others,1);         // turn scrolling on
	erase();                    // erase all windows
	clear();                    // clear the screen
	nonl();                     // no cr+lf (and no cr to nl translation)
	noecho();		    // no echo
	wtimeout(typing,250);       // turn on timeout for getch (350ms)
	wsetscrreg(others,1,LINES-1);
	move(0,0);                  // go to top
	sprintf(tmpstr,"Chat-v%s for rocat         Press ESC to Exit ",VERSION);
	addstr(tmpstr);
	refresh();
	waddch(typing,'>');         // add pointer
	wmove(others,LINES-3,0);    // to to border
	whline(others,'-',COLS);    // add splitter line
	wrefresh(others);           // refresh main window
	wrefresh(typing);           // refresh typing window
	tmp = tmpstr;
	// *$#@(%@(#* curses.  Leave them signals ALONE
	signal(SIGINT,SIG_IGN);     // ignore interrupt
	time(&now);                 // get starting time
	sprintf(tmpstr,gstrl("HASARRIVED"),user.user_alias());
	chatipc.send(tmpstr);       // send to server
                                // exit on ESC
	while (c = wgetch(typing), c != ESC)
	{
		// check input
		switch (c)
		{
			case ERR:
			{
				break;          // no input
			}
			case '\n':
			case '\r':
			{
                                // skip empty string
				if (tmp != tmpstr)
				{
					
					*tmp = 0;   // add null
					sprintf(chatmsg,"%s: %s",user.user_alias(),tmpstr);
                                // send to server
					if (chatipc.send(chatmsg) < 0)
					{           // socket closed
						c = ESC;
                                // get out immediately
						continue;
					}
					wclear(typing);
					waddch(typing,'>');
                                // get back to typing
					wmove(typing,0,1);
                                // refresh cursor
					wrefresh(typing);
					tmp = tmpstr;
				}
				break;
			}
			case 0x15:	    // kill (ctrl-u)
				wclear(typing);
				waddch(typing,'>');
			// get back to typing
				wmove(typing,0,1);
			// refresh cursor
				wrefresh(typing);
				tmp = tmpstr;
				break;
			case 0x08:          // backspace
			case 0x7f:          // delete 
			{
                                // anything?
				if (tmp > tmpstr)
				{
					wmove(typing,0,(tmp-tmpstr));
					tmp--;      // delete previous char
					wdelch(typing);
				}
                                // refresh cursor
				wrefresh(typing);
				break;
			}
			default:            // normal input
			{
				time(&now);
                                // don't overrun
				if (tmp - tmpstr < MAX_CHAT_MSG-1)
				{
					*tmp++ = c;
					waddch(typing,c);
					wrefresh(typing);
				}
				break;
			}
		}
		// check for messages from daemon
                                // something for me?
		if (chatipc.msg_avail(0))
		{
			switch(chatipc.receive(instr))
			{
				case -1:        // socket closed
					c = ESC;    // get out
					break;
				case 0:         // nuttin, honey.
					break;
				default:        // input!
					// get name of sender
					if (sscanf(instr,"%s:%*s",fromname) == 1)
					{
						// chop trailing colon
						fromname[strlen(fromname)-1] = 0;
						// check against kill list
						t = 0;
						while (killusers[t] != NULL && strcmp(fromname,killusers[t]))
							t++;
						// got a hit?
						if (killusers[t] != NULL)
							continue;
					}
					sprintf(chatmsg,"%s\n\r",instr);
	                                // add to window line
					waddstr(others,chatmsg);
	                                // to to border
					wmove(others,LINES-3,0);
	                                // add splitter
					whline(others,'-',COLS);

					wrefresh(others);
					wrefresh(typing);
					break;
			}
		}
		// check for inactivity timeout
		if (time(NULL) - now > inactivity_timeout()*60)
			c = ESC;
		// check for broadcasts
		if (bcastmsg = check_broadcast(), bcastmsg != NULL)
		{
			clear_scr();        // this might not work
			if (x = check_private(bcastmsg,1), x == 0)
			{
				sstrcrl("MESSAGERECEIVED");
				cr();
				sstrcr(bcastmsg);
				cr();
				cr();
				waitcr();
				touchwin(others);
				touchwin(typing);
				wrefresh(others);
				tmp = tmpstr;
				wclear(typing);
				waddch(typing,'>');
                                // refresh cursor
				wrefresh(typing);
			}
			else if (x == 1)
			{                   // private chat trashed windows -- get out!
				return(0);
			}
		}
		select(0,NULL,NULL,NULL,&waittime);
	}
	endwin();                   // go back to normal
	disconnect();               // close chat
	return(0);
}


// Method: check_broadcast
// Purpose:    poll the broadcast server for any broadcast messages
// Input:  none
// Output: if a message is available, it will be returned
// Author: Gregory Shaw
// Created:    4/10/95

char *Chat::check_broadcast(void)
{
	static time_t  last_check = 0;
	static char tmpstr[255];

	// check only every 5 seconds, max
                                // check time expired?
	if (time(NULL) - last_check > CHECK_BROADCAST_TIME)
	{                           // check
		time(&last_check);
		if (broadipc.open_sock(chathost(),CHAT_BROADCAST_PORT) == 0)
		{
                                // send 'me'
			broadipc.send(user.logname());
                                // send poll
			broadipc.send(BROADCAST_POLL);
                                // got anything for me?
			if (broadipc.receive(tmpstr) > 0 && strcmp(tmpstr,NO_BROADCAST))
			{                   // yup.  send it back
				broadipc.close_sock(0);
				return(tmpstr);
			}
			// nope.  exit.
			broadipc.close_sock(0);
		}
	}
	return(NULL);
}


// Method: club_connect
// Purpose:    connect to a public club room, if possible
// Input:  none
// Output: connection, if password (or ACL) correct
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::club_connect(int club)
{
	char tmpstr[255];
	char *ptr;
	char msg[9*MAX_ACL_LIST];	// 9 chars per login

	if (ptr = club_line(club), ptr == NULL)
	{
		// not in the file.  Let everybody in.
		room_connect(club);
	}
	else
	{
		strcpy(msg,ptr);
                                // password?
		if (strchr(msg,'"') != NULL)
		{
			while (ptr = strchr(msg,'"'), ptr != NULL)
				*ptr = ' ';
                                // remove spaces
			sscanf(msg,"%s",tmpstr);
			sstrl("PLEASEENTERPW");
			gstr_starsecho(msg,24);
			if (strcmp(msg,tmpstr) == 0)
			{
				sstrcrl("PWACCEPTED");
				room_connect(club);
			}
			else
			{
				sstrcrl("INVALIDPW");
				cr();
				waitcr();
			}
		}
		else                    // acl list
		{	// this really isn't very complete
			if (ptr = strstr(msg,user.user_alias()), ptr != NULL)
			{
				room_connect(club);
			}
			else
			{
				sstrcrl("NOACCESS");
				cr();
				waitcr();
			}
		}
	}
	return(0);
}


// Method: club_line
// Purpose:    connect to a public club room, if possible
// Input:  club - the club to look for
// Output: club access line from clublist file
// Author: Gregory Shaw
// Created:    4/10/95

char *Chat::club_line(int club)
{
	FILE *clubfile;
	char tmpstr[255];
	char *ptr;
	char c;
	int club_id;
	static char msg[255];

	sprintf(tmpstr,"%s/admin/clubfile",getenv("BBSDIR"));
	if (clubfile = fopen(tmpstr,"r"), clubfile == NULL)
	{
		ap_log("Unable to open clubfile for read.");
		return(0);
	}
	club_id = -1;
	while (club_id != club && !feof(clubfile))
	{
		ptr = tmpstr;
		while (c = fgetc(clubfile), c != '\n' && c != '\r' && !feof(clubfile))
			*ptr++ = c;
		*ptr = 0;               // add null
		if (feof(clubfile) || tmpstr[0] == '#')
			continue;
		if (sscanf(tmpstr,"%d %s",&club_id,msg) != 2)
			club_id = -1;
	}
	fclose(clubfile);
	if (club_id != club)
	{
		return(NULL);
	}
	return(msg);
}


// Method: disconnect
// Purpose:    disconnect from the chat server
// Input:  none
// Output: none
// Author: Gregory Shaw
// Created:    4/10/95

void Chat::disconnect(void)
{
	chatipc.close_sock(0);      // close socket
}


// Method: dynamic_connect
// Purpose:    connect to a dynamically allocated port
//     (created by someone else)
// Input:  none
// Output: none
// Author: Gregory Shaw
// Created:    4/10/95

void Chat::dynamic_connect(void)
{
	char port[25];
	int    club;
	int    done;


	clear_scr();
	done = 0;
	while (!done)
	{
		sstrcrl("RETURNTOEXIT");
		cr();
		sstrl("ENTERCLUBNUMBER");
		gstr(port,24);
		if (strlen(port) == 0)  // hit return
			return;
		if (sscanf(port,"%d",&club) == 1)
			done++;
	}
	// now check against club file
	if (club_line(club) != NULL)
	{                           // check security
		sstrcrl("PRIVATECLUBENTER");
	}
	room_connect(club);
}


// Method: edit_kill
// Purpose:    edit the chat kill file
// Input:  (kill userids or aliases)
// Output: a new kill file is saved
// Author: Gregory Shaw
// Created:    4/10/95

void Chat::edit_kill(void)
{
	int x,y;
	int tot = 0;
	int done = 0;
	int changes = 0;
	char tmpstr[255];
	char c;

	while (!done)
	{
		clear_scr();
		sstrcrl("NOTENOCHECK");
		cr();
		cr();
		sprintf(tmpstr,gstrl("USERSIGNORED"),MAX_CHAT_KILL_USERS);
		sstrcr(tmpstr);
		cr();
		tot = 0;
		for (x=0; x< MAX_CHAT_KILL_USERS; x++)
		{
			if (killusers[x] != NULL)
			{
				sprintf(tmpstr,"%2d %s",tot++,killusers[x]);
				sstrcr(tmpstr);
			}
		}
		if (!tot)
			sstrcrl("NONE");
		cr();
		sstrl("ADCQUIT");
		while (c = tolower(gch(1)), c != 'a' && c != 'd' && c !=
			'c' && c != 'q' && c != '\r');
		cr();
		cr();
		switch (c)
		{
			case '\r':
			case 'q':
				if (changes)
				{
					sstrl("SAVECHANGES");
					if (yesno())
						save_kill_file();
				}
				done++;
				break;
			case 'a':           // add
				clear_scr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("LOGINIGNORE");
				gstr(tmpstr,MAX_LOGIN_LENGTH-1);
				if (strlen(tmpstr) == 0)
					continue;
				else
				{
					x =0;
					while (killusers[x] != NULL && x < MAX_CHAT_KILL_USERS)
						x++;
					if (x == MAX_CHAT_KILL_USERS)
					{
						sprintf(tmpstr,gstrl("TOOMANYUSERS"),MAX_CHAT_KILL_USERS);
						waitcr();
						continue;
					}
					// got one.  save it.
					killusers[x] = (char *) malloc(strlen(tmpstr + 1));
					if (killusers[x] == NULL)
					{
						ap_log("edit_chat_kill: out of memory!");
						return;
					}
					strcpy(killusers[x],tmpstr);
					changes++;
				}
				break;
			case 'd':           // delete
				clear_scr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("DELETEWHICH");
				gstr(tmpstr,2);
				if (strlen(tmpstr) == 0)
					continue;
				else
				{
					sscanf(tmpstr,"%d",&y);
					if (y<0 || y > tot)
						continue;
					x = 0;
					while (x != y)
						if (killusers[x] != NULL)
							x++;
	                                // nuke it
					free(killusers[x]);
	                                // mark it empty
					killusers[x] = NULL;
					changes++;
				}
				break;
			case 'c':           // change
				clear_scr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGEWHICH");
				gstr(tmpstr,2);
				if (strlen(tmpstr) == 0)
					continue;
				else
				{
					sscanf(tmpstr,"%d",&y);
					if (y<0 || y > tot)
						continue;
					x = 0;
					while (x != y)
						if (killusers[x] != NULL)
							x++;
					sstrl("CHANGETO");
					gstr(tmpstr,MAX_LOGIN_LENGTH-1);
					if (strlen(tmpstr) == 0)
						continue;
					realloc(killusers[x],strlen(tmpstr)+1);
					if (killusers[x] == NULL)
					{
						ap_log("edit_chat_kill: out of memory!");
						return;
					}
					strcpy(killusers[x],tmpstr);
					changes++;
				}
				break;
		}
	}
	read_kill_file();	// re-read file to make sure things OK
}


// Method: private_connect
// Purpose:    attempt to connect to someone for private chat
// Input:  name - the person to connect to or NULL to prompt
//		sysop - is this a sysop-iniated (break-in) chat call?
// Output: returns 0 on success, non-zero for failure
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::private_connect(char *name, int sysop)
{
	char target[MAX_LOGIN_LENGTH];
	char tmpstr[255];
	char *broadret;
	char c;
	time_t then;
	time_t now;
	int  sent;
	int  connected;
	int  timeout;
	struct timeval waittime;



	clear_scr();
	waittime.tv_usec = 950;     // 500msec
	waittime.tv_sec = 0;
	if (name == NULL)
	{
		sstrcrl("RETURNTOEXIT");
		cr();
		sstrl("WHATISCHAT");
		gstr(target,MAX_LOGIN_LENGTH-1);
		if (strlen(target) == 0)
		{
			waitcr();
			return(0);
		}
	}
	else
		strcpy(target,name);    // we know who we want to talk to
	// format special message
	if (sysop)
		sprintf(tmpstr,gstrl("FROMSYSOPTEMPL"),user.logname(),SPECIAL_BROADCAST);
	else
		sprintf(tmpstr,gstrl("FROMTEMPL"),user.logname(),SPECIAL_BROADCAST);
	broadcast_direct(target, tmpstr);
	// now wait for a return message (or timeout)
	sstrcrl("RETURNTOEXIT");
	cr();
	cr();
	sstrcrl("ATTEMPTCONNECT");
	time(&then);
	connected = timeout = 0;
	sent = 0;
	while (!timeout && !connected)
	{
		if (c = gch(1), c == '\n')
		{
			timeout++;
			continue;
		}
		if (broadret = check_broadcast(), broadret != NULL)
		{
			if (!strcmp(broadret,PRIVATE_YES))
			{                   // chat away!
				private_room_connect(target);
				connected++;
			}
			else
			{
				sprintf(tmpstr,gstrl("SORRYNOCHAT"),target);
				sstrcr(tmpstr);
				connected++;
			}
		}
                                // 5 seconds?
		time(&now);
		if (!((now - then)%5) && !connected)
		{
			if (!sent)
			{
				sstrcrl("WAITING");
				sent++;
			}
		}
		else	
			sent = 0;

		if (now - then >= PRIVATE_TIMEOUT && !connected)
		{
			sprintf(tmpstr,gstrl("NOCHATRESPONSE"),target);
			sstrcr(tmpstr);
			sstrl("USERBUSY");
			if (!yesno())
			{
				timeout++;      // timeout
				time(&then);
				cr();
				sstrcrl("PLEASEEMAIL");
			}
			else	// try another loop
				time(&then);

		}
		if (!connected)
		{
                                // delay
			select(0,NULL,NULL,NULL,&waittime);
		}
	}
	waitcr();
	if (timeout)
	{                           // timeout -- no connection made
		return(1);
	}
	return(0);
}


// Method: private_room_connect
// Purpose:    attempt to connect to someone for private chat
// Input:  name - the name of the other person to talk to
// Output: none
// Author: Gregory Shaw
// Created:    4/22/95

int Chat::private_room_connect(char *name)
{
	int    new_socket;
	char tmpstr[255];

	// open socket to server
	if (chatipc.open_sock(chathost(),CHAT_PRIVATE_MASTER_PORT) == 0)
	{
		sprintf(tmpstr,"%s %s",user.logname(),name);
		chatipc.send(tmpstr);   // send information
		if (chatipc.receive(tmpstr) < 0)// get new socket number
		{
			ap_log("private_room_connect: disconnect!");
			return(0);
		}
		if (sscanf(tmpstr,"%d",&new_socket) != 1)
		{
			ap_log("private_room_connect: Unable to decipher socket send from server.");
			return(0);

		}
		chatipc.close_sock(0);  // close master socket
		if (chatipc.open_sock(chathost(),new_socket) == 0)
		{
			chatting();         // chat away!
		}
		else
		{
			sprintf(tmpstr,"private_room_connect: Unable to open secondary socket %d",new_socket);
			ap_log(tmpstr);
		}
	}
	else
	{

		ap_log("private_room_connect: Unable to connect to the master client socket");
	}
	return(0);
}


// Method: read_kill_file
// Purpose:    read the user's kill file
// Input:  none
// Output: the kill list is updated
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::read_kill_file(void)
{
	FILE *killfile;
	char tmpstr[255];
	int  x;

	// check for a re-read.  free storage if allocated
	for (x=0; x< MAX_CHAT_KILL_USERS; x++)
		if (killusers[x] != NULL)
		{
			free(killusers[x]);
			killusers[x] = NULL;
		}
	sprintf(tmpstr,"%s/%s",getenv("HOME"),".bbskill");
	if (killfile = fopen(tmpstr,"r"), killfile != NULL)
	{
		x = 0;
		while (!feof(killfile))
		{
			if (fscanf(killfile,"%s",tmpstr) == 1)
			{
				if (x >= MAX_CHAT_KILL_USERS)
				{
					sprintf(tmpstr,"%s has too many users in char kill file",getenv("LOGNAME"));
					ap_log(tmpstr);
				}
				else
				{
					killusers[x] = (char *)malloc(strlen(tmpstr+1));
					if (killusers[x] != NULL)
					{
						strcpy(killusers[x++],tmpstr);
					}
					else
						ap_log("chat: read_kill_file - out of memory!");
				}

			}

		}
	}
	fclose(killfile);
	return(0);
}

// Method: room_connect
// Purpose:    connect to a room
// Input:  room - the room number to connect to
// Output: none
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::room_connect(int room)
{
	int    new_socket;
	char tmpstr[255];

	// open socket to server
	if (chatipc.open_sock(chathost(),CHAT_MASTER_PORT) == 0)
	{
		sprintf(tmpstr,"%d %s %s",room,user.user_alias(),user.logname());
		chatipc.send(tmpstr);   // send information
		// get new socket number
		if (chatipc.receive(tmpstr) < 0)
		{
			ap_log("room_connect: disconnect!");
			return(0);
		}
		if (sscanf(tmpstr,"%d",&new_socket) != 1)
		{
			ap_log("room_connect: Unable to decipher socket send from server.");
			return(0);

		}
		chatipc.close_sock(0);  // close master socket
		if (chatipc.open_sock(chathost(),new_socket) == 0)
		{
			chatting();         // chat away!
		}
		else
		{
			sprintf(tmpstr,"room_connect: Unable to open secondary socket %d",new_socket);
			ap_log(tmpstr);
		}
	}
	else
	{

		ap_log("room_connect: Unable to connect to the master client socket");
	}
	return(0);
}


// Method: room_create
// Purpose:    create a (dynamic) room
// Input:  the room is prompted for, and the user is connected to the new room
// Output: none
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::room_create(void)
{
	char tmp[25];
	int  room;

	clear_scr();
	sstrcrl("RETURNTOEXIT");
	cr();
	sstrl("CREATEROOM");
	gstr(tmp,24);
	if (strlen(tmp) == 0)       // hit return
		return(0);
	if (sscanf(tmp,"%d",&room) != 1)
		return(0);
	room_connect(room);
	return(0);
}


// Method: room_info
// Purpose:    return information about all rooms
// Input:  none
// Output: none
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::room_info(void)
{

	char tmpstr[255];
	char msg[255];
	char *ptr;
	int room, users;            // room number and users in room
	int x;
	int used;                   // any rooms in use?

	if (chatipc.open_sock(chathost(),CHAT_INFO_MASTER_PORT) == 0)
	{
		used = 0;
		strcpy(tmpstr,"-1");
		chatipc.send(tmpstr);   // send request
		if (chatipc.receive(tmpstr) < 0)// receive data
		{
			ap_log("room_info: disconnect from server");
			return(0);
		}
		chatipc.close_sock(0);  // close
		// format data
		ptr = tmpstr;
		sstrcrl("ROOMUSAGE");
		while (sscanf(ptr,"%d:%d",&room, &users) == 2)
		{
			used++;
			x = 0;
			while (roomnames[x].room != room && x < room_names)
				x++;
			if (x < room_names)
			{
				if (users == 1)
					sprintf(msg,gstrl("ROOM1USER"),roomnames[x].name);
				else
					sprintf(msg,gstrl("ROOMANYUSER"),roomnames[x].name, users);
				sstrcr(msg);
			}
			else
			{
				if (users == 1)
					sprintf(msg,gstrl("ROOMNUMUSER"),room);
				else
					sprintf(msg,gstrl("ROOMNUMUSERS"),room, users);
				sstrcr(msg);
			}
			// skip processed item
			while (!isspace(*ptr++) && *ptr != 0);
		}
		if (!used)
		{
			cr();
			sstrcrl("NOROOMSINUSE");
		}
	}
	else
	{
		sstrcrl("UNABLESERVER");
	}
	cr();
	waitcr();
	return(0);

	return(0);
}


// Method: room_users
// Purpose:    print a list of the users in a particular room
// Input:  room - the room to get information about
// Output: none
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::room_users(int room)
{
	char tmpstr[255];
	int x;

	if (chatipc.open_sock(chathost(),CHAT_INFO_MASTER_PORT) == 0)
	{
		x = 0;
		while (roomnames[x].room != room && x < room_names)
			x++;
		if (x < room_names)
			sprintf(tmpstr,gstrl("ROOMNAME"),roomnames[x].name);
		else
			sprintf(tmpstr,gstrl("ROOMNUM"),room);
		sstrcr(tmpstr);
		sprintf(tmpstr,"%d",room);
		chatipc.send(tmpstr);
		while (chatipc.receive(tmpstr) >= 0)
		{
			if (!strcmp(tmpstr,NO_INFO))
				sstrcrl("ROOMNOTINUSE");
			else                // data
				sstr(tmpstr);
		}
		chatipc.close_sock(0);
	}
	else
	{
		sstrcrl("UNABLESERVER");
	}
	cr();
	waitcr();
	return(0);
}


// Method: save_kill_file
// Purpose:    save the user's kill file
// Input:  none
// Output: the kill file is updated
// Author: Gregory Shaw
// Created:    4/10/95

int Chat::save_kill_file(void)
{
	FILE *killfile;
	char tmpstr[255];
	int  x;

	sprintf(tmpstr,"%s/%s",getenv("HOME"),".bbskill");
	if (killfile = fopen(tmpstr,"w"), killfile != NULL)
	{
		for (x=0; x< MAX_CHAT_KILL_USERS; x++)
			if (killusers[x] != NULL)
				fprintf(killfile,"%s\n",killusers[x]);
	}
	else
	{
		sprintf(tmpstr,"Unable to open .bbskill for %s",getenv("LOGNAME"));
		ap_log(tmpstr);
	}
	fclose(killfile);
	return(0);
}


#endif                          // _CHAT_C_


