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