]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/clients/zwrite/zwrite.c
a7f1fc4257b73d703b51ebb89ad67eb23fd76003
[1ts-debian.git] / zephyr / clients / zwrite / zwrite.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains code for the "zwrite" command.
3  *
4  *      Created by:     Robert French
5  *
6  *      $Id$
7  *
8  *      Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
9  *      For copying and distribution information, see the file
10  *      "mit-copyright.h". 
11  */
12
13 #include <sysdep.h>
14 #include <zephyr/mit-copyright.h>
15 #include <zephyr/zephyr.h>
16 #include <netdb.h>
17 #include <pwd.h>
18
19 #ifndef lint
20 static const char rcsid_zwrite_c[] = "$Id$";
21 #endif /* lint */
22
23 #define DEFAULT_CLASS "MESSAGE"
24 #define DEFAULT_INSTANCE "PERSONAL"
25 #define URGENT_INSTANCE "URGENT"
26 #define DEFAULT_OPCODE ""
27 #define FILSRV_CLASS "FILSRV"
28
29 #define MAXRECIPS 100
30
31 int nrecips, msgarg, verbose, quiet, nodot, cc;
32 char *whoami, *inst, *class, *opcode, *realm, *recips[MAXRECIPS];
33 Z_AuthProc auth;
34 void un_tabify(char **, int *);
35
36 char *fix_filsrv_inst(char *);
37 void usage(char *);
38 void send_off(ZNotice_t *, int);
39
40 int
41 main(int argc,
42      char *argv[])
43 {
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;
49
50     whoami = argv[0];
51
52     if ((retval = ZInitialize()) != ZERR_NONE) {
53         com_err(whoami, retval, "while initializing");
54         exit(1);
55     }
56
57     if (argc < 2)
58         usage(whoami);
59
60     auth = ZAUTH;
61     verbose = quiet = msgarg = nrecips = nocheck = filsys = nodot = 0;
62     tabexpand = 1;
63
64     if (class = ZGetVariable("zwrite-class")) {
65         (void) strcpy(classbfr, class);
66         class = classbfr;
67     }
68     else
69         class = DEFAULT_CLASS;
70     if (inst = ZGetVariable("zwrite-inst")) {
71         (void) strcpy(instbfr, inst);
72         inst = instbfr;
73     }
74     else
75         inst = DEFAULT_INSTANCE;
76
77     if (opcode = ZGetVariable("zwrite-opcode"))
78       opcode = strcpy(opbfr, opcode);
79     else
80       opcode = DEFAULT_OPCODE;
81
82       signature = ZGetVariable("zwrite-signature");
83     if (signature) {
84         (void) strcpy(sigbfr, signature);
85         signature = sigbfr;
86     } 
87         
88     arg = 1;
89         
90     for (;arg<argc&&!msgarg;arg++) {
91         if (*argv[arg] != '-') {
92             recips[nrecips++] = argv[arg];
93             continue;
94         } 
95         if (strlen(argv[arg]) > 2)
96             usage(whoami);
97         switch (argv[arg][1]) {
98         case 'a':               /* Backwards compatibility */
99             auth = ZAUTH;
100             break;
101         case 'o':
102             class = DEFAULT_CLASS;
103             inst = DEFAULT_INSTANCE;
104             opcode = DEFAULT_OPCODE;
105             break;
106         case 'd':
107             auth = ZNOAUTH;
108             break;
109         case 'v':
110             verbose = 1;
111             break;
112         case 'q':
113             quiet = 1;
114             break;
115         case 'n':
116             nocheck = 1;
117             break;
118         case 't':
119             tabexpand = 0;
120             break;
121         case 'u':
122             inst = URGENT_INSTANCE;
123             break;
124         case 'O':
125             if (arg == argc-1)
126               usage(whoami);
127             arg++;
128             opcode = argv[arg];
129             break;
130         case 'i':
131             if (arg == argc-1 || filsys == 1)
132                 usage(whoami);
133             arg++;
134             inst = argv[arg];
135             filsys = -1;
136             break;
137         case 'c':
138             if (arg == argc-1 || filsys == 1)
139                 usage(whoami);
140             arg++;
141             class = argv[arg];
142             filsys = -1;
143             break;
144         case 'f':
145             if (arg == argc-1 || filsys == -1)
146                 usage(whoami);
147             arg++;
148             class = FILSRV_CLASS;
149             inst = fix_filsrv_inst(argv[arg]);
150             filsys = 1;
151             nocheck = 1;                /* implied -n (no ping) */
152             break;
153         case 's':
154             if (arg == argc-1)
155                 usage(whoami);
156             arg++;
157             signature = argv[arg];
158             break;
159         case 'm':
160             if (arg == argc-1)
161                 usage(whoami);
162             nocheck = 1;                /* implied -n (no ping) */
163             msgarg = arg+1;
164             break;
165         case 'l':                       /* literal */
166             nodot = 1;
167             break;
168         case 'F':
169             if (arg == argc-1)
170                 usage(whoami);
171             arg++;
172             format = argv[arg];
173             break;
174         case 'r':
175             if (arg == argc-1)
176                 usage(whoami);
177             arg++;
178             realm = argv[arg];
179             break;
180         case 'C':
181             cc = 1;
182             break;
183         default:
184             usage(whoami);
185         }
186     }
187
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");
194         usage(whoami);
195     }
196
197     if (!signature) {
198         /* try to find name in the password file */
199         register struct passwd *pwd;
200         register char *cp = sigbfr;
201         register char *cp2, *pp;
202
203         pwd = getpwuid(getuid());
204         if (pwd) {
205             cp2 = pwd->pw_gecos;
206             for (; *cp2 && *cp2 != ',' ; cp2++) {
207                 if (*cp2 == '&') {
208                     pp = pwd->pw_name;
209                     *cp++ = islower(*pp) ? toupper(*pp) : *pp;
210                     pp++;
211                     while (*pp)
212                         *cp++ = *pp++;
213                 } else
214                     *cp++ = *cp2;
215             }
216             signature = sigbfr;
217         }
218     }   
219
220     notice.z_kind = ACKED;
221     notice.z_port = 0;
222     notice.z_class = class;
223     notice.z_class_inst = inst;
224     notice.z_opcode = "PING";
225     notice.z_sender = 0;
226     notice.z_message_len = 0;
227     notice.z_recipient = "";
228     if (format)
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) {
233         if (signature)
234             notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold($1) <$sender>\n\n$2";
235         else
236             notice.z_default_format = "Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\n$message";
237     } else {
238         if (signature)
239             notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\nFrom: @bold($1) <$sender>\n\n$2";
240         else
241             notice.z_default_format = "@bold(UNAUTHENTIC) Class $class, Instance $instance at $time $date:\n$message";
242     }
243     if (!nocheck && nrecips)
244         send_off(&notice, 0);
245         
246     if (!msgarg && isatty(0))
247         if (nodot)
248             printf("Type your message now.  End with the end-of-file character.\n");
249         else
250             printf("Type your message now.  End with control-D or a dot on a line by itself.\n");
251         
252     message = NULL;
253     msgsize = 0;
254     if (signature) {
255         message = malloc((unsigned)(strlen(signature)+2));
256         (void) strcpy(message, signature);
257         msgsize = strlen(message);
258         message[msgsize++] = '\0';
259     } else {
260         message = malloc(1);
261         message[msgsize++] = '\0';
262     }
263
264     if (cc && nrecips > 1) {
265         int size = msgsize;
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: ");
271         msgsize += 4;
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] = ' ';
277                 msgsize++;
278             }
279         }
280         message[msgsize] = '\n';
281         msgsize += 1;
282     }
283
284     if (msgarg) {
285         int size = msgsize;
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]);
293             if (arg != argc-1) {
294                 message[msgsize] = ' ';
295                 msgsize++;
296             } 
297         }
298         message[msgsize] = '\n';
299         msgsize += 1;
300     } else {
301         if (isatty(0)) {
302             for (;;) {
303                 unsigned int l;
304                 if (!fgets(bfr, sizeof bfr, stdin))
305                     break;
306                 if (!nodot && bfr[0] == '.' &&
307                     (bfr[1] == '\n' || bfr[1] == '\0'))
308                     break;
309                 l = strlen(bfr);
310                 message = realloc(message, msgsize+l+1);
311                 (void) strcpy(message+msgsize, bfr);
312                 msgsize += l;
313             }
314             message = realloc(message, (unsigned)(msgsize+1));
315         }
316         else {  /* Use read so you can send binary messages... */
317             while (nchars = read(fileno(stdin), bfr, sizeof bfr)) {
318                 if (nchars == -1) {
319                     fprintf(stderr, "Read error from stdin!  Can't continue!\n");
320                     exit(1);
321                 }
322                 message = realloc(message, (unsigned)(msgsize+nchars));
323                 (void) memcpy(message+msgsize, bfr, nchars);
324                 msgsize += nchars;
325             }
326             /* end of msg */
327             message = realloc(message, (unsigned)(msgsize+1));
328         } 
329     }
330
331     notice.z_opcode = opcode;
332     if (tabexpand)
333         un_tabify(&message, &msgsize);
334     notice.z_message = message;
335     notice.z_message_len = msgsize;
336
337     send_off(&notice, 1);
338     exit(0);
339 }
340
341 void
342 send_off(ZNotice_t *notice,
343          int real)
344 {
345     int i, success, retval;
346     char bfr[BUFSIZ], realm_recip[BUFSIZ], dest[3 * BUFSIZ], *cp;
347     ZNotice_t retnotice;
348
349     success = 0;
350         
351     for (i=0;i<nrecips || i==0;i++) {
352         if (realm) {
353             sprintf(realm_recip, "%s@%s", (nrecips) ? recips[i] : "", realm);
354             notice->z_recipient = realm_recip;
355         } else {
356             notice->z_recipient = (nrecips) ? recips[i] : "";
357         }
358         if (nrecips)
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);
364         else
365             sprintf(dest, "class \"%s\", instance \"%s\"", class, inst);
366         if (verbose && real)
367             printf("Sending %smessage, class %s, instance %s, to %s\n", 
368                    auth?"authenticated ":"", 
369                    class, inst, 
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);
374             break;
375         }
376         if (real && !quiet) {
377             if (verbose)
378                 printf("Queued... ");
379             else
380                 printf("Message queued for %s... ", dest);
381             fflush(stdout);
382         }
383         if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
384                                 ZCompareUIDPred, 
385                                 (char *)&notice->z_uid)) !=
386             ZERR_NONE) {
387             ZFreeNotice(&retnotice);
388             (void) sprintf(bfr, "while waiting for acknowledgement for %s", 
389                     dest);
390             com_err(whoami, retval, bfr);
391             continue;
392         }
393         if (retnotice.z_kind == SERVNAK) {
394             if (!quiet) {
395                 printf("Received authorization failure while sending to %s\n", 
396                        dest);
397             }
398             ZFreeNotice(&retnotice);
399             break;                      /* if auth fails, punt */
400         } 
401         if (retnotice.z_kind != SERVACK || !retnotice.z_message_len) {
402             if (!quiet) {
403                 printf("Detected server failure while receiving acknowledgement for %s\n", 
404                        dest);
405             }
406             ZFreeNotice(&retnotice);
407             continue;
408         }
409         if (!strcmp(retnotice.z_message, ZSRVACK_SENT)) {
410             success = 1;
411             if (real && !quiet)
412                 printf("sent\n");
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", 
417                            class, inst);
418                 } else {
419                     fprintf(stderr,
420                             "Not logged in or not subscribing to messages\n");
421                 }
422             } 
423             else if (!quiet) {
424                 if (!nrecips) {
425                     fprintf(stderr,
426                             "No one subscribing to class %s, instance %s\n", 
427                             class, inst);
428                 } else {
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);
432                     } else {
433                         fprintf(stderr, "%s: Not logged in or not subscribing to messages\n", 
434                                notice->z_recipient);
435                     }
436                 }
437             }
438         } 
439         else
440             printf("Internal failure - illegal message field in server response\n");
441         ZFreeNotice(&retnotice);
442     }
443     if (!success)
444         exit(1);
445
446
447 void
448 usage(char *s)
449 {
450     fprintf(stderr,
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");
459     exit(1);
460
461
462 /*
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)
466  */
467
468 char *
469 fix_filsrv_inst(char *str)
470 {
471         static char fsinst[BUFSIZ];
472         char *ptr;
473         struct hostent *hp;
474
475         ptr = strchr(str,':');
476         if (ptr)
477                 *ptr = '\0';
478         
479         hp = gethostbyname(str);
480         if (!hp) {
481                 if (ptr)
482                         *ptr = ':';
483                 return(str);
484         }
485         (void) strcpy(fsinst, hp->h_name);
486         if (ptr) {
487                 (void) strcat(fsinst, ":");
488                 ptr++;
489                 (void) strcat(fsinst, ptr);
490         }
491         return(fsinst);
492 }
493
494 /* convert tabs in the buffer into appropriate # of spaces.
495    slightly tricky since the buffer can have NUL's in it. */
496
497 #ifndef TABSTOP
498 #define TABSTOP 8                       /* #chars between tabstops */
499 #endif /* ! TABSTOP */
500
501 void
502 un_tabify(char **bufp,
503           int *sizep)
504 {
505     register char *cp, *cp2;
506     char *cp3;
507     register int i;
508     register int column;                /* column of next character */
509     register int size = *sizep;
510
511     for (cp = *bufp, i = 0; size; size--, cp++)
512         if (*cp == '\t')
513             i++;                        /* count tabs in buffer */
514
515     if (!i)
516         return;                         /* no tabs == no work */
517
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
521        tab itself */
522     cp = malloc((unsigned)(*sizep + (i * (TABSTOP-1))));
523     if (!cp)                            /* XXX */
524         return;                         /* punt expanding if memory fails */
525     cp3 = cp;
526     /* Copy buffer, converting tabs to spaces as we go */
527     for (cp2 = *bufp, column = 1, size = *sizep; size; cp2++, size--) {
528         switch (*cp2) {
529         case '\n':
530         case '\0':
531             /* newline or null: reset column */
532             column = 1;
533             *cp++ = *cp2;               /* copy the newline */
534             break;
535         default:
536             /* copy the character */
537             *cp = *cp2;
538             cp++;
539             column++;
540             break;
541         case '\t':
542             /* it's a tab, compute how many spaces to expand into. */
543             i = TABSTOP - ((column - 1) % TABSTOP);
544             for (; i > 0; i--) {
545                 *cp++ = ' ';            /* fill in the spaces */
546                 column++;
547                 (*sizep)++;             /* increment the size */
548             }
549             (*sizep)--;                 /* remove one (we replaced the tab) */
550             break;
551         }
552     }
553     free(*bufp);                        /* free the old buf */
554     *bufp = cp3;
555     return;
556 }