home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / msgd / msg / msg.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-09  |  12.2 KB  |  547 lines

  1. /*
  2.  * $Header: /source/users/msg/msg/RCS/msg.c,v 1.24 91/08/18 18:37:29 dgharriss Exp $
  3.  */
  4.  
  5. /*
  6.  * msg.c
  7.  * MSG re-written to use a daemon
  8.  * John Sellens, University of Waterloo
  9.  */
  10.  
  11. #include "msg.h"
  12. #include <pwd.h>
  13. #include <strings.h>
  14.  
  15. #ifdef REALUSER
  16. #include <mfcf/libuw/standard.h>
  17. #endif
  18.  
  19. /* global */ char    hostname[80];
  20. private    char    *progname;
  21. #define USERIDSPACE    (80)    /* lots of room */
  22. private    char    sender[USERIDSPACE];
  23. private    char    loginname[USERIDSPACE];
  24. private    int    uid;    /* effective */
  25. private    int    ruid;    /* real */
  26.  
  27. private    char     msg_file[80];
  28. private    char     pmsg_file[80];
  29.  
  30. extern    char    *syserr();
  31. extern    char    *ttyname();
  32.  
  33. private    int    getsender(), sendmsg();
  34. private    void    replywarn();
  35. private    void    usage();
  36. private    void    getprevious();
  37. private    void    putprevious();
  38. extern    void    fatal();
  39.  
  40. #define    SU    (0)    /* uid of super-user */
  41.  
  42. main( argc, argv )
  43. int argc;
  44. char *argv[];
  45. {
  46.     register int i;
  47.     register int exitcode = 0;
  48.     int fromroot;
  49.     int useprevious = 0;    /* send previous text as msg */
  50.     int cmdlinemsg = 0;        /* msg is on cmd line */
  51.     char *p;
  52.     char message[1024];            /* to hold message */
  53.     char rawmsg[1024];            /* to hold unclean message */
  54.     char *tname;            /* name of current tty */
  55.     char *reply = NULL, *reply_to();
  56.  
  57.  
  58.     int c;
  59.     extern int optind;
  60.     extern char *optarg;
  61.  
  62.  
  63.     progname = argv[0];
  64.     if ( argc == 1 )
  65.     usage();
  66.  
  67.     if ( gethostname( hostname, (int)sizeof(hostname) ) )
  68.     fatal( "Couldn't gethostname(): %s", syserr() );
  69.  
  70.     fromroot = getsender();
  71.     strcpy( msg_file, MSGFILE );
  72.     strcat( msg_file, loginname );
  73.     strcpy( pmsg_file, PMSGFILE );
  74.     strcat( pmsg_file, loginname );
  75.  
  76.     while (( c = getopt(argc, argv, "lm:prt")) != EOF ) {
  77.     switch( c ) {
  78.     case 'l':
  79.         list_msg();
  80.         break;
  81.     case 'm':
  82.         strcpy( rawmsg, optarg ); /* hope they don't have a giant msg */
  83.         cmdlinemsg++;
  84.         break;
  85.     case 'p':
  86.         useprevious++;
  87.         break;
  88.     case 'r':
  89.         if ( (reply = reply_to()) == NULL ) {
  90.         fatal( "Nobody to reply to in last message file '%s'",
  91.             msg_file );
  92.         }
  93.         break;
  94.     case 't':
  95.         list_msgtime();
  96.         break;
  97.     default:
  98.         usage();
  99.     }
  100.     }
  101.     if ( useprevious && cmdlinemsg )
  102.     fatal( "-m and -p can't be used together" );
  103.  
  104.     /* Tell 'em now so that the lastmsg comes before you get the replying
  105.        message, and tell them so they know who they're sending to, in
  106.        case it's wrong. */
  107.     if ( reply != NULL )
  108.     errprintf("Replying to '%s'", reply);
  109.  
  110.     /*
  111.      * Simpler to pretend the options weren't there. Skip over 'em.
  112.      * argv/argc is now a vector of user names.
  113.      */
  114.     argc -= optind;
  115.     argv += optind;
  116.  
  117.  
  118.     if ( (cmdlinemsg||useprevious) && reply == NULL && argc == 0 )
  119.     fatal( "No one to send message to" );
  120.  
  121.     /*
  122.      * Quietly exit if no recipients and no reply.  This covers the case
  123.      * where all we got was the -l option.
  124.      */
  125.     if ( reply == NULL && argc == 0 )
  126.     exit(0);
  127.  
  128.     /* Old behaviour used - as last argument to indicate msg on stdin */
  129.     if ( strcmp("-", argv[argc-1]) == 0 ) {
  130.     errprintf( "'-' as last argument is obsolete - ignored" );
  131.     argc--;
  132.     }
  133.  
  134.     if ( useprevious ) {
  135.     getprevious( rawmsg, (int)sizeof(rawmsg) );
  136.     } else if ( ! cmdlinemsg ) {
  137.     if ( fgets( rawmsg, (int)sizeof(rawmsg), stdin ) == NULL ) {
  138.         /* assume user changed their mind */
  139.         return(0);
  140.     }
  141.     p = index(rawmsg, '\n');
  142.     if (p == NULL)
  143.         errprintf("Message truncated to %d characters.", sizeof(rawmsg) );
  144.     else
  145.         *p = NULL;
  146.     }
  147.  
  148.     if ( *rawmsg == '\0' )    /* No message to send. */
  149.     return(0);
  150.     sanitize( rawmsg, message );
  151.  
  152.     if ( useprevious )
  153.     printf( "Sending: %s\n", message );
  154.     else
  155.     putprevious( message );
  156.  
  157.     if( (tname=ttyname(0)) || (tname=ttyname(1)) || (tname=ttyname(2)) )
  158.     replywarn(tname);    /* warn if can't get replies on this tty */
  159. #if 0
  160.     /* Is this a good idea?  -IAN! */
  161.     else
  162.     errmsg( NO_TTY );
  163. #endif
  164.  
  165.     /*
  166.      * Send a reply if -r was used, also send to all other
  167.      * recipients on the command line.
  168.      */
  169.  
  170.     if ( reply != NULL && sendmsg(reply, message, fromroot) != OK )
  171.     exitcode++;
  172.  
  173.     while ( --argc >= 0 ) {
  174.     /* sendmsg() will destroy the argv[i] */
  175.     if ( sendmsg( *argv++, message, fromroot ) != OK )
  176.         exitcode++;
  177.     }
  178.     return( exitcode!=0 );
  179. }
  180.  
  181.  
  182. /*
  183.  * Find out who the sender is.  Try getlogin(), and if that
  184.  * differs from the REAL uid, make sure both names appear.
  185.  * Return 1 if we are root, 0 otherwise.
  186.  */
  187. static int
  188. getsender()
  189. {
  190.     extern char *getlogin(), *getenv();
  191.     struct passwd *pw;
  192.     char pwname[USERIDSPACE];
  193.     char *glog;
  194.  
  195.     ruid = uid = getuid();
  196.  
  197.     /* Check out who we're running as */
  198.     if ( (pw=getpwuid(uid)) == NULL )
  199.     fatal("Your uid number (%d) isn't recognized.\n", uid);
  200.     if(pw->pw_name == NULL || pw->pw_name[0] == '\0')
  201.     fatal("No pwname is associated with uid %d.\n", uid);
  202. #ifdef REALUSER
  203.     (void) strcpy( pwname, realuser( pw ) );
  204. #else
  205.     (void) strcpy( pwname,  pw->pw_name );
  206. #endif
  207.  
  208.     /* see if we can use getlogin() to see who it is */
  209.     (void) strcpy( loginname, (glog=getlogin()) == 0 ? "" : glog );
  210.     if ( *loginname ) {
  211.     /* check the passwd file for this guy */
  212.     pw = getpwnam( loginname );
  213.     if ( pw ) {
  214.         ruid = pw->pw_uid;
  215. #ifdef DUMBSHORT
  216.         {
  217.         struct utmp *ut;
  218.         if ( strncmp( pwname, loginname, sizeof(ut->ut_name) ) == 0 )
  219.             (void) strcpy( loginname, pwname );
  220.         }
  221. #endif
  222. #ifdef REALUSER
  223.         (void) strcpy( loginname, realuser( pw ) );
  224. #endif
  225.     }
  226.     } else {
  227.     /* no better guess at real identity, use USER if we're root */
  228.     if ( uid == SU && (glog=getenv("USER"))!=(char *)NULL && *glog ) {
  229.         /* look up the pw entry for USER */
  230.         if ( (pw=getpwnam(glog)) != (struct passwd *)NULL ) {
  231.         ruid = pw->pw_uid;
  232. #ifdef REALUSER
  233.         glog = realuser( pw );
  234. #endif
  235.         }
  236.         strcpy( loginname, glog );
  237.     } else
  238.         strcpy( loginname, pwname );
  239.     }
  240.  
  241.     if (
  242. #ifdef REALUSER
  243.         ! sameuser( pwname, loginname )
  244. #else
  245.     strcmp( pwname, loginname ) != 0
  246. #endif
  247.     && uid != SU ) {
  248.     /* Names differ, and user isn't SU, so send both */
  249.     (void) sprintf(sender, "%s(%s)@%s", loginname, pwname, hostname);
  250.     } else {
  251.     (void) sprintf(sender, "%s@%s", loginname, hostname);
  252.     }
  253.  
  254.     return( uid == SU );
  255. }
  256.  
  257.  
  258. private int
  259. sendmsg( recipient, message, fromroot )
  260. char *recipient, *message;
  261. int fromroot;
  262. {
  263.     register char *p, *user, *host;
  264. #ifndef REMOTE
  265.     int local = FALSE;
  266. #endif
  267.     int errcode;
  268.     if ( (p = index( recipient, '@' )) != CPNULL ) {
  269.     user = recipient;
  270.     host = p+1;
  271.     *p = '\0';
  272.     } else if ( (p = index( recipient, '!' )) != CPNULL ) {
  273.     user = p+1;
  274.     host = recipient;
  275.     *p = '\0';
  276.     } else {
  277.     user = recipient;
  278.     host = hostname;
  279.     }
  280. /* always go via the daemon, because it means we don't have to be
  281.   setuid, and it makes things a little bit simpler.  I hope - jms.
  282.   wrong - jms */
  283. #ifndef REMOTE
  284.     /* this only works if they use the full host name, not an alias, but
  285.        that's okay ... */
  286.     if ( strcmp( host, hostname ) == 0 )
  287.     local = TRUE;
  288.     if ( local )
  289.     errcode = deliver( sender, user, message, fromroot );
  290.     else
  291. #endif
  292.     errcode = remote( sender, user, host, message );
  293.     return( errcode );
  294. }
  295.  
  296.  
  297.  
  298. /* Warn user on tname if he/she can't receive messages.
  299.  */
  300. private void
  301. replywarn(tname)
  302.     char *tname;
  303. {
  304.     struct utmp ut;
  305.     struct stat sbuf;
  306.     FILE *fp;
  307.     char *p;
  308.     char terminal[80];    /* should be enough ... */
  309.  
  310.     if( (fp=fopen( UTMP, "r" )) == FPNULL ) {
  311.     errmsg( NO_UTMP, hostname, syserr() );
  312.     } else {
  313.     if( (p=rindex(tname,'/')) != NULL )
  314.         tname = p + 1;
  315.     while ( fread((char*)&ut,sizeof(struct utmp),1,fp) != 0 ){
  316.         if( strncmp(tname,ut.ut_line,(int)sizeof(ut.ut_line)) == 0 ){
  317.         (void) sprintf( terminal, "%s%s", DEV, tname );
  318.         if ( stat(terminal,&sbuf)!=-1 && ((sbuf.st_mode&ALLOW)==0) )
  319.             errmsg( NO_REPLY, loginname, tname );
  320. #ifdef ANSWERBACK
  321.         answerback( ANS_WARN, loginname, tname );
  322. #endif
  323.         break;    /* only need to check the first match */
  324.         }
  325.     }
  326.     (void) fclose( fp );
  327.     }
  328. }
  329.  
  330.  
  331. private void
  332. usage()
  333. {
  334.     errprintf( "Usage: %s [-l] [-m message] [-p] [-r] [-t] [user ...]",
  335.     progname );
  336.     exit( BAD_ARGS );
  337. }
  338.  
  339.  
  340. #ifdef VARARGS
  341. errprintf( s, va_alist )
  342. char *s;
  343. va_dcl
  344. {
  345.     va_list ap;
  346.     (void) fprintf( stderr, "%s: ", progname );
  347.     va_start( ap );
  348.     (void) vfprintf( stderr, s, ap );
  349.     va_end( ap );
  350.     (void) fputc( '\n', stderr );
  351. }
  352.  
  353. void
  354. fatal( s, va_alist )
  355. char *s;
  356. va_dcl
  357. {
  358.     va_list ap;
  359.     (void) fprintf( stderr, "%s: ", progname );
  360.     va_start( ap );
  361.     (void) vfprintf( stderr, s, ap );
  362.     va_end( ap );
  363.     (void) fputc( '\n', stderr );
  364.     exit( FATAL );
  365. }
  366.  
  367. /* VARARGS1 */
  368. errmsg( msgnum, va_alist )
  369. int msgnum;
  370. va_dcl
  371. {
  372.     va_list ap;
  373.     (void) fprintf( stderr, "%s: ", progname );
  374.     va_start( ap );
  375.     (void) vfprintf( stderr, errmessages[msgnum], ap );
  376.     va_end( ap );
  377.     (void) fputc( '\n', stderr );
  378. }
  379. #else
  380. /* VARARGS1 */
  381. errprintf( a, b, c, d, e, f, g, h, i, j, k )
  382. char *a;
  383. {
  384.     (void) fprintf( stderr, "%s: ", progname );
  385.     fprintf( stderr, a, b, c, d, e, f, g, h, i, j, k );
  386.     (void) fputc( '\n', stderr );
  387. }
  388.  
  389. /* VARARGS1 */
  390. void
  391. fatal( a, b, c, d, e, f, g, h, i, j, k )
  392. char *a;
  393. {
  394.     (void) fprintf( stderr, "%s: ", progname );
  395.     fprintf( stderr, a, b, c, d, e, f, g, h, i, j, k );
  396.     (void) fputc( '\n', stderr );
  397.     exit( FATAL );
  398. }
  399.  
  400. /* VARARGS1 */
  401. errmsg( msgnum, a, b, c, d, e )
  402. int msgnum;
  403. char *a, *b, *c, *d, *e;
  404. {
  405.     errprintf( errmessages[msgnum], a, b, c, d, e );
  406. }
  407. #endif /* VARARGS */
  408.  
  409. list_msgtime()
  410. {
  411.     /*
  412.      * List time last msg was received.
  413.      */
  414.     struct stat statbuf;
  415.  
  416.     if (stat(msg_file, &statbuf) == -1)
  417.     errprintf("can't get date of message file '%s': %s",
  418.         msg_file, syserr());
  419.     else
  420.     printf("last message received %s", ctime(&statbuf.st_mtime));
  421. }
  422.  
  423.  
  424. list_msg()
  425. {
  426.     /*
  427.      * List message to which we are replying.
  428.      * Just cat the last msg file.
  429.      */
  430.  
  431.     FILE *fp;
  432.     int c;
  433.         
  434.     if ( (fp = fopen(msg_file, "r")) == NULL ) {
  435.     errprintf("Cannot read last message file '%s'", msg_file );
  436.     } else {
  437.     while ( (c = getc(fp)) != EOF ) {
  438.         putchar(c);
  439.     }
  440.     }
  441. }
  442.  
  443.  
  444. private void
  445. getprevious( buf, size )
  446. char *buf;
  447. int size;
  448. {
  449.     struct stat sbuf;
  450.     FILE *pfp;
  451.     int count;
  452.     if ( stat( pmsg_file, &sbuf ) == -1 )
  453.     fatal( "Could not stat previous message file '%s': %s",
  454.         pmsg_file, syserr() );
  455.     if ( sbuf.st_uid != ruid )
  456.     fatal( "You do not own previous message file '%s'", pmsg_file );
  457.     if ( (pfp = fopen( pmsg_file, "r" )) == (FILE *)NULL )
  458.     fatal( "Could not fopen previous message file '%s' for read: %s",
  459.         pmsg_file, syserr() );
  460.     if ( fgets( buf, size, pfp ) == NULL )
  461.     fatal( "Could not read previous message from file '%s' (empty?)",
  462.         pmsg_file );
  463.     /* In case the file didn't end with a \n, we want to make sure that
  464.        we don't complain above that it has been truncated.  This is kind
  465.        of gross. */
  466.     if ( strlen( buf ) < size )
  467.     strcat( buf, "\n" );
  468.     (void) fclose( pfp );
  469. }
  470.  
  471. private void
  472. putprevious( msg )
  473. char *msg;
  474. {
  475.     FILE *pfp;
  476.     if ( (pfp = fopen( pmsg_file, "w" )) == (FILE *)NULL ) {
  477.     errprintf( "Could not fopen previous message file '%s' for write: %s",
  478.         pmsg_file, syserr() );
  479.     } else {
  480.     fprintf( pfp, "%s\n", msg );
  481.     fclose( pfp );
  482.     chown( pmsg_file, ruid, -1 );
  483.     chmod( pmsg_file, 0600 );
  484.     }
  485. }
  486.  
  487.  
  488.  
  489.  
  490. /*
  491.  * reply_to - look in MSGFILE to see who sent us
  492.  *  the last message.
  493.  */
  494.  
  495. char *
  496. reply_to()
  497. {
  498.     register char *p;
  499.     char *index();
  500.     static char buf[80];    /* Static 'cause we return what's in it. */
  501.     char userfrom[40], whocares[40], hostfrom[40];
  502.     FILE *f;
  503.  
  504.     if ( (f = fopen(msg_file, "r")) == NULL ) {
  505.     return( NULL );
  506.     }
  507.  
  508.     if ( fgets(buf, sizeof(buf),f) == NULL )
  509.     return( NULL );
  510.     /*
  511.      * The file can look like
  512.      * user@host: msg
  513.      * or
  514.      * user(somebody)@host: msg
  515.      * Scan this in a basically cheap way.
  516.      * I suppose some goofball exceptions could slip through
  517.      * here if people had () in their login or host name, blah.
  518.      */
  519.     
  520.  
  521.     if ( (p = index(buf, ':')) == NULL ) {
  522.     return( NULL );    /* Bad format. */
  523.     }
  524.     *p = '\0';
  525.  
  526.     /*
  527.      * Now see which format we have.  Strip out intermediate name.
  528.      * there are probably more rigorous ways to do this
  529.      */
  530.     p = index(buf, '(');
  531.  
  532.     if ( p != NULL ) {
  533.     *p++ = '\0';        /* First part is username */
  534.     p = index(p, ')');    /* Skip middle bit. */
  535.     if ( p == NULL ) {
  536.         return(NULL);    /* Bad format */
  537.     }
  538.  
  539.     if ( *++p != '@' )
  540.         return(NULL);
  541.  
  542.     strcat(buf, p);        /* Tack on hostname. */
  543.     }
  544.  
  545.     return( buf );
  546. }
  547.