home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume36 / msend / part01 < prev    next >
Encoding:
Text File  |  1993-03-21  |  53.5 KB  |  2,033 lines

  1. Newsgroups: comp.sources.misc
  2. From: spike@world.std.com (Joe Ilacqua)
  3. Subject: v36i018:  msend - a write/wall/rwall/talk replacement, v1.2, Part01/02
  4. Message-ID: <csm-v36i018=msend.133024@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: efbb567b17c512be250f6bcb2e96d902
  6. Date: Fri, 19 Mar 1993 19:31:20 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: spike@world.std.com (Joe Ilacqua)
  10. Posting-number: Volume 36, Issue 18
  11. Archive-name: msend/part01
  12. Environment: UNIX, networking
  13.  
  14. "msend" is a program for sending one line messages to other users on
  15. both local and remote systems.  "msend" can also do local and remote
  16. broadcast messages in the manner of wall and rwall. 
  17.  
  18. Basically, it looks like this:
  19.  
  20. world% msend jimf@centerline.com Hello
  21. world%
  22. (on centerline)
  23. [spike@world.std.com (ttyp4): Hello]
  24. centerline% msend spike@world.std.com
  25. msend>Yo!
  26. msend>What's up?
  27. msend>^D
  28. centerline%
  29. (on world)
  30. [jimf@centerline.com (ttyq7): Yo!]
  31. [jimf@centerline.com (ttyq7): What's up?]
  32.  
  33. Unlike "write", "msend" prints a Line Feed before the message, so it
  34. appears on a line by itself, not in the middle of what you were
  35. typing.  "msend" also keeps a history of messages sent to you.  Given
  36. the above session:
  37.  
  38. world% msend -huh
  39. [jimf@centerline.com (ttyq7): What's up?]
  40. world% msend -huh 2
  41. [jimf@centerline.com (ttyq7): Yo!]
  42. [jimf@centerline.com (ttyq7): What's up?]
  43.  
  44. There is a forwarding mechanism in msend so that all messages sent to
  45. you can be forwarded to a given tty on a given host.  This is very
  46. useful when you have windows open on many hosts.
  47.  
  48. msend has been around for a number of year.  It works, and has been
  49. tested on a wide range of systems.  It should port to any system with
  50. BSD style networking.      This is pretty much the same version that
  51. appeared in alt.sources, with some very minor changes to support BSDI.
  52.  
  53. ->Spike (spike@world.std.com)
  54.  
  55. #! /bin/sh
  56. # This is a shell archive.  Remove anything before this line, then feed it
  57. # into a shell via "sh file" or similar.  To overwrite existing files,
  58. # type "sh file -c".
  59. # Contents:  README Configuration Protocol misc.c msend.c network.c
  60. #   sendrecv.c write.c
  61. # Wrapped by kent@sparky on Fri Mar 19 13:24:30 1993
  62. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  63. echo If this archive is complete, you will see the following message:
  64. echo '          "shar: End of archive 1 (of 2)."'
  65. if test -f 'README' -a "${1}" != "-c" ; then 
  66.   echo shar: Will not clobber existing file \"'README'\"
  67. else
  68.   echo shar: Extracting \"'README'\" \(1846 characters\)
  69.   sed "s/^X//" >'README' <<'END_OF_FILE'
  70. XMSEND - Immediate message sending program for UNIX machines
  71. X
  72. X(c) Copyright 1988, 1989, 1990, 1991 Jim Frost.  All Rights Reserved.
  73. XPlease see the accompanying file "Copyright" for more information.
  74. X
  75. XThis utility is very similar to "rsend", allowing messages to be sent
  76. Xto different users on different machines.  "rsend" was, in turn based
  77. Xon "send" which use the SMTP SEND commands for message delivery (see
  78. XRFC 821).  "msend" implements the UMTP protocol described in
  79. X"Protocol" with other enhancements to make life easier on the user.
  80. XSee the accompanying man page for details.
  81. X
  82. XSimple instructions for installation are in "Configuration".
  83. X
  84. XWith regards to portability, the current version of msend was compiled
  85. Xand tested on the following machines:
  86. X
  87. X        Machine             OS               
  88. X        ---------------     -------------    
  89. X    Sun 3            SunOS 4.0.3,4.1.1
  90. X    Sun 4 (& clones)    SunOS 4.0.3,4.1,4.1.1,4.1.2
  91. X    Sun 386i        SunOS 4.0.2
  92. X    SGI 4D/25        IRIX 3.0
  93. X    SGI Indigo        IRIX 4.0
  94. X    IBM R/6000        AIX 3.1.5, 3.1.6, 3.2
  95. X    HP Series 700       HP-UX 8.01 (?)
  96. X    Convex C220        ConvexOS Release V9.1
  97. X    DEC DECStation        ULTRIX 4.2
  98. X    DG            DG/UX 5.4
  99. X    386 Box            SCO UNIX/ODT 1.1
  100. X    486 Box            BSDI 0.9.4
  101. X
  102. XOlder versions have run on:
  103. X
  104. X        Machine         OS               
  105. X        --------------- -------------    
  106. X    Encore Multimax    UMAX 4.2 R3.1    
  107. X    Sun 2        SunOS 3.2, 3.5
  108. X    VAX 11/750    4.3 BSD UNIX
  109. X
  110. Xmsend should run on any machine that has BSD networking support with
  111. Xfew changes and can be modified quickly to use other reliable
  112. Xnetworking systems.
  113. X
  114. XSpecial thanks to the following people for ideas and code segments:
  115. X
  116. X    Adam Bryant     (adb@bucsf.bu.edu)
  117. X    Phil Budne      (budd@bu-it.bu.edu)
  118. X    Jason Heirtzler (jdh@bu-it.bu.edu)
  119. X    Chris Riney     (chris@tisdec.tis.tandy.com)
  120. X    Barry Shein     (bzs@bu-it.bu.edu)
  121. X    John Solomon    (jsol@bu-it.bu.edu)
  122. X    Len Tower       (tower@bu-it.bu.edu)
  123. END_OF_FILE
  124.   if test 1846 -ne `wc -c <'README'`; then
  125.     echo shar: \"'README'\" unpacked with wrong size!
  126.   fi
  127.   # end of 'README'
  128. fi
  129. if test -f 'Configuration' -a "${1}" != "-c" ; then 
  130.   echo shar: Will not clobber existing file \"'Configuration'\"
  131. else
  132.   echo shar: Extracting \"'Configuration'\" \(4814 characters\)
  133.   sed "s/^X//" >'Configuration' <<'END_OF_FILE'
  134. XCUSTOMIZATION AND INSTALLATION
  135. X
  136. X  Be sure to properly customize "msend" by changing config.h and the
  137. X  Makefile to work with your system.
  138. X
  139. X
  140. X  In config.h:
  141. X
  142. X  PATH DEFINITIONS
  143. X
  144. X  The following paths are recommended:
  145. X
  146. X        #define LOGFILE  "/usr/adm/msend.log"
  147. X        #define SPOOLDIR "/usr/spool/msend"
  148. X
  149. X  LOGFILE - the name of the file that all daemon error messages will be
  150. X  sent to.
  151. X
  152. X  SPOOLDIR - the path of the directory used to save user messages for
  153. X  the "-huh" option.  It should be cleaned regularly to prevent
  154. X  overfilling.  If SPOOLDIR is undefined, ~/.msendmsgs is used instead.
  155. X
  156. X  OPTIONS
  157. X
  158. X  SECURE_PORT - When defined msend will attempt to acquire a secure
  159. X  port to make the the connect to the msend daemon.  This allows the
  160. X  remote msendd to detect when someone is trying to spoof it, that is
  161. X  make a message appear to come from a different user.  This method is
  162. X  not fool proof, but it can cut way down on abuse.  This does not
  163. X  currently work on systems the use POSIX setuid(2) semantics.  If you
  164. X  meet someone from the POSIX committee, punch them in the mouth.
  165. X
  166. X  ALONE - build msendd to run standalone instead of under inetd.
  167. X
  168. X  The following three options can also be controlled via the command
  169. X  line.  Best to just leave them defined.
  170. X
  171. X  CBROADCAST - allow broadcasting from the client (outgoing broadcasts).
  172. X
  173. X  DBROADCAST - allow broadcasting from the daemon (incoming broadcasts).
  174. X
  175. X  ROUTING - allow routing of messages to other hosts.
  176. X
  177. X  GNUREADLINE and EDIT require you to have GNU readline & history
  178. X  library that is distributed with 'bash' and 'gdb.  It is worth getting.
  179. X
  180. X  GNUREADLINE - make msend use the GNU readline & history library on
  181. X                the user interface.  This gives you bash/tcsh style
  182. X                input line editing.  
  183. X
  184. X  EDIT - Only meaningful if GNUREADLINE is defined.  Set to ON or OFF,
  185. X         EDIT determines whether input line editing is enabled or not.
  186. X         OFF is safest, because readline can fail on terminals that
  187. X         use 7 bits with parity.
  188. X
  189. X  The remaining options are for OS dependences,  The correct ones are
  190. X  automatically defined for SUNOS, AIX and IRIX.  You should only need
  191. X  these if you are porting to a new system.
  192. X
  193. X  USE_LOCKF - Use lockf() instead of flock().
  194. X
  195. X  NEEDS_LOCK - If your system doesn't have flock() *NOR* lockf().  If
  196. X               you define this it's possible that you'll get some
  197. X               conflict in writing to spool files.
  198. X
  199. X  NOHERROR - If your systems libraries do not contain h_errno.
  200. X
  201. X  SYSVUTMP - If your system uses a SYSV style "utmp" file.
  202. X
  203. X  SYSV_WAIT_STATUS - If you systems wait status is int not 'union wait'.
  204. X
  205. X  SYSV_SETUID - If your system lacks seteuid(2) and setruid(2),
  206. X                but does have SYSV setuid(2) semantics.
  207. X
  208. X  NO_BZERO -  If your system does not have bzero() and bcopy().  This
  209. X              will use memset and mcopy instead.
  210. X  In Makefile:
  211. X
  212. X  READLINELIBS - if you are using the GNU readline/history package this
  213. X                 should be the infomation to get that library and the
  214. X                 termcap library.  If 'libreadline.a' is installed:
  215. X                    'READLINELIBS = -lreadline -ltermcap'
  216. X                 should work well.
  217. X
  218. X  LIBS -  Any sytem libs you need.  Under SUNOS you may need
  219. X          "-lresolv" if you are using DNS and have not modified the
  220. X          system libraries or changed YP to use the name server.
  221. X          Under IRIX on SGI's you may need "-lsun -lbsd" if you are
  222. X          using NIS (YP).  For SCO UNIX-ODT you will need "-lx -lsocket".
  223. X
  224. X  DESTDIR  -  Directory to install "msend" in.
  225. X  DAEMONDIR - Directory to install the msend daemon in.
  226. X  DAEMONNAME - What to call the daemon  (SUN likes "in.msendd"),
  227. X               others like "msendd"
  228. X  MANDIR - Directory to install the man page in.
  229. X  MANSEC - Man section (i.e '1', 'l', or 'n').
  230. X
  231. X
  232. X  PORT NUMBER
  233. X
  234. X  Msend determines which port to use by first looking in /etc/services
  235. X  and if it's not there, using a port number that's fixed at
  236. X  compilation time.  The default port number is 56060, a number I
  237. X  picked out of my head.  At some point a universal number may be
  238. X  allocated, but for now use the default.
  239. X
  240. XINSTALLATION
  241. X
  242. X  On systems with the BSD install program 'make install' as root.  On
  243. X  other systems try 'make install-sysv'.
  244. X
  245. X
  246. XInitialization
  247. X
  248. X  If you build msend to run normally (you did not define ALONE)
  249. X  you will need to add entries to /etc/inetd.conf (/usr/etc/inetd.conf
  250. X  on some systems) and /etc/servers.
  251. X
  252. X  /etc/inetd.conf:
  253. X    msend   stream  tcp     nowait  root    /usr/etc/in.msendd  in.msendd
  254. X
  255. X  /etc/services:
  256. X    msend           56060/tcp
  257. X
  258. X  If your are using NIS (YP) you will need to rebuild the maps.  In
  259. X  any case you need to 'kill -HUP' the inetd process.
  260. X
  261. XSend bugs, fixes, and ports to 'msend-bugs@world.std.com'.
  262. END_OF_FILE
  263.   if test 4814 -ne `wc -c <'Configuration'`; then
  264.     echo shar: \"'Configuration'\" unpacked with wrong size!
  265.   fi
  266.   # end of 'Configuration'
  267. fi
  268. if test -f 'Protocol' -a "${1}" != "-c" ; then 
  269.   echo shar: Will not clobber existing file \"'Protocol'\"
  270. else
  271.   echo shar: Extracting \"'Protocol'\" \(7194 characters\)
  272.   sed "s/^X//" >'Protocol' <<'END_OF_FILE'
  273. X        User-To-User Message Transfer Protocol (UMTP)
  274. X
  275. X                 by Jim Frost
  276. X
  277. X                 May 18, 1988
  278. X
  279. XINTRODUCTION
  280. X
  281. XIn order to facilitate a real-time user-to-user communication system
  282. Xbetween networked hosts, the User-To-User Message Transfer Protocol
  283. X(UMTP) was developed.  When fully implemented, it supports manual
  284. Xrouting, remote broadcasts, and remote terminal selection.
  285. X
  286. XPROTOCOL DESCRIPTION
  287. X
  288. XUMTP works on a request-reply basis.  Every request must have a reply.
  289. XRequests are always send to a host, replies always come from a host.
  290. X
  291. XMESSAGE REQUEST PACKETS
  292. X
  293. XA message request packet is structured as follows:
  294. X
  295. X  u_short taddrlen;        /* length of taddr string */
  296. X  u_short tttylen;         /* length of ttty string */
  297. X  u_short msglen;          /* length of msg string */
  298. X  u_short fwdcount;        /* forwarding count */
  299. X  u_short mode;            /* mode bits */
  300. X  char    taddr[taddrlen]; /* "to" address string */
  301. X  char    ttty[tttylen];   /* "to" tty string */
  302. X  char    msg[msglen];     /* message string */
  303. X
  304. XU_short's are unsigned short integers in network byte order.
  305. X
  306. XStrings are non-null terminated ASCII.  Their lengths are determined
  307. Xby the length fields that preceed them.
  308. X
  309. XThe "taddr" field describes the user name that the packet is being
  310. Xsent to on the daemon's host.  Optionally a packet can be routed by
  311. Xappending "@host" to the user's name (eg "madd@bu-it").  Each daemon
  312. Xreceiving a routed packet should strip the destination host out of the
  313. Xtaddr field and send the remainder of the field.
  314. X
  315. XAs an example of how the taddr field should be treated at successive
  316. Xstops, consider the user request to deliver a message to
  317. X"madd@bu-it@bucsf".  The client will strip off the "@bucsf", connect
  318. Xto the bucsf daemon, and transmit a packet with the taddr field set to
  319. X"madd@bu-it".  Bucsf will in turn strip off the "@bu-it" and transmit
  320. Xa packet with the taddr field set to "madd".  Bu-it will then attempt
  321. Xto deliver the message to user "madd".
  322. X
  323. XThe taddr field cannot be longer than 1024 characters.
  324. X
  325. XAfter making the original connection, all subsequent message packets
  326. Xshould have the same taddr field.  Some hosts may allow different
  327. Xtaddr fields, but it is not recommended and should be avoided.
  328. X
  329. XThe "ttty" field is only used when sending to a particular terminal
  330. X(see the section on the mode field, which follows).  It is ignored
  331. Xotherwise; tttylen should be zero (indicating no ttty field).  The
  332. Xttty field should contain the minimum necessary string to identify the
  333. Xdestination terminal (eg "tty01" and "console" on BSD UNIX systems).
  334. XThe ttty field cannot be longer than 10 characters.
  335. X
  336. XThe "msg" field is used to store the message to be sent in its
  337. Xentirety.  The message should contain the text string that is to
  338. Xbe written to the destination user's terminal.  The string should
  339. Xcontain a "signature" which indicates (at a minimum) the sender's name
  340. Xand machine.  A good example msg field is:
  341. X
  342. X  "madd@bu-it: this is an example message"
  343. X
  344. XValid ASCII characters are 7, 9, 10, and 32 through 126.  These are
  345. X^G (bell), ^I (tab), ^J (newline), and space through tilde (~).  A
  346. Xnull string (msglen zero) indicates that the packet is to be sent to
  347. Xthe destination daemon (including any routing) but not processed at
  348. Xthat point.  This is usually used to request the closing of an open
  349. Xconnection.  The msg field cannot be longer than 1024 characters.
  350. X
  351. XThe "mode" field gives information describing what to do with the
  352. Xincoming packet.
  353. X
  354. X  SM_CLOSE     1 /* close connection after reply */
  355. X  SM_TTY       2 /* send to tty */
  356. X  SM_BROADCAST 4 /* broadcast */
  357. X
  358. XIf SM_CLOSE is specified, the network connection is to be closed
  359. Xfollowing the reply packet; if it is not specified, the connection
  360. Xmust remain open.
  361. X
  362. XIf SM_TTY is specified, the message should not be sent to a user, but
  363. Xinstead to a particular terminal.  The ttty field must have the
  364. Xterminal description.  If a user name is given in the taddr field, it
  365. Xshould be ignored.
  366. X
  367. XIf SM_BROADCAST is specified, a broadcast message is te be attempted
  368. Xon the destination host.  If a user name is given in the taddr field
  369. Xor a terminal name in the ttty field, it should be ignored.  If
  370. XSM_BROADCAST is specified in conjunction with SM_TTY, SM_TTY is
  371. Xignored.  SM_BROADCAST requests should always return a reply error
  372. Xnumber of RE_OK (message delivered) or RE_NOBROAD (broadcasting
  373. Xdisabled).
  374. X
  375. XThe "fwdcount" field is used when a destination host allows users to
  376. Xforward messages automatically (in a way similar to the BSD UNIX
  377. X.forward file).  Whenever a message is forwarded automatically (as
  378. Xopposed to manual routing), this field should be incremented by one.
  379. XAfter 5 hops, RE_FWDLOOP should be returned.  This insures that
  380. Xautomatic forwarding cannot cause an infinite loop of message
  381. Xforwards.  If the message is not being automatically forwarded, this
  382. Xfield should be passed without change.
  383. X
  384. XREPLY PACKETS
  385. X
  386. XA reply packet is structured as follows:
  387. X
  388. X  u_short errno;       /* error number */
  389. X  u_short msglen;      /* length of msg field */
  390. X  char    msg[msglen]; /* text string describing the error (if any) */
  391. X
  392. XValid error numbers are:
  393. X
  394. X  RE_OK       0 /* message delivered ok */
  395. X  RE_SYSERR   1 /* system error */
  396. X  RE_NOUSER   2 /* user doesn't exist on this host */
  397. X  RE_NOMSGS   3 /* user's terminal may not be written to */
  398. X  RE_NOTTHERE 4 /* user is not logged on at the destination */
  399. X  RE_NOROUTE  5 /* routing is denied at this host */
  400. X  RE_NOBROAD  6 /* broadcasting is denied at this host */
  401. X  RE_FWDLOOP  7 /* forwarding loop (too many forwards) */
  402. X  RE_INTERR   8 /* something really really bad happened */
  403. X
  404. XRE_OK indicates that all went well.  This is the only error that does
  405. Xnot cause automatic termination of a connection.
  406. X
  407. XRE_SYSERR indicates that some kind of system error occurred which
  408. Xcaused delivery to fail.
  409. X
  410. XRE_NOUSER indicates that the requested user does not have an account
  411. Xon the destination machine.
  412. X
  413. XRE_NOMSGS indicates that the requested user is logged on, but is not
  414. Xreceiving messages at any of his terminals.
  415. X
  416. XRE_NOTTHERE indicates that the requested user is not logged on.
  417. X
  418. XRE_NOROUTE indicates that routing is disabled at one of the daemons
  419. Xalong the route.
  420. X
  421. XRE_NOBROAD indicates that system broadcasts are not allowed at the
  422. Xdestination host.
  423. X
  424. XRE_FWDLOOP indicates that automatic message forwarding went more than
  425. X5 hops before finding the end of user message forwarding requests.
  426. X
  427. XRE_INTERR indicates that the message packet caused an internal error
  428. Xin the daemon.
  429. X
  430. XAll errors returned (except RE_OK) must have a description of the
  431. Xerror in the "msg" field.  The "msg" field may not be longer than 1024
  432. Xcharacters.
  433. X
  434. XTIMEOUTS
  435. X
  436. XTo prevent the accumulation of dead or idle daemons on any host,
  437. Xconnections time out after ten minutes.  No network traffic indicates
  438. Xa timeout -- it should be done independently at each client and
  439. Xdaemon.  This insures a long enough time to deliver a message over
  440. Xlong distances and through heavy traffic, but keeps idle or dead
  441. Xconnections to a minimum.
  442. X
  443. XIf you want to keep a connection from idling out, send packets with a
  444. Xzero msglen at intervals of less than ten minutes, but be sure that no
  445. Xother packet is being transmitted at the same time.
  446. END_OF_FILE
  447.   if test 7194 -ne `wc -c <'Protocol'`; then
  448.     echo shar: \"'Protocol'\" unpacked with wrong size!
  449.   fi
  450.   # end of 'Protocol'
  451. fi
  452. if test -f 'misc.c' -a "${1}" != "-c" ; then 
  453.   echo shar: Will not clobber existing file \"'misc.c'\"
  454. else
  455.   echo shar: Extracting \"'misc.c'\" \(2714 characters\)
  456.   sed "s/^X//" >'misc.c' <<'END_OF_FILE'
  457. X/* misc.c:
  458. X *
  459. X * miscellaneous functions
  460. X *
  461. X * (c) Copyright 1988, 1989, 1990 Jim Frost.  All Rights Reserved.  Please see
  462. X * the accompanying file "Copyright" for more information.
  463. X */
  464. X
  465. X#include "Copyright"
  466. X#include "config.h"
  467. X#include "msend.h"
  468. X#ifdef M_SYSV
  469. X#include <unistd.h>                     /* Unix Standard definitions */
  470. X#endif
  471. X#if defined(_AIX) && defined(USE_LOCKF)
  472. X#include <sys/lockf.h>
  473. X#endif
  474. X
  475. Xvoid error();
  476. X
  477. X/* easy way to build error messages
  478. X */
  479. X
  480. Xvoid blderr(ri,errno,msg)
  481. Xstruct rimsg *ri;
  482. Xint    errno;
  483. Xchar   *msg;
  484. X{ ri->h.errno= errno;
  485. X  ri->h.msglen= strlen(msg);
  486. X  strcpy(ri->msg,msg);
  487. X}
  488. X
  489. Xvoid die(i)
  490. Xint i;
  491. X{ error("md terminated");
  492. X  exit(i);
  493. X}
  494. X
  495. X/* when we have a problem, call this
  496. X */
  497. X
  498. Xvoid error(s)
  499. Xchar *s;
  500. X{ int  uid;
  501. X  long t;
  502. X  char when[30];
  503. X  FILE *f;
  504. X
  505. X  time(&t);
  506. X  strcpy(when,ctime(&t));
  507. X  when[strlen(when)-1]= '\0';
  508. X  if (getuid() == ROOTUID) {
  509. X    uid= geteuid();
  510. X    seteuid(ROOTUID);
  511. X  }
  512. X  f= fopen(LOGFILE,"a");
  513. X  if (getuid() == ROOTUID)
  514. X    seteuid(uid);
  515. X  if (f != NULL) {
  516. X#ifndef USE_LOCKF
  517. X    flock(fileno(f),LOCK_EX);
  518. X#else
  519. X    lockf(fileno(f),F_TLOCK, 0);
  520. X#endif
  521. X    fprintf(f,"%s: %s\n",when,s);
  522. X#ifndef USE_LOCKF
  523. X    flock(fileno(f),LOCK_UN);
  524. X#else
  525. X    lockf(fileno(f),F_ULOCK, 0);
  526. X#endif
  527. X    fclose(f);
  528. X  }
  529. X  else
  530. X    printf("%s: %s\n",when,s);
  531. X}
  532. X
  533. X/* this returns the port number to use for communication
  534. X */
  535. X
  536. Xint portnum()
  537. X{ struct servent *se;
  538. X  int    p;
  539. X
  540. X  /* if possible, return the port number in /etc/services; if not,
  541. X   * use hardcoded default
  542. X   */
  543. X
  544. X  if ((se= getservbyname("msend","tcp")) == NULL)
  545. X    p= PORTNUM;
  546. X  else
  547. X    p= ntohs(se->s_port);
  548. X
  549. X  /* oops, someone forgot to make me setuid
  550. X   */
  551. X
  552. X  if ((p < 1024) && geteuid()) {
  553. X    printf("portnum: not setuid\n");
  554. X    exit(1);
  555. X  }
  556. X  return(p);
  557. X}
  558. X
  559. X/* find the host name within an address, put it in an array, and truncate
  560. X * the address at the hostname.
  561. X */
  562. X
  563. Xchar *striphost(addr,host)
  564. Xchar addr[];
  565. Xchar *host;
  566. X{ int a;
  567. X
  568. X  for (a= strlen(addr); (a >= 0) && (addr[a] != '@'); a--)
  569. X    ;
  570. X  if (a >= 0) {
  571. X    strcpy(host,&addr[a+1]);
  572. X    addr[a]= '\0';
  573. X    return(host);
  574. X  }
  575. X  host[0]= '\0';
  576. X  return(NULL);
  577. X}
  578. X
  579. Xchar *gethome(user)
  580. X     char *user;
  581. X{ struct passwd *pw;
  582. X
  583. X  if (! (pw= getpwnam(user)))
  584. X    return(NULL);
  585. X  return(pw->pw_dir);
  586. X}
  587. X
  588. Xint getid(user)
  589. X     char *user;
  590. X{ struct passwd *pw;
  591. X
  592. X  if (! (pw= getpwnam(user)))
  593. X    return(-1);
  594. X  return(pw->pw_uid);
  595. X}
  596. X
  597. X#ifdef NEEDS_LOCK
  598. X/* if the system needs flock, put the correct locking function in here.  if
  599. X * you leave it like this it's possible that you'll get some conflict in
  600. X * writing to spool files and such, but it's not likely and it won't hurt
  601. X * anything much.
  602. X */
  603. X
  604. Xint flock(fd, how)
  605. X     int fd, how;
  606. X{
  607. X  return(0);
  608. X}
  609. X#endif
  610. X
  611. END_OF_FILE
  612.   if test 2714 -ne `wc -c <'misc.c'`; then
  613.     echo shar: \"'misc.c'\" unpacked with wrong size!
  614.   fi
  615.   # end of 'misc.c'
  616. fi
  617. if test -f 'msend.c' -a "${1}" != "-c" ; then 
  618.   echo shar: Will not clobber existing file \"'msend.c'\"
  619. else
  620.   echo shar: Extracting \"'msend.c'\" \(13872 characters\)
  621.   sed "s/^X//" >'msend.c' <<'END_OF_FILE'
  622. X/* msend.c:
  623. X *
  624. X * user interface
  625. X *
  626. X * (c) Copyright 1988, 1989, 1990 Jim Frost.  All Rights Reserved.  Please see
  627. X * the accompanying file "Copyright" for more information.
  628. X */
  629. X
  630. X#include "Copyright"
  631. X#include "config.h"
  632. X#include "msend.h"
  633. X#include "patchlevel"
  634. X
  635. Xstatic void huh(i)
  636. Xint i;
  637. X{ char   d[10];
  638. X  char   spoolfile[MAXFILENAME+1];
  639. X  struct stat   stb;
  640. X
  641. X  sprintf(d,"-%d",i);
  642. X
  643. X#ifdef SPOOLDIR
  644. X  if (getuid() == ROOTUID)
  645. X    seteuid(ROOTUID);
  646. X  sprintf(spoolfile,"%s/%s",SPOOLDIR,whoami());
  647. X#else
  648. X  if (gethome(whoami()))
  649. X    sprintf(spoolfile,"%s/.msendmsgs",gethome(whoami()));
  650. X  else {
  651. X    printf("Can't find your home directory\n");
  652. X    exit(1);
  653. X  }
  654. X#endif
  655. X
  656. X  if (stat(spoolfile,&stb) == -1) {
  657. X    printf("No old messages\n");
  658. X    exit(0);
  659. X  }
  660. X
  661. X  execlp("tail","tail",d,spoolfile,0);
  662. X  perror("huh");
  663. X  exit(0);
  664. X}
  665. X
  666. X/* wipe out a user's spool file on demand
  667. X */
  668. X
  669. Xstatic void rmspool()
  670. X{ char s[MAXFILENAME+1];
  671. X
  672. X#ifdef SPOOLDIR
  673. X  if (getuid() == ROOTUID)
  674. X    seteuid(ROOTUID);
  675. X  sprintf(s,"%s/%s",SPOOLDIR,whoami());
  676. X#else
  677. X  if (gethome(whoami()))
  678. X    sprintf(s,"%s/.msendmsgs",gethome(whoami()));
  679. X  else {
  680. X    printf("Can't find your home directory\n");
  681. X    exit(1);
  682. X  }
  683. X#endif
  684. X
  685. X  if (unlink(s) < 0)
  686. X    perror(s);
  687. X  exit(0);
  688. X}
  689. X
  690. X/* "documentation"
  691. X */
  692. X
  693. Xstatic void version() {
  694. X  printf("   msend version %s patchlevel %s\n",VERSION,PATCHLEVEL);
  695. X  printf("   by Jim Frost (%s) & Joe Ilacqua (%s)\n",MADD,SPIKE);
  696. X  printf("   %s\n",Copyright);
  697. X}
  698. X
  699. Xstatic void usage()
  700. X{ 
  701. X  version();
  702. X#ifndef GNUREADLINE
  703. X  printf("Usage: msend username[@host] [message]\n");
  704. X  printf("       msend . [message]\n");
  705. X#else
  706. X  printf("Usage: msend [-edit] username[@host] [message]\n");
  707. X  printf("       msend [-edit] . [message]\n");
  708. X#endif
  709. X  printf("       msend -huh [# of messages to display]\n");
  710. X  printf("       msend -clean\n");
  711. X  printf("       msend -T tty [@host] [message]\n");
  712. X  printf("       msend -version [@host]\n");
  713. X#ifdef CBROADCAST
  714. X  printf("       msend -B @host [message]\n");
  715. X#endif
  716. X  exit(0);
  717. X}
  718. X
  719. Xstatic void bigsig()
  720. X{ printf("Signature too big\n");
  721. X  exit(1);
  722. X}
  723. X
  724. Xstatic void sigalrm()
  725. X{
  726. X  printf("Inactivity timeout\n");
  727. X  exit(1);
  728. X}
  729. X
  730. X/* user breaks aren't really wanted, but we let the user break out if
  731. X * he or she REALLY wants to
  732. X */
  733. X
  734. Xstatic void sigint()
  735. X{ static wasint= 0;
  736. X
  737. X  if (wasint) {
  738. X    printf("\nConnection broken\n");
  739. X    exit(1);
  740. X  }
  741. X  else {
  742. X    wasint= 1;
  743. X    printf("\nInterrupt received -- one more to kill\n");
  744. X  }
  745. X}
  746. X
  747. Xmain(argc,argv)
  748. Xint  argc;
  749. Xchar *argv[];
  750. X{ int    a,b;
  751. X  int    broadcast;
  752. X  int    totty;
  753. X  int    uid;
  754. X  long   when;
  755. X  char   sig[MAXSIGNATURE+1];
  756. X  char   sigfmt[MAXSIGNATURE+1];
  757. X  char   line[MAXLINE+1];
  758. X  char   token[MAXTOKEN+1];
  759. X  char   fname[MAXFILENAME+1];
  760. X  char   localhost[MAXHOSTNAME+1];
  761. X  char   tmphost[MAXHOSTNAME+1];
  762. X  struct hostent *hp;
  763. X#ifdef GNUREADLINE
  764. X  char *gnubuf, *GNUGets();
  765. X  extern int rl_insert();
  766. X  short edit = EDIT;
  767. X#endif
  768. X  FILE   *f;
  769. X  struct simsg  si;
  770. X  struct rimsg  ri;
  771. X
  772. X  /* find out who we really are before becoming effectively root
  773. X   */
  774. X
  775. X  whoami();
  776. X
  777. X  /* since tty termination requests can leave hanging daemons, ignore
  778. X   * them.  user can still force termination, but not so easy.
  779. X   */
  780. X
  781. X  signal(SIGINT,sigint);
  782. X
  783. X  /* daemon will timeout after 10 minutes of inactivity, so we might
  784. X   * as well do it too.
  785. X   */
  786. X
  787. X  alarm(LIFETIME);
  788. X  signal(SIGALRM,sigalrm);
  789. X
  790. X  /* swap ruid and euid so our real id is root.  this enables us to toggle
  791. X   * between root uid-ness and nonroot uid-ness as necessary.
  792. X   */
  793. X
  794. X  if (geteuid() == ROOTUID) {
  795. X    uid= getuid();
  796. X    setruid(geteuid());
  797. X    seteuid(uid);
  798. X  }
  799. X
  800. X  /* please, no piping
  801. X   */
  802. X
  803. X  if (!isatty(0)) {
  804. X    printf("Input must be a tty\n");
  805. X    exit(1);
  806. X  }
  807. X
  808. X  gethostname(tmphost,MAXHOSTNAME);
  809. X  if ((hp = gethostbyname(tmphost)) != NULL)
  810. X    (void) strncpy(localhost,hp->h_name,MAXHOSTNAME);
  811. X  else /* better than nothing */
  812. X    (void) strncpy(localhost,tmphost,MAXHOSTNAME);
  813. X  /* defaults
  814. X   */
  815. X
  816. X  broadcast= 0;
  817. X  totty= 0;
  818. X  sprintf(sig,"%s@%s (%s): ",whoami(),localhost,ttyname(0)+5);
  819. X  si.ttty[0]= '\0';                /* ttyname(0)+5 to strip "/dev/" */
  820. X
  821. X  /* look at options file
  822. X   */
  823. X
  824. X  sprintf(fname,"%s/.msendrc",gethome(whoami()));
  825. X  if ((f= fopen(fname,"r")) != NULL) {
  826. X    while (fgets(line,MAXLINE,f) != NULL) {
  827. X      sscanf(line,"%s",token);
  828. X
  829. X
  830. X      /* user define history size - the default is unlimited
  831. X       */
  832. X      if (!strcmp(token,"history")) {
  833. X    /* If we are not using the GNU readline stuff history is meaningless,
  834. X     * but that is no reason for it to cause an error...
  835. X     */
  836. X#ifdef GNUREADLINE
  837. X    int n;
  838. X    if (sscanf (line, "%*s %d", &n) == 1) 
  839. X      stifle_history (n);
  840. X    else {
  841. X      printf("Bad history value in .msendrc\n");
  842. X      exit(1);
  843. X    }
  844. X#endif
  845. X    continue;
  846. X      }
  847. X      if (!strcmp(token,"editing_mode")) {
  848. X    /* If we are not using the GNU readline stuff history is meaningless,
  849. X     * but that is no reason for it to cause an error...
  850. X     */
  851. X#ifdef GNUREADLINE
  852. X    char buf[10];
  853. X    if (sscanf (line, "%*s \"%s\"", buf) == 1) {
  854. X      if (!strncmp(buf,"vi",2))
  855. X        rl_vi_editing_mode();
  856. X      else if (!strncmp(buf,"emacs",5)) /* The default is emacs, but...*/
  857. X        rl_emacs_editing_mode();
  858. X      else {
  859. X        printf("Bad editor value in .msendrc\n");
  860. X        exit(1);
  861. X      }
  862. X    }
  863. X    else {
  864. X      printf("Bad editor value in .msendrc\n");
  865. X      exit(1);
  866. X    }
  867. X#endif
  868. X    continue;
  869. X      }
  870. X      if (!strcmp(token,"edit")) {
  871. X    /* If we are not using the GNU readline stuff history is meaningless,
  872. X     * but that is no reason for it to cause an error...
  873. X     */
  874. X#ifdef GNUREADLINE
  875. X    char buf[10];
  876. X    if (sscanf (line, "%*s \"%s\"", buf) == 1) {
  877. X      if (!strncmp(buf,"on",2))
  878. X        edit = 1;
  879. X      else if (!strncmp(buf,"off",3)) /* The default is emacs, but...*/
  880. X        edit = 0;
  881. X      else {
  882. X        printf("Bad editor value in .msendrc\n");
  883. X        exit(1);
  884. X      }
  885. X    }
  886. X    else {
  887. X      printf("Bad editor value in .msendrc\n");
  888. X      exit(1);
  889. X    }
  890. X#endif
  891. X    continue;
  892. X      }
  893. X
  894. X      /* user defined signature
  895. X       */
  896. X
  897. X      if (!strcmp(token,"signature")) {
  898. X        for (a= 0; (line[a] != '\0') && (line[a] != '"'); a++)
  899. X          ;
  900. X        if (line[a] == '\0') {
  901. X          printf("Signature needs a quoted string\n");
  902. X          exit(1);
  903. X        }
  904. X        for (a++, b= 0; (line[a] != '\0') && (line[a] != '"')
  905. X                         && (b <= MAXSIGNATURE); a++)
  906. X          sigfmt[b++]= line[a];
  907. X        if (line[a] != '"') {
  908. X          printf("Signature format string has no end quotes or is too long\n");
  909. X          exit(1);
  910. X        }
  911. X        sigfmt[b]= '\0';
  912. X
  913. X        /* parse signature format and build the signature
  914. X         */
  915. X
  916. X        sprintf(sig,"%s@%s ",whoami(),localhost); /* always include this */
  917. X        for (b= 0; sigfmt[b] != '\0'; b++)
  918. X          if (sigfmt[b] == '%')
  919. X            switch (sigfmt[++b]) {
  920. X              case '%' :
  921. X                if (strlen(sig) >= MAXSIGNATURE)
  922. X                  bigsig();
  923. X                strcat(sig,"%");
  924. X                break;
  925. X              case 'd' : /* date and time */
  926. X                if (strlen(sig) + strlen(ctime(&when)) > MAXSIGNATURE)
  927. X                  bigsig();
  928. X                time(&when);
  929. X                strcat(sig,ctime(&when));
  930. X                sig[strlen(sig)-9]= '\0';
  931. X                break;
  932. X              case 't' : /* tty */
  933. X                if (strlen(sig) + strlen(ttyname(0)+5) > MAXSIGNATURE)
  934. X                  bigsig();
  935. X                strcat(sig,ttyname(0)+5);
  936. X                break;
  937. X              default :
  938. X                a= strlen(sig);
  939. X                sig[a]= sigfmt[b];
  940. X                sig[a+1]= '\0';
  941. X            }
  942. X          else {
  943. X            a= strlen(sig);
  944. X            sig[a]= sigfmt[b];
  945. X            sig[a+1]= '\0';
  946. X          }
  947. X        strcat(sig,": ");
  948. X      }
  949. X    }
  950. X  fclose(f);
  951. X  }
  952. X
  953. X  /* figure out options
  954. X   */
  955. X
  956. X  for (a= 1; (a < argc) && (*argv[a] == '-'); a++) {
  957. X
  958. X    /* the "huh" function
  959. X     */
  960. X
  961. X    if (!strcmp(argv[a],"-huh")) {
  962. X      if (++a < argc)
  963. X        huh(atoi(argv[a]));
  964. X      else
  965. X        huh(1);
  966. X    }
  967. X
  968. X    /* deliberate forgetfulness option
  969. X     */
  970. X
  971. X    if (!strcmp(argv[a],"-clean"))
  972. X      rmspool();
  973. X
  974. X    else if (!strcmp(argv[a],"-edit"))
  975. X#ifdef GNUREADLINE
  976. X      edit = !edit;
  977. X#else
  978. X    ;
  979. X#endif
  980. X    else if ((!strcmp(argv[a],"-version")) || *(argv[a]+1) == 'v') {
  981. X      if (argc == 2) {
  982. X    version();
  983. X    exit(0);
  984. X      } else if ( argc == 3 ) {
  985. X    if (*argv[2] != '@')
  986. X      usage();
  987. X    strcpy(si.taddr,argv[2]);
  988. X        (void) striphost(si.taddr,si.tohost);
  989. X    si.fwdcount= 0;
  990. X    si.msg[0]= '\0';
  991. X    sendmessage(&si,&ri,SM_CLOSE|SM_VERSION);
  992. X    if (ri.msg[0] == '\0') {
  993. X      while(striphost(si.taddr,si.tohost))
  994. X        strcpy(si.taddr,si.tohost);
  995. X      printf ("%s is running a pre-1.0 version of msend\n",si.taddr);
  996. X    } else
  997. X      printf("%s\n",ri.msg);
  998. X    exit(0);
  999. X      } else
  1000. X    usage();
  1001. X    }
  1002. X
  1003. X    /* decode option(s)
  1004. X     */
  1005. X
  1006. X    else switch (*(argv[a]+1)) {
  1007. X      case 'B' :
  1008. X#ifdef CBROADCAST
  1009. X        broadcast= SM_BROADCAST;
  1010. X        break;
  1011. X#else
  1012. X        printf("Broadcasting is not allowed from this host\n");
  1013. X        exit(1);
  1014. X#endif
  1015. X      case 'c' : /* short form of -clean for lazy people like me */
  1016. X        rmspool();
  1017. X    break;
  1018. X      case 'e' : /* short form of -edit for lazy people like jim */
  1019. X#ifdef GNUREADLINE
  1020. X    edit = !edit;
  1021. X#endif
  1022. X    break;
  1023. X      case 'T' :
  1024. X        totty= SM_TTY;
  1025. X        if (*(argv[a]+2) == '\0') {
  1026. X          if (++a == argc) {
  1027. X            printf("Tty name missing\n");
  1028. X            exit(1);
  1029. X          }
  1030. X          else if (strlen(argv[a]) > MAXTTY) {
  1031. X            printf("Tty name too long\n");
  1032. X            exit(1);
  1033. X          }
  1034. X          else
  1035. X            strcpy(si.ttty,argv[a]);
  1036. X        }
  1037. X        else if (strlen(argv[a]+2) > MAXTTY) {
  1038. X          printf("Tty name too long\n");
  1039. X          exit(1);
  1040. X        }
  1041. X        else
  1042. X          strcpy(si.ttty,argv[a]+2);
  1043. X        break;
  1044. X      default :
  1045. X        usage();
  1046. X    }
  1047. X  }
  1048. X
  1049. X  if ((!totty) && (a == argc))
  1050. X    usage();
  1051. X
  1052. X  if (broadcast && totty) {
  1053. X    printf("Broadcast and tty selection functions are mutually exclusive\n");
  1054. X    exit(1);
  1055. X  }
  1056. X
  1057. X  /* argument verification and "last send" function
  1058. X   */
  1059. X
  1060. X  if ((!totty) && (!broadcast)) {
  1061. X    sprintf(fname,"%s/.lastmsend",gethome(whoami()));
  1062. X    if (!strcmp(argv[a],".")) {
  1063. X      if ((f= fopen(fname,"r")) == NULL) {
  1064. X        printf("Last recipient name unknown\n");
  1065. X        exit(1);
  1066. X      }
  1067. X      else {
  1068. X        fscanf(f,"%s",si.taddr);
  1069. X        fclose(f);
  1070. X        if (!striphost(si.taddr,si.tohost))
  1071. X          strcpy(si.tohost,localhost);
  1072. X      }
  1073. X      a++;
  1074. X    }
  1075. X    else {
  1076. X
  1077. X      /* get name from command line argument and save it if we can
  1078. X       */
  1079. X
  1080. X      if (*argv[a] == '@') {
  1081. X        printf("You must specify a username\n");
  1082. X        exit(1);
  1083. X      }
  1084. X      strcpy(si.taddr,argv[a]);
  1085. X      if ((f= fopen(fname,"w")) != NULL) {
  1086. X        fprintf(f,"%s\n",argv[a]);
  1087. X        fclose(f);
  1088. X      }
  1089. X      if (!striphost(si.taddr,si.tohost))
  1090. X        strcpy(si.tohost,localhost);
  1091. X      a++;
  1092. X    }
  1093. X  }
  1094. X  else if (totty) {
  1095. X    if ((a < argc) && (*argv[a] == '@'))
  1096. X      strcpy(si.tohost,argv[a++]+1);
  1097. X    else
  1098. X      strcpy(si.tohost,localhost);
  1099. X  }
  1100. X  else if (*argv[a] != '@') { /* broadcast */
  1101. X    printf("You must indicate '@host' for a broadcast\n");
  1102. X    exit(1);
  1103. X  }
  1104. X  else {
  1105. X    strcpy(si.taddr,argv[a++]);
  1106. X    (void) striphost(si.taddr,si.tohost);
  1107. X  }
  1108. X
  1109. X  if (a < argc) {
  1110. X
  1111. X    /* command line mode
  1112. X     */
  1113. X
  1114. X    strcpy(&si.msg[0],sig); /* copy signature into message area */
  1115. X    for (; a < argc; a++) {
  1116. X      strcat(si.msg,argv[a]);
  1117. X      if (a != argc-1)
  1118. X        strcat(si.msg," ");
  1119. X    }
  1120. X    si.fwdcount= 0;
  1121. X    sendmessage(&si,&ri,SM_CLOSE|broadcast|totty);
  1122. X    if (ri.h.errno != RE_OK)
  1123. X      printf("%s\n",ri.msg);
  1124. X    exit(0);
  1125. X  }
  1126. X
  1127. X  /* make initial connection to see if we can
  1128. X   */
  1129. X
  1130. X  si.msg[0]= '\0';
  1131. X  sendmessage(&si,&ri,0);
  1132. X  if (ri.h.errno != RE_OK) {
  1133. X    printf("%s\n",ri.msg);
  1134. X    exit(1);
  1135. X  }
  1136. X
  1137. X  strcpy(&si.msg[0],sig); /* copy signature into message area */
  1138. X
  1139. X  for (;;) {
  1140. X    int once = 0;
  1141. X#ifdef GNUREADLINE
  1142. X    if (!edit) {
  1143. X#endif
  1144. X      printf("msend>");
  1145. X      if (fgets(&si.msg[strlen(sig)],MAXMSG-strlen(sig),stdin) == NULL) {
  1146. X    printf("^D\n");                       /* EOF */
  1147. X    si.msg[0]= '\0';                      /* send null message to close */
  1148. X    sendmessage(&si,&ri,SM_CLOSE);        /* the connection */
  1149. X    exit(0);
  1150. X      }
  1151. X#ifdef GNUREADLINE
  1152. X    } else {
  1153. X      rl_bind_key('\t',rl_insert); /* The default is "complete" */
  1154. X      bzero(&si.msg[strlen(sig)],MAXMSG-strlen(sig));
  1155. X      if((gnubuf = GNUGets("msend>")) == NULL) {
  1156. X    printf("^D\n");                       /* EOF */
  1157. X    si.msg[0]= '\0';                      /* send null message to close */
  1158. X    sendmessage(&si,&ri,SM_CLOSE);        /* the connection */
  1159. X    exit(0);
  1160. X      } else  {
  1161. X    strncpy(&si.msg[strlen(sig)],gnubuf,MAXMSG-strlen(sig)-1);
  1162. X    free(gnubuf);
  1163. X      }
  1164. X    }
  1165. X#endif
  1166. X    
  1167. X    alarm(LIFETIME);                         /* reset idle out timer */
  1168. X#ifdef GNUREADLINE
  1169. X    if (!edit)
  1170. X#endif
  1171. X      si.msg[strlen(si.msg)-1]= '\0';          /* strip newline */
  1172. X
  1173. X    if (!strcmp(&si.msg[strlen(sig)],".")) { /* exit command */
  1174. X      si.msg[0]= '\0';
  1175. X      sendmessage(&si,&ri,SM_CLOSE);
  1176. X      exit(0);
  1177. X    }
  1178. X      
  1179. X    if (si.msg[strlen(sig)] != '\0') {
  1180. X      si.fwdcount= 0;
  1181. X      sendmessage(&si,&ri,broadcast|totty);
  1182. X      switch (ri.h.errno) {
  1183. X        case RE_OK :
  1184. X          break;
  1185. X    case RE_NOTFATAL:
  1186. X          printf("%s\n",ri.msg);
  1187. X      break;
  1188. X        default :
  1189. X          printf("%s\n",ri.msg);
  1190. X          exit(1);               /* connection is already broken */
  1191. X      }
  1192. X    }
  1193. X    if (!once) {
  1194. X      /* This is some code to strip off the domain name of the host 
  1195. X       * after we have sent one message.  It works because we know the 
  1196. X       * signature is in the form "<user>@<host><SPACE>...".  If that 
  1197. X       * format changes this will break...
  1198. X       */
  1199. X      int i,j;
  1200. X      once++;
  1201. X      for (i = 0; sig[i] != ' '; i++) {
  1202. X    if (sig[i] == '.') {
  1203. X      for (j = i+1; sig[j] != ' '; j++);
  1204. X      strcpy(&sig[i],&sig[j]);
  1205. X      strcpy(&si.msg[0],sig); /* copy signature into message area */
  1206. X      break;
  1207. X    }
  1208. X      }
  1209. X    }    
  1210. X  }
  1211. X}
  1212. X
  1213. END_OF_FILE
  1214.   if test 13872 -ne `wc -c <'msend.c'`; then
  1215.     echo shar: \"'msend.c'\" unpacked with wrong size!
  1216.   fi
  1217.   # end of 'msend.c'
  1218. fi
  1219. if test -f 'network.c' -a "${1}" != "-c" ; then 
  1220.   echo shar: Will not clobber existing file \"'network.c'\"
  1221. else
  1222.   echo shar: Extracting \"'network.c'\" \(4982 characters\)
  1223.   sed "s/^X//" >'network.c' <<'END_OF_FILE'
  1224. X/* network.c:
  1225. X *
  1226. X * raw networking commands.  these commands are independent of the
  1227. X * application protocol; they are not tuned specifically for msend,
  1228. X * but instead make up a library that i use to work with networking.
  1229. X *
  1230. X * (c) Copyright 1988, 1989, 1990 Jim Frost.  All Rights Reserved.  Please see
  1231. X * the accompanying file "Copyright" for more information.
  1232. X */
  1233. X
  1234. X#include "Copyright"
  1235. X#include "config.h"    /* for NOHERROR */
  1236. X#include "msend.h"     /* for MAXHOSTNAME */
  1237. X#include <sys/errno.h>
  1238. X
  1239. X#define MAXCONNECTS 1  /* max number of connects we need */
  1240. X
  1241. Xextern errno;
  1242. Xextern int h_errno;
  1243. X
  1244. Xstatic int  init= 1;
  1245. Xstatic int  socki[MAXCONNECTS];
  1246. Xstatic char sockh[MAXCONNECTS][MAXHOSTNAME+1];
  1247. X
  1248. Xstatic void hinit()
  1249. X{ int a;
  1250. X
  1251. X  for (a= 0; a < MAXCONNECTS; a++)
  1252. X    socki[a]= -1;
  1253. X  init= 0;
  1254. X}
  1255. X
  1256. Xint hopen(hostname)
  1257. Xchar *hostname;
  1258. X{ struct sockaddr_in sa;
  1259. X  struct hostent     *hp;
  1260. X  unsigned long address = 0;
  1261. X  unsigned long inet_addr();
  1262. X  int a,i;
  1263. X#ifdef SECURE_PORT
  1264. X  int lport = IPPORT_RESERVED - 1;
  1265. X  int uid;
  1266. X#endif
  1267. X
  1268. X  if (init)
  1269. X    hinit();
  1270. X
  1271. X  /* find host table entry
  1272. X   */
  1273. X
  1274. X  if ((address = inet_addr(hostname)) == (unsigned long) -1) {
  1275. X    address = 0;
  1276. X    if((hp= gethostbyname(hostname)) == NULL) {
  1277. X      errno= ECONNREFUSED;
  1278. X#ifdef NOHERROR
  1279. X      h_errno = 1; /* Unknown Host */
  1280. X#endif
  1281. X      return(-1);
  1282. X    }
  1283. X  }
  1284. X
  1285. X  h_errno = 0;
  1286. X
  1287. X
  1288. X  /* see if we're already talking
  1289. X   */
  1290. X
  1291. X  for (a= 0; (a < MAXCONNECTS) && ((socki[a] == -1) ||
  1292. X             strcmp(sockh[a], address ? hostname : hp->h_name)); a++)
  1293. X    ;
  1294. X  if (a < MAXCONNECTS) /* great!  don't need a connection */
  1295. X    return(socki[a]);
  1296. X
  1297. X  /* find an empty spot
  1298. X   */
  1299. X
  1300. X  for (a= 0; (a < MAXCONNECTS) && (socki[a] != -1); a++)
  1301. X    ;
  1302. X  if (a >= MAXCONNECTS) {
  1303. X    errno= EMFILE;
  1304. X    return(-1);
  1305. X  }
  1306. X
  1307. X  strcpy(sockh[a],address ? hostname : hp->h_name);
  1308. X  bzero(&sa,sizeof(sa));
  1309. X  if (getprotobyname("tcp") == NULL) {
  1310. X    errno= ENOPROTOOPT;
  1311. X    return(-1);
  1312. X  }
  1313. X  if (address)
  1314. X    sa.sin_addr.s_addr = address;
  1315. X  else
  1316. X    bcopy(hp->h_addr,(char *)&sa.sin_addr,hp->h_length);
  1317. X  sa.sin_family= address ? AF_INET : hp->h_addrtype;
  1318. X  i=portnum();
  1319. X  sa.sin_port= htons((u_short)i);
  1320. X#ifndef SECURE_PORT
  1321. X  if ((socki[a]= socket(address ? AF_INET : hp->h_addrtype,SOCK_STREAM,0)) < 0)
  1322. X    return(-1);
  1323. X#else
  1324. X  uid= geteuid();
  1325. X  seteuid(ROOTUID);
  1326. X  if((socki[a] = rresvport(&lport)) < 0) {
  1327. X    seteuid(uid);
  1328. X    return(-1);
  1329. X  }
  1330. X  seteuid(uid);
  1331. X#endif
  1332. X#if defined(h_addr)  /* 4.3 or greater system. Has "h_addr_list". */
  1333. X  while((connect(socki[a],&sa,sizeof sa) < 0)) {
  1334. X    (void) close(socki[a]);
  1335. X#ifdef SECURE_PORT
  1336. X    if (errno == EADDRINUSE) {
  1337. X      lport--;
  1338. X      seteuid(ROOTUID);
  1339. X      if((socki[a] = rresvport(&lport)) < 0) {
  1340. X    seteuid(uid);
  1341. X    return(-1);
  1342. X      }
  1343. X      seteuid(uid);
  1344. X      continue;
  1345. X    }
  1346. X#endif
  1347. X    if (address) 
  1348. X      return(-1);
  1349. X    if (!hp->h_addr_list[1])
  1350. X      return(-1);
  1351. X    hp->h_addr_list++;
  1352. X    bcopy(hp->h_addr_list[0],&sa.sin_addr,hp->h_length);
  1353. X#ifndef SECURE_PORT
  1354. X    if ((socki[a]= socket( hp->h_addrtype,SOCK_STREAM,0)) < 0)
  1355. X      return(-1);
  1356. X#else
  1357. X    seteuid(ROOTUID);
  1358. X    if((socki[a] = rresvport(&lport)) < 0) {
  1359. X      seteuid(uid);
  1360. X      return(-1);
  1361. X    }
  1362. X    seteuid(uid);
  1363. X#endif
  1364. X  }
  1365. X#else
  1366. X#ifdef SECURE_PORT
  1367. Xfor(;;) {
  1368. X#endif
  1369. X  if (connect(socki[a],&sa,sizeof sa) < 0) {
  1370. X    (void) close(socki[a]);
  1371. X#ifndef SECURE_PORT
  1372. X    return(-1);
  1373. X#else
  1374. X    if (errno == EADDRINUSE) {
  1375. X      lport--;
  1376. X      seteuid(ROOTUID);
  1377. X      if((socki[a] = rresvport(&lport)) < 0) {
  1378. X    seteuid(uid);
  1379. X    return(-1);
  1380. X      }
  1381. X      seteuid(uid);
  1382. X      continue;
  1383. X    }
  1384. X    break;
  1385. X#endif
  1386. X  }
  1387. X}
  1388. X
  1389. X#endif
  1390. X  /* at this point you should be connected to the host for commands */
  1391. X
  1392. X  return(socki[a]);
  1393. X}
  1394. X
  1395. X/* close a single network connection
  1396. X */
  1397. X
  1398. Xvoid hclose(s)
  1399. Xint s;
  1400. X{ int a;
  1401. X
  1402. X  for (a= 0; (a < MAXCONNECTS) && (socki[a] != s); a++)
  1403. X  if (a < MAXCONNECTS) {
  1404. X    close(socki[a]);
  1405. X    socki[a]= -1;
  1406. X  }
  1407. X  else        /* not one of ours, but close it anyway */
  1408. X    close(s);
  1409. X}
  1410. X
  1411. X/* this closes all open network connections
  1412. X */
  1413. X
  1414. Xvoid hcleanup()
  1415. X{ int     a;
  1416. X
  1417. X  for (a= 0; a < MAXCONNECTS; a++)
  1418. X    if (socki[a] != -1) {
  1419. X      close(socki[a]);
  1420. X      socki[a]= -1;
  1421. X    }
  1422. X}
  1423. X
  1424. Xint hread(s,buf,n)
  1425. Xint  s;
  1426. Xchar *buf;
  1427. Xint  n;
  1428. X{ int bcount,                      /* counts bytes read */
  1429. X      br;                          /* bytes read this pass */
  1430. X
  1431. X  bcount= 0;
  1432. X  br= 0;
  1433. X  while (bcount < n) {             /* loop until full buffer */
  1434. X    if ((br= read(s,buf,n-bcount)) > 0) {
  1435. X      bcount += br;                /* increment byte counter */
  1436. X      buf += br;                   /* move buffer ptr for next read */
  1437. X    }
  1438. X    if (br == 0)                    /* EOF */
  1439. X      return(0);
  1440. X    if (br < 0)                    /* signal an error to the caller */
  1441. X      return(-1);
  1442. X  }
  1443. X  return(bcount);
  1444. X}
  1445. X
  1446. Xint hwrite(s,buf,n)
  1447. Xint  s;
  1448. Xchar *buf;
  1449. Xint  n;
  1450. X{ int bcount,
  1451. X      bw;
  1452. X
  1453. X  bcount=0;
  1454. X  while (bcount < n) {
  1455. X    if ((bw= write(s,buf,n-bcount))>0) {
  1456. X      bcount += bw;
  1457. X      buf += bw;
  1458. X    }
  1459. X    if (bw < 0)
  1460. X      return(-1);
  1461. X  }
  1462. X  return(bcount);
  1463. X}
  1464. END_OF_FILE
  1465.   if test 4982 -ne `wc -c <'network.c'`; then
  1466.     echo shar: \"'network.c'\" unpacked with wrong size!
  1467.   fi
  1468.   # end of 'network.c'
  1469. fi
  1470. if test -f 'sendrecv.c' -a "${1}" != "-c" ; then 
  1471.   echo shar: Will not clobber existing file \"'sendrecv.c'\"
  1472. else
  1473.   echo shar: Extracting \"'sendrecv.c'\" \(5369 characters\)
  1474.   sed "s/^X//" >'sendrecv.c' <<'END_OF_FILE'
  1475. X/* sendrecv.c:
  1476. X *
  1477. X * these routines send messages and receive replies
  1478. X *
  1479. X * (c) Copyright 1988, 1989, 1990 Jim Frost.  All Rights Reserved.  Please see
  1480. X * the accompanying file "Copyright" for more information.
  1481. X */
  1482. X
  1483. X#include "Copyright"
  1484. X#include "config.h"
  1485. X#include "msend.h"
  1486. X
  1487. Xextern int  errno;
  1488. Xextern char *sys_errlist[];
  1489. X
  1490. X#ifdef NOHERROR
  1491. Xint h_errno;
  1492. Xchar    *h_errlist[] = {
  1493. X  "Error 0",
  1494. X  "Unknown host",            /* 1 HOST_NOT_FOUND */
  1495. X  "Host name lookup failure",        /* 2 TRY_AGAIN */
  1496. X  "Unknown server error",        /* 3 NO_RECOVERY */
  1497. X  "No address associated with name",    /* 4 NO_ADDRESS */
  1498. X};
  1499. X
  1500. X#else
  1501. Xextern int h_errno;
  1502. Xextern char *h_errlist;
  1503. X#endif
  1504. X
  1505. Xstatic char *wrerr= "communications error with socket";
  1506. X
  1507. X/* send a message to another server and get a reply
  1508. X */
  1509. X
  1510. Xvoid sendmessage(si,ri,mode)
  1511. Xstruct simsg *si; /* message to send */
  1512. Xstruct rimsg *ri; /* reply we'll get */
  1513. Xint    mode;      /* communications mode */
  1514. X{ int     a;
  1515. X  int     uid;
  1516. X  static  int     s= -1;
  1517. X  struct  sheader sh;
  1518. X  struct  rheader rh;
  1519. X  u_short fwdcount;
  1520. X  u_short mmode;
  1521. X
  1522. X  /* check to see if we have too many forwards
  1523. X   */
  1524. X
  1525. X  if (si->fwdcount > MAXFORWARD) {
  1526. X    ri->h.errno= RE_FWDLOOP;
  1527. X    strcpy(ri->msg,"Forwarding loop (too many forwards)");
  1528. X    return;
  1529. X  }
  1530. X
  1531. X  /* look for illegal characters in the string.  this stops people
  1532. X   * from sending nasty codes with this
  1533. X   */
  1534. X
  1535. X  for (a= 0; a < strlen(si->msg); a++)
  1536. X    if ((si->msg[a] < ' ') && (si->msg[a] != '\t') && (si->msg[a] != '\n') &&
  1537. X        (si->msg[a] != 010)) {
  1538. X      ri->h.errno= RE_NOTFATAL;
  1539. X      strcpy(ri->msg,"Control characters are not allowed in messages (message not sent)");
  1540. X      ri->h.msglen= strlen(ri->msg);
  1541. X      return;
  1542. X    }
  1543. X
  1544. X  /* s is -1 if we're not already connected
  1545. X   */
  1546. X
  1547. X  if (s == -1) {
  1548. X
  1549. X    /* if possible, become root for duration of hopen() call so we can
  1550. X     * deal with low port numbers
  1551. X     */
  1552. X
  1553. X    if (getuid() == ROOTUID) {
  1554. X      uid= geteuid();
  1555. X      seteuid(ROOTUID);
  1556. X    }
  1557. X
  1558. X    /* return immediate error if we can't connect
  1559. X     */
  1560. X
  1561. X    if ((s= hopen(si->tohost)) < 0) {
  1562. X      if (getuid() == ROOTUID)
  1563. X        seteuid(uid);
  1564. X      if (h_errno == 0) { 
  1565. X    sprintf(ri->msg,"%s: %s",si->tohost,sys_errlist[errno]);
  1566. X    ri->h.msglen= strlen(sys_errlist[errno]);
  1567. X      } else { 
  1568. X    sprintf(ri->msg,"%s: %s",si->tohost,h_errlist[h_errno]);
  1569. X    ri->h.msglen= strlen(h_errlist[h_errno]);
  1570. X      }
  1571. X      ri->h.errno= RE_SYSERR;
  1572. X      return;
  1573. X    }
  1574. X    if (getuid() == ROOTUID)
  1575. X      seteuid(uid); /* become our old selves again */
  1576. X  }
  1577. X
  1578. X  /* format request and send it across
  1579. X   */
  1580. X
  1581. X  fwdcount= htons(si->fwdcount);
  1582. X  mmode= htons((u_short)mode);
  1583. X  sh.taddrlen= htons((u_short)strlen(si->taddr));
  1584. X  sh.tttylen= htons((u_short)strlen(si->ttty));
  1585. X  sh.msglen= htons((u_short)strlen(si->msg));
  1586. X
  1587. X  if (hwrite(s,(char *)&sh,sizeof(struct sheader)) < 0) {
  1588. X    hclose(s);
  1589. X    s= -1;
  1590. X    blderr(ri,RE_SYSERR,wrerr);
  1591. X    return;
  1592. X  }
  1593. X  if (hwrite(s,(char *)&fwdcount,sizeof(u_short)) < 0) {
  1594. X    hclose(s);
  1595. X    s= -1;
  1596. X    blderr(ri,RE_SYSERR,wrerr);
  1597. X    return;
  1598. X  }
  1599. X  if (hwrite(s,(char *)&mmode,sizeof(u_short)) < 0) {
  1600. X    hclose(s);
  1601. X    s= -1;
  1602. X    blderr(ri,RE_SYSERR,wrerr);
  1603. X    return;
  1604. X  }
  1605. X  if (hwrite(s,(char *)si->taddr,strlen(si->taddr)) < 0) {
  1606. X    hclose(s);
  1607. X    s= -1;
  1608. X    blderr(ri,RE_SYSERR,wrerr);
  1609. X    return;
  1610. X  }
  1611. X  if (hwrite(s,(char *)si->ttty,strlen(si->ttty)) < 0) {
  1612. X    hclose(s);
  1613. X    s= -1;
  1614. X    blderr(ri,RE_SYSERR,wrerr);
  1615. X    return;
  1616. X  }
  1617. X  if (hwrite(s,(char *)si->msg,strlen(si->msg)) < 0) {
  1618. X    hclose(s);
  1619. X    s= -1;
  1620. X    blderr(ri,RE_SYSERR,wrerr);
  1621. X    return;
  1622. X  }
  1623. X
  1624. X  /* Get the returned structure
  1625. X   */
  1626. X
  1627. X  hread(s,(char *)&rh,sizeof(struct rheader));
  1628. X  ri->h.errno= ntohs(rh.errno);
  1629. X  ri->h.msglen= ntohs(rh.msglen);
  1630. X  hread(s,(char *)ri->msg,ri->h.msglen);
  1631. X  ri->msg[ri->h.msglen]= '\0';
  1632. X  if ((mode & SM_CLOSE) || (ri->h.errno != RE_OK)) {
  1633. X    hclose(s);
  1634. X    s= -1;
  1635. X  }
  1636. X}
  1637. X
  1638. X/* sendreply() and recvmessage() are used only in the daemon
  1639. X */
  1640. X
  1641. X/* send reply back to whomever called us.  automatically close connection
  1642. X * if we were supposed to.
  1643. X */
  1644. X
  1645. Xvoid sendreply(s,ri,mode)
  1646. Xint s;
  1647. Xstruct rimsg *ri;
  1648. Xint mode;
  1649. X{ struct rheader rh;
  1650. X
  1651. X  rh.errno= htons(ri->h.errno);
  1652. X  rh.msglen= htons((u_short)strlen(ri->msg));
  1653. X  hwrite(s,(char *)&rh,sizeof(struct rheader));
  1654. X  hwrite(s,(char *)ri->msg,strlen(ri->msg));
  1655. X  if ((mode & SM_CLOSE) || (ri->h.errno != RE_OK))
  1656. X    hclose(s);
  1657. X}
  1658. X
  1659. X/* this gets a message from the socket.
  1660. X */
  1661. X
  1662. Xvoid recvmessage(s,si)
  1663. Xint s;
  1664. Xstruct simsg *si;
  1665. X{ struct sheader sh;
  1666. X  int    r;
  1667. X
  1668. X  /* pull the message out of the connection and into a structure
  1669. X   */
  1670. X
  1671. X  switch (hread(s,(char *)&sh,sizeof(struct sheader))) {
  1672. X  case -1:
  1673. X    error("error reading message header"); /* oh well */
  1674. X  case 0:
  1675. X    hclose(s);
  1676. X    exit(0);
  1677. X  default:
  1678. X    break;
  1679. X  }
  1680. X
  1681. X  /* adjust integer format
  1682. X   */
  1683. X
  1684. X  sh.taddrlen= ntohs(sh.taddrlen);
  1685. X  sh.tttylen= ntohs(sh.tttylen);
  1686. X  sh.msglen= ntohs(sh.msglen);
  1687. X
  1688. X  /* read the data section
  1689. X   */
  1690. X
  1691. X  r= hread(s,(char *)&si->fwdcount,sizeof(u_short));
  1692. X  r|= hread(s,(char *)&si->mode,sizeof(u_short));
  1693. X  r|= hread(s,(char *)si->taddr,sh.taddrlen);
  1694. X  r|= hread(s,(char *)si->ttty,sh.tttylen);
  1695. X  r|= hread(s,(char *)si->msg,sh.msglen);
  1696. X
  1697. X  if (r < 0) {
  1698. X    error("packet read error");
  1699. X    hclose(s);
  1700. X    exit(1);
  1701. X  }
  1702. X
  1703. X  si->fwdcount= ntohs(si->fwdcount);
  1704. X  si->mode= ntohs(si->mode);
  1705. X  si->taddr[sh.taddrlen]= '\0';
  1706. X  si->ttty[sh.tttylen]= '\0';
  1707. X  si->msg[sh.msglen]= '\0';
  1708. X}
  1709. END_OF_FILE
  1710.   if test 5369 -ne `wc -c <'sendrecv.c'`; then
  1711.     echo shar: \"'sendrecv.c'\" unpacked with wrong size!
  1712.   fi
  1713.   # end of 'sendrecv.c'
  1714. fi
  1715. if test -f 'write.c' -a "${1}" != "-c" ; then 
  1716.   echo shar: Will not clobber existing file \"'write.c'\"
  1717. else
  1718.   echo shar: Extracting \"'write.c'\" \(6464 characters\)
  1719.   sed "s/^X//" >'write.c' <<'END_OF_FILE'
  1720. X/* write.c:
  1721. X *
  1722. X * these routines are used for writing to users
  1723. X *
  1724. X * (c) Copyright 1988, 1989, 1990 Jim Frost.  All Rights Reserved.  Please see
  1725. X * the accompanying file "Copyright" for more information.
  1726. X */
  1727. X
  1728. X#include "Copyright"
  1729. X#include "config.h"
  1730. X#include "msend.h"
  1731. X#ifdef M_SYS
  1732. X#include <unistd.h>
  1733. X#endif
  1734. X#if defined(_AIX) && defined(USE_LOCKF)
  1735. X#include <sys/lockf.h>
  1736. X#endif
  1737. X
  1738. Xextern int  errno;
  1739. X
  1740. X#ifdef SECURE_PORT
  1741. Xextern short notsecure;
  1742. X#endif
  1743. X
  1744. X/* this writes to the tty specified in si and saves the message in spool
  1745. X */
  1746. X
  1747. Xstatic int ttywrite(si)
  1748. Xstruct simsg *si;
  1749. X{ FILE *f;
  1750. X  char fname[MAXFILENAME];
  1751. X  int  uid;
  1752. X
  1753. X  /* try to write to user's tty
  1754. X   */
  1755. X
  1756. X  sprintf(fname,"/dev/%s",si->ttty);
  1757. X  if ((f= fopen(fname,"w")) == NULL)
  1758. X    if (errno == EACCES)
  1759. X      return(RE_NOMSGS);
  1760. X    else
  1761. X      return(RE_SYSERR);
  1762. X#ifdef SECURE_PORT
  1763. X  if (notsecure)
  1764. X    fprintf(f,"\r\n[Bogus? %s]\r\n",si->msg);
  1765. X  else
  1766. X#endif
  1767. X    if (isatty(fileno(f)))
  1768. X      fprintf(f,"\r\n[%s]\r\n",si->msg);
  1769. X  fclose(f);
  1770. X
  1771. X  /* save the message in the spool for "huh" option
  1772. X   */
  1773. X
  1774. X  if (getuid() == ROOTUID) {
  1775. X    if ((uid= getid(si->taddr)) < 0) /* write to file as if we were user */
  1776. X      return(RE_NOUSER);
  1777. X    seteuid(uid);
  1778. X  }
  1779. X#ifdef SPOOLDIR
  1780. X  sprintf(fname,"%s/%s",SPOOLDIR,si->taddr);
  1781. X#else
  1782. X  sprintf(fname,"%s/.msendmsgs",gethome(si->taddr));
  1783. X#endif
  1784. X
  1785. X
  1786. X  if ((f= fopen(fname,"a")) != NULL) {
  1787. X#ifndef USE_LOCKF
  1788. X    flock(fileno(f),LOCK_EX);
  1789. X#else
  1790. X    lockf(fileno(f), F_TLOCK, 0);
  1791. X#endif
  1792. X    fprintf(f,"[%s]\n",si->msg);
  1793. X#ifndef USE_LOCKF
  1794. X    flock(fileno(f),LOCK_UN);
  1795. X#else
  1796. X    lockf(fileno(f), F_ULOCK, 0);
  1797. X#endif
  1798. X    fclose(f);
  1799. X  }
  1800. X
  1801. X  if (getuid() == ROOTUID)
  1802. X    seteuid(DAEMONUID);
  1803. X
  1804. X  return(RE_OK);
  1805. X}
  1806. X
  1807. Xvoid broadcast(si)
  1808. Xstruct simsg *si;
  1809. X{ char   s[80];
  1810. X  struct utmp *u;
  1811. X
  1812. X  sprintf(s,"Broadcast message %s",si->msg); /* log it! */
  1813. X  error(s);
  1814. X
  1815. X  /* loop for every utmp entry and try to write.  ignore any
  1816. X   * errors returned by ttywrite.  we have to implement the
  1817. X   * entire write command here or else we get an infinite
  1818. X   * loop on the first utmp entry.
  1819. X   */
  1820. X
  1821. X  while (u= getutent()) {
  1822. X#ifdef SYSVUTMP
  1823. X    if (u->ut_type != USER_PROCESS)
  1824. X      continue;
  1825. X#endif
  1826. X    if (strcmp(u->ut_name,"")) {
  1827. X      sprintf(si->ttty,u->ut_line);
  1828. X      sprintf(si->taddr,u->ut_name);
  1829. X      ttywrite(si);
  1830. X    }
  1831. X  }
  1832. X  endutent();
  1833. X}
  1834. X
  1835. X/* single user
  1836. X */
  1837. X
  1838. Xint writeuser(si)
  1839. Xstruct simsg     *si;
  1840. X{ struct utmp    *u;  /* utmp entry */
  1841. X  struct stat    stb;
  1842. X  struct rimsg   ri;
  1843. X  char   *home;
  1844. X  char   ttyname[sizeof("/dev/")+MAXTTY+1];
  1845. X  char   token[MAXTOKEN+1];
  1846. X  char   fname[MAXFILENAME+1];
  1847. X  char   line[MAXLINE+1];
  1848. X  FILE   *f;
  1849. X  time_t ltime;
  1850. X  int    ttystat;
  1851. X  int    notfound;
  1852. X  int    allttys;
  1853. X  int    forward;
  1854. X  int    fwderr;
  1855. X  int    ttyerr;
  1856. X  int    uid;
  1857. X
  1858. X  /* send to particular tty
  1859. X   */
  1860. X
  1861. X  if (si->mode & SM_TTY) {
  1862. X
  1863. X    /* look up user of that tty.
  1864. X     */
  1865. X
  1866. X    ttystat= RE_SYSERR;
  1867. X    while (u= getutent())
  1868. X      if (!strcmp(u->ut_line,si->ttty)) {
  1869. X        strcpy(si->taddr,u->ut_name);
  1870. X        strcpy(si->ttty,u->ut_line);
  1871. X        ttystat= ttywrite(si);
  1872. X        break;
  1873. X      }
  1874. X    endutent();
  1875. X    if (ttystat == RE_SYSERR)
  1876. X      errno= ENODEV;          /* error message for user */
  1877. X    return(ttystat);
  1878. X  }
  1879. X
  1880. X  /* open user's options file and see if they want some special
  1881. X   * propagation
  1882. X   */
  1883. X
  1884. X  allttys= 0;
  1885. X  forward= 0;
  1886. X  fwderr= RE_OK;
  1887. X
  1888. X  if ((home= gethome(si->taddr)) == NULL)
  1889. X    return(RE_NOUSER);
  1890. X  if (getuid() == ROOTUID) {
  1891. X    if ((uid= getid(si->taddr)) < 0)
  1892. X      return(RE_NOUSER);
  1893. X    seteuid(uid);
  1894. X  }
  1895. X
  1896. X  sprintf(fname,"%s/.msendrc",home);
  1897. X  if ((f= fopen(fname,"r")) != NULL) {
  1898. X    while (fgets(line,MAXLINE,f) != NULL) {
  1899. X      sscanf(line,"%s",token);
  1900. X
  1901. X      /* tty options
  1902. X       */
  1903. X
  1904. X      if (!strcmp(token,"allttys"))
  1905. X        allttys= 1;
  1906. X      else if (!strcmp(token,"write-to-tty")) {
  1907. X        sscanf(line,"%*s %s",si->ttty);
  1908. X        ttywrite(si);
  1909. X        forward= 1; /* same idea as forwarding */
  1910. X      }
  1911. X
  1912. X      /* forwarding options.  note that we return the first error that
  1913. X       * we get when forwarding so the user has some idea that something
  1914. X       * went wrong.  compound errors will be lost.  we ignore obvious
  1915. X       * host loops.
  1916. X       */
  1917. X
  1918. X      else if (!strcmp(token,"forward-to-user")) {
  1919. X        sscanf(line,"%*s %s",si->taddr);
  1920. X        if ((striphost(si->taddr,si->tohost)) && (!fwdloop(si->tohost))) {
  1921. X          si->fwdcount++;
  1922. X          forward= 1;
  1923. X          sendmessage(si,&ri,si->mode);
  1924. X          if (fwderr == RE_OK)
  1925. X            fwderr= ri.h.errno;
  1926. X    }
  1927. X      }
  1928. X      else if (!strcmp(token,"forward-to-tty")) {
  1929. X        sscanf(line,"%*s %s %s",si->ttty,si->taddr);
  1930. X        if ((striphost(si->taddr,si->tohost)) && (!fwdloop(si->tohost))) {
  1931. X          forward= 1;
  1932. X          si->fwdcount++;
  1933. X          sendmessage(si,&ri,si->mode);
  1934. X          if (fwderr == RE_OK)
  1935. X            fwderr= ri.h.errno;
  1936. X        }
  1937. X      }
  1938. X      else if (!strcmp(token,"write-and-forward-to-user")) {
  1939. X        sscanf(line,"%*s %s",si->taddr);
  1940. X        if ((striphost(si->taddr,si->tohost)) && (!fwdloop(si->tohost))) {
  1941. X          si->fwdcount++;
  1942. X          sendmessage(si,&ri,si->mode);
  1943. X          if (fwderr == RE_OK)
  1944. X            fwderr= ri.h.errno;
  1945. X        }
  1946. X      }
  1947. X    }
  1948. X    fclose(f);
  1949. X    if (forward)
  1950. X      return(fwderr);
  1951. X  }
  1952. X
  1953. X  if (getuid() == ROOTUID)
  1954. X    seteuid(DAEMONUID);
  1955. X
  1956. X  /* send to least idle user or all writable ttys, depending on mode.
  1957. X   */
  1958. X
  1959. X  ltime= (time_t)0;
  1960. X  notfound= 1;
  1961. X  ttyerr= RE_OK;
  1962. X
  1963. X  while (u= getutent()) {
  1964. X    if (!strcmp(u->ut_name,si->taddr)) {     /* match user */
  1965. X      sprintf(ttyname,"/dev/%s",u->ut_line);
  1966. X
  1967. X      if (stat(ttyname,&stb) != -1) {
  1968. X        notfound= 0;
  1969. X        if (stb.st_mode & 022) /* see if we have permission */
  1970. X          if (allttys) {
  1971. X
  1972. X            /* write to every possible tty
  1973. X             */
  1974. X
  1975. X            strcpy(si->ttty,u->ut_line);
  1976. X            if (ttyerr != RE_OK)
  1977. X              ttywrite(si);
  1978. X            else
  1979. X              ttyerr= ttywrite(si);
  1980. X          }
  1981. X          else {
  1982. X
  1983. X            /* look for least idle terminal
  1984. X             */
  1985. X
  1986. X            if (!ltime) {
  1987. X              ltime= stb.st_mtime;
  1988. X              strcpy(si->ttty,u->ut_line);
  1989. X            }
  1990. X            else if (stb.st_mtime > ltime) { /* less idle */
  1991. X              ltime= stb.st_mtime;
  1992. X              strcpy(si->ttty,u->ut_line);
  1993. X            }
  1994. X      }
  1995. X      }
  1996. X    }
  1997. X  }
  1998. X  endutent();
  1999. X
  2000. X  if (allttys)
  2001. X    return(ttyerr);
  2002. X
  2003. X  if (notfound)
  2004. X    return(RE_NOTTHERE);
  2005. X  else if (!ltime)
  2006. X    return(RE_NOMSGS);
  2007. X
  2008. X  return(ttywrite(si));
  2009. X}
  2010. END_OF_FILE
  2011.   if test 6464 -ne `wc -c <'write.c'`; then
  2012.     echo shar: \"'write.c'\" unpacked with wrong size!
  2013.   fi
  2014.   # end of 'write.c'
  2015. fi
  2016. echo shar: End of archive 1 \(of 2\).
  2017. cp /dev/null ark1isdone
  2018. MISSING=""
  2019. for I in 1 2 ; do
  2020.     if test ! -f ark${I}isdone ; then
  2021.     MISSING="${MISSING} ${I}"
  2022.     fi
  2023. done
  2024. if test "${MISSING}" = "" ; then
  2025.     echo You have unpacked both archives.
  2026.     rm -f ark[1-9]isdone
  2027. else
  2028.     echo You still must unpack the following archives:
  2029.     echo "        " ${MISSING}
  2030. fi
  2031. exit 0
  2032. exit 0 # Just in case...
  2033.