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