/* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * (BSD License, from tdelibs/doc/common/bsd-license.html) */ /* Autoconf: */ #include // strange symbol for HP-UX. It should not hurt on other systems. #ifndef notdef #define notdef 1 #endif #include "includ.h" #ifdef HAVE_SYS_STAT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "proto.h" #include "announce.h" #include "readconf.h" #include "defs.h" #include "threads.h" #include "unixsock.h" #ifdef HAVE_SGTTY_H #include #else #ifdef HAVE_BSD_SGTTY_H #include #endif #endif #define ktaltdmax(a,b) ( (a) > (b) ? (a) : (b) ) #define N_LINES 5 /* * Announce an invitation to talk. * * Because the tty driver insists on attaching a terminal-less * process to any terminal that it writes on, we must fork a child * to protect ourselves */ int announce(NEW_CTL_MSG *request, const char *remote_machine, char *disp, int usercfg, char * callee) { int pid, status; int returncode; if (strstr(remote_machine,"\033")) { syslog(LOG_WARNING, "blocked VT100 FLASH BOMB to user: %s (from field in packet contains bomb)", request->r_name); } else if (strstr(request->l_name,"\033")) { syslog(LOG_WARNING, "blocked VT100 FLASH BOMB to user: %s (apparently from: %s)", request->r_name, remote_machine); } else { if ((pid = fork())) { /* we are the parent, so wait for the child */ if (pid == -1) /* the fork failed */ return (FAILED); status = wait_process(pid); if ((status&0377) > 0) /* we were killed by some signal */ return (FAILED); /* Get the second byte, this is the exit/return code */ return ((status >> 8) & 0377); } /* we are the child, go and do it */ struct passwd * pw_buf; /** Change our uid. Once and for all that follows. */ pw_buf = getpwnam(request->r_name); /* XXX: Not good * You should also set the supplementary groups using * setgroups/initgroups */ if (setgid(pw_buf -> pw_gid) || setuid(pw_buf -> pw_uid)) { syslog(LOG_WARNING, "Warning: setgid/setuid as %s: %s", request->r_name, strerror(errno)); _exit(1); } /** Also set $HOME once and for all that follows (not used for text announce, but doesn't harm) */ setenv("HOME", pw_buf -> pw_dir, 1); #ifdef HAVE_KDE returncode = try_Xannounce(request, remote_machine, disp, usercfg, callee); if (returncode != SUCCESS) #endif returncode = print_std_mesg( request, remote_machine, usercfg, 0 ); _exit(returncode); } return (FAILED); } #ifdef HAVE_KDE int try_Xannounce(NEW_CTL_MSG *request, const char *remote_machine, char *disp, int usercfg, char * callee) { int readPipe[2]; char ch_aux; int pid; struct timeval clock; struct timezone zone; struct tm *localclock; char line_buf[N_CHARS]; char * adisp_begin, *disp_ptr, * disp_end; char extprg [S_MESSG]; /* Program used for notification. */ int Xannounceok = 0; /* Set to 1 if at least one Xannounceok succeeded */ #ifdef PROC_FIND_USER if ((Options.XAnnounce) && (strlen(disp) >= 2)) { #else if ((Options.XAnnounce) && ((int) request->r_tty[3] > (int) 'f')) { #endif /* PROC_FIND_USER */ /* * He is in X (probably :-) -> try to connect with external program */ /* No more needed : ktalkdlg will play sound itself. Other external programs can do whatever they want... Maybe a config for this could be useful to know whether the extprg handles the sound announcement... sound_or_beep(usercfg); */ gettimeofday(&clock, &zone); localclock = localtime( (const time_t *) &clock.tv_sec ); (void)snprintf(line_buf, N_CHARS, "%s@%s", request->l_name, remote_machine); #ifndef DONT_TRY_KTALK // never defined. Define if you want... Xannounceok = sendToKtalk ( request->r_name, line_buf ); if (!Xannounceok) { #endif /* XXX: I assume extprg comes from a user config file? Then * strcpy is quire a bad idea */ if ((!usercfg) || (!read_user_config("ExtPrg", extprg, S_MESSG))) /* try to read extprg from user config file, otherwise : */ strcpy(extprg,Options.extprg); /* take default */ /* disp can be a LIST of displays, such as ":0 :1", or ":0 hostname:0". Let's announce on ALL displays found. disp_end is the end of the display list (saved because we'll insert zeros). adisp_begin will point to a display in the list. disp_ptr will go char by char through disp.*/ adisp_begin=disp; disp_end=disp+strlen(disp); for (disp_ptr=disp; disp_ptrr_name ); if (callee) ktalk_debug("With mention of callee : %s",callee); } /* * point stdout of external program to the pipe * announce a talk request by execing external program */ dup2( readPipe[1], STDOUT_FILENO ); if (callee) execl( extprg, extprg, line_buf, callee, (void *)0 ); else execl( extprg, extprg, line_buf, (void *)0 ); /* * failure - tell it to father * exit with status=42 */ syslog(LOG_WARNING, "error while execl(%s): %s", extprg, strerror(errno)); _exit( 42 ); } default: { /* Register the new process for waitpid */ new_process(); /* * I don't know any way how to find that external program run ok * so parent returns always SUCCESS * Well I know a solution - read the pipe :-) - It blocks, so * we can't wait the reaction of the user * * Danny: made the pipe non blocking and check for exit-status */ int exited = 0, c = 0; int status = -1; // Set to an unlikely value (no program returns -1, Right?) ch_aux='0'; // initialise ch_aux /* make the pipe non-blocking: * does this work on systems other than BSD&Linux?? */ if (fcntl(readPipe[0], F_SETFL, O_NONBLOCK)== -1) ktalk_debug("Failed to make pipe non-blocking!!"); /* Stop this loop when: * the child exited with a value > 0 AND the pipe is empty * Maybe we can add a time-out? (what is reasonable) */ while (c > 0 || (exited < 1)){ if ( exited < 1 ) { status = wait_process(pid); exited += (WIFEXITED(status) || WIFSIGNALED(status)); // non-zero when exited OR non-caught signal } c = read( readPipe[0], &ch_aux, 1 ); // this is many times -1 because no data in pipe if (ch_aux == '#') Xannounceok = 1; } /* Check whether the # was returned, if not, return the exit status * 0 means: exit normally, but no # received-> do a text announce as well. * 1 can be for instance: cannot open X display * 42 is: failure to start external program. */ if (Xannounceok == 1) { ktalk_debug("OK - Child process responded"); } else ktalk_debug("External program failed with exit status: %i", WEXITSTATUS(status)); // close the pipes: (is this necessary?): close( readPipe[0] ); close( readPipe[1] ); } /* default */ } /* switch */ adisp_begin=disp_ptr+1; } /* if */ } /* for */ #ifndef DONT_TRY_KTALK } // if #endif if (Xannounceok) { #ifndef NO_TEXT_ANNOUNCE_IF_X // never defined. Define it if you don't want text announce in // addition to X announce if ((request->r_tty[0]!='\0') && ((int)request->r_tty[3]<(int)'f')) { // Not a pseudo terminal (such as an xterm, ...) ktalk_debug("Doing also text announce on %s",request->r_tty); print_std_mesg(request, remote_machine, usercfg, 1 /*force no sound*/); } #endif return (SUCCESS); } } return (FAILED); } #endif /* HAVE_KDE */ /* * See if the user is accepting messages. If so, announce that * a talk is requested. */ int print_std_mesg( NEW_CTL_MSG *request, const char *remote_machine, int usercfg, int force_no_sound ) { char full_tty[32]; FILE *tf; struct stat stbuf; (void)snprintf(full_tty, sizeof(full_tty), "%s/%s", _PATH_DEV, request->r_tty); if (access(full_tty, 0) != 0) return (FAILED); if ((tf = fopen(full_tty, "w")) == 0) return (PERMISSION_DENIED); /* * On first tty open, the server will have * it's pgrp set, so disconnect us from the * tty before we catch a signal. */ ioctl(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0); if (fstat(fileno(tf), &stbuf) < 0) return (PERMISSION_DENIED); if ((stbuf.st_mode&020) == 0) return (PERMISSION_DENIED); print_mesg(tf, request, remote_machine, usercfg, force_no_sound); fclose(tf); return (SUCCESS); } /* * Build a block of characters containing the message. * It is sent blank filled and in a single block to * try to keep the message in one piece if the recipient * in in vi at the time */ void print_mesg(FILE * tf, NEW_CTL_MSG * request, const char * remote_machine, int usercfg, int force_no_sound) { struct timeval clock; struct timezone zone; struct tm *localclock; char line_buf[N_LINES][N_CHARS]; char buffer [N_CHARS]; int sizes[N_LINES]; char big_buf[N_LINES*N_CHARS]; char *bptr, *lptr; int i, j, max_size; char * remotemach = new char[strlen(remote_machine)+1]; strcpy(remotemach,remote_machine); /* We have to duplicate it because param_remote_machine is contained in the hostent structure, and will be erased by gethostbyname. */ char localname[SYS_NMLN]; strcpy(localname,gethostbyname(Options.hostname)->h_name); /* We have to duplicate localname also. Same reasons as above ! */ const char *remotename = gethostbyname(remotemach)->h_name; i = 0; max_size = 0; gettimeofday(&clock, &zone); localclock = localtime( (const time_t *) &clock.tv_sec ); (void)snprintf(line_buf[i], N_CHARS, " "); sizes[i] = strlen(line_buf[i]); max_size = ktaltdmax(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, Options.announce1, localclock->tm_hour , localclock->tm_min ); sizes[i] = strlen(line_buf[i]); max_size = ktaltdmax(max_size, sizes[i]); i++; snprintf(buffer, N_CHARS, "%s@%s", request->l_name, remotename); snprintf(line_buf[i], N_CHARS, Options.announce2, buffer); sizes[i] = strlen(line_buf[i]); max_size = ktaltdmax(max_size, sizes[i]); i++; if (!(strcmp(localname,remotename))) { snprintf(line_buf[i], N_CHARS, Options.announce3, request->l_name); } else { snprintf(line_buf[i], N_CHARS, Options.announce3, buffer); } sizes[i] = strlen(line_buf[i]); max_size = ktaltdmax(max_size, sizes[i]); i++; (void)snprintf(line_buf[i], N_CHARS, " "); sizes[i] = strlen(line_buf[i]); sizes[i] = strlen(line_buf[i]); max_size = ktaltdmax(max_size, sizes[i]); i++; bptr = big_buf; if (!force_no_sound) /* set if a X announce has been done */ if (sound_or_beep(usercfg)) /* if no sound then : */ *bptr++ = ''; /* send something to wake them up */ *bptr++ = '\r'; /* add a \r in case of raw mode */ *bptr++ = '\n'; for (i = 0; i < N_LINES; i++) { /* copy the line into the big buffer */ lptr = line_buf[i]; while (*lptr != '\0') *(bptr++) = *(lptr++); /* pad out the rest of the lines with blanks */ for (j = sizes[i]; j < max_size + 2; j++) *(bptr++) = ' '; *(bptr++) = '\r'; /* add a \r in case of raw mode */ *(bptr++) = '\n'; } *bptr = '\0'; fprintf(tf, "%s", big_buf); fflush(tf); ioctl(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0); delete remotemach; } int play_sound(int usercfg) { int pid, status, returncode; char sSoundPlayer[S_CFGLINE], sSoundFile[S_CFGLINE]; char sSoundPlayerOpt[S_CFGLINE]; /* We must absolutely read the configuration before forking, because the father will close the file soon !! */ if ((!usercfg) || (!read_user_config("SoundPlayer",sSoundPlayer,S_CFGLINE))) strcpy(sSoundPlayer,Options.soundplayer); ktalk_debug(sSoundPlayer); if ((!usercfg) || (!read_user_config("SoundPlayerOpt",sSoundPlayerOpt,S_CFGLINE))) strcpy(sSoundPlayerOpt,Options.soundplayeropt); ktalk_debug(sSoundPlayerOpt); if ((!usercfg) || (!read_user_config("SoundFile",sSoundFile,S_CFGLINE))) strcpy(sSoundFile,Options.soundfile); ktalk_debug(sSoundFile); if ((pid = fork())) { /* we are the parent, so wait for the child */ if (pid == -1) /* the fork failed */ { syslog(LOG_ERR,"Fork before play_sound : FAILED."); return (FAILED); } status = wait_process(pid); if ((status&0377) > 0) /* we were killed by some signal */ return (FAILED); /* Get the second byte, this is the exit/return code */ return ((status >> 8) & 0377); } /* we are the child, go and do it */ if (strlen(sSoundPlayerOpt)>0) returncode = execl(sSoundPlayer,sSoundPlayer/*arg0*/,sSoundPlayerOpt,sSoundFile, (void *)0); else returncode = execl(sSoundPlayer,sSoundPlayer/*arg0*/,sSoundFile, (void *)0); ktalk_debug(strerror(errno)); syslog(LOG_ERR,"ERROR PLAYING SOUND FILE !!!"); syslog(LOG_ERR, "%s,%s,%s",sSoundPlayer,sSoundFile,sSoundPlayerOpt); syslog(LOG_ERR,"RETURN-CODE : %d",returncode); _exit(returncode); /*NOTREACHED*/ return 0; } /* * Calls play_sound if necessary. Returns 1 if a tty beep is needed. */ int sound_or_beep(int usercfg) { int bSound; int ret; if ((!usercfg) || (!read_bool_user_config("Sound",&bSound))) bSound=Options.sound; ktalk_debug("Sound option in sound_or_beep : %d",bSound); if (bSound) { /* try to play sound, otherwise beeps (if not in X) */ ret = play_sound(usercfg); /* 1 = it didn't work. ^G needed */ } else ret = 1; /* ^G needed */ return ret; }