#ifndef lint
static char *RCSid = "$Header: /fsys4/usr/source/users/msg/Common/RCS/deliver.c,v 1.4 90/12/29 21:59:07 jmsellen Exp $";
#endif

/* deliver.c
   Deliver a message to a user on this machine.
*/

/*
 * Old revision log:
 *
 * Revision 1.4  87/06/04  12:49:47  jmsellens
 * Changes to put the msg save file in /usr/tmp
 * rationalized the code a little, added the NO_STAT error condition
 * 
 * Revision 1.3  87/04/13  09:46:09  sahayman
 * Make sure to do answerback() on unsuccessful messages (mesg n)
 * as well as successful.
 * 
 * Revision 1.2  87/04/06  02:16:30  sahayman
 * Print a "However ... " message when unable to deliver to some
 * ttys but able to deliver to others.
 * 
 */
#include "msg.h"
#include <setjmp.h>
#include <signal.h>
#include <ctype.h>
#if vms
#include ctype
#include descrip
#include ssdef
#include brkdef.h
#endif
static jmp_buf timeout;


static
wakeup()
{
    longjmp( timeout, 1 );
}



int
deliver( sender, user, message, fromroot )
char *sender, *user, *message;
{
#if vms
    struct dsc$descriptor_s msg_desc;
    struct dsc$descriptor_s user_desc;
    char    *cp;
    char    full_message[1024];
    int status;

    sprintf(full_message, "%s: %s\n\r", sender, message);
    msg_desc.dsc$w_length = strlen(full_message);
    msg_desc.dsc$a_pointer = &full_message;
    msg_desc.dsc$b_class = DSC$K_CLASS_S;
    msg_desc.dsc$b_dtype = DSC$K_DTYPE_T;

    for (cp = user; *cp != '\0'; cp++) {
	*cp = toupper(*cp);
    }
    user_desc.dsc$w_length = strlen(user);
    user_desc.dsc$a_pointer = user;
    user_desc.dsc$b_class = DSC$K_CLASS_S;
    user_desc.dsc$b_dtype = DSC$K_DTYPE_T;
    status = sys$brkthru(NULL, &msg_desc, &user_desc, BRK$C_USERNAME, NULL,
                32, BRK$M_CLUSTER, NULL, 30, NULL, NULL);
#ifdef ANSWERBACK
    answerback( ANSWER, user, "" );
#endif
#ifdef SAVEMSG
    savemsg( 0, user, sender, message );
#endif
    return (0);
#else
    struct utmp ut;
    FILE *fp, *term;
    char terminal[40];	/* should be enough ... */
    int found = 0, errcode = 0;
    int however = 0;	/* Should we print a "however ...." msg */
    struct stat sbuf;
    char line[sizeof(ut.ut_line)+5];
    char successful_line[sizeof(ut.ut_line)+5];
    int (*osig)();
#ifdef SAVEMSG
    int uid = -1;
#endif

    successful_line[0] = '\0';

    if ( (fp=fopen( UTMP, "r" )) == FPNULL ) {
	errmsg( NO_UTMP, hostname, syserr() );
	return( NO_UTMP );
    }
    /* keep on until end of file in case s/he's signed on more than once */
    while ( fread( &ut, sizeof(struct utmp), 1, fp ) == 1 ) {
	if ( strncmp( user, ut.ut_name, sizeof(ut.ut_name) ) != 0 )
	    continue;
#ifdef SYSV_UTMP
	if ( ut.ut_type != USER_PROCESS )
	    continue;
#endif
	found++;
	(void) strncpy( line, ut.ut_line, sizeof(ut.ut_line) );
	line[sizeof(ut.ut_line)] = '\0';
	(void) sprintf( terminal, "%s%s", DEV, line );
	/* stat it to see if we're allowed to write */
	if ( stat(terminal,&sbuf) == 0 ) {
#ifdef SAVEMSG
	    uid = sbuf.st_uid;
#endif
	    if ( ((sbuf.st_mode&ALLOW)==0) && !fromroot ) {
		errmsg( MESG_OFF, user, hostname, line );
		however++;
		errcode++;
	    } else {
		if ( setjmp( timeout ) ) {
		    errmsg( TIMEOUT, user, hostname, line );
		    however++;
		} else {
		    osig = signal(SIGALRM, wakeup);
		    alarm( 5 );
		    if ( (term=fopen(terminal,"w")) != NULL ) {
			fprintf(term, "\n\007%s: %s\n", sender, message);
			/* we need this fflush() or else it would hang in the
			   fclose() after we've dealt with the alarm() */
			fflush( term );
			strcpy( successful_line, line );
		    }
		}
		alarm( 0 );
		signal(SIGALRM, osig);
		if (term == NULL) {
		    errmsg( NO_WRITE, user, hostname, line, syserr() );
		    however++;
		    errcode++;
		} else
		    fclose( term );
	    }
#ifdef ANSWERBACK
	    answerback( ANSWER, user, line );
#endif
	} else {
	    errmsg( NO_STAT, terminal, user, hostname, syserr() );
	    however++;
	    errcode++;
	}
    }
    (void) fclose( fp );
#ifdef SAVEMSG
    /* note that these saves it even if mesg n was set */
    if ( found && uid != -1 )
	savemsg( uid, user, sender, message );
#endif
    if ( !found ) {
	errmsg( NOT_ON, user, hostname );
	errcode++;
    }
    /*
     * A message like "so-and-so (ttyxx) is not receiving messages"
     * is not too helpful when they are receiving messages
     * on another terminal.  So mention that.
     * ..sah 87/04/06
     */

    if ( however && successful_line[0] ) {
	errmsg( HOWEVER, user, hostname, successful_line );
    }
    return( errcode );
#endif
}


#define DEL	(127)

sanitize( mbuf, copy )
char *mbuf;
char *copy;
{
    char *p, *r;
    /*
     * Remove control characters to prevent nasties.
     * Previously allowed isspace(*p), but having a \r in a message
     * is asking for forgeries.  isprint() is sufficient.
     * (And good programmers always use isascii() first, right?)
     */
     /*
      * Allow the first two characters of the message to be nl, cr.
      */
    r = copy;
    p = mbuf;
    if ( ( p[0] == '\n' ) && ( p[1] == '\r' ) ) {
	*r++ = *p++; 
	*r++ = *p++;
    }
    for ( ; *p; p++,r++ ) {
	if ( ! isascii( *p ) ) {
	    *r++ = '\\';
	    *p = toascii( *p );
	}
	if ( ! isprint(*p) ) {
	    if ( *p == '\t' )
		*r = ' ';
	    else if ( iscntrl( *p ) ) {
	        *r++ = '^';
	        if ( *p == DEL )
		    *r = '?';
		else
		    *r = *p + '@';
	    } else	/* this shouldn't happen, but what the hell */
		*r = 'X';
	} else
	    *r = *p;
    }
}
