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
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$";
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, *realm, *recips[MAXRECIPS];
34 void un_tabify(char **, int *);
36 char *fix_filsrv_inst(char *);
38 void send_off(ZNotice_t *, int);
44 int retval, arg, nocheck, nchars, msgsize, filsys, tabexpand;
45 char *message, *signature = NULL, *format = NULL;
46 static char bfr[BUFSIZ], classbfr[BUFSIZ], instbfr[BUFSIZ], sigbfr[BUFSIZ];
47 static char opbfr[BUFSIZ];
48 static ZNotice_t notice;
52 if ((retval = ZInitialize()) != ZERR_NONE) {
53 com_err(whoami, retval, "while initializing");
61 verbose = quiet = msgarg = nrecips = nocheck = filsys = nodot = 0;
64 if (class = ZGetVariable("zwrite-class")) {
65 (void) strcpy(classbfr, class);
69 class = DEFAULT_CLASS;
70 if (inst = ZGetVariable("zwrite-inst")) {
71 (void) strcpy(instbfr, inst);
75 inst = DEFAULT_INSTANCE;
77 if (opcode = ZGetVariable("zwrite-opcode"))
78 opcode = strcpy(opbfr, opcode);
80 opcode = DEFAULT_OPCODE;
82 signature = ZGetVariable("zwrite-signature");
84 (void) strcpy(sigbfr, signature);
90 for (;arg<argc&&!msgarg;arg++) {
91 if (*argv[arg] != '-') {
92 recips[nrecips++] = argv[arg];
95 if (strlen(argv[arg]) > 2)
97 switch (argv[arg][1]) {
98 case 'a': /* Backwards compatibility */
102 class = DEFAULT_CLASS;
103 inst = DEFAULT_INSTANCE;
104 opcode = DEFAULT_OPCODE;
122 inst = URGENT_INSTANCE;
131 if (arg == argc-1 || filsys == 1)
138 if (arg == argc-1 || filsys == 1)
145 if (arg == argc-1 || filsys == -1)
148 class = FILSRV_CLASS;
149 inst = fix_filsrv_inst(argv[arg]);
151 nocheck = 1; /* implied -n (no ping) */
157 signature = argv[arg];
162 nocheck = 1; /* implied -n (no ping) */
165 case 'l': /* literal */
188 if (!nrecips && !(strcmp(class, DEFAULT_CLASS) ||
189 (strcmp(inst, DEFAULT_INSTANCE) &&
190 strcmp(inst, URGENT_INSTANCE)))) {
191 /* must specify recipient if using default class and
192 (default instance or urgent instance) */
193 fprintf(stderr, "No recipients specified.\n");
198 /* try to find name in the password file */
199 register struct passwd *pwd;
200 register char *cp = sigbfr;
201 register char *cp2, *pp;
203 pwd = getpwuid(getuid());
206 for (; *cp2 && *cp2 != ',' ; cp2++) {
209 *cp++ = islower(*pp) ? toupper(*pp) : *pp;
220 notice.z_kind = ACKED;
222 notice.z_class = class;
223 notice.z_class_inst = inst;
224 notice.z_opcode = "PING";
226 notice.z_message_len = 0;
227 notice.z_recipient = "";
229 notice.z_default_format = format;
230 else if (filsys == 1)
231 notice.z_default_format = "@bold(Filesystem Operation Message for $instance:)\nFrom: @bold($sender) at $time $date\n$message";
232 else if (auth == ZAUTH) {
234 notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold($1) <$sender>\n\n$2";
236 notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\n$message";
239 notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\nFrom: @bold($1) <$sender>\n\n$2";
241 notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\n$message";
243 if (!nocheck && nrecips)
244 send_off(¬ice, 0);
246 if (!msgarg && isatty(0))
248 printf("Type your message now. End with the end-of-file character.\n");
250 printf("Type your message now. End with control-D or a dot on a line by itself.\n");
255 message = malloc((unsigned)(strlen(signature)+2));
256 (void) strcpy(message, signature);
257 msgsize = strlen(message);
258 message[msgsize++] = '\0';
261 message[msgsize++] = '\0';
264 if (cc && nrecips > 1) {
266 for (arg=0;arg<nrecips;arg++)
267 size += (strlen(recips[arg]) + 2);
268 size += 6; /* for the newlines and "cc: " */
269 message = realloc(message, (unsigned) size);
270 (void) strcpy(message+msgsize, "CC: ");
272 for (arg=0;arg<nrecips;arg++) {
273 (void) strcpy(message+msgsize, recips[arg]);
274 msgsize += strlen(recips[arg]);
275 if (arg != nrecips-1) {
276 message[msgsize] = ' ';
280 message[msgsize] = '\n';
286 for (arg=msgarg;arg<argc;arg++)
287 size += (strlen(argv[arg]) + 1);
288 size++; /* for the newline */
289 message = realloc(message, (unsigned) size);
290 for (arg=msgarg;arg<argc;arg++) {
291 (void) strcpy(message+msgsize, argv[arg]);
292 msgsize += strlen(argv[arg]);
294 message[msgsize] = ' ';
298 message[msgsize] = '\n';
304 if (!fgets(bfr, sizeof bfr, stdin))
306 if (!nodot && bfr[0] == '.' &&
307 (bfr[1] == '\n' || bfr[1] == '\0'))
310 message = realloc(message, msgsize+l+1);
311 (void) strcpy(message+msgsize, bfr);
314 message = realloc(message, (unsigned)(msgsize+1));
316 else { /* Use read so you can send binary messages... */
317 while (nchars = read(fileno(stdin), bfr, sizeof bfr)) {
319 fprintf(stderr, "Read error from stdin! Can't continue!\n");
322 message = realloc(message, (unsigned)(msgsize+nchars));
323 (void) memcpy(message+msgsize, bfr, nchars);
327 message = realloc(message, (unsigned)(msgsize+1));
331 notice.z_opcode = opcode;
333 un_tabify(&message, &msgsize);
334 notice.z_message = message;
335 notice.z_message_len = msgsize;
337 send_off(¬ice, 1);
342 send_off(ZNotice_t *notice,
345 int i, success, retval;
346 char bfr[BUFSIZ], realm_recip[BUFSIZ], dest[3 * BUFSIZ], *cp;
351 for (i=0;i<nrecips || i==0;i++) {
353 sprintf(realm_recip, "%s@%s", (nrecips) ? recips[i] : "", realm);
354 notice->z_recipient = realm_recip;
356 notice->z_recipient = (nrecips) ? recips[i] : "";
359 strcpy(dest, recips[i]);
360 else if (!strcmp(class, DEFAULT_CLASS))
361 sprintf(dest, "instance \"%s\"", inst);
362 else if (!strcmp(inst, DEFAULT_INSTANCE))
363 sprintf(dest, "class \"%s\"", class);
365 sprintf(dest, "class \"%s\", instance \"%s\"", class, inst);
367 printf("Sending %smessage, class %s, instance %s, to %s\n",
368 auth?"authenticated ":"",
370 nrecips?notice->z_recipient:"everyone");
371 if ((retval = ZSendNotice(notice, auth)) != ZERR_NONE) {
372 (void) sprintf(bfr, "while sending notice to %s", dest);
373 com_err(whoami, retval, bfr);
376 if (real && !quiet) {
378 printf("Queued... ");
380 printf("Message queued for %s... ", dest);
383 if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
385 (char *)¬ice->z_uid)) !=
387 ZFreeNotice(&retnotice);
388 (void) sprintf(bfr, "while waiting for acknowledgement for %s",
390 com_err(whoami, retval, bfr);
393 if (retnotice.z_kind == SERVNAK) {
395 printf("Received authorization failure while sending to %s\n",
398 ZFreeNotice(&retnotice);
399 break; /* if auth fails, punt */
401 if (retnotice.z_kind != SERVACK || !retnotice.z_message_len) {
403 printf("Detected server failure while receiving acknowledgement for %s\n",
406 ZFreeNotice(&retnotice);
409 if (!strcmp(retnotice.z_message, ZSRVACK_SENT)) {
413 } else if (!strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
414 if (verbose && real && !quiet) {
415 if (strcmp(class, DEFAULT_CLASS)) {
416 fprintf(stderr, "Not logged in or not subscribing to class %s, instance %s\n",
420 "Not logged in or not subscribing to messages\n");
426 "No one subscribing to class %s, instance %s\n",
429 if (strcmp(class, DEFAULT_CLASS)) {
430 fprintf(stderr, "%s: Not logged in or not subscribing to class %s, instance %s\n",
431 notice->z_recipient, class, inst);
433 fprintf(stderr, "%s: Not logged in or not subscribing to messages\n",
434 notice->z_recipient);
440 printf("Internal failure - illegal message field in server response\n");
441 ZFreeNotice(&retnotice);
451 "Usage: %s [-a] [-o] [-d] [-v] [-q] [-n] [-t] [-u] [-l]\n\
452 \t[-c class] [-i inst] [-O opcode] [-f fsname] [-s signature] [-C]\n\
453 \t[user ...] [-F format] [-r realm] [-m message]\n", s);
454 fprintf(stderr,"\t-f and -c are mutually exclusive\n\
455 \t-f and -i are mutually exclusive\n\
456 \trecipients must be specified unless -c or -f specifies a class\n\
457 \tother than the default class or -i or -f specifies an instance\n\
458 \tother than the default or urgent instance\n");
463 if the -f option is specified, this routine is called to canonicalize
464 an instance of the form hostname[:pack]. It turns the hostname into the
465 name returned by gethostbyname(hostname)
469 fix_filsrv_inst(char *str)
471 static char fsinst[BUFSIZ];
475 ptr = strchr(str,':');
479 hp = gethostbyname(str);
485 (void) strcpy(fsinst, hp->h_name);
487 (void) strcat(fsinst, ":");
489 (void) strcat(fsinst, ptr);
494 /* convert tabs in the buffer into appropriate # of spaces.
495 slightly tricky since the buffer can have NUL's in it. */
498 #define TABSTOP 8 /* #chars between tabstops */
499 #endif /* ! TABSTOP */
502 un_tabify(char **bufp,
505 register char *cp, *cp2;
508 register int column; /* column of next character */
509 register int size = *sizep;
511 for (cp = *bufp, i = 0; size; size--, cp++)
513 i++; /* count tabs in buffer */
516 return; /* no tabs == no work */
518 /* To avoid allocation churning, allocate enough extra space to convert
519 every tab into TABSTOP spaces */
520 /* only add (TABSTOP-1)x because we re-use the cell holding the
522 cp = malloc((unsigned)(*sizep + (i * (TABSTOP-1))));
524 return; /* punt expanding if memory fails */
526 /* Copy buffer, converting tabs to spaces as we go */
527 for (cp2 = *bufp, column = 1, size = *sizep; size; cp2++, size--) {
531 /* newline or null: reset column */
533 *cp++ = *cp2; /* copy the newline */
536 /* copy the character */
542 /* it's a tab, compute how many spaces to expand into. */
543 i = TABSTOP - ((column - 1) % TABSTOP);
545 *cp++ = ' '; /* fill in the spaces */
547 (*sizep)++; /* increment the size */
549 (*sizep)--; /* remove one (we replaced the tab) */
553 free(*bufp); /* free the old buf */