jcs@tarkus.UUCP (John C. Sucilla) (10/17/88)
Theres been quite a few people asking for multi user talk/write type programs the last few months so I figured I'd let this one out finally. Have fun... #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # makefile # partyhlp # party.c sed 's/^X//' << 'SHAR_EOF' > makefile X# Run the install phase as root X# The files target only needs to be hit once, the first time you build. X XCC=cc XCFLAGS=-O -DUSG -s -o party XLCFLAGS=-68010 XINSTALL=/local/bin/ XPFILES=/usr/bbs/ X Xparty: party.c X $(CC) $(LCFLAGS) $(CFLAGS) party.c X chmod 755 party X Xinstall: party X ln $(INSTALL)party $(INSTALL)oparty X -rm $(INSTALL)party X mv ./party $(INSTALL)party X chmod 2711 $(INSTALL)party X chgrp party $(INSTALL)party X Xfiles: X echo > $(PFILES).party X echo > $(PFILES).party1 X echo > $(PFILES).party2 X echo > $(PFILES).party3 X echo > $(PFILES).party4 X echo > $(PFILES).party.passwd X echo > $(PFILES)potd X chmod 664 $(PFILES).party* X chmod 660 $(PFILES).party4 $(PFILES).party.passwd X echo "All files created, edit $(PFILES).party.passwd to set a password" X SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > partyhlp XThings typed in here are saved in a file. Please Xuse your common sense... X XPARTY help: Xq Exit the party program X! Unix shell escape X% Party Console X i = Invite a user to party X m = Send a one line message to user X n = Change name X p = Join a different party X r = Read file into party X s = Scroll back and read again X t = Toggle this message off/on X w = Who is on the system X? This message Xh This message X(space) enter a response. SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > party.c X/* John "C". Sucilla's party hack. X * This Party program is based on the single process party X * by Jan Wolter which was a rewrite of Marcus's PD party. X * Thanks for the original version(s) Jan and Marcus! X * X * You should define either VER7 or USG to compile. X * I have no idea if it will still compile under Version 7 though... X * It works on my 3B1 (tarkus), a 3B2/300 (chinet) and an AT clown X * running Microport (igloo). X * X * This program remains in the public domain because theres lots X * of Jan/Marcus code in it. X ************************************************************************** X * Hacks by John "C". Sucilla ...ihnp4{chinet|nwutb}!jcs: X * Added "Party Console". Currently, the control center allows the user X * to: X * X * 1) Change his name (silently) in a party. A check is done to verify X * that the requested new name does not exist in the passwd file. X * Some people don't like their logname used in vain... X * X * 2) Switch from the standard party (/usr/bbs/.party) to one of 4 X * private parties (.party[1-4]). .party4 is accessed thru the X * password in /usr/bbs/.party.passwd. It is not encrypted. X * X * 3) Read file capability added. Limited to 66 lines. X * X * 01/Aug/87 - jcs X * 1) Added "who" command to console. X * X * 2) Added "invite" command to console. Echo to the invited user's X * tty if possible. If not possible, send mail. If they are not X * logged in, abort. X * X * 31/Aug/87 - jcs X * 1) Added support for the SHELL environment variable so Korn Shell X * users can use thier alias's. If SHELL is not set, it uses /bin/sh. X * 2) Added a private message function. A one line message can be sent X * to another user. X * X * 15/Feb/88 - jcs X * 1) Added the scroll back function. X * X * 03/Jul/88 - jcs X * 1) Added a "MOTD" type function. Party will now print the contents X * of /usr/bbs/potd if it exists. X */ X X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <fcntl.h> X#include <signal.h> X#include <setjmp.h> X#include <pwd.h> X#include <utmp.h> X#ifdef VER7 X#include <sgtty.h> X#endif X#ifdef USG X#include <termio.h> X#include <ctype.h> X#endif X#ifdef EWOULDBLOCK X#include <sys/file.h> /* 4.2BSD flock */ X#endif X X#ifdef TCGETA Xstruct termio cooked, cbreak; X#define EOFCHAR (cooked.c_cc[VEOF]) X#define GTTY(fd, st) ioctl(fd, TCGETA, (st)) X#define STTY(fd, st) ioctl(fd, TCSETAF, (st)) X#endif X X#ifdef TIOCGETC Xstruct tchars ctrlchars; X#define EOFCHAR (ctrlchars.t_eofc) Xstruct sgttyb cooked, cbreak; X#define GTTY(fd, st) ioctl(fd, TIOCGETP, (st)) X#define STTY(fd, st) ioctl(fd, TIOCSETN, (st)) X#endif X X#ifdef LOCK_EX X#define LOCK(file) flock(fileno(file), LOCK_EX) X#define UNLOCK(file) flock(fileno(file), LOCK_UN) X#else X#define LOCK(file) fseek(file, 0L, 0), chk_lock(file, 1, -1L) X#define UNLOCK(file) fseek(file, 0L, 0), chk_lock(file, 0, -1L) X#endif X Xchar *passfile = "/usr/bbs/.party.passwd"; Xchar *partyfile = "/usr/bbs/.party"; Xchar *basename = "/usr/bbs/."; Xchar *helpfile = "/usr/bbs/partyhlp"; Xchar *potdfile = "/usr/bbs/potd"; Xchar *name[20]; Xchar *VER = "Version 3.3.1"; Xchar id_alg; Xchar *who_cmd = "who -T"; Xchar *invitation = "echo \"Party or Die!!!\" | mail "; Xchar progname[14]; X X#define ON 0 X#define OFF 1 Xint C_FLAG = ON; X XFILE *wfd; Xint rst; Xjmp_buf jenv; Xextern int errno; X X#define BFSZ 200 Xchar inbuf[BFSZ+24]; X#define buf (inbuf+10) Xchar *cancel = " XXX\n"; X Xint alrm(), intr(); Xchar *getlogin(), *malloc(), *ctime(), *fgets(), *strchr(); Xstruct passwd *getpwnam(), *getpwuid(); Xlong lseek(), time(); X X Xmain(argc,argv) Xint argc; Xchar **argv; X{ Xregister int n, i; Xchar ch, *pnl; Xstruct passwd *pwd; Xlong now = time((long *)0); X X if (!(strcpy (name, getlogin())) || !(pwd = getpwnam(name)) X || pwd->pw_uid != getuid()) { X if (pwd = getpwuid(getuid())) X strcpy (name, pwd->pw_name); X else X strcpy (name, "(unknown)"); X } X strcpy(inbuf,name); X strcat(inbuf,": "); X X /* Get ioctl modes */ X errno = 0; X GTTY(0,&cooked); X#ifndef TCSETA X ioctl(0, TIOCGETC, &ctrlchars); X#endif X if (errno != 0) X { X printf("Input not a tty"); X exit(1); X } X cbreak = cooked; /* This works? */ X#ifdef TCSETA X cbreak.c_lflag &= ~(ECHO | ICANON); X cbreak.c_cc[VMIN] = 1; X cbreak.c_cc[VTIME] = 0; X#else /* else use TIOCGETC v7 */ X cbreak.sg_flags &= ~ECHO; X cbreak.sg_flags |= CBREAK; X#endif X X /* convert the program name to upper case */ X for (i = 0; argv[0][i] != NULL; i++) { X progname[i] = toupper (argv[0][i]); X } X progname[++i] = NULL; X X printf ("Welcome to %s -- %s Type ? for HELP, ", progname, VER); X printf ("%c for CONSOLE\n", 0x25); X X /* Print the Party message of the day if it exists */ X potd (); X X /* Put up the signal handlers */ X signal(SIGQUIT,intr); X signal(SIGINT,intr); X signal(SIGALRM,alrm); X X /* Open partyfile and put in join message */ X if ((wfd = fopen(partyfile,"a")) == NULL) X { X printf("Cannot write partyfile %s\n",partyfile); X exit(1); X } X LOCK(wfd); X fseek(wfd,0L,2); X fprintf(wfd,"---- %s joining (%.12s)\n",name,ctime(&now)+4); X fflush(wfd); X UNLOCK(wfd); X X if ((rst = open(partyfile,O_RDONLY)) < 0) X { X printf("Cannot read partyfile %s\n",partyfile); X exit(1); X } X X /* Position read pointer 300 characters from the end of the file */ X if (lseek(rst,-300L,2) < 0) X { X /* File isn't that long -- start from begining */ X lseek(rst,0L,0); X } X else X /* Skip forward to a newline */ X while (read(rst,&ch,1) > 0 && ch != '\n') X ; X X /* Get in cbreak/noecho mode */ X STTY(0,&cbreak); X X for(;;) X { X if ((n = read(rst,buf,BFSZ)) > 0) X write(1,buf,n); X else X { X alarm(4); X if (!setjmp(jenv)) X { X read(0,&ch,1); X X alarm(0); X if (ch == EOFCHAR) intr(); X STTY(0,&cooked); X switch (ch) X { X case '!': X putc('!',stderr); X if (fgets(buf,BFSZ,stdin)) X { X if ((pnl = strchr(buf,'\n')) X != NULL) X *pnl = '\0'; X usystem(buf); X fputs("!\n",stderr); X } X else X fputs(cancel,stderr); X STTY(0,&cbreak); X break; X case 'q': X intr(); X break; X case '?': X case 'h': X help(); X break; X case '%': X control(); X STTY(0, &cbreak); X break; X default: X putc ('>', stderr); X putc (' ', stderr); X fputs (ch, stderr); X if (fgets(buf,BFSZ,stdin)) X { X if (buf[0] != '\0') X { X LOCK(wfd); X fseek(wfd,0L,2); X fputs(inbuf,wfd); X fflush(wfd); X UNLOCK(wfd); X } X else X fputs(cancel,stderr); X } X else X fputs(cancel,stderr); X STTY(0,&cbreak); X break; X } X } X } X } X} X Xalrm() X{ X signal(SIGALRM,alrm); X longjmp(jenv,1); X} X Xintr() X{ Xlong now = time((long *)0); X X alarm(0); X X signal(SIGINT,SIG_IGN); X signal(SIGQUIT,SIG_IGN); X X LOCK(wfd); X fseek(wfd,0L,2); X fprintf(wfd,"---- %s leaving (%.12s)\n",name,ctime(&now)+4); X fflush(wfd); X UNLOCK(wfd); X printf("---- %s leaving (%.12s)\n",name,ctime(&now)+4); X STTY(0,&cooked); X exit(0); X} X X Xusystem(cmd) Xchar *cmd; X{ Xint cpid, wpid, status; Xint (*old_intr)(), (*old_quit)(); Xchar *myshell[20]; X X if ((cpid = fork ()) == 0) { X setuid(getuid()); X setgid(getgid()); X strcpy (myshell, getenv ("SHELL")); X if (myshell[0] == NULL) { X strcpy (myshell, "/bin/sh"); X } X execl(myshell,"sh","-c",cmd,(char *)0); X exit(-1); X } X old_intr = signal(SIGINT,SIG_IGN); X old_quit = signal(SIGQUIT,SIG_IGN); X while ((wpid = wait(&status)) != cpid && wpid != -1) X ; X signal(SIGINT,old_intr); X signal(SIGQUIT,old_quit); X} X Xhelp() X{ Xregister int hf; Xregister int n; X X if ((hf = open(helpfile,0)) < 0) X fprintf(stderr,"Cannot open help file %s\n",helpfile); X else X { X while((n = read(hf,buf,BFSZ)) > 0) X write(1,buf,n); X close(hf); X } X X} X X X#ifndef LOCK_EX X/* Keep calling locking() until it works */ X Xchk_lock(file,request,size) XFILE *file; Xint request; Xlong size; X{ X/* register int i = 5; X * X * while (locking(fileno(file), request, size) && i--) X * sleep(1); X */ X} X#endif X X/* X * This is the Party Console function. X */ X Xcontrol() X{ Xint i, n, chelp; Xchar nname[20]; Xchar myname[20]; Xstruct passwd *pwd; X X if (C_FLAG == ON) { X printf("Party Console\n"); X help_cntrl(); X } X printf("\ncmd: "); X X rewind(stdin); X chelp = getc(stdin); X X switch (chelp) { X case 'n': X nname[0] = '\0'; X printf ("Your new name: "); X rewind (stdin); X fgets (nname,80,stdin); X if (nname[0] != '\n') { X X for (n = 1; n <= strlen(nname); n++) { X if (nname[n] == '\n') { X nname[n] = '\0'; X break; X } X } X X /* X * Look for control or escapes in name, they can X * can be used to look like a valid user. X */ X X for (n = 0; n < (strlen(nname)); n++) { X if (nname[n] < 0x20 || nname[n] > 0x7e) { X printf("Illegal char in name\n"); X return(1); X } X } X X } else { X /* None given -- use real name */ X } X X /* X * Test the requested name against valid users. X * If a match is found, deny the change. X */ X X pwd = getpwnam (nname); X strcpy (myname, getlogin ()); X X /* X * Blow up if name exists in passwd file and is X * not my name. X */ X X if ((strcmp (nname, pwd->pw_name) == 0) && X (strcmp (myname, nname) != 0)) { X printf ("Sorry, name is reserved\n"); X break; X } X X if (strcmp (myname, nname) == 0) { X name[0] = NULL; X inbuf[0] = NULL; X strcpy (name,nname); X strcpy (inbuf,nname); X strcat (inbuf,": "); X break; X } else { X name[0] = NULL; X inbuf[0] = NULL; X strcpy (name,nname); X strcpy (inbuf,nname); X strcat (inbuf,": "); X break; X } X case 'h': X case '?': X fflush (stdin); X fflush (stdout); X fflush (stderr); X help_cntrl (); X break; X case 'p': X fflush (stdin); X fflush (stdout); X fflush (stderr); X private_party (); X break; X case 'r': X read_file (); X fflush (stdout); X fflush (stderr); X break; X case 'w': X who (); X break; X case 'i': X invite (); X break; X case 'm': X p_message(); X break; X case 't': X fflush (stdin); X fflush (stdout); X fflush (wfd); X if (C_FLAG == ON) X C_FLAG = OFF; X else X C_FLAG = ON; X break; X case 's': X scrollback (); X break; X default: X printf ("Input error, returning to party...\n"); X return; X } X} X X/* X * Help for Party Console. X */ X Xhelp_cntrl() X{ X printf ("i = Invite a user to party\n"); X printf ("m = Send a one line message to user\n"); X printf ("n = Change name\n"); X printf ("p = Join a different party\n"); X printf ("r = Read file into party\n"); X printf ("s = Scroll back and read again\n"); X printf ("t = Toggle this message off/on\n"); X printf ("w = Who is on the system\n"); X} X X/* X * This function will read a file into the party file. X */ X Xread_file() X{ XFILE *rfile; Xchar ifname[255]; Xchar fname[255]; Xchar fbuff[81]; Xint LIMIT = 66; /* Limit is 66 limes */ Xint line_cnt = 0; X X rewind (stdin); X printf ("File: "); X gets (ifname); X /* adjust */ X strncpy (fname, ifname, (sizeof(ifname) -1)); X X if ((rfile = fopen (fname,"r")) == NULL) { X printf ("Can't open: %s\n",fname); X return; X } X X fprintf (wfd,"\nFILE: %s entered by %s\n", fname, name); X X while (fgets (fbuff,80,rfile) != NULL) { X line_cnt++; X if (line_cnt == LIMIT) { X printf ("Input file size exceeded...\n"); X line_cnt = 0; X break; X } else { X fprintf (wfd,"%s", fbuff); X } X } X X fflush (wfd); X fclose (rfile); X X} X X/* X * This function will switch us out of the current party and into X * a new one thru a call to align(). X */ X Xprivate_party() X{ Xint c; Xchar new_table[8], pass[15]; Xchar p4pass[15]; XFILE *pfile; X X printf("File to party in (0 (standard) or 1-4): "); X rewind(stdin); X c = getc(stdin); X fflush (stdin); X switch(c) { X case '0': X strcpy (new_table, "party"); /* Standard party file */ X align(new_table); X break; X case '1': X strcpy (new_table, "party1"); X align(new_table); X break; X case '2': X strcpy (new_table, "party2"); X align(new_table); X break; X case '3': X strcpy (new_table, "party3"); X align (new_table); X break; X case '4': X rewind (stdin); X strcpy (pass, getpass ("Password: ")); X fflush (stdin); X if ((pfile = fopen (passfile, "r")) == NULL) { X printf ("Can't open: %s\n", passfile); X return; X } X fgets (p4pass, 14, pfile); X fclose (pfile); X if (strncmp (pass, p4pass, (strlen (p4pass) -1)) == 0) { X strcpy (new_table, "party4"); X align (new_table); X } else { X printf ("Attempt failed. Returning to party...\n"); X } X break; X default: X printf("\nInput error, returning to old party...\n\n"); X break; X } X} X X/* X * This function takes care of everything that needs to be modified for X * a party file switch. X */ X Xalign(table) Xchar *table; X{ Xregister int n; Xchar ch, *pnl; Xchar *pfile[80]; Xstruct passwd *pwd; Xlong now = time((long *)0); X X name[0] = NULL; X inbuf[0] = NULL; X pfile[0] = NULL; X /* Set up path to the new party file */ X strcpy (pfile, basename); X strcat (pfile, table); X /* Get user info */ X if (!(strcpy (name, getlogin())) || !(pwd = getpwnam(name)) X || pwd->pw_uid != getuid()) { X if (pwd = getpwuid(getuid())) X strcpy (name, pwd->pw_name); X else X strcpy (name, "(unknown)"); X } X strcpy(inbuf,name); X strcat(inbuf,": "); X X fprintf(wfd,"---- %s has moved to %s\n", name, table); X fprintf(stderr,"\nEntering %s!\n\n", table); X X /* close old partyfile */ X fclose(wfd); X X /* Open partyfile and put in join message */ X if ((wfd = fopen(pfile,"a")) == NULL) X { X printf("Can't write partyfile %s\n",pfile); X exit(1); X } X fseek(wfd,0L,2); X fprintf(wfd,"---- %s joining (%s) (%.12s)\n",name,table,ctime(&now)+4); X fflush(wfd); X X if ((rst = open(pfile,O_RDONLY)) < 0) X { X printf("Cannot read partyfile %s\n",pfile); X exit(1); X } X X /* Position read pointer 300 characters from the end of the file */ X if (lseek(rst,-300L,2) < 0) { X /* File isn't that long -- start from begining */ X lseek(rst,0L,0); X } else X /* Skip forward to a newline */ X while (read(rst,&ch,1) > 0 && ch != '\n') ; X} X X/* X * Display who is currently logged in X */ X Xwho() X{ XFILE *pnt; Xchar *in[81]; X X rewind (stdin); X if ((pnt = popen (who_cmd, "r")) == NULL) { X printf ("Can't get users, popen() failed.\n"); X return; X } X X while (fgets (in, 80, pnt) != NULL) { X printf ("%s", in); X } X pclose (pnt); X} X X/* X * Invite a user into party using a canned message X */ X Xinvite() X{ XFILE *pnt; XFILE *user_tty; Xchar *User[20]; Xchar *message[100]; Xchar *write_buf[40]; Xchar *theirtty; Xchar tmp[512]; Xstruct utmp *ptr, *user, *getutent(); Xvoid setutent(); Xint found; X X found = 0; X rewind (stdin); X message[0] = NULL; X tmp[0] = NULL; X write_buf[0] = NULL; X User[0] = NULL; X printf ("User: "); X fgets (User, 19, stdin); X X setutent(); X while ((ptr = getutent()) != NULL) { X if (strncmp(ptr->ut_user, User, (strlen (User) -1)) == 0) { X user = ptr; X found = 1; X break; X } X } X X if (!found) { X printf ("User is not logged on\n"); X return; X } X X strcpy (tmp, "/dev/"); X strcat (tmp, user->ut_line); X if ((user_tty = fopen (tmp, "w")) == NULL) { X printf ("%s has disabled messages\n", user->ut_user); X /* Cant write, so mail it... */ X strcpy (message, invitation); X strcat (message, User); X printf ("Sending mail to %s\n", user->ut_user); X if ((pnt = popen (message, "r")) == NULL) { X printf ("Can't invite user, popen() failed.\n"); X return; X } X pclose (pnt); X } else { X fclose (user_tty); X printf ("Inviting: /dev/%s (%s)\n", user->ut_line, X user->ut_user); X fflush (stdout); X strcpy (write_buf, "echo \"From "); X strcat (write_buf, name); X strcat (write_buf, ": Party or Die!!\""); X strcat (write_buf, ""); X strcat (write_buf, " > /dev/"); X strcat (write_buf, user->ut_line); X system (write_buf); X return; X } X} X X/* X * Send a one line private message to another user. X */ X Xp_message() X{ XFILE *user_tty; Xchar *User[20]; Xchar *m_buff[80]; Xchar *write_buf[40]; Xchar tmp[512]; Xstruct utmp *ptr, *user, *getutent(); Xvoid setutent(); Xint found; X X found = 0; X rewind (stdin); X tmp[0] = NULL; X write_buf[0] = NULL; X User[0] = NULL; X printf ("User: "); X fgets (User, 19, stdin); X rewind (stdin); X X setutent(); X while ((ptr = getutent()) != NULL) { X if (strncmp(ptr->ut_user, User, (strlen (User) -1)) == 0) { X user = ptr; X found = 1; X break; X } X } X X if ( !found) { X printf ("User is not logged on\n"); X return (1); X } X X printf ("Message: "); X fgets (m_buff, 79, stdin); X strcpy (tmp, "/dev/"); X strcat (tmp, user->ut_line); X if ((user_tty = fopen (tmp, "w")) == NULL) { X printf ("%s has disabled messages, aborted\n", user->ut_user); X return (1); X } else { X fclose (user_tty); X printf ("Message sent to: /dev/%s (%s)\n", user->ut_line, X user->ut_user); X strcpy (write_buf, "echo \"From "); X strcat (write_buf, name); X strcat (write_buf, ": "); X strcat (write_buf, m_buff); X strcat (write_buf, "\""); X strcat (write_buf, " > /dev/"); X strcat (write_buf, user->ut_line); X fflush (stdout); X sleep (1); X system (write_buf); X return; X } X} X Xscrollback () X{ Xint c, i, n, lines, orst, s_flg, q_flg; Xchar *inp[6]; Xchar ch; X X printf ("Number of lines to review: "); X X rewind (stdin); X fgets (inp, 5, stdin); X c = atoi (inp); X fflush (stdin); X if (c == 0) c = 20; X X /* Save previous location */ X orst = rst; X X /* Reverse seek c lines */ X for ( i = 1; i <= c;) { X if (lseek (rst, -1L, 1) < 0) { X lseek (rst, 0L, 0); X break; X } X if (read (rst, &ch, 1) > 0 && ch == '\n') { X i++; X } X lseek (rst, -1L, 1); X } X X /* Seek forward to EOF and print each line */ X i = q_flg = s_flg = 0; X while ((n = read (rst, buf, BFSZ)) > 0) { X i++; X if (i == 5) { X i = 0; X if (s_flg == 0) { X printf ("\nMore (q=quit, c=continuous): "); X c = getc (stdin); X switch (c) { X case 'q': X q_flg = 1; X break; X case 'c': X s_flg = 1; X break; X default: X break; X } X fflush (stdin); X } X } X if (q_flg == 1) X break; X else X write (1, buf, n); X } X rst = orst; X lseek (rst, 0L, 2); X printf ("\n------------- review completed ------------\n"); X} X X/* This function prints the Party Message Of The Day */ Xpotd() X{ XFILE *potdf; Xchar *pbuf[81]; X X if ((potdf = fopen(potdfile, "r")) == NULL) { X return; X } X X while ((fgets (pbuf, 80, potdf)) != NULL) X fputs (pbuf, stdout); X X fclose (potdf); X} X SHAR_EOF exit -- John "C". Sucilla, A silicon based life form. {att,chinet,ddsw1}!tarkus!jcs You have a better idea? Now's the time..