]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/clients/zwrite/zwrite.c
need automake as a build-dep, even though we don't use most of it
[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, char *argv[])
42 {
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;
48     char *charset = NULL;
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     class = ZGetVariable("zwrite-class");
65     if (class) {
66         (void) strcpy(classbfr, class);
67         class = classbfr;
68     }
69     else
70         class = DEFAULT_CLASS;
71     inst = ZGetVariable("zwrite-inst");
72     if (inst) {
73         (void) strcpy(instbfr, inst);
74         inst = instbfr;
75     }
76     else
77         inst = DEFAULT_INSTANCE;
78
79     opcode = ZGetVariable("zwrite-opcode");
80     if (opcode)
81       opcode = strcpy(opbfr, opcode);
82     else
83       opcode = DEFAULT_OPCODE;
84
85       signature = ZGetVariable("zwrite-signature");
86     if (signature) {
87         (void) strcpy(sigbfr, signature);
88         signature = sigbfr;
89     } 
90         
91     arg = 1;
92         
93     for (;arg<argc && !msgarg; arg++) {
94         if (*argv[arg] != '-') {
95             recips[nrecips++] = argv[arg];
96             continue;
97         } 
98         if (strlen(argv[arg]) > 2)
99             usage(whoami);
100         switch (argv[arg][1]) {
101         case 'a':               /* Backwards compatibility */
102             auth = ZAUTH;
103             break;
104         case 'o':
105             class = DEFAULT_CLASS;
106             inst = DEFAULT_INSTANCE;
107             opcode = DEFAULT_OPCODE;
108             break;
109         case 'd':
110             auth = ZNOAUTH;
111             break;
112         case 'v':
113             verbose = 1;
114             break;
115         case 'q':
116             quiet = 1;
117             break;
118         case 'n':
119             nocheck = 1;
120             break;
121         case 't':
122             tabexpand = 0;
123             break;
124         case 'u':
125             inst = URGENT_INSTANCE;
126             break;
127         case 'O':
128             if (arg == argc-1)
129               usage(whoami);
130             arg++;
131             opcode = argv[arg];
132             break;
133         case 'i':
134             if (arg == argc-1 || filsys == 1)
135                 usage(whoami);
136             arg++;
137             inst = argv[arg];
138             filsys = -1;
139             break;
140         case 'c':
141             if (arg == argc-1 || filsys == 1)
142                 usage(whoami);
143             arg++;
144             class = argv[arg];
145             filsys = -1;
146             break;
147         case 'f':
148             if (arg == argc-1 || filsys == -1)
149                 usage(whoami);
150             arg++;
151             class = FILSRV_CLASS;
152             inst = fix_filsrv_inst(argv[arg]);
153             filsys = 1;
154             nocheck = 1;                /* implied -n (no ping) */
155             break;
156         case 's':
157             if (arg == argc-1)
158                 usage(whoami);
159             arg++;
160             signature = argv[arg];
161             break;
162         case 'm':
163             if (arg == argc-1)
164                 usage(whoami);
165             nocheck = 1;                /* implied -n (no ping) */
166             msgarg = arg+1;
167             break;
168         case 'l':                       /* literal */
169             nodot = 1;
170             break;
171         case 'F':
172             if (arg == argc-1)
173                 usage(whoami);
174             arg++;
175             format = argv[arg];
176             break;
177         case 'r':
178             if (arg == argc-1)
179                 usage(whoami);
180             arg++;
181             realm = argv[arg];
182             break;
183         case 'C':
184             cc = 1;
185             break;
186         case 'x':
187             if (arg == argc-1)
188                 usage(whoami);
189             arg++;
190             charset = argv[arg];
191             break;
192         default:
193             usage(whoami);
194         }
195     }
196
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");
203         usage(whoami);
204     }
205
206     if (!signature) {
207         /* try to find name in the password file */
208         register struct passwd *pwd;
209         register char *cp = sigbfr;
210         register char *cp2, *pp;
211
212         pwd = getpwuid(getuid());
213         if (pwd) {
214             cp2 = pwd->pw_gecos;
215             for (; *cp2 && *cp2 != ',' ; cp2++) {
216                 if (*cp2 == '&') {
217                     pp = pwd->pw_name;
218                     *cp++ = islower(*pp) ? toupper(*pp) : *pp;
219                     pp++;
220                     while (*pp)
221                         *cp++ = *pp++;
222                 } else
223                     *cp++ = *cp2;
224             }
225             signature = sigbfr;
226         }
227     }   
228
229     notice.z_kind = ACKED;
230     notice.z_port = 0;
231     notice.z_class = class;
232     notice.z_class_inst = inst;
233     notice.z_opcode = "PING";
234     notice.z_sender = 0;
235     notice.z_message_len = 0;
236     notice.z_recipient = "";
237     notice.z_charset = ZGetCharset(charset);
238     if (format)
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) {
243         if (signature)
244             notice.z_default_format = "Class $class Instance $instance\nTo @b($recipient) @ $time $date\nFrom @b($1) <$sender>\n\n$2";
245         else
246             notice.z_default_format = "Class $class Instance $instance\nTo @b($recipient) @ $time $date\n$message";
247     } else {
248         if (signature)
249             notice.z_default_format = "@b(UNAUTHENTIC) Class $class Instance $instance @ $time $date\nFrom @b($1) <$sender>\n\n$2";
250         else
251             notice.z_default_format = "@b(UNAUTHENTIC) Class $class Instance $instance @ $time $date\n$message";
252     }
253     if (!nocheck && nrecips)
254         send_off(&notice, 0);
255         
256     if (!msgarg && isatty(0)) {
257         if (nodot)
258             printf("Type your message now.  End with the end-of-file character.\n");
259         else
260             printf("Type your message now.  End with control-D or a dot on a line by itself.\n");
261     }
262         
263     message = NULL;
264     msgsize = 0;
265     if (signature) {
266         message = malloc((unsigned)(strlen(signature)+2));
267         (void) strcpy(message, signature);
268         msgsize = strlen(message);
269         message[msgsize++] = '\0';
270     } else {
271         message = malloc(1);
272         message[msgsize++] = '\0';
273     }
274
275     if (cc && nrecips > 1) {
276         int size = msgsize;
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: ");
282         msgsize += 4;
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] = ' ';
288                 msgsize++;
289             }
290         }
291         message[msgsize] = '\n';
292         msgsize += 1;
293     }
294
295     if (msgarg) {
296         int size = msgsize;
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]);
304             if (arg != argc-1) {
305                 message[msgsize] = ' ';
306                 msgsize++;
307             } 
308         }
309         message[msgsize] = '\n';
310         msgsize += 1;
311     } else {
312         if (isatty(0)) {
313             for (;;) {
314                 unsigned int l;
315                 if (!fgets(bfr, sizeof bfr, stdin))
316                     break;
317                 if (!nodot && bfr[0] == '.' &&
318                     (bfr[1] == '\n' || bfr[1] == '\0'))
319                     break;
320                 l = strlen(bfr);
321                 message = realloc(message, msgsize+l+1);
322                 (void) strcpy(message+msgsize, bfr);
323                 msgsize += l;
324             }
325             if (ferror(stdin)) /* drop the message */
326                 exit(1);
327             message = realloc(message, (unsigned)(msgsize+1));
328         }
329         else {  /* Use read so you can send binary messages... */
330             while ((nchars = read(fileno(stdin), bfr, sizeof bfr))) {
331                 if (nchars == -1) {
332                     fprintf(stderr, "Read error from stdin!  Can't continue!\n");
333                     exit(1);
334                 }
335                 message = realloc(message, (unsigned)(msgsize+nchars));
336                 (void) memcpy(message+msgsize, bfr, nchars);
337                 msgsize += nchars;
338             }
339             /* end of msg */
340             message = realloc(message, (unsigned)(msgsize+1));
341         } 
342     }
343
344     notice.z_opcode = opcode;
345     if (tabexpand)
346         un_tabify(&message, &msgsize);
347     notice.z_message = message;
348     notice.z_message_len = msgsize;
349
350     send_off(&notice, 1);
351     exit(0);
352 }
353
354 void
355 send_off(ZNotice_t *notice, int real)
356 {
357     int i, success, retval;
358     char bfr[BUFSIZ], realm_recip[BUFSIZ], dest[3 * BUFSIZ];
359     ZNotice_t retnotice;
360
361     success = 0;
362         
363     for (i=0;i<nrecips || i==0;i++) {
364         if (realm) {
365             sprintf(realm_recip, "%s@%s", (nrecips) ? recips[i] : "", realm);
366             notice->z_recipient = realm_recip;
367         } else {
368             notice->z_recipient = (nrecips) ? recips[i] : "";
369         }
370         if (nrecips)
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);
376         else
377             sprintf(dest, "class \"%s\", instance \"%s\"", class, inst);
378         if (verbose && real)
379             printf("Sending %smessage, class %s, instance %s, to %s\n", 
380                    auth?"authenticated ":"", 
381                    class, inst, 
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);
386             break;
387         }
388         if (real && !quiet) {
389             if (verbose)
390                 printf("Queued... ");
391             else
392                 printf("Message queued for %s... ", dest);
393             fflush(stdout);
394         }
395         if ((retval = ZIfNotice(&retnotice, (struct sockaddr_in *) 0,
396                                 ZCompareUIDPred, 
397                                 (char *)&notice->z_uid)) != ZERR_NONE) {
398             (void) sprintf(bfr, "while waiting for acknowledgement for %s", 
399                     dest);
400             com_err(whoami, retval, bfr);
401             continue;
402         }
403         if (retnotice.z_kind == SERVNAK) {
404             if (!quiet) {
405                 printf("Received authorization failure while sending to %s\n", 
406                        dest);
407             }
408             ZFreeNotice(&retnotice);
409             break;                      /* if auth fails, punt */
410         } 
411         if (retnotice.z_kind != SERVACK || !retnotice.z_message_len) {
412             if (!quiet) {
413                 printf("Detected server failure while receiving acknowledgement for %s\n", 
414                        dest);
415             }
416             ZFreeNotice(&retnotice);
417             continue;
418         }
419         if (!strcmp(retnotice.z_message, ZSRVACK_SENT)) {
420             success = 1;
421             if (real && !quiet)
422                 printf("sent\n");
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", 
427                            class, inst);
428                 } else {
429                     fprintf(stderr,
430                             "Not logged in or not subscribing to messages\n");
431                 }
432             } 
433             else if (!quiet) {
434                 if (!nrecips) {
435                     fprintf(stderr,
436                             "No one subscribing to class %s, instance %s\n", 
437                             class, inst);
438                 } else {
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);
442                     } else {
443                         fprintf(stderr, "%s: Not logged in or not subscribing to messages\n", 
444                                notice->z_recipient);
445                     }
446                 }
447             }
448         } 
449         else
450             printf("Internal failure - illegal message field in server response\n");
451         ZFreeNotice(&retnotice);
452     }
453     if (!success)
454         exit(1);
455
456
457 void
458 usage(char *s)
459 {
460     fprintf(stderr,
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");
469     exit(1);
470
471
472 /*
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)
476  */
477
478 char *
479 fix_filsrv_inst(char *str)
480 {
481         static char fsinst[BUFSIZ];
482         char *ptr;
483         struct hostent *hp;
484
485         ptr = strchr(str,':');
486         if (ptr)
487                 *ptr = '\0';
488         
489         hp = gethostbyname(str);
490         if (!hp) {
491                 if (ptr)
492                         *ptr = ':';
493                 return(str);
494         }
495         (void) strcpy(fsinst, hp->h_name);
496         if (ptr) {
497                 (void) strcat(fsinst, ":");
498                 ptr++;
499                 (void) strcat(fsinst, ptr);
500         }
501         return(fsinst);
502 }
503
504 /* convert tabs in the buffer into appropriate # of spaces.
505    slightly tricky since the buffer can have NUL's in it. */
506
507 #ifndef TABSTOP
508 #define TABSTOP 8                       /* #chars between tabstops */
509 #endif /* ! TABSTOP */
510
511 void
512 un_tabify(char **bufp,
513           int *sizep)
514 {
515     register char *cp, *cp2;
516     char *cp3;
517     register int i;
518     register int column;                /* column of next character */
519     register int size = *sizep;
520
521     for (cp = *bufp, i = 0; size; size--, cp++)
522         if (*cp == '\t')
523             i++;                        /* count tabs in buffer */
524
525     if (!i)
526         return;                         /* no tabs == no work */
527
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
531        tab itself */
532     cp = malloc((unsigned)(*sizep + (i * (TABSTOP-1))));
533     if (!cp)                            /* XXX */
534         return;                         /* punt expanding if memory fails */
535     cp3 = cp;
536     /* Copy buffer, converting tabs to spaces as we go */
537     for (cp2 = *bufp, column = 1, size = *sizep; size; cp2++, size--) {
538         switch (*cp2) {
539         case '\n':
540         case '\0':
541             /* newline or null: reset column */
542             column = 1;
543             *cp++ = *cp2;               /* copy the newline */
544             break;
545         default:
546             /* copy the character */
547             *cp = *cp2;
548             cp++;
549             column++;
550             break;
551         case '\t':
552             /* it's a tab, compute how many spaces to expand into. */
553             i = TABSTOP - ((column - 1) % TABSTOP);
554             for (; i > 0; i--) {
555                 *cp++ = ' ';            /* fill in the spaces */
556                 column++;
557                 (*sizep)++;             /* increment the size */
558             }
559             (*sizep)--;                 /* remove one (we replaced the tab) */
560             break;
561         }
562     }
563     free(*bufp);                        /* free the old buf */
564     *bufp = cp3;
565     return;
566 }