fix build: update SOURCE0 consists of name and version macro
[pkgs/xorg/util/xorg-x11-xauth.git] / process.c
1 /* $Xorg: process.c,v 1.6 2001/02/09 02:05:38 xorgcvs Exp $ */
2 /* $XdotOrg: xc/programs/xauth/process.c,v 1.3 2004/04/24 23:26:55 alanc Exp $ */
3 /*
4
5 Copyright 1989, 1998  The Open Group
6
7 Permission to use, copy, modify, distribute, and sell this software and its
8 documentation for any purpose is hereby granted without fee, provided that
9 the above copyright notice appear in all copies and that both that
10 copyright notice and this permission notice appear in supporting
11 documentation.
12
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 OTHER DEALINGS IN THE SOFTWARE.
23
24 Except as contained in this notice, the name of The Open Group shall
25 not be used in advertising or otherwise to promote the sale, use or
26 other dealings in this Software without prior written authorization
27 from The Open Group.
28
29 */
30 /* $XFree86: xc/programs/xauth/process.c,v 3.23 2003/11/25 03:15:04 dawes Exp $ */
31
32 /*
33  * Author:  Jim Fulton, MIT X Consortium
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include "xauth.h"
41 #include <ctype.h>
42 #include <errno.h>
43 #include <sys/stat.h>
44 #include <sys/socket.h>
45
46 #include <signal.h>
47 #include <X11/X.h>                      /* for Family constants */
48
49 #include <X11/Xlib.h>
50 #include <X11/extensions/security.h>
51
52 #ifndef DEFAULT_PROTOCOL_ABBREV         /* to make add command easier */
53 #define DEFAULT_PROTOCOL_ABBREV "."
54 #endif
55 #ifndef DEFAULT_PROTOCOL                /* for protocol abbreviation */
56 #define DEFAULT_PROTOCOL "MIT-MAGIC-COOKIE-1"
57 #endif
58
59 #define SECURERPC "SUN-DES-1"
60 #define K5AUTH "MIT-KERBEROS-5"
61
62 #define XAUTH_DEFAULT_RETRIES 10        /* number of competitors we expect */
63 #define XAUTH_DEFAULT_TIMEOUT 2         /* in seconds, be quick */
64 #define XAUTH_DEFAULT_DEADTIME 600L     /* 10 minutes in seconds */
65
66 typedef struct _AuthList {              /* linked list of entries */
67     struct _AuthList *next;
68     Xauth *auth;
69 } AuthList;
70
71 typedef int (*ProcessFunc)(char *, int, int, char**);
72
73 #define add_to_list(h,t,e) {if (t) (t)->next = (e); else (h) = (e); (t) = (e);}
74
75 typedef struct _CommandTable {          /* commands that are understood */
76     char *name;                         /* full name */
77     int minlen;                         /* unique prefix */
78     int maxlen;                         /* strlen(name) */
79     ProcessFunc processfunc;            /* handler */
80     char *helptext;                     /* what to print for help */
81 } CommandTable;
82
83 struct _extract_data {                  /* for iterating */
84     FILE *fp;                           /* input source */
85     char *filename;                     /* name of input */
86     Bool used_stdout;                   /* whether or not need to close */
87     Bool numeric;                       /* format in which to write */
88     int nwritten;                       /* number of entries written */
89     char *cmd;                          /* for error messages */
90 };
91
92 struct _list_data {                     /* for iterating */
93     FILE *fp;                           /* output file */
94     Bool numeric;                       /* format in which to write */
95 };
96
97
98 /*
99  * private data
100  */
101 static char *stdin_filename = "(stdin)";  /* for messages */
102 static char *stdout_filename = "(stdout)";  /* for messages */
103 static char *Yes = "yes";               /* for messages */
104 static char *No = "no";                 /* for messages */
105
106 static int do_help ( char *inputfilename, int lineno, int argc, char **argv );
107 static int do_questionmark ( char *inputfilename, int lineno, int argc, char **argv );
108 static int do_list ( char *inputfilename, int lineno, int argc, char **argv );
109 static int do_merge ( char *inputfilename, int lineno, int argc, char **argv );
110 static int do_extract ( char *inputfilename, int lineno, int argc, char **argv );
111 static int do_add ( char *inputfilename, int lineno, int argc, char **argv );
112 static int do_remove ( char *inputfilename, int lineno, int argc, char **argv );
113 static int do_info ( char *inputfilename, int lineno, int argc, char **argv );
114 static int do_exit ( char *inputfilename, int lineno, int argc, char **argv );
115 static int do_quit ( char *inputfilename, int lineno, int argc, char **argv );
116 static int do_source ( char *inputfilename, int lineno, int argc, char **argv );
117 static int do_generate ( char *inputfilename, int lineno, int argc, char **argv );
118
119 static CommandTable command_table[] = { /* table of known commands */
120     { "add",      2, 3, do_add,
121         "add dpyname protoname hexkey   add entry" },
122     { "exit",     3, 4, do_exit,
123         "exit                           save changes and exit program" },
124     { "extract",  3, 7, do_extract,
125         "extract filename dpyname...    extract entries into file" },
126     { "help",     1, 4, do_help,
127         "help [topic]                   print help" },
128     { "info",     1, 4, do_info,
129         "info                           print information about entries" },
130     { "list",     1, 4, do_list,
131         "list [dpyname...]              list entries" },
132     { "merge",    1, 5, do_merge,
133         "merge filename...              merge entries from files" },
134     { "nextract", 2, 8, do_extract,
135         "nextract filename dpyname...   numerically extract entries" },
136     { "nlist",    2, 5, do_list,
137         "nlist [dpyname...]             numerically list entries" },
138     { "nmerge",   2, 6, do_merge,
139         "nmerge filename...             numerically merge entries" },
140     { "quit",     1, 4, do_quit,
141         "quit                           abort changes and exit program" },
142     { "remove",   1, 6, do_remove,
143         "remove dpyname...              remove entries" },
144     { "source",   1, 6, do_source,
145         "source filename                read commands from file" },
146     { "?",        1, 1, do_questionmark,
147         "?                              list available commands" },
148     { "generate", 1, 8, do_generate,
149         "generate dpyname protoname [options]  use server to generate entry\n" 
150         "    options are:\n"
151         "      timeout n    authorization expiration time in seconds\n"
152         "      trusted      clients using this entry are trusted\n"
153         "      untrusted    clients using this entry are untrusted\n"
154         "      group n      clients using this entry belong to application group n\n"
155         "      data hexkey  auth protocol specific data needed to generate the entry\n"
156     }, 
157     { NULL,       0, 0, NULL, NULL },
158 };
159
160 #define COMMAND_NAMES_PADDED_WIDTH 10   /* wider than anything above */
161
162
163 static Bool okay_to_use_stdin = True;   /* set to false after using */
164
165 static char *hex_table[] = {            /* for printing hex digits */
166     "00", "01", "02", "03", "04", "05", "06", "07", 
167     "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", 
168     "10", "11", "12", "13", "14", "15", "16", "17", 
169     "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", 
170     "20", "21", "22", "23", "24", "25", "26", "27", 
171     "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", 
172     "30", "31", "32", "33", "34", "35", "36", "37", 
173     "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", 
174     "40", "41", "42", "43", "44", "45", "46", "47", 
175     "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", 
176     "50", "51", "52", "53", "54", "55", "56", "57", 
177     "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", 
178     "60", "61", "62", "63", "64", "65", "66", "67", 
179     "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", 
180     "70", "71", "72", "73", "74", "75", "76", "77", 
181     "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", 
182     "80", "81", "82", "83", "84", "85", "86", "87", 
183     "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", 
184     "90", "91", "92", "93", "94", "95", "96", "97", 
185     "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", 
186     "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", 
187     "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", 
188     "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", 
189     "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", 
190     "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", 
191     "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", 
192     "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", 
193     "d8", "d9", "da", "db", "dc", "dd", "de", "df", 
194     "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", 
195     "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", 
196     "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", 
197     "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", 
198 };
199
200 static unsigned int hexvalues[256];     /* for parsing hex input */
201
202 static int original_umask = 0;          /* for restoring */
203
204
205 /*
206  * private utility procedures
207  */
208
209 static void 
210 prefix(char *fn, int n)
211 {
212     fprintf (stderr, "%s: %s:%d:  ", ProgramName, fn, n);
213 }
214
215 static void 
216 baddisplayname(char *dpy, char *cmd)
217 {
218     fprintf (stderr, "bad display name \"%s\" in \"%s\" command\n",
219              dpy, cmd);
220 }
221
222 static void 
223 badcommandline(char *cmd)
224 {
225     fprintf (stderr, "bad \"%s\" command line\n", cmd);
226 }
227
228 static char *
229 skip_space(register char *s)
230 {
231     if (!s) return NULL;
232
233     for ( ; *s && isascii(*s) && isspace(*s); s++)
234         ;
235     return s;
236 }
237
238
239 static char *
240 skip_nonspace(register char *s)
241 {
242     if (!s) return NULL;
243
244     /* put quoting into loop if need be */
245     for ( ; *s && isascii(*s) && !isspace(*s); s++)
246         ;
247     return s;
248 }
249
250 static char **
251 split_into_words(char *src, int *argcp)  /* argvify string */
252 {
253     char *jword;
254     char savec;
255     char **argv;
256     int cur, total;
257
258     *argcp = 0;
259 #define WORDSTOALLOC 4                  /* most lines are short */
260     argv = (char **) malloc (WORDSTOALLOC * sizeof (char *));
261     if (!argv) return NULL;
262     cur = 0;
263     total = WORDSTOALLOC;
264
265     /*
266      * split the line up into separate, nul-terminated tokens; the last
267      * "token" will point to the empty string so that it can be bashed into
268      * a null pointer.
269      */
270
271     do {
272         jword = skip_space (src);
273         src = skip_nonspace (jword);
274         savec = *src;
275         *src = '\0';
276         if (cur == total) {
277             total += WORDSTOALLOC;
278             argv = (char **) realloc (argv, total * sizeof (char *));
279             if (!argv) return NULL;
280         }
281         argv[cur++] = jword;
282         if (savec) src++;               /* if not last on line advance */
283     } while (jword != src);
284
285     argv[--cur] = NULL;                 /* smash empty token to end list */
286     *argcp = cur;
287     return argv;
288 }
289
290
291 static FILE *
292 open_file(char **filenamep, 
293           char *mode, 
294           Bool *usedstdp, 
295           char *srcfn, 
296           int srcln, 
297           char *cmd)
298 {
299     FILE *fp;
300
301     if (strcmp (*filenamep, "-") == 0) {
302         *usedstdp = True;
303                                         /* select std descriptor to use */
304         if (mode[0] == 'r') {
305             if (okay_to_use_stdin) {
306                 okay_to_use_stdin = False;
307                 *filenamep = stdin_filename;
308                 return stdin;
309             } else {
310                 prefix (srcfn, srcln);
311                 fprintf (stderr, "%s:  stdin already in use\n", cmd);
312                 return NULL;
313             }
314         } else {
315             *filenamep = stdout_filename;
316             return stdout;              /* always okay to use stdout */
317         }
318     }
319
320     fp = fopen (*filenamep, mode);
321     if (!fp) {
322         prefix (srcfn, srcln);
323         fprintf (stderr, "%s:  unable to open file %s\n", cmd, *filenamep);
324     }
325     return fp;
326 }
327
328 static int 
329 getinput(FILE *fp)
330 {
331     register int c;
332
333     while ((c = getc (fp)) != EOF && isascii(c) && c != '\n' && isspace(c)) ;
334     return c;
335 }
336
337 static int 
338 get_short(FILE *fp, unsigned short *sp) /* for reading numeric input */
339 {
340     int c;
341     int i;
342     unsigned short us = 0;
343
344     /*
345      * read family:  written with %04x
346      */
347     for (i = 0; i < 4; i++) {
348         switch (c = getinput (fp)) {
349           case EOF:
350           case '\n':
351             return 0;
352         }
353         if (c < 0 || c > 255) return 0;
354         us = (us * 16) + hexvalues[c];  /* since msb */
355     }
356     *sp = us;
357     return 1;
358 }
359
360 static int
361 get_bytes(FILE *fp, unsigned int n, char **ptr) /* for reading numeric input */
362 {
363     char *s;
364     register char *cp;
365     int c1, c2;
366
367     cp = s = malloc (n);
368     if (!cp) return 0;
369
370     while (n > 0) {
371         if ((c1 = getinput (fp)) == EOF || c1 == '\n' ||
372             (c2 = getinput (fp)) == EOF || c2 == '\n') {
373             free (s);
374             return 0;
375         }
376         *cp = (char) ((hexvalues[c1] * 16) + hexvalues[c2]);
377         cp++;
378         n--;
379     }
380
381     *ptr = s;
382     return 1;
383 }
384
385
386 static Xauth *
387 read_numeric(FILE *fp)
388 {
389     Xauth *auth;
390
391     auth = (Xauth *) malloc (sizeof (Xauth));
392     if (!auth) goto bad;
393     auth->family = 0;
394     auth->address = NULL;
395     auth->address_length = 0;
396     auth->number = NULL;
397     auth->number_length = 0;
398     auth->name = NULL;
399     auth->name_length = 0;
400     auth->data = NULL;
401     auth->data_length = 0;
402
403     if (!get_short (fp, (unsigned short *) &auth->family))
404       goto bad;
405     if (!get_short (fp, (unsigned short *) &auth->address_length))
406       goto bad;
407     if (!get_bytes (fp, (unsigned int) auth->address_length, &auth->address))
408       goto bad;
409     if (!get_short (fp, (unsigned short *) &auth->number_length))
410       goto bad;
411     if (!get_bytes (fp, (unsigned int) auth->number_length, &auth->number))
412       goto bad;
413     if (!get_short (fp, (unsigned short *) &auth->name_length))
414       goto bad;
415     if (!get_bytes (fp, (unsigned int) auth->name_length, &auth->name))
416       goto bad;
417     if (!get_short (fp, (unsigned short *) &auth->data_length))
418       goto bad;
419     if (!get_bytes (fp, (unsigned int) auth->data_length, &auth->data))
420       goto bad;
421     
422     switch (getinput (fp)) {            /* get end of line */
423       case EOF:
424       case '\n':
425         return auth;
426     }
427
428   bad:
429     if (auth) XauDisposeAuth (auth);    /* won't free null pointers */
430     return NULL;
431 }
432
433 typedef Xauth *(*ReadFunc)(FILE *);
434
435 static int 
436 read_auth_entries(FILE *fp, Bool numeric, AuthList **headp, AuthList **tailp)
437 {
438     ReadFunc readfunc = (numeric ? read_numeric : XauReadAuth);
439     Xauth *auth;
440     AuthList *head, *tail;
441     int n;
442
443     head = tail = NULL;
444     n = 0;
445                                         /* put all records into linked list */
446     while ((auth = ((*readfunc) (fp))) != NULL) {
447         AuthList *l = (AuthList *) malloc (sizeof (AuthList));
448         if (!l) {
449             fprintf (stderr,
450                      "%s:  unable to alloc entry reading auth file\n",
451                      ProgramName);
452             exit (1);
453         }
454         l->next = NULL;
455         l->auth = auth;
456         if (tail)                       /* if not first time through append */
457           tail->next = l;
458         else
459           head = l;                     /* first time through, so assign */
460         tail = l;
461         n++;
462     }
463     *headp = head;
464     *tailp = tail;
465     return n;
466 }
467
468 static Bool 
469 get_displayname_auth(char *displayname, AuthList **authl)
470 {
471     int family;
472     char *host = NULL, *rest = NULL;
473     int dpynum, scrnum;
474     char *cp;
475     int prelen = 0;
476     struct addrlist *addrlist_head, *addrlist_cur;
477     AuthList *authl_cur = NULL;
478
479     *authl = NULL;
480     /*
481      * check to see if the display name is of the form "host/unix:"
482      * which is how the list routine prints out local connections
483      */
484     cp = strchr(displayname, '/');
485     if (cp && strncmp (cp, "/unix:", 6) == 0)
486       prelen = (cp - displayname);
487     
488     if (!parse_displayname (displayname + ((prelen > 0) ? prelen + 1 : 0),
489                             &family, &host, &dpynum, &scrnum, &rest)) {
490         return False;
491     }
492
493     addrlist_head = get_address_info(family, displayname, prelen, host);
494     if (addrlist_head) {
495         char buf[40];                   /* want to hold largest display num */
496         unsigned short dpylen;
497
498         buf[0] = '\0';
499         sprintf (buf, "%d", dpynum);
500         dpylen = strlen (buf);
501         if (dpylen > 0) {
502             for (addrlist_cur = addrlist_head; addrlist_cur != NULL;
503                  addrlist_cur = addrlist_cur->next) {
504                 AuthList *newal = malloc(sizeof(AuthList));
505                 Xauth *auth = malloc(sizeof(Xauth));
506
507                 if ((newal == NULL) || (auth == NULL)) {
508                     if (newal != NULL) free(newal);
509                     if (auth != NULL) free(auth);
510                     break;
511                 }
512
513                 if (authl_cur == NULL) {
514                     *authl = authl_cur = newal;
515                 } else {
516                     authl_cur->next = newal;
517                     authl_cur = newal;
518                 }
519
520                 newal->next = NULL;
521                 newal->auth = auth;
522
523                 auth->family = addrlist_cur->family;
524                 auth->address = addrlist_cur->address;
525                 auth->address_length = addrlist_cur->len;
526                 auth->number = copystring(buf, dpylen);
527                 auth->number_length = dpylen;
528                 auth->name = NULL;
529                 auth->name_length = 0;
530                 auth->data = NULL;
531                 auth->data_length = 0;
532             }
533         }
534     }
535
536     if (host) free (host);
537     if (rest) free (rest);
538
539     if (*authl != NULL) {
540         return True;
541     } else {
542         return False;
543     }
544 }
545
546 static int 
547 cvthexkey(char *hexstr, char **ptrp)    /* turn hex key string into octets */
548 {
549     int i;
550     int len = 0;
551     char *retval, *s;
552     unsigned char *us;
553     char c;
554     char savec = '\0';
555
556     /* count */
557     for (s = hexstr; *s; s++) {
558         if (!isascii(*s)) return -1;
559         if (isspace(*s)) continue;
560         if (!isxdigit(*s)) return -1;
561         len++;
562     }
563
564     /* if odd then there was an error */
565     if ((len & 1) == 1) return -1;
566
567
568     /* now we know that the input is good */
569     len >>= 1;
570     retval = malloc (len);
571     if (!retval) {
572         fprintf (stderr, "%s:  unable to allocate %d bytes for hexkey\n",
573                  ProgramName, len);
574         return -1;
575     }
576
577     for (us = (unsigned char *) retval, i = len; i > 0; hexstr++) {
578         c = *hexstr;
579         if (isspace(c)) continue;        /* already know it is ascii */
580         if (isupper(c))
581             c = tolower(c);
582         if (savec) {
583 #define atoh(c) ((c) - (((c) >= '0' && (c) <= '9') ? '0' : ('a'-10)))
584             *us = (unsigned char)((atoh(savec) << 4) + atoh(c));
585 #undef atoh
586             savec = 0;          /* ready for next character */
587             us++;
588             i--;
589         } else {
590             savec = c;
591         }
592     }
593     *ptrp = retval;
594     return len;
595 }
596
597 static int 
598 dispatch_command(char *inputfilename, 
599                  int lineno, 
600                  int argc, 
601                  char **argv, 
602                  CommandTable *tab, 
603                  int *statusp)
604 {
605     CommandTable *ct;
606     char *cmd;
607     int n;
608                                         /* scan table for command */
609     cmd = argv[0];
610     n = strlen (cmd);
611     for (ct = tab; ct->name; ct++) {
612                                         /* look for unique prefix */
613         if (n >= ct->minlen && n <= ct->maxlen &&
614             strncmp (cmd, ct->name, n) == 0) {
615             *statusp = (*(ct->processfunc))(inputfilename, lineno, argc, argv);
616             return 1;
617         }
618     }
619
620     *statusp = 1;
621     return 0;
622 }
623
624
625 static AuthList *xauth_head = NULL;     /* list of auth entries */
626 static Bool xauth_existed = False;      /* if was present at initialize */
627 static Bool xauth_modified = False;     /* if added, removed, or merged */
628 static Bool xauth_allowed = True;       /* if allowed to write auth file */
629 static Bool xauth_locked = False;     /* if has been locked */
630 static char *xauth_filename = NULL;
631 static volatile Bool dieing = False;
632
633
634 /* poor man's puts(), for under signal handlers */
635 #define WRITES(fd, S) (void)write((fd), (S), strlen((S)))
636
637 /* ARGSUSED */
638 static RETSIGTYPE 
639 die(int sig)
640 {
641     dieing = True;
642     _exit (auth_finalize ());
643     /* NOTREACHED */
644 #ifdef SIGNALRETURNSINT
645     return -1;                          /* for picky compilers */
646 #endif
647 }
648
649 static RETSIGTYPE 
650 catchsig(int sig)
651 {
652 #ifdef SYSV
653     if (sig > 0) signal (sig, die);     /* re-establish signal handler */
654 #endif
655     /*
656      * fileno() might not be reentrant, avoid it if possible, and use
657      * stderr instead of stdout
658      */
659 #ifdef STDERR_FILENO
660     if (verbose && xauth_modified) WRITES(STDERR_FILENO, "\r\n");
661 #else
662     if (verbose && xauth_modified) WRITES(fileno(stderr), "\r\n");
663 #endif
664     die (sig);
665     /* NOTREACHED */
666 #ifdef SIGNALRETURNSINT
667     return -1;                          /* for picky compilers */
668 #endif
669 }
670
671 static void 
672 register_signals(void)
673 {
674     signal (SIGINT, catchsig);
675     signal (SIGTERM, catchsig);
676 #ifdef SIGHUP
677     signal (SIGHUP, catchsig);
678 #endif
679 #ifdef SIGPIPE
680     signal (SIGPIPE, catchsig);
681 #endif
682     return;
683 }
684
685
686 /*
687  * public procedures for parsing lines of input
688  */
689
690 int 
691 auth_initialize(char *authfilename)
692 {
693     int n;
694     AuthList *head, *tail;
695     FILE *authfp;
696     Bool exists;
697
698     xauth_filename = authfilename;    /* used in cleanup, prevent race with 
699                                          signals */
700     register_signals ();
701
702     bzero ((char *) hexvalues, sizeof hexvalues);
703     hexvalues['0'] = 0;
704     hexvalues['1'] = 1;
705     hexvalues['2'] = 2;
706     hexvalues['3'] = 3;
707     hexvalues['4'] = 4;
708     hexvalues['5'] = 5;
709     hexvalues['6'] = 6;
710     hexvalues['7'] = 7;
711     hexvalues['8'] = 8;
712     hexvalues['9'] = 9;
713     hexvalues['a'] = hexvalues['A'] = 0xa;
714     hexvalues['b'] = hexvalues['B'] = 0xb;
715     hexvalues['c'] = hexvalues['C'] = 0xc;
716     hexvalues['d'] = hexvalues['D'] = 0xd;
717     hexvalues['e'] = hexvalues['E'] = 0xe;
718     hexvalues['f'] = hexvalues['F'] = 0xf;
719
720     if (break_locks && verbose) {
721         printf ("Attempting to break locks on authority file %s\n",
722                 authfilename);
723     }
724
725     if (ignore_locks) {
726         if (break_locks) XauUnlockAuth (authfilename);
727     } else {
728         n = XauLockAuth (authfilename, XAUTH_DEFAULT_RETRIES,
729                          XAUTH_DEFAULT_TIMEOUT, 
730                          (break_locks ? 0L : XAUTH_DEFAULT_DEADTIME));
731         if (n != LOCK_SUCCESS) {
732             char *reason = "unknown error";
733             switch (n) {
734               case LOCK_ERROR:
735                 reason = "error";
736                 break;
737               case LOCK_TIMEOUT:
738                 reason = "timeout";
739                 break;
740             }
741             fprintf (stderr, "%s:  %s in locking authority file %s\n",
742                      ProgramName, reason, authfilename);
743             return -1;
744         } else
745             xauth_locked = True;
746     }
747
748     /* these checks can only be done reliably after the file is locked */
749     exists = (access (authfilename, F_OK) == 0);
750     if (exists && access (authfilename, W_OK) != 0) {
751         fprintf (stderr,
752          "%s:  %s not writable, changes will be ignored\n",
753                  ProgramName, authfilename);
754         xauth_allowed = False;
755     }
756
757     original_umask = umask (0077);      /* disallow non-owner access */
758
759     authfp = fopen (authfilename, "rb");
760     if (!authfp) {
761         int olderrno = errno;
762
763                                         /* if file there then error */
764         if (access (authfilename, F_OK) == 0) {  /* then file does exist! */
765             errno = olderrno;
766             return -1;
767         }                               /* else ignore it */
768         fprintf (stderr, 
769                  "%s:  file %s does not exist\n",
770                  ProgramName, authfilename);
771     } else {
772         xauth_existed = True;
773         n = read_auth_entries (authfp, False, &head, &tail);
774         (void) fclose (authfp);
775         if (n < 0) {
776             fprintf (stderr,
777                      "%s:  unable to read auth entries from file \"%s\"\n",
778                      ProgramName, authfilename);
779             return -1;
780         }
781         xauth_head = head;
782     }
783
784     n = strlen (authfilename);
785     xauth_filename = malloc (n + 1);
786     if (xauth_filename) strcpy (xauth_filename, authfilename);
787     else {
788         fprintf(stderr,"cannot allocate memory\n");
789         return -1;
790     }
791     
792     xauth_modified = False;
793
794     if (verbose) {
795         printf ("%s authority file %s\n", 
796                 ignore_locks ? "Ignoring locks on" : "Using", authfilename);
797     }
798     return 0;
799 }
800
801 static int 
802 write_auth_file(char *tmp_nam)
803 {
804     FILE *fp = NULL;
805     int fd;
806     AuthList *list;
807
808     /*
809      * xdm and auth spec assumes auth file is 12 or fewer characters
810      */
811     strcpy (tmp_nam, xauth_filename);
812     strcat (tmp_nam, "-n");             /* for new */
813     (void) unlink (tmp_nam);
814     /* CPhipps 2000/02/12 - fix file unlink/fopen race */
815     fd = open(tmp_nam, O_WRONLY | O_CREAT | O_EXCL, 0600);
816     if (fd != -1) fp = fdopen (fd, "wb");
817     if (!fp) {
818         if (fd != -1) close(fd);
819         fprintf (stderr, "%s:  unable to open tmp file \"%s\"\n",
820                  ProgramName, tmp_nam);
821         return -1;
822     } 
823
824     /*
825      * Write MIT-MAGIC-COOKIE-1 first, because R4 Xlib knows
826      * only that and uses the first authorization it finds.
827      */
828     for (list = xauth_head; list; list = list->next) {
829         if (list->auth->name_length == 18
830             && strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) == 0) {
831             if (!XauWriteAuth(fp, list->auth)) {
832                 (void) fclose(fp);
833                 return -1;
834             }
835         }
836     }
837     for (list = xauth_head; list; list = list->next) {
838         if (list->auth->name_length != 18
839             || strncmp(list->auth->name, "MIT-MAGIC-COOKIE-1", 18) != 0) {
840             if (!XauWriteAuth(fp, list->auth)) {
841                 (void) fclose(fp);
842                 return -1;
843             }
844         }
845     }
846
847     (void) fclose (fp);
848     return 0;
849 }
850
851 int 
852 auth_finalize(void)
853 {
854     char temp_name[1024];       /* large filename size */
855
856     if (xauth_modified) {
857         if (dieing) {
858             if (verbose) {
859                 /*
860                  * called from a signal handler -- printf is *not* reentrant; also
861                  * fileno() might not be reentrant, avoid it if possible, and use
862                  * stderr instead of stdout
863                  */
864 #ifdef STDERR_FILENO
865                 WRITES(STDERR_FILENO, "\nAborting changes to authority file ");
866                 WRITES(STDERR_FILENO, xauth_filename);
867                 WRITES(STDERR_FILENO, "\n");
868 #else
869                 WRITES(fileno(stderr), "\nAborting changes to authority file ");
870                 WRITES(fileno(stderr), xauth_filename);
871                 WRITES(fileno(stderr), "\n");
872 #endif
873             }
874         } else if (!xauth_allowed) {
875             fprintf (stderr, 
876                      "%s:  %s not writable, changes ignored\n",
877                      ProgramName, xauth_filename);
878         } else {
879             if (verbose) {
880                 printf ("%s authority file %s\n", 
881                         ignore_locks ? "Ignoring locks and writing" :
882                         "Writing", xauth_filename);
883             }
884             temp_name[0] = '\0';
885             if (write_auth_file (temp_name) == -1) {
886                 fprintf (stderr,
887                          "%s:  unable to write authority file %s\n",
888                          ProgramName, temp_name);
889             } else {
890                 (void) unlink (xauth_filename);
891 #if defined(WIN32) || defined(__UNIXOS2__)
892                 if (rename(temp_name, xauth_filename) == -1)
893 #else
894                 if (link (temp_name, xauth_filename) == -1)
895 #endif
896                 {
897                     fprintf (stderr,
898                      "%s:  unable to link authority file %s, use %s\n",
899                              ProgramName, xauth_filename, temp_name);
900                 } else {
901                     (void) unlink (temp_name);
902                 }
903             }
904         }
905     }
906
907     if (xauth_locked) {
908         XauUnlockAuth (xauth_filename);
909     }
910     (void) umask (original_umask);
911     return 0;
912 }
913
914 int 
915 process_command(char *inputfilename, int lineno, int argc, char **argv)
916 {
917     int status;
918
919     if (argc < 1 || !argv || !argv[0]) return 1;
920
921     if (dispatch_command (inputfilename, lineno, argc, argv,
922                           command_table, &status))
923       return status;
924
925     prefix (inputfilename, lineno);
926     fprintf (stderr, "unknown command \"%s\"\n", argv[0]);
927     return 1;
928 }
929
930
931 /*
932  * utility routines
933  */
934
935 static char * 
936 bintohex(unsigned int len, char *bindata)
937 {
938     char *hexdata, *starthex;
939
940     /* two chars per byte, plus null termination */
941     starthex = hexdata = (char *)malloc(2*len + 1); 
942     if (!hexdata)
943         return NULL;
944
945     for (; len > 0; len--, bindata++) {
946         register char *s = hex_table[(unsigned char)*bindata];
947         *hexdata++ = s[0];
948         *hexdata++ = s[1];
949     }
950     *hexdata = '\0';
951     return starthex;
952 }
953
954 static void 
955 fprintfhex(register FILE *fp, int len, char *cp)
956 {
957     char *hex;
958
959     hex = bintohex(len, cp);
960     fprintf(fp, "%s", hex);
961     free(hex);
962 }
963
964 static int
965 dump_numeric(register FILE *fp, register Xauth *auth)
966 {
967     fprintf (fp, "%04x", auth->family);  /* unsigned short */
968     fprintf (fp, " %04x ", auth->address_length);  /* short */
969     fprintfhex (fp, auth->address_length, auth->address);
970     fprintf (fp, " %04x ", auth->number_length);  /* short */
971     fprintfhex (fp, auth->number_length, auth->number);
972     fprintf (fp, " %04x ", auth->name_length);  /* short */
973     fprintfhex (fp, auth->name_length, auth->name);
974     fprintf (fp, " %04x ", auth->data_length);  /* short */
975     fprintfhex (fp, auth->data_length, auth->data);
976     putc ('\n', fp);
977     return 1;
978 }
979
980 /* ARGSUSED */
981 static int 
982 dump_entry(char *inputfilename, int lineno, Xauth *auth, char *data)
983 {
984     struct _list_data *ld = (struct _list_data *) data;
985     FILE *fp = ld->fp;
986
987     if (ld->numeric) {
988         dump_numeric (fp, auth);
989     } else {
990         char *dpyname = NULL;
991
992         switch (auth->family) {
993           case FamilyLocal:
994             fwrite (auth->address, sizeof (char), auth->address_length, fp);
995             fprintf (fp, "/unix");
996             break;
997           case FamilyInternet:
998 #if defined(IPv6) && defined(AF_INET6)
999           case FamilyInternet6:
1000 #endif
1001           case FamilyDECnet:
1002             dpyname = get_hostname (auth);
1003             if (dpyname) {
1004                 fprintf (fp, "%s", dpyname);
1005                 break;
1006             }
1007             /* else fall through to default */
1008           default:
1009             fprintf (fp, "#%04x#", auth->family);
1010             fprintfhex (fp, auth->address_length, auth->address);
1011             putc ('#', fp);
1012         }
1013         putc (':', fp);
1014         fwrite (auth->number, sizeof (char), auth->number_length, fp);
1015         putc (' ', fp);
1016         putc (' ', fp);
1017         fwrite (auth->name, sizeof (char), auth->name_length, fp);
1018         putc (' ', fp);
1019         putc (' ', fp);
1020         if (!strncmp(auth->name, SECURERPC, auth->name_length) ||
1021             !strncmp(auth->name, K5AUTH, auth->name_length))
1022             fwrite (auth->data, sizeof (char), auth->data_length, fp);
1023         else
1024             fprintfhex (fp, auth->data_length, auth->data);
1025         putc ('\n', fp);
1026     }
1027     return 0;
1028 }
1029
1030 static int 
1031 extract_entry(char *inputfilename, int lineno, Xauth *auth, char *data)
1032 {
1033     struct _extract_data *ed = (struct _extract_data *) data;
1034
1035     if (!ed->fp) {
1036         ed->fp = open_file (&ed->filename,
1037                             ed->numeric ? "w" : "wb",
1038                             &ed->used_stdout,
1039                             inputfilename, lineno, ed->cmd);
1040         if (!ed->fp) {
1041             prefix (inputfilename, lineno);
1042             fprintf (stderr,
1043                      "unable to open extraction file \"%s\"\n",
1044                      ed->filename);
1045             return -1;
1046         }
1047     }
1048     (*(ed->numeric ? dump_numeric : XauWriteAuth)) (ed->fp, auth);
1049     ed->nwritten++;
1050
1051     return 0;
1052 }
1053
1054
1055 static int
1056 eq_auth(Xauth *a, Xauth *b)
1057 {
1058     return((a->family == b->family &&
1059             a->address_length == b->address_length &&
1060             a->number_length == b->number_length &&
1061             a->name_length == b->name_length &&
1062             a->data_length == b->data_length &&
1063             memcmp(a->address, b->address, a->address_length) == 0 &&
1064             memcmp(a->number, b->number, a->number_length) == 0 &&
1065             memcmp(a->name, b->name, a->name_length) == 0 &&
1066             memcmp(a->data, b->data, a->data_length) == 0) ? 1 : 0);
1067 }
1068             
1069 static int 
1070 match_auth_dpy(register Xauth *a, register Xauth *b)
1071 {
1072     return ((a->family == b->family &&
1073              a->address_length == b->address_length &&
1074              a->number_length == b->number_length &&
1075              memcmp(a->address, b->address, a->address_length) == 0 &&
1076              memcmp(a->number, b->number, a->number_length) == 0) ? 1 : 0);
1077 }
1078
1079 /* return non-zero iff display and authorization type are the same */
1080
1081 static int 
1082 match_auth(register Xauth *a, register Xauth *b)
1083 {
1084     return ((match_auth_dpy(a, b)
1085              && a->name_length == b->name_length
1086              && memcmp(a->name, b->name, a->name_length) == 0) ? 1 : 0);
1087 }
1088
1089
1090 static int 
1091 merge_entries(AuthList **firstp, AuthList *second, int *nnewp, int *nreplp)
1092 {
1093     AuthList *a, *b, *first, *tail;
1094     int n = 0, nnew = 0, nrepl = 0;
1095
1096     if (!second) return 0;
1097
1098     if (!*firstp) {                     /* if nothing to merge into */
1099         *firstp = second;
1100         for (tail = *firstp, n = 1; tail->next; n++, tail = tail->next) ;
1101         *nnewp = n;
1102         *nreplp = 0;
1103         return n;
1104     }
1105
1106     first = *firstp;
1107     /*
1108      * find end of first list and stick second list on it
1109      */
1110     for (tail = first; tail->next; tail = tail->next) ;
1111     tail->next = second;
1112
1113     /*
1114      * run down list freeing duplicate entries; if an entry is okay, then
1115      * bump the tail up to include it, otherwise, cut the entry out of
1116      * the chain.
1117      */
1118     for (b = second; b; ) {
1119         AuthList *next = b->next;       /* in case we free it */
1120
1121         a = first;
1122         for (;;) {
1123             if (match_auth (a->auth, b->auth)) {  /* found a duplicate */
1124                 AuthList tmp;           /* swap it in for old one */
1125                 tmp = *a;
1126                 *a = *b;
1127                 *b = tmp;
1128                 a->next = b->next;
1129                 XauDisposeAuth (b->auth);
1130                 free ((char *) b);
1131                 b = NULL;
1132                 tail->next = next;
1133                 nrepl++;
1134                 nnew--;
1135                 break;
1136             }
1137             if (a == tail) break;       /* if have looked at left side */
1138             a = a->next;
1139         }
1140         if (b) {                        /* if we didn't remove it */
1141             tail = b;                   /* bump end of first list */
1142         }
1143         b = next;
1144         n++;
1145         nnew++;
1146     }
1147
1148     *nnewp = nnew;
1149     *nreplp = nrepl;
1150     return n;
1151
1152 }
1153
1154 static Xauth *
1155 copyAuth(Xauth *auth)
1156 {
1157     Xauth *a;
1158
1159     a = (Xauth *)malloc(sizeof(Xauth));
1160     if (a == NULL) {
1161         return NULL;
1162     }
1163     memset(a, 0, sizeof(Xauth));
1164     a->family = auth->family;
1165     if (auth->address_length != 0) {
1166         a->address = malloc(auth->address_length);
1167         if (a->address == NULL) {
1168             free(a);
1169             return NULL;
1170         }
1171         memcpy(a->address, auth->address, auth->address_length);
1172         a->address_length = auth->address_length;
1173     }
1174     if (auth->number_length != 0) {
1175         a->number = malloc(auth->number_length);
1176         if (a->number == NULL) {
1177             free(a->address);
1178             free(a);
1179             return NULL;
1180         }
1181         memcpy(a->number, auth->number, auth->number_length);
1182         a->number_length = auth->number_length;
1183     }
1184     if (auth->name_length != 0) {
1185         a->name = malloc(auth->name_length);
1186         if (a->name == NULL) {
1187             free(a->address);
1188             free(a->number);
1189             free(a);
1190             return NULL;
1191         }
1192         memcpy(a->name, auth->name, auth->name_length);
1193         a->name_length = auth->name_length;
1194     }
1195     if (auth->data_length != 0) {
1196         a->data = malloc(auth->data_length);
1197         if (a->data == NULL) {
1198             free(a->address);
1199             free(a->number);
1200             free(a->name);
1201             free(a);
1202             return NULL;
1203         }
1204         memcpy(a->data, auth->data, auth->data_length);
1205         a->data_length = auth->data_length;
1206     }
1207     return a;
1208 }
1209     
1210 typedef int (*YesNoFunc)(char *, int, Xauth *, char *);
1211
1212 static int 
1213 iterdpy (char *inputfilename, int lineno, int start,
1214          int argc, char *argv[], 
1215          YesNoFunc yfunc, YesNoFunc nfunc, char *data)
1216 {
1217     int i;
1218     int status;
1219     int errors = 0;
1220     Xauth *tmp_auth;
1221     AuthList *proto_head, *proto;
1222     AuthList *l, *next;
1223
1224     /*
1225      * iterate
1226      */
1227     for (i = start; i < argc; i++) {
1228         char *displayname = argv[i];
1229         if (!get_displayname_auth (displayname, &proto_head)) {
1230             prefix (inputfilename, lineno);
1231             baddisplayname (displayname, argv[0]);
1232             errors++;
1233             continue;
1234         }
1235         status = 0;
1236         for (l = xauth_head; l; l = next) {
1237             Bool matched = False;
1238
1239             /* l may be freed by remove_entry below. so save its contents */
1240             next = l->next;
1241             tmp_auth = copyAuth(l->auth);
1242             for (proto = proto_head; proto; proto = proto->next) {
1243                 if (match_auth_dpy (proto->auth, tmp_auth)) {
1244                     matched = True;
1245                     if (yfunc) {
1246                         status = (*yfunc) (inputfilename, lineno,
1247                                            tmp_auth, data);
1248                         if (status < 0) break;
1249                     }
1250                 }
1251             }
1252             XauDisposeAuth(tmp_auth);
1253             if (matched == False) {
1254                 if (nfunc) {
1255                     status = (*nfunc) (inputfilename, lineno,
1256                                        l->auth, data);
1257                 }
1258             }
1259             if (status < 0) break;
1260         }
1261         for (proto = proto_head; proto ; proto = next) {
1262             next = proto->next;
1263             if (proto->auth->address) free (proto->auth->address);
1264             if (proto->auth->number) free (proto->auth->number);
1265             free (proto->auth);
1266             free (proto);
1267         }
1268         if (status < 0) {
1269             errors -= status;           /* since status is negative */
1270             break;
1271         }
1272     }
1273
1274     return errors;
1275 }
1276
1277 /* ARGSUSED */
1278 static int 
1279 remove_entry(char *inputfilename, int lineno, Xauth *auth, char *data)
1280 {
1281     int *nremovedp = (int *) data;
1282     AuthList **listp = &xauth_head;
1283     AuthList *list;
1284
1285     /*
1286      * unlink the auth we were asked to
1287      */
1288     while (!eq_auth((list = *listp)->auth, auth))
1289         listp = &list->next;
1290     *listp = list->next;
1291     XauDisposeAuth (list->auth);                    /* free the auth */
1292     free (list);                                    /* free the link */
1293     xauth_modified = True;
1294     (*nremovedp)++;
1295     return 1;
1296 }
1297
1298 /*
1299  * action routines
1300  */
1301
1302 /*
1303  * help
1304  */
1305 int 
1306 print_help(FILE *fp, char *cmd, char *prefix)
1307 {
1308     CommandTable *ct;
1309     int n = 0;
1310
1311     if (!prefix) prefix = "";
1312
1313     if (!cmd) {                         /* if no cmd, print all help */
1314         for (ct = command_table; ct->name; ct++) {
1315             fprintf (fp, "%s%s\n", prefix, ct->helptext);
1316             n++;
1317         }
1318     } else {
1319         int len = strlen (cmd);
1320         for (ct = command_table; ct->name; ct++) {
1321             if (strncmp (cmd, ct->name, len) == 0) {
1322                 fprintf (fp, "%s%s\n", prefix, ct->helptext);
1323                 n++;
1324             }
1325         }
1326     }
1327         
1328     return n;
1329 }
1330
1331 static int 
1332 do_help(char *inputfilename, int lineno, int argc, char **argv)
1333 {
1334     char *cmd = (argc > 1 ? argv[1] : NULL);
1335     int n;
1336
1337     n = print_help (stdout, cmd, "    ");  /* a nice amount */
1338
1339     if (n < 0 || (n == 0 && !cmd)) {
1340         prefix (inputfilename, lineno);
1341         fprintf (stderr, "internal error with help");
1342         if (cmd) {
1343             fprintf (stderr, " on command \"%s\"", cmd);
1344         }
1345         fprintf (stderr, "\n");
1346         return 1;
1347     }
1348
1349     if (n == 0) {
1350         prefix (inputfilename, lineno);
1351         /* already know that cmd is set in this case */
1352         fprintf (stderr, "no help for noexistent command \"%s\"\n", cmd);
1353     }
1354
1355     return 0;
1356 }
1357
1358 /*
1359  * questionmark
1360  */
1361 /* ARGSUSED */
1362 static int 
1363 do_questionmark(char *inputfilename, int lineno, int argc, char **argv)
1364 {
1365     CommandTable *ct;
1366     int i;
1367 #define WIDEST_COLUMN 72
1368     int col = WIDEST_COLUMN;
1369
1370     printf ("Commands:\n");
1371     for (ct = command_table; ct->name; ct++) {
1372         if ((col + ct->maxlen) > WIDEST_COLUMN) {
1373             if (ct != command_table) {
1374                 putc ('\n', stdout);
1375             }
1376             fputs ("        ", stdout);
1377             col = 8;                    /* length of string above */
1378         }
1379         fputs (ct->name, stdout);
1380         col += ct->maxlen;
1381         for (i = ct->maxlen; i < COMMAND_NAMES_PADDED_WIDTH; i++) {
1382             putc (' ', stdout);
1383             col++;
1384         }
1385     }
1386     if (col != 0) {
1387         putc ('\n', stdout);
1388     }
1389
1390     /* allow bad lines since this is help */
1391     return 0;
1392 }
1393
1394 /*
1395  * list [displayname ...]
1396  */
1397 static int 
1398 do_list (char *inputfilename, int lineno, int argc, char **argv)
1399 {
1400     struct _list_data ld;
1401
1402     ld.fp = stdout;
1403     ld.numeric = (argv[0][0] == 'n');
1404
1405     if (argc == 1) {
1406         register AuthList *l;
1407
1408         if (xauth_head) {
1409             for (l = xauth_head; l; l = l->next) {
1410                 dump_entry (inputfilename, lineno, l->auth, (char *) &ld);
1411             }
1412         }
1413         return 0;
1414     }
1415
1416     return iterdpy (inputfilename, lineno, 1, argc, argv,
1417                     dump_entry, NULL, (char *) &ld);
1418 }
1419
1420 /*
1421  * merge filename [filename ...]
1422  */
1423 static int 
1424 do_merge(char *inputfilename, int lineno, int argc, char **argv)
1425 {
1426     int i;
1427     int errors = 0;
1428     AuthList *head, *tail, *listhead, *listtail;
1429     int nentries, nnew, nrepl;
1430     Bool numeric = False;
1431
1432     if (argc < 2) {
1433         prefix (inputfilename, lineno);
1434         badcommandline (argv[0]);
1435         return 1;
1436     }
1437
1438     if (argv[0][0] == 'n') numeric = True;
1439     listhead = listtail = NULL;
1440
1441     for (i = 1; i < argc; i++) {
1442         char *filename = argv[i];
1443         FILE *fp;
1444         Bool used_stdin = False;
1445
1446         fp = open_file (&filename,
1447                         numeric ? "r" : "rb",
1448                         &used_stdin, inputfilename, lineno,
1449                         argv[0]);
1450         if (!fp) {
1451             errors++;
1452             continue;
1453         }
1454
1455         head = tail = NULL;
1456         nentries = read_auth_entries (fp, numeric, &head, &tail);
1457         if (nentries == 0) {
1458             prefix (inputfilename, lineno);
1459             fprintf (stderr, "unable to read any entries from file \"%s\"\n",
1460                      filename);
1461             errors++;
1462         } else {                        /* link it in */
1463             add_to_list (listhead, listtail, head);
1464         }
1465
1466         if (!used_stdin) (void) fclose (fp);
1467     }
1468
1469     /*
1470      * if we have new entries, merge them in (freeing any duplicates)
1471      */
1472     if (listhead) {
1473         nentries = merge_entries (&xauth_head, listhead, &nnew, &nrepl);
1474         if (verbose) 
1475           printf ("%d entries read in:  %d new, %d replacement%s\n", 
1476                   nentries, nnew, nrepl, nrepl != 1 ? "s" : "");
1477         if (nentries > 0) xauth_modified = True;
1478     }
1479
1480     return 0;
1481 }
1482
1483 /*
1484  * extract filename displayname [displayname ...]
1485  */
1486 static int 
1487 do_extract(char *inputfilename, int lineno, int argc, char **argv)
1488 {
1489     int errors;
1490     struct _extract_data ed;
1491
1492     if (argc < 3) {
1493         prefix (inputfilename, lineno);
1494         badcommandline (argv[0]);
1495         return 1;
1496     }
1497
1498     ed.fp = NULL;
1499     ed.filename = argv[1];
1500     ed.used_stdout = False;
1501     ed.numeric = (argv[0][0] == 'n');
1502     ed.nwritten = 0;
1503     ed.cmd = argv[0];
1504
1505     errors = iterdpy (inputfilename, lineno, 2, argc, argv, 
1506                       extract_entry, NULL, (char *) &ed);
1507
1508     if (!ed.fp) {
1509         fprintf (stderr, 
1510                  "No matches found, authority file \"%s\" not written\n",
1511                  ed.filename);
1512     } else {
1513         if (verbose) {
1514             printf ("%d entries written to \"%s\"\n", 
1515                     ed.nwritten, ed.filename);
1516         }
1517         if (!ed.used_stdout) {
1518             (void) fclose (ed.fp);
1519         }
1520     }
1521
1522     return errors;
1523 }
1524
1525
1526 /*
1527  * add displayname protocolname hexkey
1528  */
1529 static int 
1530 do_add(char *inputfilename, int lineno, int argc, char **argv)
1531
1532     int n, nnew, nrepl;
1533     int len;
1534     char *dpyname;
1535     char *protoname;
1536     char *hexkey;
1537     char *key;
1538     AuthList *list, *list_cur, *list_next;
1539
1540     if (argc != 4 || !argv[1] || !argv[2] || !argv[3]) {
1541         prefix (inputfilename, lineno);
1542         badcommandline (argv[0]);
1543         return 1;
1544     }
1545
1546     dpyname = argv[1];
1547     protoname = argv[2];
1548     hexkey = argv[3];
1549
1550     len = strlen(hexkey);
1551     if (hexkey[0] == '"' && hexkey[len-1] == '"') {
1552         key = malloc(len-1);
1553         strncpy(key, hexkey+1, len-2);
1554         len -= 2;
1555     } else if (!strcmp(protoname, SECURERPC) ||
1556                !strcmp(protoname, K5AUTH)) {
1557         key = malloc(len+1);
1558         strcpy(key, hexkey);
1559     } else {
1560         len = cvthexkey (hexkey, &key);
1561         if (len < 0) {
1562             prefix (inputfilename, lineno);
1563             fprintf (stderr,
1564                      "key contains odd number of or non-hex characters\n");
1565             return 1;
1566         }
1567     }
1568
1569     if (!get_displayname_auth (dpyname, &list)) {
1570         prefix (inputfilename, lineno);
1571         baddisplayname (dpyname, argv[0]);
1572         free (key);
1573         return 1;
1574     }
1575
1576     /*
1577      * allow an abbreviation for common protocol names
1578      */
1579     if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV) == 0) {
1580         protoname = DEFAULT_PROTOCOL;
1581     }
1582
1583     for (list_cur = list;  list_cur != NULL; list_cur = list_cur->next) {
1584         Xauth *auth = list_cur->auth;
1585
1586         auth->name_length = strlen (protoname);
1587         auth->name = copystring (protoname, auth->name_length);
1588         if (!auth->name) {
1589             prefix (inputfilename, lineno);
1590             fprintf (stderr, "unable to allocate %d character protocol name\n",
1591                      auth->name_length);
1592             for (list_cur = list; list_cur != NULL; list_cur = list_next) {
1593                 list_next = list_cur->next;
1594                 XauDisposeAuth(list_cur->auth);
1595                 free(list_cur);
1596             }
1597             free (key);
1598             return 1;
1599         }
1600         auth->data_length = len;
1601         auth->data = malloc(len);
1602         if (!auth->data) {
1603                 prefix(inputfilename, lineno);
1604                 fprintf(stderr, "unable to allocate %d bytes for key\n", len);
1605                 for (list_cur = list; list_cur != NULL; list_cur = list_next) {
1606                         list_next = list_cur->next;
1607                         XauDisposeAuth(list_cur->auth);
1608                         free(list_cur);
1609                 }
1610                 free(key);
1611                 return 1;
1612         }
1613         memcpy(auth->data, key, len);
1614     }
1615     free(key);
1616     /*
1617      * merge it in; note that merge will deal with allocation
1618      */
1619     n = merge_entries (&xauth_head, list, &nnew, &nrepl);
1620     if (n <= 0) {
1621         prefix (inputfilename, lineno);
1622         fprintf (stderr, "unable to merge in added record\n");
1623         return 1;
1624     }
1625
1626     xauth_modified = True;
1627     return 0;
1628 }
1629
1630 /*
1631  * remove displayname
1632  */
1633 static int 
1634 do_remove(char *inputfilename, int lineno, int argc, char **argv)
1635 {
1636     int nremoved = 0;
1637     int errors;
1638
1639     if (argc < 2) {
1640         prefix (inputfilename, lineno);
1641         badcommandline (argv[0]);
1642         return 1;
1643     }
1644
1645     errors = iterdpy (inputfilename, lineno, 1, argc, argv,
1646                       remove_entry, NULL, (char *) &nremoved);
1647     if (verbose) printf ("%d entries removed\n", nremoved);
1648     return errors;
1649 }
1650
1651 /*
1652  * info
1653  */
1654 static int 
1655 do_info(char *inputfilename, int lineno, int argc, char **argv)
1656 {
1657     int n;
1658     AuthList *l;
1659
1660     if (argc != 1) {
1661         prefix (inputfilename, lineno);
1662         badcommandline (argv[0]);
1663         return 1;
1664     }
1665
1666     for (l = xauth_head, n = 0; l; l = l->next, n++) ;
1667
1668     printf ("Authority file:       %s\n",
1669             xauth_filename ? xauth_filename : "(none)");
1670     printf ("File new:             %s\n", xauth_existed ? No : Yes);
1671     printf ("File locked:          %s\n", xauth_locked ? No : Yes);
1672     printf ("Number of entries:    %d\n", n);
1673     printf ("Changes honored:      %s\n", xauth_allowed ? Yes : No);
1674     printf ("Changes made:         %s\n", xauth_modified ? Yes : No);
1675     printf ("Current input:        %s:%d\n", inputfilename, lineno);
1676     return 0;
1677 }
1678
1679
1680 /*
1681  * exit
1682  */
1683 static Bool alldone = False;
1684
1685 /* ARGSUSED */
1686 static int 
1687 do_exit(char *inputfilename, int lineno, int argc, char **argv)
1688 {
1689     /* allow bogus stuff */
1690     alldone = True;
1691     return 0;
1692 }
1693
1694 /*
1695  * quit
1696  */
1697 /* ARGSUSED */
1698 static int 
1699 do_quit(char *inputfilename, int lineno, int argc, char **argv)
1700 {
1701     /* allow bogus stuff */
1702     die (0);
1703     /* NOTREACHED */
1704     return -1;                          /* for picky compilers */
1705 }
1706
1707
1708 /*
1709  * source filename
1710  */
1711 static int 
1712 do_source(char *inputfilename, int lineno, int argc, char **argv)
1713 {
1714     char *script;
1715     char buf[BUFSIZ];
1716     FILE *fp;
1717     Bool used_stdin = False;
1718     int len;
1719     int errors = 0, status;
1720     int sublineno = 0;
1721     char **subargv;
1722     int subargc;
1723     Bool prompt = False;                /* only true if reading from tty */
1724
1725     if (argc != 2 || !argv[1]) {
1726         prefix (inputfilename, lineno);
1727         badcommandline (argv[0]);
1728         return 1;
1729     }
1730
1731     script = argv[1];
1732
1733     fp = open_file (&script, "r", &used_stdin, inputfilename, lineno, argv[0]);
1734     if (!fp) {
1735         return 1;
1736     }
1737
1738     if (verbose && used_stdin && isatty (fileno (fp))) prompt = True;
1739
1740     while (!alldone) {
1741         buf[0] = '\0';
1742         if (prompt) {
1743             printf ("xauth> ");
1744             fflush (stdout);
1745         }
1746         if (fgets (buf, sizeof buf, fp) == NULL) break;
1747         sublineno++;
1748         len = strlen (buf);
1749         if (len == 0 || buf[0] == '#') continue;
1750         if (buf[len-1] != '\n') {
1751             prefix (script, sublineno);
1752             fprintf (stderr, "line too long\n");
1753             errors++;
1754             break;
1755         }
1756         buf[--len] = '\0';              /* remove new line */
1757         subargv = split_into_words (buf, &subargc);
1758         if (subargv) {
1759             status = process_command (script, sublineno, subargc, subargv);
1760             free ((char *) subargv);
1761             errors += status;
1762         } else {
1763             prefix (script, sublineno);
1764             fprintf (stderr, "unable to break line into words\n");
1765             errors++;
1766         }
1767     }
1768
1769     if (!used_stdin) {
1770         (void) fclose (fp);
1771     }
1772     return errors;
1773 }
1774
1775 static int x_protocol_error;
1776 static int
1777 catch_x_protocol_error(Display *dpy, XErrorEvent *errevent)
1778 {
1779     char buf[80];
1780     XGetErrorText(dpy, errevent->error_code, buf, sizeof (buf));
1781     fprintf(stderr, "%s\n", buf);
1782     x_protocol_error = errevent->error_code;
1783     return 1;
1784 }
1785
1786 /*
1787  * generate
1788  */
1789 static int 
1790 do_generate(char *inputfilename, int lineno, int argc, char **argv)
1791 {
1792     char *displayname;
1793     int major_version, minor_version;
1794     XSecurityAuthorization id_return;
1795     Xauth *auth_in, *auth_return;
1796     XSecurityAuthorizationAttributes attributes;
1797     unsigned long attrmask = 0;
1798     Display *dpy;
1799     int status;
1800     char *args[4];
1801     char *protoname = ".";
1802     int i;
1803     int authdatalen = 0;
1804     char *hexdata;
1805     char *authdata = NULL;
1806
1807     if (argc < 2 || !argv[1]) {
1808         prefix (inputfilename, lineno);
1809         badcommandline (argv[0]);
1810         return 1;
1811     }
1812
1813     displayname = argv[1];
1814
1815     if (argc > 2) {
1816         protoname = argv[2];
1817     }
1818
1819     for (i = 3; i < argc; i++) {
1820         if (0 == strcmp(argv[i], "timeout")) {
1821             if (++i == argc) {
1822                 prefix (inputfilename, lineno);
1823                 badcommandline (argv[i-1]);
1824                 return 1;
1825             } 
1826             attributes.timeout = atoi(argv[i]);
1827             attrmask |= XSecurityTimeout;
1828
1829         } else if (0 == strcmp(argv[i], "trusted")) {
1830             attributes.trust_level = XSecurityClientTrusted;
1831             attrmask |= XSecurityTrustLevel;
1832
1833         } else if (0 == strcmp(argv[i], "untrusted")) {
1834             attributes.trust_level = XSecurityClientUntrusted;
1835             attrmask |= XSecurityTrustLevel;
1836
1837         } else if (0 == strcmp(argv[i], "group")) {
1838             if (++i == argc) {
1839                 prefix (inputfilename, lineno);
1840                 badcommandline (argv[i-1]);
1841                 return 1;
1842             } 
1843             attributes.group = atoi(argv[i]);
1844             attrmask |= XSecurityGroup;
1845
1846         } else if (0 == strcmp(argv[i], "data")) {
1847             if (++i == argc) {
1848                 prefix (inputfilename, lineno);
1849                 badcommandline (argv[i-1]);
1850                 return 1;
1851             } 
1852             hexdata = argv[i];
1853             authdatalen = strlen(hexdata);
1854             if (hexdata[0] == '"' && hexdata[authdatalen-1] == '"') {
1855                 authdata = malloc(authdatalen-1);
1856                 strncpy(authdata, hexdata+1, authdatalen-2);
1857                 authdatalen -= 2;
1858             } else {
1859                 authdatalen = cvthexkey (hexdata, &authdata);
1860                 if (authdatalen < 0) {
1861                     prefix (inputfilename, lineno);
1862                     fprintf (stderr,
1863                              "data contains odd number of or non-hex characters\n");
1864                     return 1;
1865                 }
1866             }
1867         } else {
1868             prefix (inputfilename, lineno);
1869             badcommandline (argv[i]);
1870             return 1;
1871         }
1872     }
1873
1874     /* generate authorization using the Security extension */
1875
1876     dpy = XOpenDisplay (displayname);
1877     if (!dpy) {
1878         prefix (inputfilename, lineno);
1879         fprintf (stderr, "unable to open display \"%s\".\n", displayname);
1880         return 1;
1881     }
1882
1883     status = XSecurityQueryExtension(dpy, &major_version, &minor_version);
1884     if (!status)
1885     {
1886         prefix (inputfilename, lineno);
1887         fprintf (stderr, "couldn't query Security extension on display \"%s\"\n",
1888                  displayname);
1889         return 1;
1890     }
1891
1892     /* fill in input Xauth struct */
1893
1894     auth_in = XSecurityAllocXauth();
1895     if (strcmp (protoname, DEFAULT_PROTOCOL_ABBREV) == 0) {
1896          auth_in->name = DEFAULT_PROTOCOL;
1897     }
1898     else
1899         auth_in->name = protoname;
1900     auth_in->name_length = strlen(auth_in->name);
1901     auth_in->data = authdata;
1902     auth_in->data_length = authdatalen;
1903
1904     x_protocol_error = 0;
1905     XSetErrorHandler(catch_x_protocol_error);
1906     auth_return = XSecurityGenerateAuthorization(dpy, auth_in, attrmask,
1907                                                  &attributes, &id_return);
1908     XSync(dpy, False);
1909
1910     if (!auth_return || x_protocol_error)
1911     {
1912         prefix (inputfilename, lineno);
1913         fprintf (stderr, "couldn't generate authorization\n");
1914         return 1;
1915     }
1916
1917     if (verbose)
1918         printf("authorization id is %ld\n", id_return);
1919
1920     /* create a fake input line to give to do_add */
1921
1922     args[0] = "add";
1923     args[1] = displayname;
1924     args[2] = auth_in->name;
1925     args[3] = bintohex(auth_return->data_length, auth_return->data);
1926
1927     status = do_add(inputfilename, lineno, 4, args);
1928
1929     if (authdata) free(authdata);
1930     XSecurityFreeXauth(auth_in);
1931     XSecurityFreeXauth(auth_return);
1932     free(args[3]); /* hex data */
1933     XCloseDisplay(dpy);
1934     return status;
1935 }