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 2589 2010-08-22 18:15:05Z kcr@ATHENA.MIT.EDU $
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 2589 2010-08-22 18:15:05Z kcr@ATHENA.MIT.EDU $";
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);
41 main(int argc, char *argv[])
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;
52 if ((retval = ZInitialize()) != ZERR_NONE) {
53 com_err(whoami, retval, "while initializing");
61 verbose = quiet = msgarg = nrecips = nocheck = filsys = nodot = 0;
64 class = ZGetVariable("zwrite-class");
66 (void) strcpy(classbfr, class);
70 class = DEFAULT_CLASS;
71 inst = ZGetVariable("zwrite-inst");
73 (void) strcpy(instbfr, inst);
77 inst = DEFAULT_INSTANCE;
79 opcode = ZGetVariable("zwrite-opcode");
81 opcode = strcpy(opbfr, opcode);
83 opcode = DEFAULT_OPCODE;
85 signature = ZGetVariable("zwrite-signature");
87 (void) strcpy(sigbfr, signature);
93 for (;arg<argc && !msgarg; arg++) {
94 if (*argv[arg] != '-') {
95 recips[nrecips++] = argv[arg];
98 if (strlen(argv[arg]) > 2)
100 switch (argv[arg][1]) {
101 case 'a': /* Backwards compatibility */
105 class = DEFAULT_CLASS;
106 inst = DEFAULT_INSTANCE;
107 opcode = DEFAULT_OPCODE;
125 inst = URGENT_INSTANCE;
134 if (arg == argc-1 || filsys == 1)
141 if (arg == argc-1 || filsys == 1)
148 if (arg == argc-1 || filsys == -1)
151 class = FILSRV_CLASS;
152 inst = fix_filsrv_inst(argv[arg]);
154 nocheck = 1; /* implied -n (no ping) */
160 signature = argv[arg];
165 nocheck = 1; /* implied -n (no ping) */
168 case 'l': /* literal */
197 if (!nrecips && !(strcmp(class, DEFAULT_CLASS) ||
198 (strcmp(inst, DEFAULT_INSTANCE) &&
199 strcmp(inst, URGENT_INSTANCE)))) {
200 /* must specify recipient if using default class and
201 (default instance or urgent instance) */
202 fprintf(stderr, "No recipients specified.\n");
207 /* try to find name in the password file */
208 register struct passwd *pwd;
209 register char *cp = sigbfr;
210 register char *cp2, *pp;
212 pwd = getpwuid(getuid());
215 for (; *cp2 && *cp2 != ',' ; cp2++) {
218 *cp++ = islower(*pp) ? toupper(*pp) : *pp;
229 notice.z_kind = ACKED;
231 notice.z_class = class;
232 notice.z_class_inst = inst;
233 notice.z_opcode = "PING";
235 notice.z_message_len = 0;
236 notice.z_recipient = "";
237 notice.z_charset = ZGetCharset(charset);
239 notice.z_default_format = format;
240 else if (filsys == 1)
241 notice.z_default_format = "@b(Filesystem Info Message for $instance)\nFrom: @b($sender) @ $time $date\n$message";
242 else if (auth == ZAUTH) {
244 notice.z_default_format = "Class $class Instance $instance\nTo @b($recipient) @ $time $date\nFrom @b($1) <$sender>\n\n$2";
246 notice.z_default_format = "Class $class Instance $instance\nTo @b($recipient) @ $time $date\n$message";
249 notice.z_default_format = "@b(UNAUTHENTIC) Class $class Instance $instance @ $time $date\nFrom @b($1) <$sender>\n\n$2";
251 notice.z_default_format = "@b(UNAUTHENTIC) Class $class Instance $instance @ $time $date\n$message";
253 if (!nocheck && nrecips)
254 send_off(¬ice, 0);
256 if (!msgarg && isatty(0)) {
258 printf("Type your message now. End with the end-of-file character.\n");
260 printf("Type your message now. End with control-D or a dot on a line by itself.\n");
266 message = malloc((unsigned)(strlen(signature)+2));
267 (void) strcpy(message, signature);
268 msgsize = strlen(message);
269 message[msgsize++] = '\0';
272 message[msgsize++] = '\0';
275 if (cc && nrecips > 1) {
277 for (arg=0;arg<nrecips;arg++)
278 size += (strlen(recips[arg]) + 2);
279 size += 6; /* for the newlines and "cc: " */
280 message = realloc(message, (unsigned) size);
281 (void) strcpy(message+msgsize, "CC: ");
283 for (arg=0;arg<nrecips;arg++) {
284 (void) strcpy(message+msgsize, recips[arg]);
285 msgsize += strlen(recips[arg]);
286 if (arg != nrecips-1) {
287 message[msgsize] = ' ';
291 message[msgsize] = '\n';
297 for (arg=msgarg;arg<argc;arg++)
298 size += (strlen(argv[arg]) + 1);
299 size++; /* for the newline */
300 message = realloc(message, (unsigned) size);
301 for (arg=msgarg;arg<argc;arg++) {
302 (void) strcpy(message+msgsize, argv[arg]);
303 msgsize += strlen(argv[arg]);
305 message[msgsize] = ' ';
309 message[msgsize] = '\n';
315 if (!fgets(bfr, sizeof bfr, stdin))
317 if (!nodot && bfr[0] == '.' &&
318 (bfr[1] == '\n' || bfr[1] == '\0'))
321 message = realloc(message, msgsize+l+1);
322 (void) strcpy(message+msgsize, bfr);
325 if (ferror(stdin)) /* drop the message */
327 message = realloc(message, (unsigned)(msgsize+1));
329 else { /* Use read so you can send binary messages... */
330 while ((nchars = read(fileno(stdin), bfr, sizeof bfr))) {
332 fprintf(stderr, "Read error from stdin! Can't continue!\n");
335 message = realloc(message, (unsigned)(msgsize+nchars));
336 (void) memcpy(message+msgsize, bfr, nchars);
340 message = realloc(message, (unsigned)(msgsize+1));
344 notice.z_opcode = opcode;
346 un_tabify(&message, &msgsize);
347 notice.z_message = message;
348 notice.z_message_len = msgsize;
350 send_off(¬ice, 1);
355 send_off(ZNotice_t *notice, int real)
357 int i, success, retval;
358 char bfr[BUFSIZ], realm_recip[BUFSIZ], dest[3 * BUFSIZ];
363 for (i=0;i<nrecips || i==0;i++) {
365 sprintf(realm_recip, "%s@%s", (nrecips) ? recips[i] : "", realm);
366 notice->z_recipient = realm_recip;
368 notice->z_recipient = (nrecips) ? recips[i] : "";
371 strcpy(dest, recips[i]);
372 else if (!strcmp(class, DEFAULT_CLASS))
373 sprintf(dest, "instance \"%s\"", inst);
374 else if (!strcmp(inst, DEFAULT_INSTANCE))
375 sprintf(dest, "class \"%s\"", class);
377 sprintf(dest, "class \"%s\", instance \"%s\"", class, inst);
379 printf("Sending %smessage, class %s, instance %s, to %s\n",
380 auth?"authenticated ":"",
382 nrecips?notice->z_recipient:"everyone");
383 if ((retval = ZSendNotice(notice, auth)) != ZERR_NONE) {
384 (void) sprintf(bfr, "while sending notice to %s", dest);
385 com_err(whoami, retval, bfr);
388 if (real && !quiet) {
390 printf("Queued... ");
392 printf("Message queued for %s... ", dest);
395 if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
397 (char *)¬ice->z_uid)) != ZERR_NONE) {
398 (void) sprintf(bfr, "while waiting for acknowledgement for %s",
400 com_err(whoami, retval, bfr);
403 if (retnotice.z_kind == SERVNAK) {
405 printf("Received authorization failure while sending to %s\n",
408 ZFreeNotice(&retnotice);
409 break; /* if auth fails, punt */
411 if (retnotice.z_kind != SERVACK || !retnotice.z_message_len) {
413 printf("Detected server failure while receiving acknowledgement for %s\n",
416 ZFreeNotice(&retnotice);
419 if (!strcmp(retnotice.z_message, ZSRVACK_SENT)) {
423 } else if (!strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
424 if (verbose && real && !quiet) {
425 if (strcmp(class, DEFAULT_CLASS)) {
426 fprintf(stderr, "Not logged in or not subscribing to class %s, instance %s\n",
430 "Not logged in or not subscribing to messages\n");
436 "No one subscribing to class %s, instance %s\n",
439 if (strcmp(class, DEFAULT_CLASS)) {
440 fprintf(stderr, "%s: Not logged in or not subscribing to class %s, instance %s\n",
441 notice->z_recipient, class, inst);
443 fprintf(stderr, "%s: Not logged in or not subscribing to messages\n",
444 notice->z_recipient);
450 printf("Internal failure - illegal message field in server response\n");
451 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] [-C]\n\
463 \t[user ...] [-F format] [-r realm] [-x charset] [-m message]\n", s);
464 fprintf(stderr,"\t-f and -c are mutually exclusive\n\
465 \t-f and -i are mutually exclusive\n\
466 \trecipients must be specified unless -c or -f specifies a class\n\
467 \tother than the default class or -i or -f specifies an instance\n\
468 \tother than the default or urgent instance\n");
473 if the -f option is specified, this routine is called to canonicalize
474 an instance of the form hostname[:pack]. It turns the hostname into the
475 name returned by gethostbyname(hostname)
479 fix_filsrv_inst(char *str)
481 static char fsinst[BUFSIZ];
485 ptr = strchr(str,':');
489 hp = gethostbyname(str);
495 (void) strcpy(fsinst, hp->h_name);
497 (void) strcat(fsinst, ":");
499 (void) strcat(fsinst, ptr);
504 /* convert tabs in the buffer into appropriate # of spaces.
505 slightly tricky since the buffer can have NUL's in it. */
508 #define TABSTOP 8 /* #chars between tabstops */
509 #endif /* ! TABSTOP */
512 un_tabify(char **bufp,
515 register char *cp, *cp2;
518 register int column; /* column of next character */
519 register int size = *sizep;
521 for (cp = *bufp, i = 0; size; size--, cp++)
523 i++; /* count tabs in buffer */
526 return; /* no tabs == no work */
528 /* To avoid allocation churning, allocate enough extra space to convert
529 every tab into TABSTOP spaces */
530 /* only add (TABSTOP-1)x because we re-use the cell holding the
532 cp = malloc((unsigned)(*sizep + (i * (TABSTOP-1))));
534 return; /* punt expanding if memory fails */
536 /* Copy buffer, converting tabs to spaces as we go */
537 for (cp2 = *bufp, column = 1, size = *sizep; size; cp2++, size--) {
541 /* newline or null: reset column */
543 *cp++ = *cp2; /* copy the newline */
546 /* copy the character */
552 /* it's a tab, compute how many spaces to expand into. */
553 i = TABSTOP - ((column - 1) % TABSTOP);
555 *cp++ = ' '; /* fill in the spaces */
557 (*sizep)++; /* increment the size */
559 (*sizep)--; /* remove one (we replaced the tab) */
563 free(*bufp); /* free the old buf */