x11-ssh-askpass.c revision 8f12b1d8
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 reason); 854 exitApp(app, EXIT_STATUS_ERROR); 855 } 856 } 857} 858 859void ungrabKeyboard(AppInfo *app) 860{ 861 if (app->grabKeyboard) { 862 XUngrabKeyboard(app->dpy, CurrentTime); 863 } 864} 865 866void grabPointer(AppInfo *app) 867{ 868 if ((!(app->grabPointer)) || (app->isPointerGrabbed)) { 869 return; 870 } else { 871 int status; 872 Window grabWindow = app->dialog->dialogWindow; 873 Bool ownerEvents = False; 874 unsigned int eventMask = ButtonPressMask | ButtonReleaseMask; 875 Bool pointerMode = GrabModeAsync; 876 Bool keyboardMode = GrabModeAsync; 877 Window confineTo = None; 878 Cursor cursor = None; 879 880 app->isPointerGrabbed = True; 881 XSync(app->dpy, False); 882 status = XGrabPointer(app->dpy, grabWindow, ownerEvents, eventMask, 883 pointerMode, keyboardMode, confineTo, cursor, 884 CurrentTime); 885 XSync(app->dpy, False); 886 if (GrabSuccess != status) { 887 char *reason = "reason unknown"; 888 889 switch (status) { 890 case AlreadyGrabbed: 891 reason = "someone else already has the pointer"; 892 break; 893 case GrabFrozen: 894 reason = "someone else has frozen the pointer"; 895 break; 896 case GrabInvalidTime: 897 reason = "bad grab time [this shouldn't happen]"; 898 break; 899 case GrabNotViewable: 900 reason = "grab not viewable [this shouldn't happen]"; 901 break; 902 } 903 fprintf(stderr, "%s: Could not grab pointer (%s)\n", app->appName, 904 reason); 905 exitApp(app, EXIT_STATUS_ERROR); 906 } 907 } 908} 909 910void ungrabPointer(AppInfo *app) 911{ 912 if (app->grabPointer) { 913 XUngrabPointer(app->dpy, CurrentTime); 914 } 915} 916 917void grabServer(AppInfo *app) 918{ 919 if ((!(app->grabServer)) || (app->isServerGrabbed)) { 920 return; 921 } else { 922 app->isServerGrabbed = True; 923 XSync(app->dpy, False); 924 XGrabServer(app->dpy); 925 XSync(app->dpy, False); 926 } 927} 928 929void ungrabServer(AppInfo *app) 930{ 931 if (app->grabServer) { 932 XUngrabServer(app->dpy); 933 } 934} 935 936void cleanUp(AppInfo *app) 937{ 938 XDestroyWindow(app->dpy, app->dialog->dialogWindow); 939 destroyGCs(app); 940 destroyDialog(app); 941 if (app->buf) { 942 memset(app->buf, 0, app->bufSize); 943 } 944 freeIf(app->buf); 945 ungrabPointer(app); 946 ungrabKeyboard(app); 947 ungrabServer(app); 948} 949 950void exitApp(AppInfo *app, int exitCode) 951{ 952 cleanUp(app); 953 exit(exitCode); 954} 955 956void acceptAction(AppInfo *app) 957{ 958 int status = append_to_buf(&(app->buf), &(app->bufSize), 959 &(app->bufIndex), '\0'); 960 if (APPEND_FAILURE == status) { 961 cleanUp(app); 962 outOfMemory(app, __LINE__); 963 } 964 fputs(app->buf, stdout); 965 fputc('\n', stdout); 966 exitApp(app, EXIT_STATUS_ACCEPT); 967} 968 969void cancelAction(AppInfo *app) 970{ 971 exitApp(app, EXIT_STATUS_CANCEL); 972} 973 974void backspacePassphrase(AppInfo *app) 975{ 976 if (0 >= app->bufIndex) { 977 XBell(app->dpy, 0); 978 return; 979 } 980 (app->bufIndex)--; 981 updateIndicators(app, -1); 982} 983 984void erasePassphrase(AppInfo *app) 985{ 986 if (0 >= app->bufIndex) { 987 XBell(app->dpy, 0); 988 return; 989 } 990 updateIndicators(app, 0); 991 app->bufIndex = 0; 992} 993 994void addToPassphrase(AppInfo *app, char c) 995{ 996 int status = append_to_buf(&(app->buf), &(app->bufSize), 997 &(app->bufIndex), c); 998 if (APPEND_FAILURE == status) { 999 cleanUp(app); 1000 outOfMemory(app, __LINE__); 1001 } 1002 updateIndicators(app, 1); 1003} 1004 1005void handleKeyPress(AppInfo *app, XKeyEvent *event) 1006{ 1007 char s[2]; 1008 int n; 1009 1010 if (event->send_event) { 1011 /* Pay no attention to synthetic key events. */ 1012 return; 1013 } 1014 n = XLookupString(event, s, 1, NULL, NULL); 1015 1016 if (1 != n) { 1017 return; 1018 } 1019 s[1] = '\0'; 1020 switch (s[0]) { 1021 case '\010': 1022 case '\177': 1023 backspacePassphrase(app); 1024 break; 1025 case '\025': 1026 case '\030': 1027 erasePassphrase(app); 1028 break; 1029 case '\012': 1030 case '\015': 1031 acceptAction(app); 1032 break; 1033 case '\033': 1034 cancelAction(app); 1035 break; 1036 default: 1037 addToPassphrase(app, s[0]); 1038 break; 1039 } 1040} 1041 1042Bool eventIsInsideButton(AppInfo *app, XButtonEvent *event, ButtonInfo button) 1043{ 1044 int status = False; 1045 1046 if ((event->x >= (button.w3.w.x + button.w3.borderWidth)) && 1047 (event->x < (button.w3.w.x + button.w3.w.width - 1048 (2 * button.w3.borderWidth))) && 1049 (event->y >= (button.w3.w.y + button.w3.borderWidth)) && 1050 (event->y < (button.w3.w.y + button.w3.w.height - 1051 (2 * button.w3.borderWidth)))) { 1052 status = True; 1053 } 1054 return(status); 1055} 1056 1057void handleButtonPress(AppInfo *app, XButtonEvent *event) 1058{ 1059 DialogInfo *d = app->dialog; 1060 1061 if (event->button != Button1) { 1062 return; 1063 } 1064 if (ButtonPress == event->type) { 1065 if (eventIsInsideButton(app, event, d->okButton)) { 1066 d->pressedButton = OK_BUTTON; 1067 d->okButton.pressed = True; 1068 paintButton(app, d->dialogWindow, d->okButton); 1069 } else if (eventIsInsideButton(app, event, d->cancelButton)) { 1070 d->pressedButton = CANCEL_BUTTON; 1071 d->cancelButton.pressed = True; 1072 paintButton(app, d->dialogWindow, d->cancelButton); 1073 } else { 1074 d->pressedButton = NO_BUTTON; 1075 } 1076 } else if (ButtonRelease == event->type) { 1077 if (OK_BUTTON == d->pressedButton) { 1078 if (eventIsInsideButton(app, event, d->okButton)) { 1079 acceptAction(app); 1080 } else { 1081 d->okButton.pressed = False; 1082 paintButton(app, d->dialogWindow, d->okButton); 1083 } 1084 } else if (CANCEL_BUTTON == d->pressedButton) { 1085 if (eventIsInsideButton(app, event, d->cancelButton)) { 1086 cancelAction(app); 1087 } else { 1088 d->cancelButton.pressed = False; 1089 paintButton(app, d->dialogWindow, d->cancelButton); 1090 } 1091 } 1092 d->pressedButton = NO_BUTTON; 1093 } 1094} 1095 1096int main(int argc, char **argv) 1097{ 1098 AppInfo app; 1099 XEvent event; 1100 1101 memset(&app, 0, sizeof(app)); 1102 1103 app.argc = argc; 1104 app.argv = argv; 1105 1106 progclass = "SshAskpass"; 1107 app.toplevelShell = XtAppInitialize(&(app.appContext), progclass, 1108 NULL, 0, &argc, argv, 1109 defaults, NULL, 0); 1110 app.dpy = XtDisplay(app.toplevelShell); 1111 app.screen = DefaultScreenOfDisplay(app.dpy); 1112 app.rootWindow = RootWindowOfScreen(app.screen); 1113 app.black = BlackPixel(app.dpy, DefaultScreen(app.dpy)); 1114 app.white = WhitePixel(app.dpy, DefaultScreen(app.dpy)); 1115 app.colormap = DefaultColormapOfScreen(app.screen); 1116 app.resourceDb = XtDatabase(app.dpy); 1117 XtGetApplicationNameAndClass(app.dpy, &progname, &progclass); 1118 app.appName = progname; 1119 app.appClass = progclass; 1120 /* For resources.c. */ 1121 db = app.resourceDb; 1122 1123 { 1124 struct rlimit resourceLimit; 1125 int status; 1126 1127 status = getrlimit(RLIMIT_CORE, &resourceLimit); 1128 if (-1 == status) { 1129 fprintf(stderr, "%s: getrlimit failed (%s)\n", app.appName, 1130 strerror(errno)); 1131 exit(EXIT_STATUS_ERROR); 1132 } 1133 resourceLimit.rlim_cur = 0; 1134 status = setrlimit(RLIMIT_CORE, &resourceLimit); 1135 if (-1 == status) { 1136 fprintf(stderr, "%s: setrlimit failed (%s)\n", app.appName, 1137 strerror(errno)); 1138 exit(EXIT_STATUS_ERROR); 1139 } 1140 } 1141 1142 createDialog(&app); 1143 createGCs(&app); 1144 createDialogWindow(&app); 1145 1146 XMapWindow(app.dpy, app.dialog->dialogWindow); 1147 1148 while(True) { 1149 XNextEvent(app.dpy, &event); 1150 switch (event.type) { 1151 case Expose: 1152 grabServer(&app); 1153 grabKeyboard(&app); 1154 grabPointer(&app); 1155 if (event.xexpose.count) { 1156 break; 1157 } 1158 paintDialog(&app); 1159 break; 1160 case ButtonPress: 1161 case ButtonRelease: 1162 handleButtonPress(&app, &(event.xbutton)); 1163 break; 1164 case KeyPress: 1165 handleKeyPress(&app, &(event.xkey)); 1166 break; 1167 case ClientMessage: 1168 if ((32 == event.xclient.format) && 1169 (event.xclient.data.l[0] == app.wmDeleteWindowAtom)) { 1170 cancelAction(&app); 1171 } 1172 break; 1173 default: 1174 break; 1175 } 1176 } 1177 1178 fprintf(stderr, "%s: This should not happen.\n", app.appName); 1179 return(EXIT_STATUS_ANOMALY); 1180} 1181 1182