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: 2001-Sep-16 18:08
4  *
5  * by Jim Knoble <jmknoble@pobox.com>
6  * Copyright (C) 1999,2000,2001 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 <ctype.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 /* For (get|set)rlimit() ... */
43 #include <sys/time.h>
44 #include <sys/resource.h>
45 /* ... end */
46 /* For (get|set)rlimit(), sleep(), and getpid() ... */
47 #include <unistd.h>
48 /* ... end */
49 
50 /* For errno ... */
51 #include <errno.h>
52 /* ... end */
53 
54 #include <X11/Xlib.h>
55 #include <X11/Intrinsic.h>
56 #include <X11/Shell.h>
57 #include <X11/Xos.h>
58 #include "dynlist.h"
59 #include "drawing.h"
60 #include "resources.h"
61 #include "x11-ssh-askpass.h"
62 
63 #undef MAX
64 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
65 
66 char *progname = NULL;
67 char *progclass = NULL;
68 XrmDatabase db = 0;
69 
70 static char *defaults[] = {
71 #include "SshAskpass_ad.h"
72    0
73 };
74 
outOfMemory(AppInfo * app,int line)75 void outOfMemory(AppInfo *app, int line)
76 {
77    fprintf(stderr, "%s[%ld]: Aaahhh! I ran out of memory at line %d.\n",
78 	   app->appName, (long) app->pid, line);
79    exit(EXIT_STATUS_NO_MEMORY);
80 }
81 
freeIf(void * p)82 void freeIf(void *p)
83 {
84    if (p) {
85       free(p);
86    }
87 }
88 
freeFontIf(AppInfo * app,XFontStruct * f)89 void freeFontIf(AppInfo *app, XFontStruct *f)
90 {
91    if (f) {
92       XFreeFont(app->dpy, f);
93    }
94 }
95 
getFontResource(AppInfo * app,char * instanceName,char * className)96 XFontStruct *getFontResource(AppInfo *app, char *instanceName, char *className)
97 {
98    char *fallbackFont = "fixed";
99 
100    XFontStruct *f = NULL;
101    char *s = get_string_resource(instanceName, className);
102    f = XLoadQueryFont(app->dpy, (s ? s : fallbackFont));
103    if (!f) {
104       f = XLoadQueryFont(app->dpy, fallbackFont);
105    }
106    if (s) {
107       free(s);
108    }
109    return(f);
110 }
111 
getStringResourceWithDefault(char * instanceName,char * className,char * defaultText)112 char *getStringResourceWithDefault(char *instanceName, char *className,
113 				   char *defaultText)
114 {
115    char *s = get_string_resource(instanceName, className);
116    if (!s) {
117       if (!defaultText) {
118 	 s = strdup("");
119       } else {
120 	 s = strdup(defaultText);
121       }
122    }
123    return(s);
124 }
125 
getUnsignedIntegerResource(AppInfo * app,char * instanceName,char * className,unsigned int defaultValue)126 unsigned int getUnsignedIntegerResource(AppInfo *app, char *instanceName,
127 					char *className,
128 					unsigned int defaultValue)
129 {
130    int n;
131    unsigned int value;
132    char c;
133    char *s = get_string_resource(instanceName, className);
134    char *cp = s;
135 
136    if (NULL == s) {
137       return(defaultValue);
138    }
139    while ((*cp) && isspace(*cp)) {
140       /* Skip whitespace. */
141       cp++;
142    }
143    if (*cp) {
144       if (('0' == cp[0]) && cp[1]) {
145 	 if (('x' == cp[1]) || ('X' == cp[1])) {
146 	    /* Hex */
147 	    n = sscanf(cp + 2, "%x %c", &value, &c);
148 	 } else {
149 	    /* Octal */
150 	    n = sscanf(cp + 1, "%o %c", &value, &c);
151 	 }
152 	 if (1 == n) {
153 	    free(s);
154 	    return(value);
155 	 }
156       } else {
157 	 /* Unsigned Decimal */
158 	 n = sscanf(cp, "%u %c", &value, &c);
159 	 if (1 == n) {
160 	    free(s);
161 	    return(value);
162 	 }
163       }
164    }
165    /* If we get here, no conversion succeeded. */
166    fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n",
167 	   app->appName, (long) app->pid, s, instanceName);
168    free(s);
169    return(defaultValue);
170 }
171 
172 /* Default resolution is 75 dots/inch.  1 in = 2.54 cm. */
173 #define DefaultResolution ((75 * 10000) / 254)
getResolutionResource(AppInfo * app,char * instanceName,char * className,char * defaultResolutionSpec)174 long getResolutionResource(AppInfo *app, char *instanceName, char *className,
175 			   char *defaultResolutionSpec)
176 {
177    char units[3];
178    char *s;
179    int n;
180    long resolution;
181    unsigned int i;
182 
183    memset(units, 0, sizeof(units));
184    s = getStringResourceWithDefault(instanceName, className,
185 				    defaultResolutionSpec);
186    /* NOTE: The width of the %s format must be one less than
187     * the length of the units[] array above!
188     */
189    n = sscanf(s, "%ld / %2s", &resolution, units);
190    if (n != 2) {
191       fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n",
192 	      app->appName, (long) app->pid, s, instanceName);
193       resolution = DefaultResolution;
194    } else {
195       if (resolution < 0) {
196 	 /* Resolution specifications should not be negative. */
197 	 resolution = -(resolution);
198       }
199       for (i = 0; i < (sizeof(units) - 1); i++) {
200 	 units[i] = tolower(units[i]);
201       }
202       if ((0 == strcmp(units, "in")) ||
203 	  (0 == strcmp(units, "i")) ||
204 	  (0 == strcmp(units, "\""))) {
205 	 /* dots/inch */
206 	 resolution = resolution * 10000 / 254;
207       } else if ((0 == strcmp(units, "m")) ||
208 		 (0 == strcmp(units, "me"))) {
209 	 /* dots/meter; no conversion necessary */
210 	 ;
211       } else {
212 	 /* some unit we don't recognize; cringe and stare at the floor */
213 	 resolution = DefaultResolution;
214       }
215    }
216    return(resolution);
217 }
218 #undef DefaultResolution
219 
calcTextObjectExtents(TextObject * t,XFontStruct * font)220 void calcTextObjectExtents(TextObject *t, XFontStruct *font) {
221    if ((!t) || (!(t->text))) {
222       return;
223    }
224    t->textLength = strlen(t->text);
225    XTextExtents(font, t->text, t->textLength, &(t->direction),
226 		&(t->ascent), &(t->descent), &(t->overall));
227 }
228 
calcLabelTextExtents(LabelInfo * label)229 void calcLabelTextExtents(LabelInfo *label)
230 {
231    TextObject *t;
232 
233    if ((!label) || (!(label->fullText)) || (!(label->font))) {
234       return;
235    }
236    t = label->multiText;
237    while (NULL != t) {
238       calcTextObjectExtents(t, label->font);
239       label->w.height += (t->ascent + t->descent);
240       if (label->w.width < t->overall.width) {
241 	 label->w.width = t->overall.width;
242       }
243       t = t->next;
244    }
245 }
246 
calcTotalButtonExtents(ButtonInfo * button)247 void calcTotalButtonExtents(ButtonInfo *button)
248 {
249    if (!button) {
250       return;
251    }
252    button->w3.w.width = (button->w3.interiorWidth +
253 			 (2 * button->w3.shadowThickness));
254    button->w3.w.width += (2 * button->w3.borderWidth);
255    button->w3.w.height = (button->w3.interiorHeight +
256 			  (2 * button->w3.shadowThickness));
257    button->w3.w.height += (2 * button->w3.borderWidth);
258 }
259 
calcButtonExtents(ButtonInfo * button)260 void calcButtonExtents(ButtonInfo *button)
261 {
262    if (!button) {
263       return;
264    }
265    calcLabelTextExtents(&(button->label));
266    button->w3.interiorWidth = (button->label.w.width +
267 			       (2 * button->w3.horizontalSpacing));
268    button->w3.interiorHeight = (button->label.w.height +
269 				(2 * button->w3.verticalSpacing));
270    calcTotalButtonExtents(button);
271 }
272 
balanceButtonExtents(ButtonInfo * button1,ButtonInfo * button2)273 void balanceButtonExtents(ButtonInfo *button1, ButtonInfo *button2)
274 {
275    if ((!button1) || (!button2)) {
276       return;
277    }
278    button1->w3.interiorWidth = button2->w3.interiorWidth =
279       MAX(button1->w3.interiorWidth, button2->w3.interiorWidth);
280    button1->w3.interiorHeight = button2->w3.interiorHeight =
281       MAX(button1->w3.interiorHeight, button2->w3.interiorHeight);
282    calcTotalButtonExtents(button1);
283    calcTotalButtonExtents(button2);
284 }
285 
calcButtonLabelPosition(ButtonInfo * button)286 void calcButtonLabelPosition(ButtonInfo *button)
287 {
288    if (!button) {
289       return;
290    }
291    button->label.w.x = button->w3.w.x +
292       ((button->w3.w.width - button->label.w.width) / 2);
293    button->label.w.y = button->w3.w.y +
294       ((button->w3.w.height - button->label.w.height) / 2);
295 }
296 
scaleXDimension(AppInfo * app,Dimension unscaled)297 Dimension scaleXDimension(AppInfo *app, Dimension unscaled)
298 {
299    Dimension scaled;
300 
301    if (((app->defaultXResolution < app->xResolution) &&
302 	((app->defaultXResolution + app->xFuzz) < app->xResolution)) ||
303        ((app->xResolution < app->defaultXResolution) &&
304 	((app->xResolution + app->xFuzz) < app->defaultXResolution))) {
305       scaled = (unscaled * app->xResolution) / app->defaultXResolution;
306    } else {
307       scaled = unscaled;
308    }
309    return(scaled);
310 }
311 
scaleYDimension(AppInfo * app,Dimension unscaled)312 Dimension scaleYDimension(AppInfo *app, Dimension unscaled)
313 {
314    Dimension scaled;
315 
316    if (((app->defaultYResolution < app->yResolution) &&
317 	((app->defaultYResolution + app->yFuzz) < app->yResolution)) ||
318        ((app->yResolution < app->defaultYResolution) &&
319 	((app->yResolution + app->yFuzz) < app->defaultYResolution))) {
320       scaled = (unscaled * app->yResolution) / app->defaultYResolution;
321    } else {
322       scaled = unscaled;
323    }
324    return(scaled);
325 }
326 
327 /* Assumes 's' is non-NULL. */
createTextObject(AppInfo * app,char * s)328 TextObject *createTextObject(AppInfo *app, char *s)
329 {
330    TextObject *t = malloc(sizeof(*t));
331    if (NULL == t) {
332       outOfMemory(app, __LINE__);
333    }
334    memset(t, 0, sizeof(*t));
335    if (('\n' == *s) || ('\0' == *s)) {
336       t->text = " ";
337    } else {
338       t->text = s;
339    }
340    return(t);
341 }
342 
343 /* Assumes 'label' object exists and is zeroed. */
createLabel(AppInfo * app,char * text,LabelInfo * label)344 void createLabel(AppInfo *app, char *text, LabelInfo *label)
345 {
346    char *substring;
347    TextObject *t;
348 
349    if ((!app) || (!text)) {
350       return;
351    }
352    label->fullText = strdup(text);
353    label->multiText = createTextObject(app, label->fullText);
354    t = label->multiText;
355    substring = strchr(label->fullText, '\n');
356    while (NULL != substring) {
357       *(substring++) = '\0';
358       t->next = createTextObject(app, substring);
359       if (t->next) {
360 	 t = t->next;
361       }
362       substring = strchr(substring, '\n');
363    }
364 }
365 
createDialog(AppInfo * app)366 void createDialog(AppInfo *app)
367 {
368    DialogInfo *d;
369    char *labelText;
370 
371    if (app->dialog) {
372       return;
373    }
374    d = malloc(sizeof(*d));
375    if (NULL == d) {
376       outOfMemory(app, __LINE__);
377    }
378    memset(d, 0, sizeof(*d));
379 
380    app->grabKeyboard =
381       get_boolean_resource("grabKeyboard", "GrabKeyboard", True);
382    app->grabPointer =
383       get_boolean_resource("grabPointer", "GrabPointer", False);
384    app->grabServer =
385       get_boolean_resource("grabServer", "GrabServer", False);
386 
387    /* inputTimeout resource specified in seconds for easy human interface.
388     * Convert to milliseconds here.
389     */
390    app->inputTimeout = (unsigned long) 1000 *
391       getUnsignedIntegerResource(app, "inputTimeout", "InputTimeout", 0);
392 
393    app->defaultXResolution =
394       getResolutionResource(app, "defaultXResolution", "DefaultXResolution",
395 			    "75/in");
396    app->defaultYResolution =
397       getResolutionResource(app, "defaultYResolution", "DefaultYResolution",
398 			    "75/in");
399    app->xFuzz =
400       getResolutionResource(app, "xResolutionFuzz", "XResolutionFuzz", "20/in");
401    app->yFuzz =
402       getResolutionResource(app, "yResolutionFuzz", "YResolutionFuzz", "20/in");
403 
404    d->title =
405       getStringResourceWithDefault("dialog.title", "Dialog.Title",
406 				   "OpenSSH Authentication Passphrase Request");
407    d->w3.w.foreground =
408       get_pixel_resource("foreground", "Foreground",
409 			 app->dpy, app->colormap, app->black);
410    d->w3.w.background =
411       get_pixel_resource("background", "Background",
412 			 app->dpy, app->colormap, app->white);
413    d->w3.topShadowColor =
414       get_pixel_resource("topShadowColor", "TopShadowColor",
415 			 app->dpy, app->colormap, app->white);
416    d->w3.bottomShadowColor =
417       get_pixel_resource("bottomShadowColor", "BottomShadowColor",
418 			 app->dpy, app->colormap, app->black);
419    d->w3.shadowThickness =
420       get_integer_resource("shadowThickness", "ShadowThickness", 3);
421    d->w3.borderColor =
422       get_pixel_resource("borderColor", "BorderColor",
423 			 app->dpy, app->colormap, app->black);
424    d->w3.borderWidth =
425       get_integer_resource("borderWidth", "BorderWidth", 1);
426 
427    d->w3.horizontalSpacing = scaleXDimension(app,
428       get_integer_resource("horizontalSpacing", "Spacing", 5));
429    d->w3.verticalSpacing = scaleYDimension(app,
430       get_integer_resource("verticalSpacing", "Spacing", 6));
431 
432    if (2 == app->argc) {
433       labelText = strdup(app->argv[1]);
434    } else {
435       labelText =
436 	 getStringResourceWithDefault("dialog.label", "Dialog.Label",
437 				      "Please enter your authentication passphrase:");
438    }
439    createLabel(app, labelText, &(d->label));
440    freeIf(labelText);
441    d->label.font = getFontResource(app, "dialog.font", "Dialog.Font");
442    calcLabelTextExtents(&(d->label));
443    d->label.w.foreground = d->w3.w.foreground;
444    d->label.w.background = d->w3.w.background;
445 
446    d->okButton.w3.w.foreground =
447       get_pixel_resource("okButton.foreground", "Button.Foreground",
448 			 app->dpy, app->colormap, app->black);
449    d->okButton.w3.w.background =
450       get_pixel_resource("okButton.background", "Button.Background",
451 			 app->dpy, app->colormap, app->white);
452    d->okButton.w3.topShadowColor =
453       get_pixel_resource("okButton.topShadowColor", "Button.TopShadowColor",
454 			 app->dpy, app->colormap, app->white);
455    d->okButton.w3.bottomShadowColor =
456       get_pixel_resource("okButton.bottomShadowColor",
457 			 "Button.BottomShadowColor",
458 			 app->dpy, app->colormap, app->black);
459    d->okButton.w3.shadowThickness =
460       get_integer_resource("okButton.shadowThickness",
461 			   "Button.ShadowThickness", 2);
462    d->okButton.w3.borderColor =
463       get_pixel_resource("okButton.borderColor", "Button.BorderColor",
464 			 app->dpy, app->colormap, app->black);
465    d->okButton.w3.borderWidth =
466       get_integer_resource("okButton.borderWidth", "Button.BorderWidth", 1);
467    d->okButton.w3.horizontalSpacing = scaleXDimension(app,
468       get_integer_resource("okButton.horizontalSpacing", "Button.Spacing", 4));
469    d->okButton.w3.verticalSpacing = scaleYDimension(app,
470       get_integer_resource("okButton.verticalSpacing", "Button.Spacing", 2));
471    labelText =
472       getStringResourceWithDefault("okButton.label", "Button.Label", "OK");
473    createLabel(app, labelText, &(d->okButton.label));
474    freeIf(labelText);
475    d->okButton.label.font =
476       getFontResource(app, "okButton.font", "Button.Font");
477    calcButtonExtents(&(d->okButton));
478    d->okButton.label.w.foreground = d->okButton.w3.w.foreground;
479    d->okButton.label.w.background = d->okButton.w3.w.background;
480 
481    d->cancelButton.w3.w.foreground =
482       get_pixel_resource("cancelButton.foreground", "Button.Foreground",
483 			 app->dpy, app->colormap, app->black);
484    d->cancelButton.w3.w.background =
485       get_pixel_resource("cancelButton.background", "Button.Background",
486 			 app->dpy, app->colormap, app->white);
487    d->cancelButton.w3.topShadowColor =
488       get_pixel_resource("cancelButton.topShadowColor",
489 			 "Button.TopShadowColor",
490 			 app->dpy, app->colormap, app->white);
491    d->cancelButton.w3.bottomShadowColor =
492       get_pixel_resource("cancelButton.bottomShadowColor",
493 			 "Button.BottomShadowColor",
494 			 app->dpy, app->colormap, app->black);
495    d->cancelButton.w3.shadowThickness =
496       get_integer_resource("cancelButton.shadowThickness",
497 			   "Button.ShadowThickness", 2);
498    d->cancelButton.w3.borderColor =
499       get_pixel_resource("cancelButton.borderColor", "Button.BorderColor",
500 			 app->dpy, app->colormap, app->black);
501    d->cancelButton.w3.borderWidth =
502       get_integer_resource("cancelButton.borderWidth", "Button.BorderWidth",
503 			   1);
504    d->cancelButton.w3.horizontalSpacing = scaleXDimension(app,
505       get_integer_resource("cancelButton.horizontalSpacing", "Button.Spacing",
506 			   4));
507    d->cancelButton.w3.verticalSpacing = scaleYDimension(app,
508       get_integer_resource("cancelButton.verticalSpacing", "Button.Spacing",
509 			   2));
510    labelText =
511       getStringResourceWithDefault("cancelButton.label", "Button.Label",
512 				   "Cancel");
513    createLabel(app, labelText, &(d->cancelButton.label));
514    freeIf(labelText);
515    d->cancelButton.label.font =
516       getFontResource(app, "cancelButton.font", "Button.Font");
517    calcButtonExtents(&(d->cancelButton));
518    d->cancelButton.label.w.foreground = d->cancelButton.w3.w.foreground;
519    d->cancelButton.label.w.background = d->cancelButton.w3.w.background;
520 
521    balanceButtonExtents(&(d->okButton), &(d->cancelButton));
522 
523    d->indicator.w3.w.foreground =
524       get_pixel_resource("indicator.foreground", "Indicator.Foreground",
525 			 app->dpy, app->colormap, app->black);
526    d->indicator.w3.w.background =
527       get_pixel_resource("indicator.background", "Indicator.Background",
528 			 app->dpy, app->colormap, app->white);
529    d->indicator.w3.w.width = scaleXDimension(app,
530       get_integer_resource("indicator.width", "Indicator.Width", 15));
531    d->indicator.w3.w.height = scaleYDimension(app,
532       get_integer_resource("indicator.height", "Indicator.Height", 7));
533    d->indicator.w3.topShadowColor =
534       get_pixel_resource("indicator.topShadowColor",
535 			 "Indicator.TopShadowColor",
536 			 app->dpy, app->colormap, app->white);
537    d->indicator.w3.bottomShadowColor =
538       get_pixel_resource("indicator.bottomShadowColor",
539 			 "Indicator.BottomShadowColor",
540 			 app->dpy, app->colormap, app->black);
541    d->indicator.w3.shadowThickness =
542       get_integer_resource("indicator.shadowThickness",
543 			   "Indicator.ShadowThickness", 2);
544    d->indicator.w3.borderColor =
545       get_pixel_resource("indicator.borderColor", "Indicator.BorderColor",
546 			 app->dpy, app->colormap, app->black);
547    d->indicator.w3.borderWidth =
548       get_integer_resource("indicator.borderWidth", "Indicator.BorderWidth",
549 			   0);
550    d->indicator.w3.horizontalSpacing = scaleXDimension(app,
551       get_integer_resource("indicator.horizontalSpacing", "Indicator.Spacing",
552 			   2));
553    d->indicator.w3.verticalSpacing =scaleYDimension(app,
554       get_integer_resource("indicator.verticalSpacing", "Indicator.Spacing",
555 			   4));
556    d->indicator.minimumCount =
557       get_integer_resource("indicator.minimumCount", "Indicator.MinimumCount",
558 			   8);
559    d->indicator.maximumCount =
560       get_integer_resource("indicator.maximumCount", "Indicator.MaximumCount",
561 			   24);
562    d->indicator.w3.interiorWidth = d->indicator.w3.w.width;
563    d->indicator.w3.interiorHeight = d->indicator.w3.w.height;
564    d->indicator.w3.w.width += (2 * d->indicator.w3.shadowThickness);
565    d->indicator.w3.w.width += (2 * d->indicator.w3.borderWidth);
566    d->indicator.w3.w.height += (2 * d->indicator.w3.shadowThickness);
567    d->indicator.w3.w.height += (2 * d->indicator.w3.borderWidth);
568    {
569       /* Make sure the indicators can all fit on the screen.
570        * 80% of the screen width seems fine.
571        */
572       Dimension maxWidth = (WidthOfScreen(app->screen) * 8 / 10);
573       Dimension extraSpace = ((2 * d->w3.horizontalSpacing) +
574 			      (2 * d->w3.shadowThickness));
575 
576       if (d->indicator.maximumCount < 8) {
577 	 d->indicator.maximumCount = 8;
578       }
579       if (((d->indicator.maximumCount * d->indicator.w3.w.width) +
580 	   ((d->indicator.maximumCount - 1) *
581 	    d->indicator.w3.horizontalSpacing) + extraSpace) > maxWidth) {
582 	 d->indicator.maximumCount =
583 	    ((maxWidth - extraSpace - d->indicator.w3.w.width) /
584 	     (d->indicator.w3.w.width + d->indicator.w3.horizontalSpacing))
585 	    + 1;
586       }
587       if (d->indicator.minimumCount <= 6) {
588 	 d->indicator.minimumCount = 6;
589       }
590       if (d->indicator.minimumCount > d->indicator.maximumCount) {
591 	 d->indicator.minimumCount = d->indicator.maximumCount;
592       }
593    }
594 
595    {
596       /* Calculate the width and horizontal position of things. */
597       Dimension labelAreaWidth;
598       Dimension buttonAreaWidth;
599       Dimension indicatorAreaWidth;
600       Dimension extraIndicatorSpace;
601       Dimension singleIndicatorSpace;
602       Dimension interButtonSpace;
603       Dimension w;
604       Position leftX;
605       int i;
606 
607       labelAreaWidth = d->label.w.width + (2 * d->w3.horizontalSpacing);
608       buttonAreaWidth = ((3 * d->w3.horizontalSpacing) +
609 			 d->okButton.w3.w.width +
610 			 d->cancelButton.w3.w.width);
611       w = MAX(labelAreaWidth, buttonAreaWidth);
612       extraIndicatorSpace = ((2 * d->w3.horizontalSpacing) +
613 			     d->indicator.w3.w.width);
614       singleIndicatorSpace = (d->indicator.w3.w.width +
615 			      d->indicator.w3.horizontalSpacing);
616       d->indicator.count = ((w - extraIndicatorSpace) / singleIndicatorSpace);
617       d->indicator.current = 0;
618       d->indicator.count++; /* For gatepost indicator in extra space. */
619       if (((w - extraIndicatorSpace) % singleIndicatorSpace) >
620 	  (singleIndicatorSpace / 2)) {
621 	 d->indicator.count++;
622       }
623       if (d->indicator.count < d->indicator.minimumCount) {
624 	 d->indicator.count = d->indicator.minimumCount;
625       }
626       if (d->indicator.count > d->indicator.maximumCount) {
627 	 d->indicator.count = d->indicator.maximumCount;
628       }
629       indicatorAreaWidth = ((singleIndicatorSpace * (d->indicator.count - 1)) +
630 			    extraIndicatorSpace);
631       d->w3.interiorWidth = MAX(w, indicatorAreaWidth);
632       d->w3.w.width = d->w3.interiorWidth + (2 * d->w3.shadowThickness);
633 
634       leftX = (d->w3.w.width - d->label.w.width) / 2;
635       d->label.w.x = leftX;
636 
637       leftX = ((d->w3.w.width -
638 	       (d->indicator.count * d->indicator.w3.w.width) -
639 	       ((d->indicator.count - 1) * d->indicator.w3.horizontalSpacing))
640 	       / 2);
641       {
642 	 int n = d->indicator.count * sizeof(IndicatorElement);
643 	 d->indicators = malloc(n);
644 	 if (NULL == d->indicators) {
645 	    destroyDialog(app);
646 	    outOfMemory(app, __LINE__);
647 	 }
648 	 memset(d->indicators, 0, n);
649       }
650       d->indicators[0].parent = &(d->indicator);
651       d->indicators[0].w.x = d->indicator.w3.w.x = leftX;
652       d->indicators[0].w.width = d->indicator.w3.w.width;
653       d->indicators[0].isLit = False;
654       for (i = 1; i < d->indicator.count; i++) {
655 	 d->indicators[i].parent = &(d->indicator);
656 	 d->indicators[i].w.x = (d->indicators[i - 1].w.x +
657 				 d->indicator.w3.w.width +
658 				 d->indicator.w3.horizontalSpacing);
659 	 d->indicators[i].w.width = d->indicator.w3.w.width;
660 	 d->indicators[i].isLit = False;
661       }
662       interButtonSpace = ((d->w3.interiorWidth - d->okButton.w3.w.width -
663 			   d->cancelButton.w3.w.width) / 3);
664       d->okButton.w3.w.x = interButtonSpace + d->w3.shadowThickness;
665       d->cancelButton.w3.w.x = (d->okButton.w3.w.x + d->okButton.w3.w.width +
666 				interButtonSpace);
667    }
668    {
669       /* Calculate the height and vertical position of things. */
670       int i;
671 
672       d->w3.interiorHeight = ((4 * d->w3.verticalSpacing) +
673 			      (2 * d->indicator.w3.verticalSpacing) +
674 			      d->label.w.height +
675 			      d->indicator.w3.w.height +
676 			      d->okButton.w3.w.height);
677       d->w3.w.height = d->w3.interiorHeight + (2 * d->w3.shadowThickness);
678       d->label.w.y = d->w3.shadowThickness + d->w3.verticalSpacing;
679       d->indicator.w3.w.y = (d->label.w.y + d->label.w.height +
680 			     d->w3.verticalSpacing +
681 			     d->indicator.w3.verticalSpacing);
682       for (i = 0; i < d->indicator.count; i++) {
683 	 d->indicators[i].w.y = d->indicator.w3.w.y;
684 	 d->indicators[i].w.height = d->indicator.w3.w.height;
685       }
686       d->okButton.w3.w.y = d->cancelButton.w3.w.y =
687 	 (d->indicator.w3.w.y + d->indicator.w3.w.height +
688 	  d->w3.verticalSpacing + d->indicator.w3.verticalSpacing);
689    }
690    calcButtonLabelPosition(&(d->okButton));
691    calcButtonLabelPosition(&(d->cancelButton));
692 
693    d->w3.w.x = (WidthOfScreen(app->screen) - d->w3.w.width) / 2;
694    d->w3.w.y = (HeightOfScreen(app->screen) - d->w3.w.height) / 3;
695 
696    app->dialog = d;
697 }
698 
destroyLabel(AppInfo * app,LabelInfo * label)699 void destroyLabel(AppInfo *app, LabelInfo *label)
700 {
701    TextObject *thisTextObject;
702    TextObject *nextTextObject;
703 
704    thisTextObject = label->multiText;
705    nextTextObject = thisTextObject->next;
706    freeIf(thisTextObject);
707    while (NULL != nextTextObject) {
708       thisTextObject = nextTextObject;
709       nextTextObject = thisTextObject->next;
710       freeIf(thisTextObject);
711    }
712    freeIf(label->fullText);
713    freeFontIf(app, label->font);
714 }
715 
destroyDialog(AppInfo * app)716 void destroyDialog(AppInfo *app)
717 {
718    DialogInfo *d = app->dialog;
719 
720    freeIf(d->title);
721    freeIf(d->indicators);
722 
723    destroyLabel(app, &(d->label));
724    destroyLabel(app, &(d->okButton.label));
725    destroyLabel(app, &(d->cancelButton.label));
726 
727    XFree(d->sizeHints);
728    XFree(d->wmHints);
729    XFree(d->classHints);
730    XFree(d->windowName.value);
731 
732    freeIf(d);
733 }
734 
createDialogWindow(AppInfo * app)735 void createDialogWindow(AppInfo *app)
736 {
737    XSetWindowAttributes attr;
738    unsigned long attrMask = 0;
739    DialogInfo *d = app->dialog;
740 
741    attr.background_pixel = d->w3.w.background;
742    attrMask |= CWBackPixel;
743    attr.border_pixel = d->w3.borderColor;
744    attrMask |= CWBorderPixel;
745    attr.cursor = None;
746    attrMask |= CWCursor;
747    attr.event_mask = app->eventMask;
748    attrMask |= CWEventMask;
749 
750    d->dialogWindow = XCreateWindow(app->dpy, app->rootWindow,
751 				   d->w3.w.x, d->w3.w.y,
752 				   d->w3.w.width, d->w3.w.height,
753 				   d->w3.borderWidth,
754 				   DefaultDepthOfScreen(app->screen),
755 				   InputOutput,
756 				   DefaultVisualOfScreen(app->screen),
757 				   attrMask, &attr);
758 
759    d->sizeHints = XAllocSizeHints();
760    if (!(d->sizeHints)) {
761       destroyDialog(app);
762       outOfMemory(app, __LINE__);
763    }
764    d->sizeHints->flags = 0;
765    d->sizeHints->flags |= PPosition;
766    d->sizeHints->flags |= PSize;
767    d->sizeHints->x = d->w3.w.x;
768    d->sizeHints->y = d->w3.w.y;
769    d->sizeHints->width = d->w3.w.width;
770    d->sizeHints->height = d->w3.w.height;
771    d->sizeHints->min_width = d->w3.w.width;
772    d->sizeHints->min_height = d->w3.w.height;
773    d->sizeHints->flags |= PMinSize;
774    d->sizeHints->max_width = d->w3.w.width;
775    d->sizeHints->max_height = d->w3.w.height;
776    d->sizeHints->flags |= PMaxSize;
777    d->sizeHints->base_width = d->w3.w.width;
778    d->sizeHints->base_height = d->w3.w.height;
779    d->sizeHints->flags |= PBaseSize;
780 
781    d->wmHints = XAllocWMHints();
782    if (!(d->wmHints)) {
783       destroyDialog(app);
784       outOfMemory(app, __LINE__);
785    }
786    d->wmHints->flags = 0;
787    d->wmHints->input = True;
788    d->wmHints->flags |= InputHint;
789    d->wmHints->initial_state = NormalState;
790    d->wmHints->flags |= StateHint;
791 
792    d->classHints = XAllocClassHint();
793    if (!(d->classHints)) {
794       destroyDialog(app);
795       outOfMemory(app, __LINE__);
796    }
797    d->classHints->res_name = app->appName;
798    d->classHints->res_class = app->appClass;
799 
800    if (!XStringListToTextProperty(&(d->title), 1, &(d->windowName))) {
801       destroyDialog(app);
802       outOfMemory(app, __LINE__);
803    }
804    XSetWMProperties(app->dpy, d->dialogWindow, &(d->windowName), NULL,
805 		    app->argv, app->argc, d->sizeHints,
806 		    d->wmHints, d->classHints);
807    XSetTransientForHint(app->dpy, d->dialogWindow, d->dialogWindow);
808 
809    app->wmDeleteWindowAtom = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False);
810    XSetWMProtocols(app->dpy, d->dialogWindow, &(app->wmDeleteWindowAtom), 1);
811 }
812 
createGCs(AppInfo * app)813 void createGCs(AppInfo *app)
814 {
815    DialogInfo *d = app->dialog;
816 
817    XGCValues gcv;
818    unsigned long gcvMask;
819 
820    gcvMask = 0;
821    gcv.foreground = d->w3.w.background;
822    gcvMask |= GCForeground;
823    gcv.fill_style = FillSolid;
824    gcvMask |= GCFillStyle;
825    app->fillGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
826 
827    gcvMask = 0;
828    gcv.foreground = d->w3.borderColor;
829    gcvMask |= GCForeground;
830    gcv.line_width = d->w3.borderWidth;
831    gcvMask |= GCLineWidth;
832    gcv.line_style = LineSolid;
833    gcvMask |= GCLineStyle;
834    gcv.cap_style = CapButt;
835    gcvMask |= GCCapStyle;
836    gcv.join_style = JoinMiter;
837    gcvMask |= GCJoinStyle;
838    app->borderGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
839 
840    gcvMask = 0;
841    gcv.foreground = d->label.w.foreground;
842    gcvMask |= GCForeground;
843    gcv.background = d->label.w.background;
844    gcvMask |= GCBackground;
845    gcv.font = d->label.font->fid;
846    gcvMask |= GCFont;
847    app->textGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
848 
849    gcvMask = 0;
850    gcv.foreground = d->indicator.w3.w.foreground;
851    gcvMask |= GCForeground;
852    gcv.fill_style = FillSolid;
853    gcvMask |= GCFillStyle;
854    app->brightGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
855 
856    gcvMask = 0;
857    gcv.foreground = d->indicator.w3.w.background;
858    gcvMask |= GCForeground;
859    gcv.fill_style = FillSolid;
860    gcvMask |= GCFillStyle;
861    app->dimGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv);
862 }
863 
destroyGCs(AppInfo * app)864 void destroyGCs(AppInfo *app)
865 {
866    XFreeGC(app->dpy, app->fillGC);
867    XFreeGC(app->dpy, app->borderGC);
868    XFreeGC(app->dpy, app->textGC);
869    XFreeGC(app->dpy, app->brightGC);
870    XFreeGC(app->dpy, app->dimGC);
871 }
872 
paintLabel(AppInfo * app,Drawable draw,LabelInfo label)873 void paintLabel(AppInfo *app, Drawable draw, LabelInfo label)
874 {
875    TextObject *t;
876    Position x;
877    Position y;
878 
879    if (!(label.fullText)) {
880       return;
881    }
882    XSetForeground(app->dpy, app->textGC, label.w.foreground);
883    XSetBackground(app->dpy, app->textGC, label.w.background);
884    XSetFont(app->dpy, app->textGC, label.font->fid);
885 
886    t = label.multiText;
887    x = label.w.x;
888    y = label.w.y + t->ascent;
889    while (NULL != t) {
890       if (t->text) {
891 	 XDrawString(app->dpy, draw, app->textGC, x, y, t->text,
892 		     t->textLength);
893       }
894       y += t->descent;
895       t = t->next;
896       if (t) {
897 	 y += t->ascent;
898       }
899    }
900 }
901 
paintButton(AppInfo * app,Drawable draw,ButtonInfo button)902 void paintButton(AppInfo *app, Drawable draw, ButtonInfo button)
903 {
904    Position x;
905    Position y;
906    Dimension width;
907    Dimension height;
908 
909    if (button.w3.borderWidth > 0) {
910       XSetForeground(app->dpy, app->borderGC, button.w3.borderColor);
911       XFillRectangle(app->dpy, draw, app->borderGC, button.w3.w.x,
912 		     button.w3.w.y, button.w3.w.width, button.w3.w.height);
913    }
914    if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
915       Pixel tmp = button.w3.w.background;
916       button.w3.w.background = button.w3.w.foreground;
917       button.w3.w.foreground = tmp;
918       tmp = button.label.w.background;
919       button.label.w.background = button.label.w.foreground;
920       button.label.w.foreground = tmp;
921    }
922    x = (button.w3.w.x + button.w3.borderWidth);
923    y = (button.w3.w.y + button.w3.borderWidth);
924    width = (button.w3.w.width - (2 * button.w3.borderWidth));
925    height = (button.w3.w.height - (2 * button.w3.borderWidth));
926    if ((button.w3.shadowThickness > 0) && (button.pressed)) {
927       XSetForeground(app->dpy, app->fillGC, button.w3.topShadowColor);
928    } else {
929       XSetForeground(app->dpy, app->fillGC, button.w3.w.background);
930    }
931    XFillRectangle(app->dpy, draw, app->fillGC, x, y, width, height);
932    if (button.w3.shadowThickness > 0) {
933       if (button.pressed) {
934 	 draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
935 			       button.w3.shadowThickness,
936 			       button.w3.bottomShadowColor,
937 			       button.w3.topShadowColor);
938       } else {
939 	 draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
940 			       button.w3.shadowThickness,
941 			       button.w3.topShadowColor,
942 			       button.w3.bottomShadowColor);
943       }
944    }
945    if ((button.w3.shadowThickness > 0) && (button.pressed)) {
946       Dimension pressedAdjustment;
947 
948       pressedAdjustment = button.w3.shadowThickness / 2;
949       if (pressedAdjustment < 1) {
950 	 pressedAdjustment = 1;
951       }
952       x = button.label.w.x;
953       y = button.label.w.y;
954       button.label.w.x += pressedAdjustment;
955       button.label.w.y += pressedAdjustment;
956       paintLabel(app, draw, button.label);
957       button.label.w.x = x;
958       button.label.w.y = y;
959    } else {
960       paintLabel(app, draw, button.label);
961    }
962    if ((button.w3.shadowThickness <= 0) && (button.pressed)) {
963       Pixel tmp = button.w3.w.background;
964       button.w3.w.background = button.w3.w.foreground;
965       button.w3.w.foreground = tmp;
966       tmp = button.label.w.background;
967       button.label.w.background = button.label.w.foreground;
968       button.label.w.foreground = tmp;
969    }
970 }
971 
paintIndicator(AppInfo * app,Drawable draw,IndicatorElement indicator)972 void paintIndicator(AppInfo *app, Drawable draw, IndicatorElement indicator)
973 {
974    Position x;
975    Position y;
976    Dimension width;
977    Dimension height;
978    GC gc = app->dimGC;
979 
980    if (indicator.parent->w3.borderWidth > 0) {
981       XSetForeground(app->dpy, app->borderGC,
982 		     indicator.parent->w3.borderColor);
983       XFillRectangle(app->dpy, draw, app->borderGC, indicator.w.x,
984 		     indicator.w.y, indicator.w.width, indicator.w.height);
985    }
986    if (indicator.isLit) {
987       gc = app->brightGC;
988    }
989    x = (indicator.w.x + indicator.parent->w3.borderWidth);
990    y = (indicator.w.y + indicator.parent->w3.borderWidth);
991    width = (indicator.w.width - (2 * indicator.parent->w3.borderWidth));
992    height = (indicator.w.height - (2 * indicator.parent->w3.borderWidth));
993    XFillRectangle(app->dpy, draw, gc, x, y, width, height);
994    if (indicator.parent->w3.shadowThickness > 0) {
995       draw_shaded_rectangle(app->dpy, draw, x, y, width, height,
996 			    indicator.parent->w3.shadowThickness,
997 			    indicator.parent->w3.bottomShadowColor,
998 			    indicator.parent->w3.topShadowColor);
999    }
1000 }
1001 
updateIndicatorElement(AppInfo * app,int i)1002 void updateIndicatorElement(AppInfo *app, int i)
1003 {
1004    DialogInfo *d = app->dialog;
1005 
1006    d->indicators[i].isLit = !(d->indicators[i].isLit);
1007    paintIndicator(app, d->dialogWindow, d->indicators[i]);
1008 }
1009 
updateIndicators(AppInfo * app,int condition)1010 void updateIndicators(AppInfo *app, int condition)
1011 {
1012    DialogInfo *d = app->dialog;
1013 
1014    if (condition > 0) {
1015       /* Move forward one. */
1016       updateIndicatorElement(app, d->indicator.current);
1017       if (d->indicator.current < (d->indicator.count - 1)) {
1018 	 (d->indicator.current)++;
1019       } else {
1020 	 d->indicator.current = 0;
1021       }
1022    } else if (condition < 0) {
1023       /* Move backward one. */
1024       if (d->indicator.current > 0) {
1025 	 (d->indicator.current)--;
1026       } else {
1027 	 d->indicator.current = d->indicator.count - 1;
1028       }
1029       updateIndicatorElement(app, d->indicator.current);
1030    } else {
1031       /* Erase them all. */
1032       int i;
1033 
1034       for (i = 0; i < d->indicator.count; i++) {
1035 	 d->indicators[i].isLit = False;
1036 	 paintIndicator(app, d->dialogWindow, d->indicators[i]);
1037       }
1038       d->indicator.current = 0;
1039    }
1040    XSync(app->dpy, False);
1041 }
1042 
paintDialog(AppInfo * app)1043 void paintDialog(AppInfo *app)
1044 {
1045    DialogInfo *d = app->dialog;
1046    Drawable draw = d->dialogWindow;
1047    int i;
1048 
1049    XSetForeground(app->dpy, app->fillGC, d->w3.w.background);
1050    XFillRectangle(app->dpy, draw, app->fillGC, 0, 0,
1051 		  d->w3.w.width, d->w3.w.height);
1052    if (d->w3.shadowThickness > 0) {
1053       draw_shaded_rectangle(app->dpy, draw, 0, 0,
1054 			    d->w3.w.width, d->w3.w.height,
1055 			    d->w3.shadowThickness,
1056 			    d->w3.topShadowColor,
1057 			    d->w3.bottomShadowColor);
1058    }
1059    paintLabel(app, draw, d->label);
1060    for (i = 0; i < d->indicator.count; i++) {
1061       paintIndicator(app, draw, d->indicators[i]);
1062    }
1063    paintButton(app, draw, d->okButton);
1064    paintButton(app, draw, d->cancelButton);
1065    XSync(app->dpy, False);
1066 }
1067 
performGrab(AppInfo * app,int grabType,char * grabTypeName,Bool shouldGrab,Bool * isGrabbed)1068 void performGrab(AppInfo *app, int grabType, char *grabTypeName,
1069 		 Bool shouldGrab, Bool *isGrabbed) {
1070    if ((!(shouldGrab)) || (*isGrabbed)) {
1071       return;
1072    } else if ((GRAB_KEYBOARD != grabType) && (GRAB_POINTER != grabType)) {
1073       fprintf(stderr, "%s[%ld]: performGrab: invalid grab type (%d).\n",
1074 	      app->appName, (long) app->pid, grabType);
1075       return;
1076    } else {
1077       int status = GrabInvalidTime;	/* keep gcc -Wall from complaining */
1078       unsigned int seconds = 0;
1079       int helpful_message = 0;
1080       /* keyboard and pointer */
1081       Window grabWindow = app->dialog->dialogWindow;
1082       Bool ownerEvents = False;
1083       Bool pointerMode = GrabModeAsync;
1084       Bool keyboardMode = GrabModeAsync;
1085       /* pointer only */
1086       unsigned int eventMask = ButtonPressMask | ButtonReleaseMask;
1087       Window confineTo = None;
1088       Cursor cursor = None;
1089 
1090       *isGrabbed = True;
1091 
1092       if (NULL == grabTypeName) {
1093 	 fprintf(stderr, "%s[%ld]: performGrab: null grab type name.\n",
1094 		 app->appName, (long) app->pid);
1095       }
1096 
1097       if (0 == app->grabFailTimeout) {
1098 	 /* Ensure we try to perform the grab at least once. */
1099 	 app->grabFailTimeout = 1;
1100       }
1101       while (seconds < app->grabFailTimeout) {
1102 	 XSync(app->dpy, False);
1103 	 switch (grabType) {
1104 	  case GRAB_KEYBOARD:
1105 	    status = XGrabKeyboard(app->dpy, grabWindow, ownerEvents,
1106 				   pointerMode, keyboardMode, CurrentTime);
1107 	    break;
1108 	  case GRAB_POINTER:
1109 	    status = XGrabPointer(app->dpy, grabWindow, ownerEvents,
1110 				  eventMask, pointerMode, keyboardMode,
1111 				  confineTo, cursor, CurrentTime);
1112 	    break;
1113 	 }
1114 	 XSync(app->dpy, False);
1115 	 if (GrabSuccess == status) {
1116 	    if (helpful_message) {
1117 	       fprintf(stderr, "%s[%ld]: Got %s.\n",
1118 		       app->appName, (long) app->pid, grabTypeName);
1119 	    }
1120 	    break;
1121 	 }
1122 	 if (!helpful_message) {
1123 	    fprintf(stderr, "%s[%ld]: Trying to grab %s ...\n",
1124 		    app->appName, (long) app->pid, grabTypeName);
1125 	    helpful_message = 1;
1126 	 }
1127 	 seconds += app->grabRetryInterval;
1128 	 sleep(app->grabRetryInterval);
1129       }
1130       if (GrabSuccess != status) {
1131 	 char *reason = "reason unknown";
1132 
1133 	 switch (status) {
1134 	  case AlreadyGrabbed:
1135 	    reason = "someone else already has it";
1136 	    break;
1137 	  case GrabFrozen:
1138 	    reason = "someone else has frozen it";
1139 	    break;
1140 	  case GrabInvalidTime:
1141 	    reason = "bad grab time [this shouldn't happen]";
1142 	    break;
1143 	  case GrabNotViewable:
1144 	    reason = "grab not viewable [this shouldn't happen]";
1145 	    break;
1146 	 }
1147 	 fprintf(stderr, "%s[%ld]: Could not grab %s (%s)\n",
1148 		 app->appName, (long) app->pid, grabTypeName, reason);
1149 	 exitApp(app, EXIT_STATUS_ERROR);
1150       }
1151    }
1152 }
1153 
1154 
grabKeyboard(AppInfo * app)1155 void grabKeyboard(AppInfo *app)
1156 {
1157    performGrab(app, GRAB_KEYBOARD, "keyboard", app->grabKeyboard,
1158 	       &(app->isKeyboardGrabbed));
1159 }
1160 
ungrabKeyboard(AppInfo * app)1161 void ungrabKeyboard(AppInfo *app)
1162 {
1163    if (app->grabKeyboard) {
1164       XUngrabKeyboard(app->dpy, CurrentTime);
1165    }
1166 }
1167 
grabPointer(AppInfo * app)1168 void grabPointer(AppInfo *app)
1169 {
1170    performGrab(app, GRAB_POINTER, "pointer", app->grabPointer,
1171 	       &(app->isPointerGrabbed));
1172 }
1173 
ungrabPointer(AppInfo * app)1174 void ungrabPointer(AppInfo *app)
1175 {
1176    if (app->grabPointer) {
1177       XUngrabPointer(app->dpy, CurrentTime);
1178    }
1179 }
1180 
grabServer(AppInfo * app)1181 void grabServer(AppInfo *app)
1182 {
1183    if ((!(app->grabServer)) || (app->isServerGrabbed)) {
1184       return;
1185    } else {
1186       app->isServerGrabbed = True;
1187       XSync(app->dpy, False);
1188       XGrabServer(app->dpy);
1189       XSync(app->dpy, False);
1190    }
1191 }
1192 
ungrabServer(AppInfo * app)1193 void ungrabServer(AppInfo *app)
1194 {
1195    if (app->grabServer) {
1196       XUngrabServer(app->dpy);
1197    }
1198 }
1199 
cleanUp(AppInfo * app)1200 void cleanUp(AppInfo *app)
1201 {
1202    cancelInputTimeout(app);
1203    XDestroyWindow(app->dpy, app->dialog->dialogWindow);
1204    destroyGCs(app);
1205    destroyDialog(app);
1206    if (app->buf) {
1207       memset(app->buf, 0, app->bufSize);
1208    }
1209    freeIf(app->buf);
1210    ungrabPointer(app);
1211    ungrabKeyboard(app);
1212    ungrabServer(app);
1213 }
1214 
exitApp(AppInfo * app,int exitCode)1215 void exitApp(AppInfo *app, int exitCode)
1216 {
1217    cleanUp(app);
1218    exit(exitCode);
1219 }
1220 
acceptAction(AppInfo * app)1221 void acceptAction(AppInfo *app)
1222 {
1223    int status = append_to_buf(&(app->buf), &(app->bufSize),
1224 			      &(app->bufIndex), '\0');
1225    if (APPEND_FAILURE == status) {
1226       cleanUp(app);
1227       outOfMemory(app, __LINE__);
1228    }
1229    fputs(app->buf, stdout);
1230    fputc('\n', stdout);
1231    exitApp(app, EXIT_STATUS_ACCEPT);
1232 }
1233 
cancelAction(AppInfo * app)1234 void cancelAction(AppInfo *app)
1235 {
1236    exitApp(app, EXIT_STATUS_CANCEL);
1237 }
1238 
backspacePassphrase(AppInfo * app)1239 void backspacePassphrase(AppInfo *app)
1240 {
1241    if (0 >= app->bufIndex) {
1242       XBell(app->dpy, 0);
1243       return;
1244    }
1245    (app->bufIndex)--;
1246    updateIndicators(app, -1);
1247 }
1248 
erasePassphrase(AppInfo * app)1249 void erasePassphrase(AppInfo *app)
1250 {
1251    if (0 >= app->bufIndex) {
1252       XBell(app->dpy, 0);
1253       return;
1254    }
1255    updateIndicators(app, 0);
1256    app->bufIndex = 0;
1257 }
1258 
addToPassphrase(AppInfo * app,char c)1259 void addToPassphrase(AppInfo *app, char c)
1260 {
1261    int status = append_to_buf(&(app->buf), &(app->bufSize),
1262 			      &(app->bufIndex), c);
1263    if (APPEND_FAILURE == status) {
1264       cleanUp(app);
1265       outOfMemory(app, __LINE__);
1266    }
1267    updateIndicators(app, 1);
1268 }
1269 
handleKeyPress(AppInfo * app,XEvent * event)1270 void handleKeyPress(AppInfo *app, XEvent *event)
1271 {
1272    char s[2];
1273    int n;
1274 
1275    if (event->xkey.send_event) {
1276       /* Pay no attention to synthetic key events. */
1277       return;
1278    }
1279    cancelInputTimeout(app);
1280    n = XLookupString(&(event->xkey), s, 1, NULL, NULL);
1281 
1282    if (1 != n) {
1283       return;
1284    }
1285    s[1] = '\0';
1286    switch (s[0]) {
1287     case '\010':
1288     case '\177':
1289       backspacePassphrase(app);
1290       break;
1291     case '\025':
1292     case '\030':
1293       erasePassphrase(app);
1294       break;
1295     case '\012':
1296     case '\015':
1297       acceptAction(app);
1298       break;
1299     case '\033':
1300       cancelAction(app);
1301       break;
1302     default:
1303       addToPassphrase(app, s[0]);
1304       break;
1305    }
1306 }
1307 
eventIsInsideButton(AppInfo * app,XEvent * event,ButtonInfo button)1308 Bool eventIsInsideButton(AppInfo *app, XEvent *event, ButtonInfo button)
1309 {
1310    /* 'gcc -Wall' complains about 'app' being an unused parameter.
1311     * Tough.  We might want to use it later, and then we don't have
1312     * to change it in each place it's called.  Performance won't suffer.
1313     */
1314    int status = False;
1315    int x, y;
1316 
1317    switch(event->type) {
1318     case ButtonPress:
1319     case ButtonRelease:
1320       x = event->xbutton.x;
1321       y = event->xbutton.y;
1322       break;
1323     case MotionNotify:
1324       x = event->xmotion.x;
1325       y = event->xmotion.y;
1326       break;
1327     default:
1328       return(False);
1329    }
1330    if ((x >= (button.w3.w.x + button.w3.borderWidth)) &&
1331        (x < (button.w3.w.x + button.w3.w.width -
1332 	     (2 * button.w3.borderWidth))) &&
1333        (y >= (button.w3.w.y + button.w3.borderWidth)) &&
1334        (y < (button.w3.w.y + button.w3.w.height -
1335 	     (2 * button.w3.borderWidth)))) {
1336       status = True;
1337    }
1338    return(status);
1339 }
1340 
handleButtonPress(AppInfo * app,XEvent * event)1341 void handleButtonPress(AppInfo *app, XEvent *event)
1342 {
1343    DialogInfo *d = app->dialog;
1344 
1345    cancelInputTimeout(app);
1346    if (event->xbutton.button != Button1) {
1347       return;
1348    }
1349    if (ButtonPress == event->type) {
1350       if (eventIsInsideButton(app, event, d->okButton)) {
1351 	 d->pressedButton = OK_BUTTON;
1352 	 d->okButton.pressed = True;
1353 	 paintButton(app, d->dialogWindow, d->okButton);
1354       } else if (eventIsInsideButton(app, event, d->cancelButton)) {
1355 	 d->pressedButton = CANCEL_BUTTON;
1356 	 d->cancelButton.pressed = True;
1357 	 paintButton(app, d->dialogWindow, d->cancelButton);
1358       } else {
1359 	 d->pressedButton = NO_BUTTON;
1360       }
1361    } else if (ButtonRelease == event->type) {
1362       if (OK_BUTTON == d->pressedButton) {
1363 	 if (eventIsInsideButton(app, event, d->okButton)) {
1364 	    acceptAction(app);
1365 	 } else {
1366 	    if (d->okButton.pressed) {
1367 	       d->okButton.pressed = False;
1368 	       paintButton(app, d->dialogWindow, d->okButton);
1369 	    }
1370 	 }
1371       } else if (CANCEL_BUTTON == d->pressedButton) {
1372 	 if (eventIsInsideButton(app, event, d->cancelButton)) {
1373 	    cancelAction(app);
1374 	 } else {
1375 	    if (d->cancelButton.pressed) {
1376 	       d->cancelButton.pressed = False;
1377 	       paintButton(app, d->dialogWindow, d->cancelButton);
1378 	    }
1379 	 }
1380       }
1381       d->pressedButton = NO_BUTTON;
1382    }
1383 }
1384 
handlePointerMotion(AppInfo * app,XEvent * event)1385 void handlePointerMotion(AppInfo *app, XEvent *event)
1386 {
1387    DialogInfo *d = app->dialog;
1388 
1389    if (NO_BUTTON == d->pressedButton) {
1390       return;
1391    } else if (OK_BUTTON == d->pressedButton) {
1392       if (eventIsInsideButton(app, event, d->okButton)) {
1393 	 if (!(d->okButton.pressed)) {
1394 	    d->okButton.pressed = True;
1395 	    paintButton(app, d->dialogWindow, d->okButton);
1396 	 }
1397       } else {
1398 	 if (d->okButton.pressed) {
1399 	    d->okButton.pressed = False;
1400 	    paintButton(app, d->dialogWindow, d->okButton);
1401 	 }
1402       }
1403    } else if (CANCEL_BUTTON == d->pressedButton) {
1404       if (eventIsInsideButton(app, event, d->cancelButton)) {
1405 	 if (!(d->cancelButton.pressed)) {
1406 	    d->cancelButton.pressed = True;
1407 	    paintButton(app, d->dialogWindow, d->cancelButton);
1408 	 }
1409       } else {
1410 	 if (d->cancelButton.pressed) {
1411 	    d->cancelButton.pressed = False;
1412 	    paintButton(app, d->dialogWindow, d->cancelButton);
1413 	 }
1414       }
1415    }
1416 }
1417 
handleInputTimeout(XtPointer data,XtIntervalId * timerId)1418 void handleInputTimeout(XtPointer data, XtIntervalId *timerId)
1419 {
1420    /* 'gcc -Wall' complains about 'timerId' being an unused parameter.
1421     * Tough.  Xt forces us to have it here.  Like it.
1422     */
1423    AppInfo *app = (AppInfo *) data;
1424    if (app->inputTimeoutActive) {
1425       app->inputTimeoutActive = False;
1426       fprintf(stderr, "%s[%ld]: *Yawn*...timed out after %lu seconds.\n",
1427 	      app->appName, (long) app->pid, (app->inputTimeout / 1000));
1428       exitApp(app, EXIT_STATUS_TIMEOUT);
1429    }
1430 }
1431 
cancelInputTimeout(AppInfo * app)1432 void cancelInputTimeout(AppInfo *app)
1433 {
1434    if (app->inputTimeoutActive) {
1435       app->inputTimeoutActive = False;
1436       XtRemoveTimeOut(app->inputTimeoutTimerId);
1437    }
1438 }
1439 
main(int argc,char ** argv)1440 int main(int argc, char **argv)
1441 {
1442    AppInfo app;
1443    XEvent event;
1444 
1445    memset(&app, 0, sizeof(app));
1446 
1447    progclass = "SshAskpass";
1448    app.toplevelShell = XtAppInitialize(&(app.appContext), progclass,
1449 					NULL, 0, &argc, argv,
1450 					defaults, NULL, 0);
1451    app.argc = argc;
1452    app.argv = argv;
1453    app.dpy = XtDisplay(app.toplevelShell);
1454    app.screen = DefaultScreenOfDisplay(app.dpy);
1455    app.rootWindow = RootWindowOfScreen(app.screen);
1456    app.black = BlackPixel(app.dpy, DefaultScreen(app.dpy));
1457    app.white = WhitePixel(app.dpy, DefaultScreen(app.dpy));
1458    app.colormap = DefaultColormapOfScreen(app.screen);
1459    app.resourceDb = XtDatabase(app.dpy);
1460    XtGetApplicationNameAndClass(app.dpy, &progname, &progclass);
1461    app.appName = progname;
1462    app.appClass = progclass;
1463    /* For resources.c. */
1464    db = app.resourceDb;
1465 
1466    /* Seconds after which keyboard/pointer grab fail. */
1467    app.grabFailTimeout = 5;
1468    /* Number of seconds to wait between grab attempts. */
1469    app.grabRetryInterval = 1;
1470 
1471    app.pid = getpid();
1472 
1473    {
1474       struct rlimit resourceLimit;
1475       int status;
1476 
1477       status = getrlimit(RLIMIT_CORE, &resourceLimit);
1478       if (-1 == status) {
1479 	 fprintf(stderr, "%s[%ld]: getrlimit failed (%s)\n", app.appName,
1480 		 (long) app.pid, strerror(errno));
1481 	 exit(EXIT_STATUS_ERROR);
1482       }
1483       resourceLimit.rlim_cur = 0;
1484       status = setrlimit(RLIMIT_CORE, &resourceLimit);
1485       if (-1 == status) {
1486 	 fprintf(stderr, "%s[%ld]: setrlimit failed (%s)\n", app.appName,
1487 		 (long) app.pid, strerror(errno));
1488 	 exit(EXIT_STATUS_ERROR);
1489       }
1490    }
1491 
1492    app.xResolution =
1493       WidthOfScreen(app.screen) * 1000 / WidthMMOfScreen(app.screen);
1494    app.yResolution =
1495       HeightOfScreen(app.screen) * 1000 / HeightMMOfScreen(app.screen);
1496 
1497    createDialog(&app);
1498    createGCs(&app);
1499 
1500    app.eventMask = 0;
1501    app.eventMask |= ExposureMask;
1502    app.eventMask |= ButtonPressMask;
1503    app.eventMask |= ButtonReleaseMask;
1504    app.eventMask |= Button1MotionMask;
1505    app.eventMask |= KeyPressMask;
1506 
1507    createDialogWindow(&app);
1508 
1509    XMapWindow(app.dpy, app.dialog->dialogWindow);
1510    if (app.inputTimeout > 0) {
1511       app.inputTimeoutActive = True;
1512       app.inputTimeoutTimerId =
1513 	 XtAppAddTimeOut(app.appContext, app.inputTimeout,
1514 			 handleInputTimeout, (XtPointer) &app);
1515    }
1516 
1517 
1518    while(True) {
1519       XtAppNextEvent(app.appContext, &event);
1520       switch (event.type) {
1521        case Expose:
1522 	 grabServer(&app);
1523 	 grabKeyboard(&app);
1524 	 grabPointer(&app);
1525 	 if (event.xexpose.count) {
1526 	    break;
1527 	 }
1528 	 paintDialog(&app);
1529 	 break;
1530        case ButtonPress:
1531        case ButtonRelease:
1532 	 handleButtonPress(&app, &event);
1533 	 break;
1534        case MotionNotify:
1535 	 handlePointerMotion(&app, &event);
1536        case KeyPress:
1537 	 handleKeyPress(&app, &event);
1538 	 break;
1539        case ClientMessage:
1540 	 if ((32 == event.xclient.format) &&
1541 	     ((unsigned long) event.xclient.data.l[0] ==
1542 	      app.wmDeleteWindowAtom)) {
1543 	    cancelAction(&app);
1544 	 }
1545 	 break;
1546        default:
1547 	 break;
1548       }
1549    }
1550 
1551    fprintf(stderr, "%s[%ld]: This should not happen.\n", app.appName,
1552 	   (long) app.pid);
1553    return(EXIT_STATUS_ANOMALY);
1554 }
1555 
1556