br@uunet.UU.NET@laura.irb.informatik.uni-dortmund.de.UUCP (Bodo Rueskamp) (10/05/87)
[I may have to declare a moratorium on chat programs, the way r$ had to on
editors. ++bsa]
I wrote a chat program several weeks ago. This chat
program uses sockets to communicate, not named pipes.
I think that this program is useful for others, do
i want you to post it into comp.sources.unix.
[this was forwarded from r$, I take it the answer was no. ++bsa]
Bodo Rueskamp
br@exunido.uucp
...!seismo!exunido!br
- -------- cut here ---------------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# README
# chatd.c
# chat.c
# This archive created: Tue Sep 1 18:45:50 1987
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
Chat - A Multi-User Communications System for BSD 4.2
written by Bodo Rueskamp (br@exunido.uucp)
Installation:
- create an entry for "chat/tcp" in /etc/services
- change HOSTNAME in chat.c to chatd's host
Server Commands:
/bye signoff from chat
/channel <ch #> change channel
/invite <nick> invite <nick> to your channel
/list list active channels
/help print help
/signon <nick> <ch #> signon to chat
Client Commands (line edit):
^D exit from chat
CR/LF send line to chat server
^X erase line
BS/DEL erase char
SHAR_EOF
fi
if test -f 'chatd.c'
then
echo shar: "will not over-write existing file 'chatd.c'"
else
cat << \SHAR_EOF > 'chatd.c'
/*
** Chat Server
**
** written by Bodo Rueskamp (...!seismo!exunido!br)
*/
#define Welcome "* Welcome to CHAT v1.01 11-Aug-87\n"
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <strings.h>
#include <time.h>
int sock; /* path number of chatd socket */
int rmask; /* bit masks of active paths */
char *argerr = "* Invalid arguments.\n";
char *invcmd = "* Invalid command, try /help.\n";
char *notsig = "* You are not signed on.\n";
char *alrsig = "* You are already signed on.\n";
char *invchn = "* Invalid channel number.\n";
char *nonick = "* No nickname.\n";
char *nfound = "* Nickname not found.\n";
struct user_rec {
char flag; /* 0 = unused */
/* 1 = waiting for name */
/* 2 = waiting for host */
/* 3 = signed off */
/* 4 = signed on */
int channel; /* channel number */
char name[9]; /* username */
char host[16]; /* hostname */
char nick[11]; /* nickname */
int path; /* socket path number */
char buf[257]; /* input buffer */
} usr[32];
main() /* setup & main loop */
{
struct sockaddr_in saddr;
struct servent *sp;
int i, rfd, wfd = 0, xfd = 0;
/* prepare socket */
bzero((char *) &saddr, sizeof(saddr));
if ((sp = getservbyname("chat", "tcp")) == NULL) {
fprintf(stderr, "chatd: chat/tcp unknown service\n");
exit(1);
}
saddr.sin_family = AF_INET;
saddr.sin_port = sp->s_port;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "chatd: socket() failed, errno = %d\n", errno);
exit(1);
}
if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
fprintf(stderr, "chatd: bind() failed, errno = %d\n", errno);
exit(1);
}
(void) listen(sock, 10);
for (i=0; i<32; ++i)
usr[i].flag == 0; /* no user connected */
rmask = 1 << sock; /* listen only to chatd socket */
for (;;) {
rfd = rmask;
(void) select(32, &rfd, &wfd, &xfd, (struct timeval *) 0); /* wait for action */
if ((rfd >> sock) & 1) /* got connection */
NewUser(); /* handle new user */
for (i=0; i<32; ++i) /* scan each users path */
if (usr[i].flag && ((rfd >> usr[i].path) & 1))
ServeUser(i);
}
}
NewUser() /* handle new user */
{
char *MaxMsg = "* User limit reached\n";
int path; /* path to new user */
int i;
/* accept connection */
if ((path = accept(sock, (struct sockaddr *) 0, (int *) 0)) == -1) {
fprintf(stderr, "chatd: accept() failed, errno = %d\n", errno);
exit(1);
}
/* send welcome, exit if welcome failed */
if (write(path, Welcome, sizeof(Welcome)-1) != sizeof(Welcome)-1) {
(void) close(path);
return;
}
for (i=0; i<32; ++i) /* search for free entry */
if (usr[i].flag == 0)
break;
if ((i == 32) || (path > 31)) {
(void) write(path, MaxMsg, sizeof(MaxMsg)-1); /* send "sorry" */
(void) close(path);
return;
}
/* prepare entry for new user */
usr[i].flag = 1;
usr[i].path = path;
usr[i].name[0] = '\0';
usr[i].host[0] = '\0';
usr[i].nick[0] = '\0';
rmask |= 1 << path; /* listen to this path too */
return;
}
LostUser(i) /* lost connection to user */
int i; /* number of user */
{
char buf[256];
(void) close(usr[i].path); /* close path */
if (usr[i].flag == 4) {
(void) sprintf(buf, "|signoff| %s@%s (%s)\n",
usr[i].name, usr[i].host, usr[i].nick);
BroadCast(usr[i].channel, buf, i);
}
usr[i].flag = 0; /* free entry */
rmask &= ~(1 << usr[i].path); /* don't listen to close path */
}
ServeUser(i) /* get & analyze line from user */
int i; /* number of user */
{
char buf[256], bf[256];
if (GetLine(usr[i].path, buf) == 0) { /* get line */
LostUser(i);
} else switch(usr[i].flag) {
case 1:
if ((strlen(buf) > 10) ||
(sscanf(buf, "u=%s", usr[i].name) != 1)) {
LostUser(i);
} else {
usr[i].flag = 2;
}
break;
case 2:
if ((strlen(buf) > 17) ||
(sscanf(buf, "h=%s", usr[i].host) != 1)) {
LostUser(i);
} else {
usr[i].flag = 3;
}
break;
case 3:
if (buf[0] == '/') {
Command(i, buf+1);
} else {
ToUser(i, notsig);
}
break;
case 4:
if (buf[0] == '/') {
Command(i, buf+1);
} else {
(void) sprintf(bf, "<%s> %s", usr[i].nick, buf);
BroadCast(usr[i].channel, bf, i);
}
break;
}
}
GetLine(path, buf) /* get line */
int path; /* path number */
char *buf; /* pointer to buffer */
{
register int i;
char c;
i = 0;
while (i < 79) {
if (read(path, &c, 1) != 1)
return(0);
buf[i++] = c;
if (c == '\n')
break;
}
buf[i] = '\0';
return(1);
}
BroadCast(channel, msg, user) /* broadcast message to users on channel */
int channel; /* channel number */
char *msg; /* pointer to message */
int user; /* number of user, who sent the message */
{
register int i;
for (i=0; i<32; ++i)
if ((usr[i].flag == 4) &&
(i != user) &&
(usr[i].channel == channel))
ToUser(i, msg);
}
Command(num, msg) /* execute command for user */
int num; /* number of user */
char *msg; /* pointer to command from user */
{
int argc;
char *argv[5];
register char *p;
int i, count[200];
char buf[256], cnum[3];
argc = 0;
for (p=msg; *p;) {
if (argc == 5) {
ToUser(num, invcmd);
return;
}
argv[argc++] = p;
while ((*p != '\0') && (*p != '\n') && (*p != ' '))
++p;
if (*p != '\0')
*p++ = '\0';
while ((*p == '\n') || (*p == ' '))
++p;
}
if (strncmp("bye", argv[0], strlen(argv[0])) == 0) {
if (argc > 1) {
ToUser(num, argerr);
return;
}
if (usr[num].flag == 4) {
(void) sprintf(buf, "|signoff| %s@%s (%s)\n",
usr[num].name, usr[num].host, usr[num].nick);
BroadCast(usr[num].channel, buf, -1);
}
if (usr[num].flag) {
(void) close(usr[num].path);
usr[num].flag = 0;
}
return;
}
if (strncmp("channel", argv[0], strlen(argv[0])) == 0) {
if (argc != 2) {
ToUser(num, argerr);
return;
}
if ((sscanf(argv[1], "%d", &i) != 1) || (i < 0) || (i > 199)) {
ToUser(num, invchn);
return;
}
(void) sprintf(buf, "|change| %s@%s (%s) has left this channel\n",
usr[num].name, usr[num].host, usr[num].nick);
BroadCast(usr[num].channel, buf, num);
usr[num].channel = i;
(void) sprintf(buf, "* You are now on channel %d\n", i);
ToUser(num, buf);
if (usr[num].flag == 0)
return;
(void) sprintf(buf, "|change| %s@%s (%s) has joined this channel\n",
usr[num].name, usr[num].host, usr[num].nick);
BroadCast(usr[num].channel, buf, num);
return;
}
if (strncmp("help", argv[0], strlen(argv[0])) == 0) {
if (argc > 1) {
ToUser(num, argerr);
return;
}
PrintHelp(num);
return;
}
if (strncmp("invite", argv[0], strlen(argv[0])) == 0) {
if (usr[num].flag != 4) {
ToUser(num, notsig);
return;
}
if (argc != 2) {
ToUser(num, nonick);
return;
}
for (i=0; i<32; ++i)
if (strcmp(argv[1], usr[i].nick) == 0)
break;
if (i == 32) {
ToUser(num, nfound);
return;
}
(void) sprintf(buf, "* %s invites you to channel %d\n",
usr[num].nick, usr[num].channel);
ToUser(i, buf);
return;
}
if (strncmp("list", argv[0], strlen(argv[0])) == 0) {
if (argc > 1) {
ToUser(num, argerr);
return;
}
for (i=0; i<200; ++i)
count[i] = 0;
for (i=0; i<32; ++i)
if (usr[i].flag == 4)
++count[usr[i].channel];
ToUser(num, "* Ch Users\n");
if (usr[num].flag == 0)
return;
for (i=0; i<200; ++i) {
if (count[i] == 0)
continue;
if (i < 100) {
(void) sprintf(cnum, "%2d", i);
} else {
(void) strcpy(cnum, "??");
}
(void) sprintf(buf, "* %s %d\n", cnum, count[i]);
ToUser(num, buf);
if (usr[num].flag == 0)
return;
}
ToUser(num, "*\n");
return;
}
if (strncmp("names", argv[0], strlen(argv[0])) == 0) {
if (argc > 1) {
ToUser(num, argerr);
return;
}
ToUser(num, "*\n* Ch Nickname UserID@Node\n");
if (usr[num].flag == 0)
return;
for (i=0; i<32; ++i) {
if (usr[i].flag != 4)
continue;
if (usr[i].channel < 100) {
(void) sprintf(cnum, "%2d", usr[i].channel);
} else {
(void) strcpy(cnum, "??");
}
(void) sprintf(buf, "* %s %-10s %s@%s\n",
cnum, usr[i].nick, usr[i].name, usr[i].host);
ToUser(num, buf);
if (usr[num].flag == 0)
return;
}
ToUser(num, "*\n");
return;
}
if (strncmp("signon", argv[0], strlen(argv[0])) == 0) {
if (usr[num].flag != 3) {
ToUser(num, alrsig);
return;
}
if (argc > 1) {
if (strlen(argv[1]) > 10)
argv[1][10] = '\0';
(void) strcpy(usr[num].nick, argv[1]);
} else {
(void) strcpy(usr[num].nick, usr[num].name);
}
if (argc > 2) {
if (sscanf(argv[2], "%d", &usr[num].channel) != 1) {
ToUser(num, invchn);
return;
}
} else {
usr[num].channel = 1;
}
usr[num].flag = 4;
(void) sprintf(buf, "|signon| %s@%s (%s)\n",
usr[num].name, usr[num].host, usr[num].nick);
BroadCast(usr[num].channel, buf, -1);
return;
}
ToUser(num, invcmd);
}
PrintHelp(i) /* send help to user */
int i; /* user number */
{
static char *msg[] = {
"*\n",
"* Chat Help:\n",
"* /bye signoff from chat\n",
"* /channel <channel #> change channel\n",
"* /invite <nick> invite <nick> to your channel\n",
"* /list list active channels\n",
"* /help print help\n",
"* /signon <nick> <channel #> signon to chat\n",
"*\n",
NULL};
int line;
for (line=0; usr[i].flag && msg[line]; ++line) {
ToUser(i, msg[line]);
}
}
ToUser(i, msg) /* send line to user */
int i; /* user number */
char *msg; /* pointer to line */
{
if (write(usr[i].path, msg, strlen(msg)) != strlen(msg))
LostUser(i);
}
SHAR_EOF
fi
if test -f 'chat.c'
then
echo shar: "will not over-write existing file 'chat.c'"
else
cat << \SHAR_EOF > 'chat.c'
/*
** Chat Client
**
** written by Bodo Rueskamp (...!seismo!exunido!br(
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#define HOSTNAME "laura" /* name of host, where chat daemon lives */
struct sockaddr_in sin;
struct hostent *hp;
struct servent *sp;
int s, pos, lines, idx;
char inbuf[62];
char hostname[16], buf[64];
char tc_entry[1024], PC, *UP, *BC, *CM, *CL, *CE, *tgoto();
outch(c) /* send byte to screen */
char c;
{
swrite(1, &c, 1);
}
quit() /* exit */
{
(void) system("stty echo -raw");
tputs(tgoto(CM, 0, lines-1), 1, outch);
printf("\n\007Connection closed.\n");
exit(-1);
}
swrite(path, buf, len) /* write with error checking */
int path; /* path number */
char *buf; /* pointer to string */
int len; /* length of string */
{
if (write(path, buf, len) != len)
quit();
}
wstring(str, update) /* write string to user */
char *str; /* pointer to string */
int update; /* if 1: update top line */
{
register char *p;
int flag = 0;
tputs(tgoto(CM, pos, lines-1), 1, outch); /* goto last line */
for (p=str; *p; ++p) {
swrite(1, p, 1);
++pos;
if (*p == '\n') {
flag = 1;
tputs(tgoto(CM, 0, lines-1), 1, outch);
pos = 0;
}
}
if (flag && update) {
tputs(tgoto(CM, 0, 0), 1, outch);
tputs(CE, 1, outch);
swrite(1, "-> ", 3);
inbuf[idx] = '\0';
swrite(1, inbuf, strlen(inbuf));
}
}
term()
{
char c;
int rfd, wfd = 0, xfd = 0, rmask = (1 << s) | (1 << 0);
char buf[256], *p;
char *termcap, tcbuff[256], *tcbuffp = tcbuff, *tgetstr(), *getenv();
int len, columns;
if ((termcap = getenv("TERM")) == NULL) {
fprintf(stderr, "no TERM environment variable\n");
exit(1);
}
if (tgetent(tc_entry, termcap) != 1) {
fprintf(stderr, "unable to get '%s' capabilities\n", termcap);
exit(1);
}
if ((lines = tgetnum("li")) == -1)
lines = 24;
if (lines < 5) {
fprintf(stderr, "terminal must have at least five lines\n");
exit(1);
}
if ((columns = tgetnum("co")) == -1)
columns = 80;
if (columns < 80) {
fprintf(stderr, "terminal must have at least 80 columns\n");
exit(1);
}
p = tgetstr("pc", &tcbuffp);
PC = (p != NULL) ? *p : ' ';
if ((UP = tgetstr("up", &tcbuffp)) == NULL) {
fprintf(stderr, "terminal hasn't UP capability\n");
exit(1);
}
BC = tgetstr("bc", &tcbuffp);
if ((CM = tgetstr("cm", &tcbuffp)) == NULL) {
fprintf(stderr, "terminal hasn't CM caiability\n");
exit(1);
}
if ((CL = tgetstr("cl", &tcbuffp)) == NULL) {
fprintf(stderr, "terminal hasn't CL capability\n");
exit(1);
}
if ((CE = tgetstr("ce", &tcbuffp)) == NULL) {
fprintf(stderr, "terminal hasn't CE capability\n");
exit(1);
}
(void) system("stty -echo raw");
tputs(CL, lines, outch);
swrite(1, "->", 2);
idx = 0;
pos = 0;
for (;;) {
tputs(tgoto(CM, idx+3, 0), 1, outch);
rfd = rmask;
(void) select(s+1, &rfd, &wfd, &xfd, (struct timeval *) 0);
if ((rfd >> 0) & 1) {
if (read(0, &c, 1) != 1)
quit();
switch(c) {
case 0x04:
quit();
case 0x08:
case 0x7f:
if (idx) {
--idx;
swrite(1, BC, strlen(BC));
}
break;
case 0x18:
idx = 0;
tputs(tgoto(CM, 3, 0), 1, outch);
tputs(CE, 1, outch);
break;
default:
swrite(1, &c, 1);
inbuf[idx++] = c;
if (idx < 60)
break;
case 0x0a:
case 0x0d:
inbuf[idx++] = '\n';
inbuf[idx] = '\0';
if (inbuf[0] != '\n')
swrite(s, inbuf, strlen(inbuf));
idx = 0;
wstring(inbuf, 0);
tputs(tgoto(CM, 3, 0), 1, outch);
tputs(CE, 1, outch);
break;
}
}
if ((rfd >> s) & 1) {
if ((len = read(s, buf, sizeof(buf)-1)) < 1)
quit();
buf[len] = '\0';
wstring(buf, 1);
}
}
}
main()
{
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
hp = gethostbyname(HOSTNAME);
if (hp == NULL) {
fprintf(stderr, "chat: host '%s' unknown\n", HOSTNAME);
exit(1);
}
bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
if ((sp = getservbyname("chat", "tcp")) == NULL) {
fprintf(stderr, "chat: chat/tcp unknown service\n");
exit(1);
}
sin.sin_port = sp->s_port;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "chat: can't create socket\n");
exit(1);
}
if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
fprintf(stderr, "chat: can't connect to socket\n");
exit(1);
}
(void) gethostname(hostname, sizeof(hostname));
(void) sprintf(buf, "u=%s\nh=%s\n", getlogin(), hostname);
swrite(s, buf, strlen(buf));
term();
}
SHAR_EOF
fi
exit 0
# End of shell archive