x11-ssh-askpass.c revision 8db30ca8
1/* x11-ssh-askpass.c:  A generic X11-based password dialog for OpenSSH.
2 * created 1999-Nov-17 03:40 Jim Knoble <jmknoble@pobox.com>
3 * autodate: 1999-Nov-23 02:52
4 *
5 * by Jim Knoble <jmknoble@pobox.com>
6 * Copyright � 1999 Jim Knoble
7 *
8 * Disclaimer:
9 *
10 * The software is provided "as is", without warranty of any kind,
11 * express or implied, including but not limited to the warranties of
12 * merchantability, fitness for a particular purpose and
13 * noninfringement. In no event shall the author(s) be liable for any
14 * claim, damages or other liability, whether in an action of
15 * contract, tort or otherwise, arising from, out of or in connection
16 * with the software or the use or other dealings in the software.
17 *
18 * Portions of this code are distantly derived from code in xscreensaver
19 * by Jamie Zawinski <jwz@jwz.org>.  That code says:
20 *
21 * --------8<------------------------------------------------8<--------
22 * xscreensaver, Copyright (c) 1991-1999 Jamie Zawinski <jwz@jwz.org>
23 *
24 * Permission to use, copy, modify, distribute, and sell this software and its
25 * documentation for any purpose is hereby granted without fee, provided that
26 * the above copyright notice appear in all copies and that both that
27 * copyright notice and this permission notice appear in supporting
28 * documentation.  No representations are made about the suitability of this
29 * software for any purpose.  It is provided "as is" without express or
30 * implied warranty.
31 * --------8<------------------------------------------------8<--------
32 *
33 * The remainder of this code falls under the same permissions and
34 * provisions as those of the xscreensaver code.
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41/* For (get|set)rlimit() ... */
42#include <sys/time.h>
43#include <sys/resource.h>
44#include <unistd.h>
45/* ... end */
46
47/* For errno ... */
48#include <errno.h>
49/* ... end */
50
51#include <X11/Xlib.h>
52#include <X11/Intrinsic.h>
53#include <X11/Shell.h>
54#include <X11/Xos.h>
55#include "dynlist.h"
56#include "drawing.h"
57#include "resources.h"
58#include "x11-ssh-askpass.h"
59
60#undef MAX
61#define MAX(a,b) (((a) > (b)) ? (a) : (b))
62
63char *progname = NULL;
64char *progclass = NULL;
65XrmDatabase db = 0;
66
67static char *defaults[] = {
68#include "SshAskpass_ad.h"
69   0
70};
71
72void outOfMemory(AppInfo *app, int line)
73{
74   fprintf(stderr, "%s: Aaahhh! I ran out of memory at line %d.\n",
75	   app->appName, line);
76   exit(EXIT_STATUS_NO_MEMORY);
77}
78
79void freeIf(void *p)
80{
81   if (p) {
82      free(p);
83   }
84}
85
86void freeFontIf(AppInfo *app, XFontStruct *f)
87{
88   if (f) {
89      XFreeFont(app->dpy, f);
90   }
91}
92
93XFontStruct *getFontResource(AppInfo *app, char *instanceName, char *className)
94{
95   char *fallbackFont = "fixed";
96
97   XFontStruct *f = NULL;
98   char *s = get_string_resource(instanceName, className);
99   f = XLoadQueryFont(app->dpy, (s ? s : fallbackFont));
100   if (!f) {
101      f = XLoadQueryFont(app->dpy, fallbackFont);
102   }
103   if (s) {
104      free(s);
105   }
106   return(f);
107}
108
109char *getStringResourceWithDefault(char *instanceName, char *className,
110				   char *defaultText)
111{
112   char *s = get_string_resource(instanceName, className);
113   if (!s) {
114      if (!defaultText) {
115	 s = strdup("");
116      } else {
117	 s = strdup(defaultText);
118      }
119   }
120   return(s);
121}
122
123void calcLabelTextExtents(LabelInfo *label)
124{
125   if ((!label) || (!(label->text)) || (!(label->font))) {
126      return;
127   }
128   label->textLength = strlen(label->text);
129   XTextExtents(label->font, label->text, label->textLength,
130		&(label->direction), &(label->ascent), &(label->descent),
131		&(label->overall));
132   label->w.height = label->descent + label->ascent;
133   label->w.width = label->overall.width;
134}
135
136void calcTotalButtonExtents(ButtonInfo *button)
137{
138   if (!button) {
139      return;
140   }
141   button->w3.w.width = (button->w3.interiorWidth +
142			 (2 * button->w3.shadowThickness));
143   button->w3.w.width += (2 * button->w3.borderWidth);
144   button->w3.w.height = (button->w3.interiorHeight +
145			  (2 * button->w3.shadowThickness));
146   button->w3.w.height += (2 * button->w3.borderWidth);
147}
148
149void calcButtonExtents(ButtonInfo *button)
150{
151   if (!button) {
152      return;
153   }
154   calcLabelTextExtents(&(button->label));
155   button->w3.interiorWidth = (button->label.w.width +
156			       (2 * button->w3.horizontalSpacing));
157   button->w3.interiorHeight = (button->label.w.height +
158				(2 * button->w3.verticalSpacing));
159   calcTotalButtonExtents(button);
160}
161
162void balanceButtonExtents(ButtonInfo *button1, ButtonInfo *button2)
163{
164   if ((!button1) || (!button2)) {
165      return;
166   }
167   button1->w3.interiorWidth = button2->w3.interiorWidth =
168      MAX(button1->w3.interiorWidth, button2->w3.interiorWidth);
169   button1->w3.interiorHeight = button2->w3.interiorHeight =
170      MAX(button1->w3.interiorHeight, button2->w3.interiorHeight);
171   calcTotalButtonExtents(button1);
172   calcTotalButtonExtents(button2);
173}
174
175void calcButtonLabelPosition(ButtonInfo *button)
176{
177   if (!button) {
178      return;
179   }
180   button->label.w.x = button->w3.w.x +
181      ((button->w3.w.width - button->label.w.width) / 2);
182   button->label.w.y = button->w3.w.y +
183      ((button->w3.w.height - button->label.w.height) / 2)
184      + button->label.ascent;
185}
186
187void createDialog(AppInfo *app)
188{
189   DialogInfo *d;
190
191   if (app->dialog) {
192      return;
193   }
194   d = malloc(sizeof(*d));
195   if (NULL == d) {
196      outOfMemory(app, __LINE__);
197   }
198   memset(d, 0, sizeof(*d));
199
200   app->grabKeyboard =
201      get_boolean_resource("grabKeyboard", "GrabKeyboard", True);
202   app->grabPointer =
203      get_boolean_resource("grabPointer", "GrabPointer", False);
204   app->grabPointer =
205      get_boolean_resource("grabServer", "GrabServer", False);
206
207   d->title =
208      getStringResourceWithDefault("dialog.title", "Dialog.Title",
209				   "OpenSSH Authentication Passphrase Request");
210   d->w3.w.foreground =
211      get_pixel_resource("foreground", "Foreground",
212			 app->dpy, app->colormap, app->black);
213   d->w3.w.background =
214      get_pixel_resource("background", "Background",
215			 app->dpy, app->colormap, app->white);
216   d->w3.topShadowColor =
217      get_pixel_resource("topShadowColor", "TopShadowColor",
218			 app->dpy, app->colormap, app->white);
219   d->w3.bottomShadowColor =
220      get_pixel_resource("bottomShadowColor", "BottomShadowColor",
221			 app->dpy, app->colormap, app->black);
222   d->w3.shadowThickness =
223      get_integer_resource("shadowThickness", "ShadowThickness", 3);
224   d->w3.borderColor =
225      get_pixel_resource("borderColor", "BorderColor",
226			 app->dpy, app->colormap, app->black);
227   d->w3.borderWidth =
228      get_integer_resource("borderWidth", "BorderWidth", 1);
229
230   d->w3.horizontalSpacing =
231      get_integer_resource("horizontalSpacing", "Spacing", 5);
232   d->w3.verticalSpacing =
233      get_integer_resource("verticalSpacing", "Spacing", 6);
234
235   if (2 == app->argc) {
236      d->label.text = strdup(app->argv[1]);
237   } else {
238      d->label.text =
239	 getStringResourceWithDefault("dialog.label", "Dialog.Label",
240				      "Please enter your authentication passphrase:");
241   }
242   d->label.font = getFontResource(app, "dialog.font", "Dialog.Font");
243   calcLabelTextExtents(&(d->label));
244   d->label.w.foreground = d->w3.w.foreground;
245   d->label.w.background = d->w3.w.background;
246
247   d->okButton.w3.w.foreground =
248      get_pixel_resource("okButton.foreground", "Button.Foreground",
249			 app->dpy, app->colormap, app->black);
250   d->okButton.w3.w.background =
251      get_pixel_resource("okButton.background", "Button.Background",
252			 app->dpy, app->colormap, app->white);
253   d->okButton.w3.topShadowColor =
254      get_pixel_resource("okButton.topShadowColor", "Button.TopShadowColor",
255			 app->dpy, app->colormap, app->white);
256   d->okButton.w3.bottomShadowColor =
257      get_pixel_resource("okButton.bottomShadowColor",
258			 "Button.BottomShadowColor",
259			 app->dpy, app->colormap, app->black);
260   d->okButton.w3.shadowThickness =
261      get_integer_resource("okButton.shadowThickness",
262			   "Button.ShadowThickness", 2);
263   d->okButton.w3.borderColor =
264      get_pixel_resource("okButton.borderColor", "Button.BorderColor",
265			 app->dpy, app->colormap, app->black);
266   d->okButton.w3.borderWidth =
267      get_integer_resource("okButton.borderWidth", "Button.BorderWidth", 1);
268   d->okButton.w3.horizontalSpacing =
269      get_integer_resource("okButton.horizontalSpacing", "Button.Spacing", 4);
270   d->okButton.w3.verticalSpacing =
271      get_integer_resource("okButton.verticalSpacing", "Button.Spacing", 2);
272   d->okButton.label.text =
273      getStringResourceWithDefault("okButton.label", "Button.Label", "OK");
274   d->okButton.label.font =
275      getFontResource(app, "okButton.font", "Button.Font");
276   calcButtonExtents(&(d->okButton));
277   d->okButton.label.w.foreground = d->okButton.w3.w.foreground;
278   d->okButton.label.w.background = d->okButton.w3.w.background;
279
280   d->cancelButton.w3.w.foreground =
281      get_pixel_resource("cancelButton.foreground", "Button.Foreground",
282			 app->dpy, app->colormap, app->black);
283   d->cancelButton.w3.w.background =
284      get_pixel_resource("cancelButton.background", "Button.Background",
285			 app->dpy, app->colormap, app->white);
286   d->cancelButton.w3.topShadowColor =
287      get_pixel_resource("cancelButton.topShadowColor",
288			 "Button.TopShadowColor",
289			 app->dpy, app->colormap, app->white);
290   d->cancelButton.w3.bottomShadowColor =
291      get_pixel_resource("cancelButton.bottomShadowColor",
292			 "Button.BottomShadowColor",
293			 app->dpy, app->colormap, app->black);
294   d->cancelButton.w3.shadowThickness =
295      get_integer_resource("cancelButton.shadowThickness",
296			   "Button.ShadowThickness", 2);
297   d->cancelButton.w3.borderColor =
298      get_pixel_resource("cancelButton.borderColor", "Button.BorderColor",
299			 app->dpy, app->colormap, app->black);
300   d->cancelButton.w3.borderWidth =
301      get_integer_resource("cancelButton.borderWidth", "Button.BorderWidth",
302			   1);
303   d->cancelButton.w3.horizontalSpacing =
304      get_integer_resource("cancelButton.horizontalSpacing", "Button.Spacing",
305			   4);
306   d->cancelButton.w3.verticalSpacing =
307      get_integer_resource("cancelButton.verticalSpacing", "Button.Spacing",
308			   2);
309   d->cancelButton.label.text =
310      getStringResourceWithDefault("cancelButton.label", "Button.Label",
311				   "Cancel");
312   d->cancelButton.label.font =
313      getFontResource(app, "cancelButton.font", "Button.Font");
314   calcButtonExtents(&(d->cancelButton));
315   d->cancelButton.label.w.foreground = d->cancelButton.w3.w.foreground;
316   d->cancelButton.label.w.background = d->cancelButton.w3.w.background;
317
318   balanceButtonExtents(&(d->okButton), &(d->cancelButton));
319
320   d->indicator.w3.w.foreground =
321      get_pixel_resource("indicator.foreground", "Indicator.Foreground",
322			 app->dpy, app->colormap, app->black);
323   d->indicator.w3.w.background =
324      get_pixel_resource("indicator.background", "Indicator.Background",
325			 app->dpy, app->colormap, app->white);
326   d->indicator.w3.w.width =
327      get_integer_resource("indicator.width", "Indicator.Width", 15);
328   d->indicator.w3.w.height =
329      get_integer_resource("indicator.height", "Indicator.Height", 7);
330   d->indicator.w3.topShadowColor =
331      get_pixel_resource("indicator.topShadowColor",
332			 "Indicator.TopShadowColor",
333			 app->dpy, app->colormap, app->white);
334   d->indicator.w3.bottomShadowColor =
335      get_pixel_resource("indicator.bottomShadowColor",
336			 "Indicator.BottomShadowColor",
337			 app->dpy, app->colormap, app->black);
338   d->indicator.w3.shadowThickness =
339      get_integer_resource("indicator.shadowThickness",
340			   "Indicator.ShadowThickness", 2);
341   d->indicator.w3.borderColor =
342      get_pixel_resource("indicator.borderColor", "Indicator.BorderColor",
343			 app->dpy, app->colormap, app->black);
344   d->indicator.w3.borderWidth =
345      get_integer_resource("indicator.borderWidth", "Indicator.BorderWidth",
346			   0);
347   d->indicator.w3.horizontalSpacing =
348      get_integer_resource("indicator.horizontalSpacing", "Indicator.Spacing",
349			   2);
350   d->indicator.w3.verticalSpacing =
351      get_integer_resource("indicator.verticalSpacing", "Indicator.Spacing",
352			   4);
353   d->indicator.minimumCount =
354      get_integer_resource("indicator.minimumCount", "Indicator.MinimumCount",
355			   8);
356   d->indicator.maximumCount =
357      get_integer_resource("indicator.maximumCount", "Indicator.MaximumCount",
358			   24);
359   d->indicator.w3.interiorWidth = d->indicator.w3.w.width;
360   d->indicator.w3.interiorHeight = d->indicator.w3.w.height;
361   d->indicator.w3.w.width += (2 * d->indicator.w3.shadowThickness);
362   d->indicator.w3.w.width += (2 * d->indicator.w3.borderWidth);
363   d->indicator.w3.w.height += (2 * d->indicator.w3.shadowThickness);
364   d->indicator.w3.w.height += (2 * d->indicator.w3.borderWidth);
365   {
366      /* Make sure the indicators can all fit on the screen.
367       * 80% of the screen width seems fine.
368       */
369      Dimension maxWidth = (WidthOfScreen(app->screen) * 8 / 10);
370      Dimension extraSpace = ((2 * d->w3.horizontalSpacing) +
371			      (2 * d->w3.shadowThickness));
372
373      if (d->indicator.maximumCount < 8) {
374	 d->indicator.maximumCount = 8;
375      }
376      if (((d->indicator.maximumCount * d->indicator.w3.w.width) +
377	   ((d->indicator.maximumCount - 1) *
378	    d->indicator.w3.horizontalSpacing) + extraSpace) > maxWidth) {
379	 d->indicator.maximumCount =
380	    ((maxWidth - extraSpace - d->indicator.w3.w.width) /
381	     (d->indicator.w3.w.width + d->indicator.w3.horizontalSpacing))
382	    + 1;
383      }
384      if (d->indicator.minimumCount <= 6) {
385	 d->indicator.minimumCount = 6;
386      }
387      if (d->indicator.minimumCount > d->indicator.maximumCount) {
388	 d->indicator.minimumCount = d->indicator.maximumCount;
389      }
390   }
391
392   {
393      /* Calculate the width and horizontal position of things. */
394      Dimension labelAreaWidth;
395      Dimension buttonAreaWidth;
396      Dimension indicatorAreaWidth;
397      Dimension extraIndicatorSpace;
398      Dimension singleIndicatorSpace;
399      Dimension interButtonSpace;
400      Dimension w;
401      Position leftX;
402      int i;
403
404      labelAreaWidth = d->label.w.width + (2 * d->w3.horizontalSpacing);
405      buttonAreaWidth = ((3 * d->w3.horizontalSpacing) +
406			 d->okButton.w3.w.width +
407			 d->cancelButton.w3.w.width);
408      w = MAX(labelAreaWidth, buttonAreaWidth);
409      extraIndicatorSpace = ((2 * d->w3.horizontalSpacing) +
410			     d->indicator.w3.w.width);
411      singleIndicatorSpace = (d->indicator.w3.w.width +
412			      d->indicator.w3.horizontalSpacing);
413      d->indicator.count = ((w - extraIndicatorSpace) / singleIndicatorSpace);
414      d->indicator.current = 0;
415      d->indicator.count++; /* For gatepost indicator in extra space. */
416      if (((w - extraIndicatorSpace) % singleIndicatorSpace) >
417	  (singleIndicatorSpace / 2)) {
418	 d->indicator.count++;
419      }
420      if (d->indicator.count < d->indicator.minimumCount) {
421	 d->indicator.count = d->indicator.minimumCount;
422      }
423      if (d->indicator.count > d->indicator.maximumCount) {
424	 d->indicator.count = d->indicator.maximumCount;
425      }
426      indicatorAreaWidth = ((singleIndicatorSpace * (d->indicator.count - 1)) +
427			    extraIndicatorSpace);
428      d->w3.interiorWidth = MAX(w, indicatorAreaWidth);
429      d->w3.w.width = d->w3.interiorWidth + (2 * d->w3.shadowThickness);
430
431      leftX = (d->w3.w.width - d->label.w.width) / 2;
432      d->label.w.x = leftX;
433
434      leftX = ((d->w3.w.width -
435	       (d->indicator.count * d->indicator.w3.w.width) -
436	       ((d->indicator.count - 1) * d->indicator.w3.horizontalSpacing))
437	       / 2);
438      {
439	 int n = d->indicator.count * sizeof(IndicatorElement);
440	 d->indicators = malloc(n);
441	 if (NULL == d->indicators) {
442	    destroyDialog(app);
443	    outOfMemory(app, __LINE__);
444	 }
445	 memset(d->indicators, 0, n);
446      }
447      d->indicators[0].parent = &(d->indicator);
448      d->indicators[0].w.x = d->indicator.w3.w.x = leftX;
449      d->indicators[0].w.width = d->indicator.w3.w.width;
450      d->indicators[0].isLit = False;
451      for (i = 1; i < d->indicator.count; i++) {
452	 d->indicators[i].parent = &(d->indicator);
453	 d->indicators[i].w.x = (d->indicators[i - 1].w.x +
454				 d->indicator.w3.w.width +
455				 d->indicator.w3.horizontalSpacing);
456	 d->indicators[i].w.width = d->indicator.w3.w.width;
457	 d->indicators[i].isLit = False;
458      }
459      interButtonSpace = ((d->w3.interiorWidth - d->okButton.w3.w.width -
460			   d->cancelButton.w3.w.width) / 3);
461      d->okButton.w3.w.x = interButtonSpace + d->w3.shadowThickness;
462      d->cancelButton.w3.w.x = (d->okButton.w3.w.x + d->okButton.w3.w.width +
463				interButtonSpace);
464   }
465   {
466      /* Calculate the height and vertical position of things. */
467      int i;
468
469      d->w3.interiorHeight = ((4 * d->w3.verticalSpacing) +
470			      (2 * d->indicator.w3.verticalSpacing) +
471			      d->label.w.height +
472			      d->indicator.w3.w.height +
473			      d->okButton.w3.w.height);
474      d->w3.w.height = d->w3.interiorHeight + (2 * d->w3.shadowThickness);
475      d->label.w.y = (d->w3.shadowThickness + d->w3.verticalSpacing +
476		      d->label.ascent);
477      d->indicator.w3.w.y = (d->label.w.y + d->label.descent +
478			     d->w3.verticalSpacing +
479			     d->indicator.w3.verticalSpacing);
480      for (i = 0; i < d->indicator.count; i++) {
481	 d->indicators[i].w.y = d->indicator.w3.w.y;
482	 d->indicators[i].w.height = d->indicator.w3.w.height;
483      }
484      d->okButton.w3.w.y = d->cancelButton.w3.w.y =
485	 (d->indicator.w3.w.y + d->indicator.w3.w.height +
486	  d->w3.verticalSpacing + d->indicator.w3.verticalSpacing);
487   }
488   calcButtonLabelPosition(&(d->okButton));
489   calcButtonLabelPosition(&(d->cancelButton));
490
491   d->w3.w.x = (WidthOfScreen(app->screen) - d->w3.w.width) / 2;
492   d->w3.w.y = (HeightOfScreen(app->screen) - d->w3.w.height) / 3;
493
494   app->dialog = d;
495}
496
497void destroyDialog(AppInfo *app)
498{
499   DialogInfo *d = app->dialog;
500
501   freeIf(d->title);
502   freeIf(d->label.text);
503   freeIf(d->okButton.label.text);
504   freeIf(d->cancelButton.label.text);
505   freeIf(d->indicators);
506
507   freeFontIf(app, d->label.font);
508   freeFontIf(app, d->okButton.label.font);
509   freeFontIf(app, d->cancelButton.label.font);
510
511   XFree(d->sizeHints);
512   XFree(d->wmHints);
513   XFree(d->classHints);
514   XFree(d->windowName.value);
515
516   freeIf(d);
517}
518
519void createDialogWindow(AppInfo *app)
520{
521   XSetWindowAttributes attr;
522   unsigned long attrMask = 0;
523   DialogInfo *d = app->dialog;
524
525   attr.background_pixel = d->w3.w.background;
526   attrMask |= CWBackPixel;
527   attr.border_pixel = d->w3.borderColor;
528   attrMask |= CWBorderPixel;
529   attr.cursor = None;
530   attrMask |= CWCursor;
531   attr.event_mask = 0;
532   attr.event_mask |= ExposureMask;
533   attr.event_mask |= ButtonPressMask;
534   attr.event_mask |= ButtonReleaseMask;
535   attr.event_mask |= KeyPressMask;
536   attrMask |= CWEventMask;
537
538   d->dialogWindow = XCreateWindow(app->dpy, app->rootWindow,
539				   d->w3.w.x, d->w3.w.y,
540				   d->w3.w.width, d->w3.w.height,
541				   d->w3.borderWidth,
542				   DefaultDepthOfScreen(app->screen),
543				   InputOutput,
544				   DefaultVisualOfScreen(app->screen),
545				   attrMask, &attr);
546
547   d->sizeHints = XAllocSizeHints();
548   if (!(d->sizeHints)) {
549      destroyDialog(app);
550      outOfMemory(app, __LINE__);
551   }
552   d->sizeHints->flags = 0;
553   d->sizeHints->flags |= PPosition;
554   d->sizeHints->flags |= PSize;
555   d->sizeHints->min_width = d->w3.w.width;
556   d->sizeHints->min_height = d->w3.w.height;
557   d->sizeHints->flags |= PMinSize;
558   d->sizeHints->max_width = d->w3.w.width;
559   d->sizeHints->max_height = d->w3.w.height;
560   d->sizeHints->flags |= PMaxSize;
561   d->sizeHints->base_width = d->w3.w.width;
562   d->sizeHints->base_height = d->w3.w.height;
563   d->sizeHints->flags |= PBaseSize;
564
565   d->wmHints = XAllocWMHints();
566   if (!(d->wmHints)) {
567      destroyDialog(app);
568      outOfMemory(app, __LINE__);
569   }
570   d->wmHints->flags = 0;
571   d->wmHints->input = True;
572   d->wmHints->flags |= InputHint;
573   d->wmHints->initial_state = NormalState;
574   d->wmHints->flags |= StateHint;
575
576   d->classHints = XAllocClassHint();
577   if (!(d->classHints)) {
578      destroyDialog(app);
579      outOfMemory(app, __LINE__);
580   }
581   d->classHints->res_name = app->appName;
582   d->classHints->res_class = app->appClass;
583
584   if (!XStringListToTextProperty(&(d->title), 1, &(d->windowName))) {
585      destroyDialog(app);
586      outOfMemory(app, __LINE__);
587   }
588   XSetWMProperties(app->dpy, d->dialogWindow, &(d->windowName), NULL,
589		    app->argv, app->argc, d->sizeHints,
590		    d->wmHints, d->classHints);
591   XSetTransientForHint(app->dpy, d->dialogWindow, d->dialogWindow);
592
593   app->wmDeleteWindowAtom = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False);
594   XSetWMProtocols(app->dpy, d->dialogWindow, &(app->wmDeleteWindowAtom), 1);
595}
596
597void createGCs(AppInfo *app)
598{
599   DialogInfo *d = app->dialog;
600
601   XGCValues gcv;
602   unsigned long gcvMask;
603
604   gcvMask = 0;
605   gcv.foreground = d->w3.w.background;
606   gcvMask |= GCForeground;
607   gcv.fill_style = FillSolid;
608   gcvMask |= GCFillStyle;
609   app->fillGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
610
611   gcvMask = 0;
612   gcv.foreground = d->w3.borderColor;
613   gcvMask |= GCForeground;
614   gcv.line_width = d->w3.borderWidth;
615   gcvMask |= GCLineWidth;
616   gcv.line_style = LineSolid;
617   gcvMask |= GCLineStyle;
618   gcv.cap_style = CapButt;
619   gcvMask |= GCCapStyle;
620   gcv.join_style = JoinMiter;
621   gcvMask |= GCJoinStyle;
622   app->borderGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
623
624   gcvMask = 0;
625   gcv.foreground = d->label.w.foreground;
626   gcvMask |= GCForeground;
627   gcv.background = d->label.w.background;
628   gcvMask |= GCBackground;
629   gcv.font = d->label.font->fid;
630   gcvMask |= GCFont;
631   app->textGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
632
633   gcvMask = 0;
634   gcv.foreground = d->indicator.w3.w.foreground;
635   gcvMask |= GCForeground;
636   gcv.fill_style = FillSolid;
637   gcvMask |= GCFillStyle;
638   app->brightGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
639
640   gcvMask = 0;
641   gcv.foreground = d->indicator.w3.w.background;
642   gcvMask |= GCForeground;
643   gcv.fill_style = FillSolid;
644   gcvMask |= GCFillStyle;
645   app->dimGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
646}
647
648void destroyGCs(AppInfo *app)
649{
650   XFreeGC(app->dpy, app->fillGC);
651   XFreeGC(app->dpy, app->borderGC);
652   XFreeGC(app->dpy, app->textGC);
653   XFreeGC(app->dpy, app->brightGC);
654   XFreeGC(app->dpy, app->dimGC);
655}
656
657void paintLabel(AppInfo *app, Drawable draw, LabelInfo label)
658{
659   if (!(label.text)) {
660      return;
661   }
662   XSetForeground(app->dpy, app->textGC, label.w.foreground);
663   XSetBackground(app->dpy, app->textGC, label.w.background);
664   XSetFont(app->dpy, app->textGC, label.font->fid);
665   XDrawString(app->dpy, draw, app->textGC, label.w.x, label.w.y, label.text,
666	       label.textLength);
667}
668
669void paintButton(AppInfo *app, Drawable draw, ButtonInfo button)
670{
671   Position x;
672   Position y;
673   Dimension width;
674   Dimension height;
675
676   if (button.w3.borderWidth > 0) {
677      XSetForeground(app->dpy, app->borderGC, button.w3.borderColor);
678      XFillRectangle(app->dpy, draw, app->borderGC, button.w3.w.x,
679		     button.w3.w.y, button.w3.w.width, button.w3.w.height);
680   }
681   if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
682      Pixel tmp = button.w3.w.background;
683      button.w3.w.background = button.w3.w.foreground;
684      button.w3.w.foreground = tmp;
685      tmp = button.label.w.background;
686      button.label.w.background = button.label.w.foreground;
687      button.label.w.foreground = tmp;
688   }
689   x = (button.w3.w.x + button.w3.borderWidth);
690   y = (button.w3.w.y + button.w3.borderWidth);
691   width = (button.w3.w.width - (2 * button.w3.borderWidth));
692   height = (button.w3.w.height - (2 * button.w3.borderWidth));
693   if ((button.w3.shadowThickness > 0) && (button.pressed)) {
694      XSetForeground(app->dpy, app->fillGC, button.w3.topShadowColor);
695   } else {
696      XSetForeground(app->dpy, app->fillGC, button.w3.w.background);
697   }
698   XFillRectangle(app->dpy, draw, app->fillGC, x, y, width, height);
699   if (button.w3.shadowThickness > 0) {
700      if (button.pressed) {
701	 draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
702			       button.w3.shadowThickness,
703			       button.w3.bottomShadowColor,
704			       button.w3.topShadowColor);
705      } else {
706	 draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
707			       button.w3.shadowThickness,
708			       button.w3.topShadowColor,
709			       button.w3.bottomShadowColor);
710      }
711   }
712   paintLabel(app, draw, button.label);
713   if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
714      Pixel tmp = button.w3.w.background;
715      button.w3.w.background = button.w3.w.foreground;
716      button.w3.w.foreground = tmp;
717      tmp = button.label.w.background;
718      button.label.w.background = button.label.w.foreground;
719      button.label.w.foreground = tmp;
720   }
721}
722
723void paintIndicator(AppInfo *app, Drawable draw, IndicatorElement indicator)
724{
725   Position x;
726   Position y;
727   Dimension width;
728   Dimension height;
729   GC gc = app->dimGC;
730
731   if (indicator.parent->w3.borderWidth > 0) {
732      XSetForeground(app->dpy, app->borderGC,
733		     indicator.parent->w3.borderColor);
734      XFillRectangle(app->dpy, draw, app->borderGC, indicator.w.x,
735		     indicator.w.y, indicator.w.width, indicator.w.height);
736   }
737   if (indicator.isLit) {
738      gc = app->brightGC;
739   }
740   x = (indicator.w.x + indicator.parent->w3.borderWidth);
741   y = (indicator.w.y + indicator.parent->w3.borderWidth);
742   width = (indicator.w.width - (2 * indicator.parent->w3.borderWidth));
743   height = (indicator.w.height - (2 * indicator.parent->w3.borderWidth));
744   XFillRectangle(app->dpy, draw, gc, x, y, width, height);
745   if (indicator.parent->w3.shadowThickness > 0) {
746      draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
747			    indicator.parent->w3.shadowThickness,
748			    indicator.parent->w3.bottomShadowColor,
749			    indicator.parent->w3.topShadowColor);
750   }
751}
752
753void updateIndicatorElement(AppInfo *app, int i)
754{
755   DialogInfo *d = app->dialog;
756
757   d->indicators[i].isLit = !(d->indicators[i].isLit);
758   paintIndicator(app, d->dialogWindow, d->indicators[i]);
759}
760
761void updateIndicators(AppInfo *app, int condition)
762{
763   DialogInfo *d = app->dialog;
764
765   if (condition > 0) {
766      /* Move forward one. */
767      updateIndicatorElement(app, d->indicator.current);
768      if (d->indicator.current < (d->indicator.count - 1)) {
769	 (d->indicator.current)++;
770      } else {
771	 d->indicator.current = 0;
772      }
773   } else if (condition < 0) {
774      /* Move backward one. */
775      if (d->indicator.current > 0) {
776	 (d->indicator.current)--;
777      } else {
778	 d->indicator.current = d->indicator.count - 1;
779      }
780      updateIndicatorElement(app, d->indicator.current);
781   } else {
782      /* Erase them all. */
783      int i;
784
785      for (i = 0; i < d->indicator.count; i++) {
786	 d->indicators[i].isLit = False;
787	 paintIndicator(app, d->dialogWindow, d->indicators[i]);
788      }
789      d->indicator.current = 0;
790   }
791   XSync(app->dpy, False);
792}
793
794void paintDialog(AppInfo *app)
795{
796   DialogInfo *d = app->dialog;
797   Drawable draw = d->dialogWindow;
798   int i;
799
800   XSetForeground(app->dpy, app->fillGC, d->w3.w.background);
801   XFillRectangle(app->dpy, draw, app->fillGC, 0, 0,
802		  d->w3.w.width, d->w3.w.height);
803   if (d->w3.shadowThickness > 0) {
804      draw_shaded_rectangle(app->dpy, draw, 0, 0,
805			    d->w3.w.width, d->w3.w.height,
806			    d->w3.shadowThickness,
807			    d->w3.topShadowColor,
808			    d->w3.bottomShadowColor);
809   }
810   paintLabel(app, draw, d->label);
811   for (i = 0; i < d->indicator.count; i++) {
812      paintIndicator(app, draw, d->indicators[i]);
813   }
814   paintButton(app, draw, d->okButton);
815   paintButton(app, draw, d->cancelButton);
816   XSync(app->dpy, False);
817}
818
819void grabKeyboard(AppInfo *app)
820{
821   if ((!(app->grabKeyboard)) || (app->isKeyboardGrabbed)) {
822      return;
823   } else {
824      int status;
825      Window grabWindow = app->dialog->dialogWindow;
826      Bool ownerEvents = False;
827      Bool pointerMode = GrabModeAsync;
828      Bool keyboardMode = GrabModeAsync;
829
830      app->isKeyboardGrabbed = True;
831      XSync(app->dpy, False);
832      status = XGrabKeyboard(app->dpy, grabWindow, ownerEvents,
833			     pointerMode, keyboardMode, CurrentTime);
834      XSync(app->dpy, False);
835      if (GrabSuccess != status) {
836	 char *reason = "reason unknown";
837
838	 switch (status) {
839	  case AlreadyGrabbed:
840	    reason = "someone else already has the keyboard";
841	    break;
842	  case GrabFrozen:
843	    reason = "someone else has frozen the keyboard";
844	    break;
845	  case GrabInvalidTime:
846	    reason = "bad grab time [this shouldn't happen]";
847	    break;
848	  case GrabNotViewable:
849	    reason = "grab not viewable [this shouldn't happen]";
850	    break;
851	 }
852	 fprintf(stderr, "%s: Could not grab keyboard (%s)\n", app->appName);
853	 exitApp(app, EXIT_STATUS_ERROR);
854      }
855   }
856}
857
858void ungrabKeyboard(AppInfo *app)
859{
860   if (app->grabKeyboard) {
861      XUngrabKeyboard(app->dpy, CurrentTime);
862   }
863}
864
865void grabPointer(AppInfo *app)
866{
867   if ((!(app->grabPointer)) || (app->isPointerGrabbed)) {
868      return;
869   } else {
870      int status;
871      Window grabWindow = app->dialog->dialogWindow;
872      Bool ownerEvents = False;
873      unsigned int eventMask = ButtonPressMask | ButtonReleaseMask;
874      Bool pointerMode = GrabModeAsync;
875      Bool keyboardMode = GrabModeAsync;
876      Window confineTo = None;
877      Cursor cursor = None;
878
879      app->isPointerGrabbed = True;
880      XSync(app->dpy, False);
881      status = XGrabPointer(app->dpy, grabWindow, ownerEvents, eventMask,
882			    pointerMode, keyboardMode, confineTo, cursor,
883			    CurrentTime);
884      XSync(app->dpy, False);
885      if (GrabSuccess != status) {
886	 char *reason = "reason unknown";
887
888	 switch (status) {
889	  case AlreadyGrabbed:
890	    reason = "someone else already has the pointer";
891	    break;
892	  case GrabFrozen:
893	    reason = "someone else has frozen the pointer";
894	    break;
895	  case GrabInvalidTime:
896	    reason = "bad grab time [this shouldn't happen]";
897	    break;
898	  case GrabNotViewable:
899	    reason = "grab not viewable [this shouldn't happen]";
900	    break;
901	 }
902	 fprintf(stderr, "%s: Could not grab pointer (%s)\n", app->appName);
903	 exitApp(app, EXIT_STATUS_ERROR);
904      }
905   }
906}
907
908void ungrabPointer(AppInfo *app)
909{
910   if (app->grabPointer) {
911      XUngrabPointer(app->dpy, CurrentTime);
912   }
913}
914
915void grabServer(AppInfo *app)
916{
917   if ((!(app->grabServer)) || (app->isServerGrabbed)) {
918      return;
919   } else {
920      app->isServerGrabbed = True;
921      XSync(app->dpy, False);
922      XGrabServer(app->dpy);
923      XSync(app->dpy, False);
924   }
925}
926
927void ungrabServer(AppInfo *app)
928{
929   if (app->grabServer) {
930      XUngrabServer(app->dpy);
931   }
932}
933
934void cleanUp(AppInfo *app)
935{
936   XDestroyWindow(app->dpy, app->dialog->dialogWindow);
937   destroyGCs(app);
938   destroyDialog(app);
939   if (app->buf) {
940      memset(app->buf, 0, app->bufSize);
941   }
942   freeIf(app->buf);
943   ungrabPointer(app);
944   ungrabKeyboard(app);
945   ungrabServer(app);
946}
947
948void exitApp(AppInfo *app, int exitCode)
949{
950   cleanUp(app);
951   exit(exitCode);
952}
953
954void acceptAction(AppInfo *app)
955{
956   int status = append_to_buf(&(app->buf), &(app->bufSize),
957			      &(app->bufIndex), '\0');
958   if (APPEND_FAILURE == status) {
959      cleanUp(app);
960      outOfMemory(app, __LINE__);
961   }
962   fputs(app->buf, stdout);
963   fputc('\n', stdout);
964   exitApp(app, EXIT_STATUS_ACCEPT);
965}
966
967void cancelAction(AppInfo *app)
968{
969   exitApp(app, EXIT_STATUS_CANCEL);
970}
971
972void backspacePassphrase(AppInfo *app)
973{
974   if (0 >= app->bufIndex) {
975      XBell(app->dpy, 0);
976      return;
977   }
978   (app->bufIndex)--;
979   updateIndicators(app, -1);
980}
981
982void erasePassphrase(AppInfo *app)
983{
984   if (0 >= app->bufIndex) {
985      XBell(app->dpy, 0);
986      return;
987   }
988   updateIndicators(app, 0);
989   app->bufIndex = 0;
990}
991
992void addToPassphrase(AppInfo *app, char c)
993{
994   int status = append_to_buf(&(app->buf), &(app->bufSize),
995			      &(app->bufIndex), c);
996   if (APPEND_FAILURE == status) {
997      cleanUp(app);
998      outOfMemory(app, __LINE__);
999   }
1000   updateIndicators(app, 1);
1001}
1002
1003void handleKeyPress(AppInfo *app, XKeyEvent *event)
1004{
1005   char s[2];
1006   int n;
1007
1008   if (event->send_event) {
1009      /* Pay no attention to synthetic key events. */
1010      return;
1011   }
1012   n = XLookupString(event, s, 1, NULL, NULL);
1013
1014   if (1 != n) {
1015      return;
1016   }
1017   s[1] = '\0';
1018   switch (s[0]) {
1019    case '\010':
1020    case '\177':
1021      backspacePassphrase(app);
1022      break;
1023    case '\025':
1024    case '\030':
1025      erasePassphrase(app);
1026      break;
1027    case '\012':
1028    case '\015':
1029      acceptAction(app);
1030      break;
1031    case '\033':
1032      cancelAction(app);
1033      break;
1034    default:
1035      addToPassphrase(app, s[0]);
1036      break;
1037   }
1038}
1039
1040Bool eventIsInsideButton(AppInfo *app, XButtonEvent *event, ButtonInfo button)
1041{
1042   int status = False;
1043
1044   if ((event->x >= (button.w3.w.x + button.w3.borderWidth)) &&
1045       (event->x < (button.w3.w.x + button.w3.w.width -
1046		    (2 * button.w3.borderWidth))) &&
1047       (event->y >= (button.w3.w.y + button.w3.borderWidth)) &&
1048       (event->y < (button.w3.w.y + button.w3.w.height -
1049		    (2 * button.w3.borderWidth)))) {
1050      status = True;
1051   }
1052   return(status);
1053}
1054
1055void handleButtonPress(AppInfo *app, XButtonEvent *event)
1056{
1057   DialogInfo *d = app->dialog;
1058
1059   if (event->button != Button1) {
1060      return;
1061   }
1062   if (ButtonPress == event->type) {
1063      if (eventIsInsideButton(app, event, d->okButton)) {
1064	 d->pressedButton = OK_BUTTON;
1065	 d->okButton.pressed = True;
1066	 paintButton(app, d->dialogWindow, d->okButton);
1067      } else if (eventIsInsideButton(app, event, d->cancelButton)) {
1068	 d->pressedButton = CANCEL_BUTTON;
1069	 d->cancelButton.pressed = True;
1070	 paintButton(app, d->dialogWindow, d->cancelButton);
1071      } else {
1072	 d->pressedButton = NO_BUTTON;
1073      }
1074   } else if (ButtonRelease == event->type) {
1075      if (OK_BUTTON == d->pressedButton) {
1076	 if (eventIsInsideButton(app, event, d->okButton)) {
1077	    acceptAction(app);
1078	 } else {
1079	    d->okButton.pressed = False;
1080	    paintButton(app, d->dialogWindow, d->okButton);
1081	 }
1082      } else if (CANCEL_BUTTON == d->pressedButton) {
1083	 if (eventIsInsideButton(app, event, d->cancelButton)) {
1084	    cancelAction(app);
1085	 } else {
1086	    d->cancelButton.pressed = False;
1087	    paintButton(app, d->dialogWindow, d->cancelButton);
1088	 }
1089      }
1090      d->pressedButton = NO_BUTTON;
1091   }
1092}
1093
1094int main(int argc, char **argv)
1095{
1096   AppInfo app;
1097   XEvent event;
1098
1099   memset(&app, 0, sizeof(app));
1100
1101   app.argc = argc;
1102   app.argv = argv;
1103
1104   progclass = "SshAskpass";
1105   app.toplevelShell = XtAppInitialize(&(app.appContext), progclass,
1106					NULL, 0, &argc, argv,
1107					defaults, NULL, 0);
1108   app.dpy = XtDisplay(app.toplevelShell);
1109   app.screen = DefaultScreenOfDisplay(app.dpy);
1110   app.rootWindow = RootWindowOfScreen(app.screen);
1111   app.black = BlackPixel(app.dpy, DefaultScreen(app.dpy));
1112   app.white = WhitePixel(app.dpy, DefaultScreen(app.dpy));
1113   app.colormap = DefaultColormapOfScreen(app.screen);
1114   app.resourceDb = XtDatabase(app.dpy);
1115   XtGetApplicationNameAndClass(app.dpy, &progname, &progclass);
1116   app.appName = progname;
1117   app.appClass = progclass;
1118   /* For resources.c. */
1119   db = app.resourceDb;
1120
1121   {
1122      struct rlimit resourceLimit;
1123      int status;
1124
1125      status = getrlimit(RLIMIT_CORE, &resourceLimit);
1126      if (-1 == status) {
1127	 fprintf(stderr, "%s: getrlimit failed (%s)\n", app.appName,
1128		 strerror(errno));
1129	 exit(EXIT_STATUS_ERROR);
1130      }
1131      resourceLimit.rlim_cur = 0;
1132      status = setrlimit(RLIMIT_CORE, &resourceLimit);
1133      if (-1 == status) {
1134	 fprintf(stderr, "%s: setrlimit failed (%s)\n", app.appName,
1135		 strerror(errno));
1136	 exit(EXIT_STATUS_ERROR);
1137      }
1138   }
1139
1140   createDialog(&app);
1141   createGCs(&app);
1142   createDialogWindow(&app);
1143
1144   XMapWindow(app.dpy, app.dialog->dialogWindow);
1145
1146   while(True) {
1147      XNextEvent(app.dpy, &event);
1148      switch (event.type) {
1149       case Expose:
1150	 grabServer(&app);
1151	 grabKeyboard(&app);
1152	 grabPointer(&app);
1153	 if (event.xexpose.count) {
1154	    break;
1155	 }
1156	 paintDialog(&app);
1157	 break;
1158       case ButtonPress:
1159       case ButtonRelease:
1160	 handleButtonPress(&app, &(event.xbutton));
1161	 break;
1162       case KeyPress:
1163	 handleKeyPress(&app, &(event.xkey));
1164	 break;
1165       case ClientMessage:
1166	 if ((32 == event.xclient.format) &&
1167	     (event.xclient.data.l[0] == app.wmDeleteWindowAtom)) {
1168	    cancelAction(&app);
1169	 }
1170	 break;
1171       default:
1172	 break;
1173      }
1174   }
1175
1176   fprintf(stderr, "%s: This should not happen.\n", app.appName);
1177   return(EXIT_STATUS_ANOMALY);
1178}
1179
1180