1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains code for the "zwrite" command.
4 * Created by: Robert French
6 * $Id: zwrite.c,v 1.49 1999/08/13 00:19:42 danw Exp $
8 * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file
14 #include <zephyr/mit-copyright.h>
15 #include <zephyr/zephyr.h>
20 static const char rcsid_zwrite_c[] = "$Id: zwrite.c,v 1.49 1999/08/13 00:19:42 danw Exp $";
23 #define DEFAULT_CLASS "MESSAGE"
24 #define DEFAULT_INSTANCE "PERSONAL"
25 #define URGENT_INSTANCE "URGENT"
26 #define DEFAULT_OPCODE ""
27 #define FILSRV_CLASS "FILSRV"
31 int nrecips, msgarg, verbose, quiet, nodot, cc;
32 char *whoami, *inst, *class, *opcode, *rhs, *galaxy, *recips[MAXRECIPS];
36 char *fix_filsrv_inst();
37 void usage(), send_off();
43 int retval, arg, nocheck, nchars, msgsize, filsys, tabexpand;
44 char *message, *signature = NULL, *format = NULL;
45 static char bfr[BUFSIZ], classbfr[BUFSIZ], instbfr[BUFSIZ], sigbfr[BUFSIZ];
46 static char opbfr[BUFSIZ];
47 static ZNotice_t notice;
51 if ((retval = ZInitialize()) != ZERR_NONE) {
52 com_err(whoami, retval, "while initializing");
60 verbose = quiet = msgarg = nrecips = nocheck = filsys = nodot = 0;
63 if (class = ZGetVariable("zwrite-class")) {
64 (void) strcpy(classbfr, class);
68 class = DEFAULT_CLASS;
69 if (inst = ZGetVariable("zwrite-inst")) {
70 (void) strcpy(instbfr, inst);
74 inst = DEFAULT_INSTANCE;
76 if (opcode = ZGetVariable("zwrite-opcode"))
77 opcode = strcpy(opbfr, opcode);
79 opcode = DEFAULT_OPCODE;
81 signature = ZGetVariable("zwrite-signature");
83 (void) strcpy(sigbfr, signature);
89 for (;arg<argc&&!msgarg;arg++) {
90 if (*argv[arg] != '-') {
91 recips[nrecips++] = argv[arg];
94 if (strlen(argv[arg]) > 2)
96 switch (argv[arg][1]) {
97 case 'a': /* Backwards compatibility */
101 class = DEFAULT_CLASS;
102 inst = DEFAULT_INSTANCE;
103 opcode = DEFAULT_OPCODE;
121 inst = URGENT_INSTANCE;
130 if (arg == argc-1 || filsys == 1)
137 if (arg == argc-1 || filsys == 1)
144 if (arg == argc-1 || filsys == -1)
147 class = FILSRV_CLASS;
148 inst = fix_filsrv_inst(argv[arg]);
150 nocheck = 1; /* implied -n (no ping) */
156 signature = argv[arg];
161 nocheck = 1; /* implied -n (no ping) */
164 case 'l': /* literal */
193 if (!nrecips && !(strcmp(class, DEFAULT_CLASS) ||
194 (strcmp(inst, DEFAULT_INSTANCE) &&
195 strcmp(inst, URGENT_INSTANCE)))) {
196 /* must specify recipient if using default class and
197 (default instance or urgent instance) */
198 fprintf(stderr, "No recipients specified.\n");
203 /* try to find name in the password file */
204 register struct passwd *pwd;
205 register char *cp = sigbfr;
206 register char *cp2, *pp;
208 pwd = getpwuid(getuid());
211 for (; *cp2 && *cp2 != ',' ; cp2++) {
214 *cp++ = islower(*pp) ? toupper(*pp) : *pp;
225 notice.z_kind = ACKED;
227 notice.z_class = class;
228 notice.z_class_inst = inst;
229 notice.z_opcode = "PING";
231 notice.z_message_len = 0;
232 notice.z_recipient = "";
233 notice.z_dest_galaxy = galaxy;
235 notice.z_default_format = format;
236 else if (filsys == 1)
237 notice.z_default_format = "@bold(Filesystem Operation Message for $instance:)\nFrom: @bold($sender) at $time $date\n$message";
238 else if (auth == ZAUTH) {
240 notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold($1) <$sender>\n\n$2";
242 notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\n$message";
245 notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\nFrom: @bold($1) <$sender>\n\n$2";
247 notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\n$message";
249 if (!nocheck && nrecips)
250 send_off(¬ice, 0);
252 if (!msgarg && isatty(0))
254 printf("Type your message now. End with the end-of-file character.\n");
256 printf("Type your message now. End with control-D or a dot on a line by itself.\n");
261 message = malloc((unsigned)(strlen(signature)+2));
262 (void) strcpy(message, signature);
263 msgsize = strlen(message);
264 message[msgsize++] = '\0';
267 message[msgsize++] = '\0';
270 if (cc && nrecips > 1) {
272 for (arg=0;arg<nrecips;arg++)
273 size += (strlen(recips[arg]) + 2);
274 size += 6; /* for the newlines and "cc: " */
275 message = realloc(message, (unsigned) size);
276 (void) strcpy(message+msgsize, "CC: ");
278 for (arg=0;arg<nrecips;arg++) {
279 (void) strcpy(message+msgsize, recips[arg]);
280 msgsize += strlen(recips[arg]);
281 if (arg != nrecips-1) {
282 message[msgsize] = ' ';
286 message[msgsize] = '\n';
292 for (arg=msgarg;arg<argc;arg++)
293 size += (strlen(argv[arg]) + 1);
294 size++; /* for the newline */
295 message = realloc(message, (unsigned) size);
296 for (arg=msgarg;arg<argc;arg++) {
297 (void) strcpy(message+msgsize, argv[arg]);
298 msgsize += strlen(argv[arg]);
300 message[msgsize] = ' ';
304 message[msgsize] = '\n';
310 if (!fgets(bfr, sizeof bfr, stdin))
312 if (!nodot && bfr[0] == '.' &&
313 (bfr[1] == '\n' || bfr[1] == '\0'))
316 message = realloc(message, msgsize+l+1);
317 (void) strcpy(message+msgsize, bfr);
320 message = realloc(message, (unsigned)(msgsize+1));
322 else { /* Use read so you can send binary messages... */
323 while (nchars = read(fileno(stdin), bfr, sizeof bfr)) {
325 fprintf(stderr, "Read error from stdin! Can't continue!\n");
328 message = realloc(message, (unsigned)(msgsize+nchars));
329 (void) memcpy(message+msgsize, bfr, nchars);
333 message = realloc(message, (unsigned)(msgsize+1));
337 notice.z_opcode = opcode;
339 un_tabify(&message, &msgsize);
340 notice.z_message = message;
341 notice.z_message_len = msgsize;
343 send_off(¬ice, 1);
348 send_off(notice, real)
352 int i, success, retval;
353 char bfr[BUFSIZ], rhs_recip[BUFSIZ], dest[3 * BUFSIZ], *cp;
358 for (i=0;i<nrecips || !nrecips;i++) {
360 sprintf(rhs_recip, "%s@%s", (nrecips) ? recips[i] : "", rhs);
361 notice->z_recipient = rhs_recip;
363 notice->z_recipient = (nrecips) ? recips[i] : "";
366 strcpy(dest, recips[i]);
367 else if (!strcmp(class, DEFAULT_CLASS))
368 sprintf(dest, "instance \"%s\"", inst);
369 else if (!strcmp(inst, DEFAULT_INSTANCE))
370 sprintf(dest, "class \"%s\"", class);
372 sprintf(dest, "class \"%s\", instance \"%s\"", class, inst);
374 printf("Sending %smessage, class %s, instance %s, to %s\n",
375 auth?"authenticated ":"",
377 nrecips?notice->z_recipient:"everyone");
378 if ((retval = ZSendNotice(notice, auth)) != ZERR_NONE) {
379 (void) sprintf(bfr, "while sending notice to %s", dest);
380 com_err(whoami, retval, bfr);
383 if (real && !quiet) {
385 printf("Queued... ");
387 printf("Message queued for %s... ", dest);
390 if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
392 (char *)¬ice->z_uid)) !=
394 ZFreeNotice(&retnotice);
395 (void) sprintf(bfr, "while waiting for acknowledgement for %s",
397 com_err(whoami, retval, bfr);
400 if (retnotice.z_kind == SERVNAK) {
402 printf("Received authorization failure while sending to %s\n",
405 ZFreeNotice(&retnotice);
406 break; /* if auth fails, punt */
408 if (retnotice.z_kind != SERVACK || !retnotice.z_message_len) {
410 printf("Detected server failure while receiving acknowledgement for %s\n",
413 ZFreeNotice(&retnotice);
416 if (!strcmp(retnotice.z_message, ZSRVACK_SENT)) {
420 } else if (!strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
421 if (verbose && real && !quiet) {
422 if (strcmp(class, DEFAULT_CLASS)) {
423 fprintf(stderr, "Not logged in or not subscribing to class %s, instance %s\n",
427 "Not logged in or not subscribing to messages\n");
433 "No one subscribing to class %s, instance %s\n",
436 if (strcmp(class, DEFAULT_CLASS)) {
437 fprintf(stderr, "%s: Not logged in or not subscribing to class %s, instance %s\n",
438 notice->z_recipient, class, inst);
440 fprintf(stderr, "%s: Not logged in or not subscribing to messages\n",
441 notice->z_recipient);
447 printf("Internal failure - illegal message field in server response\n");
448 ZFreeNotice(&retnotice);
461 "Usage: %s [-a] [-o] [-d] [-v] [-q] [-n] [-t] [-u] [-l]\n\
462 \t[-c class] [-i inst] [-O opcode] [-f fsname] [-s signature] \n\
463 \t[-G galaxy] [-C]\n\
464 \t[user ...] [-F format] [-r rhs] [-m message]\n", s);
465 fprintf(stderr,"\t-f and -c are mutually exclusive\n\
466 \t-f and -i are mutually exclusive\n\
467 \trecipients must be specified unless -c or -f specifies a class\n\
468 \tother than the default class or -i or -f specifies an instance\n\
469 \tother than the default or urgent instance\n");
474 if the -f option is specified, this routine is called to canonicalize
475 an instance of the form hostname[:pack]. It turns the hostname into the
476 name returned by gethostbyname(hostname)
479 char *fix_filsrv_inst(str)
482 static char fsinst[BUFSIZ];
486 ptr = strchr(str,':');
490 hp = gethostbyname(str);
496 (void) strcpy(fsinst, hp->h_name);
498 (void) strcat(fsinst, ":");
500 (void) strcat(fsinst, ptr);
505 /* convert tabs in the buffer into appropriate # of spaces.
506 slightly tricky since the buffer can have NUL's in it. */
509 #define TABSTOP 8 /* #chars between tabstops */
510 #endif /* ! TABSTOP */
513 un_tabify(bufp, sizep)
517 register char *cp, *cp2;
520 register int column; /* column of next character */
521 register int size = *sizep;
523 for (cp = *bufp, i = 0; size; size--, cp++)
525 i++; /* count tabs in buffer */
528 return; /* no tabs == no work */
530 /* To avoid allocation churning, allocate enough extra space to convert
531 every tab into TABSTOP spaces */
532 /* only add (TABSTOP-1)x because we re-use the cell holding the
534 cp = malloc((unsigned)(*sizep + (i * (TABSTOP-1))));
536 return; /* punt expanding if memory fails */
538 /* Copy buffer, converting tabs to spaces as we go */
539 for (cp2 = *bufp, column = 1, size = *sizep; size; cp2++, size--) {
543 /* newline or null: reset column */
545 *cp++ = *cp2; /* copy the newline */
548 /* copy the character */
554 /* it's a tab, compute how many spaces to expand into. */
555 i = TABSTOP - ((column - 1) % TABSTOP);
557 *cp++ = ' '; /* fill in the spaces */
559 (*sizep)++; /* increment the size */
561 (*sizep)--; /* remove one (we replaced the tab) */
565 free(*bufp); /* free the old buf */