/* x11-ssh-askpass.c: A generic X11-based password dialog for OpenSSH. * created 1999-Nov-17 03:40 Jim Knoble * autodate: 2001-Feb-14 04:00 * * by Jim Knoble * Copyright (C) 1999,2000,2001 Jim Knoble * * Disclaimer: * * The software is provided "as is", without warranty of any kind, * express or implied, including but not limited to the warranties of * merchantability, fitness for a particular purpose and * noninfringement. In no event shall the author(s) be liable for any * claim, damages or other liability, whether in an action of * contract, tort or otherwise, arising from, out of or in connection * with the software or the use or other dealings in the software. * * Portions of this code are distantly derived from code in xscreensaver * by Jamie Zawinski . That code says: * * --------8<------------------------------------------------8<-------- * xscreensaver, Copyright (c) 1991-1999 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * --------8<------------------------------------------------8<-------- * * The remainder of this code falls under the same permissions and * provisions as those of the xscreensaver code. */ #include #include #include #include /* For (get|set)rlimit() ... */ #include #include /* ... end */ /* For (get|set)rlimit(), sleep(), and getpid() ... */ #include /* ... end */ /* For errno ... */ #include /* ... end */ #include #include #include #include #include #include "dynlist.h" #include "drawing.h" #include "resources.h" #include "x11-ssh-askpass.h" #undef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) char *progname = NULL; char *progclass = NULL; XrmDatabase db = 0; static char *defaults[] = { #include "SshAskpass_ad.h" 0 }; void outOfMemory(AppInfo *app, int line) { fprintf(stderr, "%s[%ld]: Aaahhh! I ran out of memory at line %d.\n", app->appName, (long) app->pid, line); exit(EXIT_STATUS_NO_MEMORY); } void freeIf(void *p) { if (p) { free(p); } } void freeFontIf(AppInfo *app, XFontStruct *f) { if (f) { XFreeFont(app->dpy, f); } } XFontStruct *getFontResource(AppInfo *app, char *instanceName, char *className) { char *fallbackFont = "fixed"; XFontStruct *f = NULL; char *s = get_string_resource(instanceName, className); f = XLoadQueryFont(app->dpy, (s ? s : fallbackFont)); if (!f) { f = XLoadQueryFont(app->dpy, fallbackFont); } if (s) { free(s); } return(f); } char *getStringResourceWithDefault(char *instanceName, char *className, char *defaultText) { char *s = get_string_resource(instanceName, className); if (!s) { if (!defaultText) { s = strdup(""); } else { s = strdup(defaultText); } } return(s); } unsigned int getUnsignedIntegerResource(AppInfo *app, char *instanceName, char *className, unsigned int defaultValue) { int n; unsigned int value; char c; char *s = get_string_resource(instanceName, className); char *cp = s; if (NULL == s) { return(defaultValue); } while ((*cp) && isspace(*cp)) { /* Skip whitespace. */ cp++; } if (*cp) { if (('0' == cp[0]) && cp[1]) { if (('x' == cp[1]) || ('X' == cp[1])) { /* Hex */ n = sscanf(cp + 2, "%x %c", &value, &c); } else { /* Octal */ n = sscanf(cp + 1, "%o %c", &value, &c); } if (1 == n) { free(s); return(value); } } else { /* Unsigned Decimal */ n = sscanf(cp, "%u %c", &value, &c); if (1 == n) { free(s); return(value); } } } /* If we get here, no conversion succeeded. */ fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n", app->appName, (long) app->pid, s, instanceName); free(s); return(defaultValue); } /* Default resolution is 75 dots/inch. 1 in = 2.54 cm. */ #define DefaultResolution ((75 * 10000) / 254) long getResolutionResource(AppInfo *app, char *instanceName, char *className, char *defaultResolutionSpec) { char units[3]; char *s; int n; long resolution; unsigned int i; memset(units, 0, sizeof(units)); s = getStringResourceWithDefault(instanceName, className, defaultResolutionSpec); /* NOTE: The width of the %s format must be one less than * the length of the units[] array above! */ n = sscanf(s, "%ld / %2s", &resolution, units); if (n != 2) { fprintf(stderr, "%s[%ld]: invalid value '%s' for %s.\n", app->appName, (long) app->pid, s, instanceName); resolution = DefaultResolution; } else { if (resolution < 0) { /* Resolution specifications should not be negative. */ resolution = -(resolution); } for (i = 0; i < (sizeof(units) - 1); i++) { units[i] = tolower(units[i]); } if ((0 == strcmp(units, "in")) || (0 == strcmp(units, "i")) || (0 == strcmp(units, "\""))) { /* dots/inch */ resolution = resolution * 10000 / 254; } else if ((0 == strcmp(units, "m")) || (0 == strcmp(units, "me"))) { /* dots/meter; no conversion necessary */ ; } else { /* some unit we don't recognize; cringe and stare at the floor */ resolution = DefaultResolution; } } return(resolution); } #undef DefaultResolution void calcTextObjectExtents(TextObject *t, XFontStruct *font) { if ((!t) || (!(t->text))) { return; } t->textLength = strlen(t->text); XTextExtents(font, t->text, t->textLength, &(t->direction), &(t->ascent), &(t->descent), &(t->overall)); } void calcLabelTextExtents(LabelInfo *label) { TextObject *t; int first = 1; if ((!label) || (!(label->fullText)) || (!(label->font))) { return; } t = label->multiText; while (NULL != t) { if (first) { calcTextObjectExtents(t, label->font); first = 0; } else calcTextObjectExtents(t, label->fixedFont); label->w.height += (t->ascent + t->descent); if (label->w.width < t->overall.width) { label->w.width = t->overall.width; } t = t->next; } } void calcTotalButtonExtents(ButtonInfo *button) { if (!button) { return; } button->w3.w.width = (button->w3.interiorWidth + (2 * button->w3.shadowThickness)); button->w3.w.width += (2 * button->w3.borderWidth); button->w3.w.height = (button->w3.interiorHeight + (2 * button->w3.shadowThickness)); button->w3.w.height += (2 * button->w3.borderWidth); } void calcButtonExtents(ButtonInfo *button) { if (!button) { return; } calcLabelTextExtents(&(button->label)); button->w3.interiorWidth = (button->label.w.width + (2 * button->w3.horizontalSpacing)); button->w3.interiorHeight = (button->label.w.height + (2 * button->w3.verticalSpacing)); calcTotalButtonExtents(button); } void balanceButtonExtents(ButtonInfo *button1, ButtonInfo *button2) { if ((!button1) || (!button2)) { return; } button1->w3.interiorWidth = button2->w3.interiorWidth = MAX(button1->w3.interiorWidth, button2->w3.interiorWidth); button1->w3.interiorHeight = button2->w3.interiorHeight = MAX(button1->w3.interiorHeight, button2->w3.interiorHeight); calcTotalButtonExtents(button1); calcTotalButtonExtents(button2); } void calcButtonLabelPosition(ButtonInfo *button) { if (!button) { return; } button->label.w.x = button->w3.w.x + ((button->w3.w.width - button->label.w.width) / 2); button->label.w.y = button->w3.w.y + ((button->w3.w.height - button->label.w.height) / 2); } Dimension scaleXDimension(AppInfo *app, Dimension unscaled) { Dimension scaled; if (((app->defaultXResolution < app->xResolution) && ((app->defaultXResolution + app->xFuzz) < app->xResolution)) || ((app->xResolution < app->defaultXResolution) && ((app->xResolution + app->xFuzz) < app->defaultXResolution))) { scaled = (unscaled * app->xResolution) / app->defaultXResolution; } else { scaled = unscaled; } return(scaled); } Dimension scaleYDimension(AppInfo *app, Dimension unscaled) { Dimension scaled; if (((app->defaultYResolution < app->yResolution) && ((app->defaultYResolution + app->yFuzz) < app->yResolution)) || ((app->yResolution < app->defaultYResolution) && ((app->yResolution + app->yFuzz) < app->defaultYResolution))) { scaled = (unscaled * app->yResolution) / app->defaultYResolution; } else { scaled = unscaled; } return(scaled); } /* Assumes 's' is non-NULL. */ TextObject *createTextObject(AppInfo *app, char *s) { TextObject *t = malloc(sizeof(*t)); if (NULL == t) { outOfMemory(app, __LINE__); } memset(t, 0, sizeof(*t)); if (('\n' == *s) || ('\0' == *s)) { t->text = " "; } else { t->text = s; } return(t); } /* Assumes 'label' object exists and is zeroed. */ void createLabel(AppInfo *app, char *text, LabelInfo *label) { char *substring; TextObject *t; if ((!app) || (!text)) { return; } label->fullText = strdup(text); label->multiText = createTextObject(app, label->fullText); t = label->multiText; substring = strchr(label->fullText, '\n'); while (NULL != substring) { *(substring++) = '\0'; t->next = createTextObject(app, substring); if (t->next) { t = t->next; } substring = strchr(substring, '\n'); } } void createDialog(AppInfo *app) { DialogInfo *d; char *labelText; if (app->dialog) { return; } d = malloc(sizeof(*d)); if (NULL == d) { outOfMemory(app, __LINE__); } memset(d, 0, sizeof(*d)); app->grabKeyboard = get_boolean_resource("grabKeyboard", "GrabKeyboard", True); app->grabPointer = get_boolean_resource("grabPointer", "GrabPointer", False); app->grabServer = get_boolean_resource("grabServer", "GrabServer", False); /* inputTimeout resource specified in seconds for easy human interface. * Convert to milliseconds here. */ app->inputTimeout = (unsigned long) 1000 * getUnsignedIntegerResource(app, "inputTimeout", "InputTimeout", 0); app->defaultXResolution = getResolutionResource(app, "defaultXResolution", "DefaultXResolution", "75/in"); app->defaultYResolution = getResolutionResource(app, "defaultYResolution", "DefaultYResolution", "75/in"); app->xFuzz = getResolutionResource(app, "xResolutionFuzz", "XResolutionFuzz", "20/in"); app->yFuzz = getResolutionResource(app, "yResolutionFuzz", "YResolutionFuzz", "20/in"); d->title = getStringResourceWithDefault("dialog.title", "Dialog.Title", "OpenSSH Authentication Passphrase Request"); d->w3.w.foreground = get_pixel_resource("foreground", "Foreground", app->dpy, app->colormap, app->black); d->w3.w.background = get_pixel_resource("background", "Background", app->dpy, app->colormap, app->white); d->w3.topShadowColor = get_pixel_resource("topShadowColor", "TopShadowColor", app->dpy, app->colormap, app->white); d->w3.bottomShadowColor = get_pixel_resource("bottomShadowColor", "BottomShadowColor", app->dpy, app->colormap, app->black); d->w3.shadowThickness = get_integer_resource("shadowThickness", "ShadowThickness", 3); d->w3.borderColor = get_pixel_resource("borderColor", "BorderColor", app->dpy, app->colormap, app->black); d->w3.borderWidth = get_integer_resource("borderWidth", "BorderWidth", 1); d->w3.horizontalSpacing = scaleXDimension(app, get_integer_resource("horizontalSpacing", "Spacing", 5)); d->w3.verticalSpacing = scaleYDimension(app, get_integer_resource("verticalSpacing", "Spacing", 6)); if (2 == app->argc) { labelText = strdup(app->argv[1]); } else { labelText = getStringResourceWithDefault("dialog.label", "Dialog.Label", "Please enter your authentication passphrase:"); } createLabel(app, labelText, &(d->label)); freeIf(labelText); d->label.font = getFontResource(app, "dialog.font", "Dialog.Font"); d->label.fixedFont = getFontResource(app, "dialog.fixedFont", "Dialog.FixedFont"); calcLabelTextExtents(&(d->label)); d->label.w.foreground = d->w3.w.foreground; d->label.w.background = d->w3.w.background; d->okButton.w3.w.foreground = get_pixel_resource("okButton.foreground", "Button.Foreground", app->dpy, app->colormap, app->black); d->okButton.w3.w.background = get_pixel_resource("okButton.background", "Button.Background", app->dpy, app->colormap, app->white); d->okButton.w3.topShadowColor = get_pixel_resource("okButton.topShadowColor", "Button.TopShadowColor", app->dpy, app->colormap, app->white); d->okButton.w3.bottomShadowColor = get_pixel_resource("okButton.bottomShadowColor", "Button.BottomShadowColor", app->dpy, app->colormap, app->black); d->okButton.w3.shadowThickness = get_integer_resource("okButton.shadowThickness", "Button.ShadowThickness", 2); d->okButton.w3.borderColor = get_pixel_resource("okButton.borderColor", "Button.BorderColor", app->dpy, app->colormap, app->black); d->okButton.w3.borderWidth = get_integer_resource("okButton.borderWidth", "Button.BorderWidth", 1); d->okButton.w3.horizontalSpacing = scaleXDimension(app, get_integer_resource("okButton.horizontalSpacing", "Button.Spacing", 4)); d->okButton.w3.verticalSpacing = scaleYDimension(app, get_integer_resource("okButton.verticalSpacing", "Button.Spacing", 2)); labelText = getStringResourceWithDefault("okButton.label", "Button.Label", "OK"); createLabel(app, labelText, &(d->okButton.label)); freeIf(labelText); d->okButton.label.font = getFontResource(app, "okButton.font", "Button.Font"); calcButtonExtents(&(d->okButton)); d->okButton.label.w.foreground = d->okButton.w3.w.foreground; d->okButton.label.w.background = d->okButton.w3.w.background; d->cancelButton.w3.w.foreground = get_pixel_resource("cancelButton.foreground", "Button.Foreground", app->dpy, app->colormap, app->black); d->cancelButton.w3.w.background = get_pixel_resource("cancelButton.background", "Button.Background", app->dpy, app->colormap, app->white); d->cancelButton.w3.topShadowColor = get_pixel_resource("cancelButton.topShadowColor", "Button.TopShadowColor", app->dpy, app->colormap, app->white); d->cancelButton.w3.bottomShadowColor = get_pixel_resource("cancelButton.bottomShadowColor", "Button.BottomShadowColor", app->dpy, app->colormap, app->black); d->cancelButton.w3.shadowThickness = get_integer_resource("cancelButton.shadowThickness", "Button.ShadowThickness", 2); d->cancelButton.w3.borderColor = get_pixel_resource("cancelButton.borderColor", "Button.BorderColor", app->dpy, app->colormap, app->black); d->cancelButton.w3.borderWidth = get_integer_resource("cancelButton.borderWidth", "Button.BorderWidth", 1); d->cancelButton.w3.horizontalSpacing = scaleXDimension(app, get_integer_resource("cancelButton.horizontalSpacing", "Button.Spacing", 4)); d->cancelButton.w3.verticalSpacing = scaleYDimension(app, get_integer_resource("cancelButton.verticalSpacing", "Button.Spacing", 2)); labelText = getStringResourceWithDefault("cancelButton.label", "Button.Label", "Cancel"); createLabel(app, labelText, &(d->cancelButton.label)); freeIf(labelText); d->cancelButton.label.font = getFontResource(app, "cancelButton.font", "Button.Font"); calcButtonExtents(&(d->cancelButton)); d->cancelButton.label.w.foreground = d->cancelButton.w3.w.foreground; d->cancelButton.label.w.background = d->cancelButton.w3.w.background; balanceButtonExtents(&(d->okButton), &(d->cancelButton)); d->indicator.w3.w.foreground = get_pixel_resource("indicator.foreground", "Indicator.Foreground", app->dpy, app->colormap, app->black); d->indicator.w3.w.background = get_pixel_resource("indicator.background", "Indicator.Background", app->dpy, app->colormap, app->white); d->indicator.w3.w.width = scaleXDimension(app, get_integer_resource("indicator.width", "Indicator.Width", 15)); d->indicator.w3.w.height = scaleYDimension(app, get_integer_resource("indicator.height", "Indicator.Height", 7)); d->indicator.w3.topShadowColor = get_pixel_resource("indicator.topShadowColor", "Indicator.TopShadowColor", app->dpy, app->colormap, app->white); d->indicator.w3.bottomShadowColor = get_pixel_resource("indicator.bottomShadowColor", "Indicator.BottomShadowColor", app->dpy, app->colormap, app->black); d->indicator.w3.shadowThickness = get_integer_resource("indicator.shadowThickness", "Indicator.ShadowThickness", 2); d->indicator.w3.borderColor = get_pixel_resource("indicator.borderColor", "Indicator.BorderColor", app->dpy, app->colormap, app->black); d->indicator.w3.borderWidth = get_integer_resource("indicator.borderWidth", "Indicator.BorderWidth", 0); d->indicator.w3.horizontalSpacing = scaleXDimension(app, get_integer_resource("indicator.horizontalSpacing", "Indicator.Spacing", 2)); d->indicator.w3.verticalSpacing =scaleYDimension(app, get_integer_resource("indicator.verticalSpacing", "Indicator.Spacing", 4)); d->indicator.minimumCount = get_integer_resource("indicator.minimumCount", "Indicator.MinimumCount", 8); d->indicator.maximumCount = get_integer_resource("indicator.maximumCount", "Indicator.MaximumCount", 24); d->indicator.w3.interiorWidth = d->indicator.w3.w.width; d->indicator.w3.interiorHeight = d->indicator.w3.w.height; d->indicator.w3.w.width += (2 * d->indicator.w3.shadowThickness); d->indicator.w3.w.width += (2 * d->indicator.w3.borderWidth); d->indicator.w3.w.height += (2 * d->indicator.w3.shadowThickness); d->indicator.w3.w.height += (2 * d->indicator.w3.borderWidth); { /* Make sure the indicators can all fit on the screen. * 80% of the screen width seems fine. */ Dimension maxWidth = (app->screen_width * 8 / 10); Dimension extraSpace = ((2 * d->w3.horizontalSpacing) + (2 * d->w3.shadowThickness)); if (d->indicator.maximumCount < 8) { d->indicator.maximumCount = 8; } if (((d->indicator.maximumCount * d->indicator.w3.w.width) + ((d->indicator.maximumCount - 1) * d->indicator.w3.horizontalSpacing) + extraSpace) > maxWidth) { d->indicator.maximumCount = ((maxWidth - extraSpace - d->indicator.w3.w.width) / (d->indicator.w3.w.width + d->indicator.w3.horizontalSpacing)) + 1; } if (d->indicator.minimumCount <= 6) { d->indicator.minimumCount = 6; } if (d->indicator.minimumCount > d->indicator.maximumCount) { d->indicator.minimumCount = d->indicator.maximumCount; } } { /* Calculate the width and horizontal position of things. */ Dimension labelAreaWidth; Dimension buttonAreaWidth; Dimension indicatorAreaWidth; Dimension extraIndicatorSpace; Dimension singleIndicatorSpace; Dimension interButtonSpace; Dimension w; Position leftX; int i; labelAreaWidth = d->label.w.width + (2 * d->w3.horizontalSpacing); buttonAreaWidth = ((3 * d->w3.horizontalSpacing) + d->okButton.w3.w.width + d->cancelButton.w3.w.width); w = MAX(labelAreaWidth, buttonAreaWidth); extraIndicatorSpace = ((2 * d->w3.horizontalSpacing) + d->indicator.w3.w.width); singleIndicatorSpace = (d->indicator.w3.w.width + d->indicator.w3.horizontalSpacing); d->indicator.count = ((w - extraIndicatorSpace) / singleIndicatorSpace); d->indicator.current = 0; d->indicator.count++; /* For gatepost indicator in extra space. */ if (((w - extraIndicatorSpace) % singleIndicatorSpace) > (singleIndicatorSpace / 2)) { d->indicator.count++; } if (d->indicator.count < d->indicator.minimumCount) { d->indicator.count = d->indicator.minimumCount; } if (d->indicator.count > d->indicator.maximumCount) { d->indicator.count = d->indicator.maximumCount; } indicatorAreaWidth = ((singleIndicatorSpace * (d->indicator.count - 1)) + extraIndicatorSpace); d->w3.interiorWidth = MAX(w, indicatorAreaWidth); d->w3.w.width = d->w3.interiorWidth + (2 * d->w3.shadowThickness); leftX = (d->w3.w.width - d->label.w.width) / 2; d->label.w.x = leftX; leftX = ((d->w3.w.width - (d->indicator.count * d->indicator.w3.w.width) - ((d->indicator.count - 1) * d->indicator.w3.horizontalSpacing)) / 2); { int n = d->indicator.count * sizeof(IndicatorElement); d->indicators = malloc(n); if (NULL == d->indicators) { destroyDialog(app); outOfMemory(app, __LINE__); } memset(d->indicators, 0, n); } d->indicators[0].parent = &(d->indicator); d->indicators[0].w.x = d->indicator.w3.w.x = leftX; d->indicators[0].w.width = d->indicator.w3.w.width; d->indicators[0].isLit = False; for (i = 1; i < d->indicator.count; i++) { d->indicators[i].parent = &(d->indicator); d->indicators[i].w.x = (d->indicators[i - 1].w.x + d->indicator.w3.w.width + d->indicator.w3.horizontalSpacing); d->indicators[i].w.width = d->indicator.w3.w.width; d->indicators[i].isLit = False; } interButtonSpace = ((d->w3.interiorWidth - d->okButton.w3.w.width - d->cancelButton.w3.w.width) / 3); d->okButton.w3.w.x = interButtonSpace + d->w3.shadowThickness; d->cancelButton.w3.w.x = (d->okButton.w3.w.x + d->okButton.w3.w.width + interButtonSpace); } { /* Calculate the height and vertical position of things. */ int i; d->w3.interiorHeight = ((4 * d->w3.verticalSpacing) + (2 * d->indicator.w3.verticalSpacing) + d->label.w.height + d->indicator.w3.w.height + d->okButton.w3.w.height); d->w3.w.height = d->w3.interiorHeight + (2 * d->w3.shadowThickness); d->label.w.y = d->w3.shadowThickness + d->w3.verticalSpacing; d->indicator.w3.w.y = (d->label.w.y + d->label.w.height + d->w3.verticalSpacing + d->indicator.w3.verticalSpacing); for (i = 0; i < d->indicator.count; i++) { d->indicators[i].w.y = d->indicator.w3.w.y; d->indicators[i].w.height = d->indicator.w3.w.height; } d->okButton.w3.w.y = d->cancelButton.w3.w.y = (d->indicator.w3.w.y + d->indicator.w3.w.height + d->w3.verticalSpacing + d->indicator.w3.verticalSpacing); } calcButtonLabelPosition(&(d->okButton)); calcButtonLabelPosition(&(d->cancelButton)); d->w3.w.x = (app->screen_width - d->w3.w.width) / 2; d->w3.w.y = (app->screen_height - d->w3.w.height) / 3; app->dialog = d; } void destroyLabel(AppInfo *app, LabelInfo *label) { TextObject *thisTextObject; TextObject *nextTextObject; thisTextObject = label->multiText; nextTextObject = thisTextObject->next; freeIf(thisTextObject); while (NULL != nextTextObject) { thisTextObject = nextTextObject; nextTextObject = thisTextObject->next; freeIf(thisTextObject); } freeIf(label->fullText); freeFontIf(app, label->font); freeFontIf(app, label->fixedFont); } void destroyDialog(AppInfo *app) { DialogInfo *d = app->dialog; freeIf(d->title); freeIf(d->indicators); destroyLabel(app, &(d->label)); destroyLabel(app, &(d->okButton.label)); destroyLabel(app, &(d->cancelButton.label)); XFree(d->sizeHints); XFree(d->wmHints); XFree(d->classHints); XFree(d->windowName.value); freeIf(d); } void createDialogWindow(AppInfo *app) { XSetWindowAttributes attr; unsigned long attrMask = 0; DialogInfo *d = app->dialog; attr.background_pixel = d->w3.w.background; attrMask |= CWBackPixel; attr.border_pixel = d->w3.borderColor; attrMask |= CWBorderPixel; attr.cursor = None; attrMask |= CWCursor; attr.event_mask = app->eventMask; attrMask |= CWEventMask; d->dialogWindow = XCreateWindow(app->dpy, app->rootWindow, d->w3.w.x, d->w3.w.y, d->w3.w.width, d->w3.w.height, d->w3.borderWidth, DefaultDepthOfScreen(app->screen), InputOutput, DefaultVisualOfScreen(app->screen), attrMask, &attr); d->sizeHints = XAllocSizeHints(); if (!(d->sizeHints)) { destroyDialog(app); outOfMemory(app, __LINE__); } d->sizeHints->flags = 0; d->sizeHints->flags |= PPosition; d->sizeHints->flags |= PSize; d->sizeHints->min_width = d->w3.w.width; d->sizeHints->min_height = d->w3.w.height; d->sizeHints->flags |= PMinSize; d->sizeHints->max_width = d->w3.w.width; d->sizeHints->max_height = d->w3.w.height; d->sizeHints->flags |= PMaxSize; d->sizeHints->base_width = d->w3.w.width; d->sizeHints->base_height = d->w3.w.height; d->sizeHints->flags |= PBaseSize; d->wmHints = XAllocWMHints(); if (!(d->wmHints)) { destroyDialog(app); outOfMemory(app, __LINE__); } d->wmHints->flags = 0; d->wmHints->input = True; d->wmHints->flags |= InputHint; d->wmHints->initial_state = NormalState; d->wmHints->flags |= StateHint; d->classHints = XAllocClassHint(); if (!(d->classHints)) { destroyDialog(app); outOfMemory(app, __LINE__); } d->classHints->res_name = app->appName; d->classHints->res_class = app->appClass; if (!XStringListToTextProperty(&(d->title), 1, &(d->windowName))) { destroyDialog(app); outOfMemory(app, __LINE__); } XSetWMProperties(app->dpy, d->dialogWindow, &(d->windowName), NULL, app->argv, app->argc, d->sizeHints, d->wmHints, d->classHints); XSetTransientForHint(app->dpy, d->dialogWindow, d->dialogWindow); app->wmDeleteWindowAtom = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(app->dpy, d->dialogWindow, &(app->wmDeleteWindowAtom), 1); } void createGCs(AppInfo *app) { DialogInfo *d = app->dialog; XGCValues gcv; unsigned long gcvMask; gcvMask = 0; gcv.foreground = d->w3.w.background; gcvMask |= GCForeground; gcv.fill_style = FillSolid; gcvMask |= GCFillStyle; app->fillGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); gcvMask = 0; gcv.foreground = d->w3.borderColor; gcvMask |= GCForeground; gcv.line_width = d->w3.borderWidth; gcvMask |= GCLineWidth; gcv.line_style = LineSolid; gcvMask |= GCLineStyle; gcv.cap_style = CapButt; gcvMask |= GCCapStyle; gcv.join_style = JoinMiter; gcvMask |= GCJoinStyle; app->borderGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); gcvMask = 0; gcv.foreground = d->label.w.foreground; gcvMask |= GCForeground; gcv.background = d->label.w.background; gcvMask |= GCBackground; gcv.font = d->label.font->fid; gcvMask |= GCFont; app->textGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); gcvMask = 0; gcv.foreground = d->indicator.w3.w.foreground; gcvMask |= GCForeground; gcv.fill_style = FillSolid; gcvMask |= GCFillStyle; app->brightGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); gcvMask = 0; gcv.foreground = d->indicator.w3.w.background; gcvMask |= GCForeground; gcv.fill_style = FillSolid; gcvMask |= GCFillStyle; app->dimGC = XCreateGC(app->dpy, app->rootWindow, gcvMask, &gcv); } void destroyGCs(AppInfo *app) { XFreeGC(app->dpy, app->fillGC); XFreeGC(app->dpy, app->borderGC); XFreeGC(app->dpy, app->textGC); XFreeGC(app->dpy, app->brightGC); XFreeGC(app->dpy, app->dimGC); } void paintLabel(AppInfo *app, Drawable draw, LabelInfo label) { TextObject *t; Position x; Position y; int first = 1; if (!(label.fullText)) { return; } XSetForeground(app->dpy, app->textGC, label.w.foreground); XSetBackground(app->dpy, app->textGC, label.w.background); XSetFont(app->dpy, app->textGC, label.font->fid); t = label.multiText; x = label.w.x; y = label.w.y + t->ascent; while (NULL != t) { if (!first) XSetFont(app->dpy, app->textGC, label.fixedFont->fid); else first = 0; if (t->text) { XDrawString(app->dpy, draw, app->textGC, x, y, t->text, t->textLength); } y += t->descent; t = t->next; if (t) { y += t->ascent; } } } void paintButton(AppInfo *app, Drawable draw, ButtonInfo button) { Position x; Position y; Dimension width; Dimension height; if (button.w3.borderWidth > 0) { XSetForeground(app->dpy, app->borderGC, button.w3.borderColor); XFillRectangle(app->dpy, draw, app->borderGC, button.w3.w.x, button.w3.w.y, button.w3.w.width, button.w3.w.height); } if ((button.w3.shadowThickness <= 0) && (button.pressed)) { Pixel tmp = button.w3.w.background; button.w3.w.background = button.w3.w.foreground; button.w3.w.foreground = tmp; tmp = button.label.w.background; button.label.w.background = button.label.w.foreground; button.label.w.foreground = tmp; } x = (button.w3.w.x + button.w3.borderWidth); y = (button.w3.w.y + button.w3.borderWidth); width = (button.w3.w.width - (2 * button.w3.borderWidth)); height = (button.w3.w.height - (2 * button.w3.borderWidth)); if ((button.w3.shadowThickness > 0) && (button.pressed)) { XSetForeground(app->dpy, app->fillGC, button.w3.topShadowColor); } else { XSetForeground(app->dpy, app->fillGC, button.w3.w.background); } XFillRectangle(app->dpy, draw, app->fillGC, x, y, width, height); if (button.w3.shadowThickness > 0) { if (button.pressed) { draw_shaded_rectangle(app->dpy, draw, x, y, width, height, button.w3.shadowThickness, button.w3.bottomShadowColor, button.w3.topShadowColor); } else { draw_shaded_rectangle(app->dpy, draw, x, y, width, height, button.w3.shadowThickness, button.w3.topShadowColor, button.w3.bottomShadowColor); } } if ((button.w3.shadowThickness > 0) && (button.pressed)) { Dimension pressedAdjustment; pressedAdjustment = button.w3.shadowThickness / 2; if (pressedAdjustment < 1) { pressedAdjustment = 1; } x = button.label.w.x; y = button.label.w.y; button.label.w.x += pressedAdjustment; button.label.w.y += pressedAdjustment; paintLabel(app, draw, button.label); button.label.w.x = x; button.label.w.y = y; } else { paintLabel(app, draw, button.label); } if ((button.w3.shadowThickness <= 0) && (button.pressed)) { Pixel tmp = button.w3.w.background; button.w3.w.background = button.w3.w.foreground; button.w3.w.foreground = tmp; tmp = button.label.w.background; button.label.w.background = button.label.w.foreground; button.label.w.foreground = tmp; } } void paintIndicator(AppInfo *app, Drawable draw, IndicatorElement indicator) { Position x; Position y; Dimension width; Dimension height; GC gc = app->dimGC; if (indicator.parent->w3.borderWidth > 0) { XSetForeground(app->dpy, app->borderGC, indicator.parent->w3.borderColor); XFillRectangle(app->dpy, draw, app->borderGC, indicator.w.x, indicator.w.y, indicator.w.width, indicator.w.height); } if (indicator.isLit) { gc = app->brightGC; } x = (indicator.w.x + indicator.parent->w3.borderWidth); y = (indicator.w.y + indicator.parent->w3.borderWidth); width = (indicator.w.width - (2 * indicator.parent->w3.borderWidth)); height = (indicator.w.height - (2 * indicator.parent->w3.borderWidth)); XFillRectangle(app->dpy, draw, gc, x, y, width, height); if (indicator.parent->w3.shadowThickness > 0) { draw_shaded_rectangle(app->dpy, draw, x, y, width, height, indicator.parent->w3.shadowThickness, indicator.parent->w3.bottomShadowColor, indicator.parent->w3.topShadowColor); } } void updateIndicatorElement(AppInfo *app, int i) { DialogInfo *d = app->dialog; d->indicators[i].isLit = !(d->indicators[i].isLit); paintIndicator(app, d->dialogWindow, d->indicators[i]); } void updateIndicators(AppInfo *app, int condition) { DialogInfo *d = app->dialog; if (condition > 0) { /* Move forward one. */ updateIndicatorElement(app, d->indicator.current); if (d->indicator.current < (d->indicator.count - 1)) { (d->indicator.current)++; } else { d->indicator.current = 0; } } else if (condition < 0) { /* Move backward one. */ if (d->indicator.current > 0) { (d->indicator.current)--; } else { d->indicator.current = d->indicator.count - 1; } updateIndicatorElement(app, d->indicator.current); } else { /* Erase them all. */ int i; for (i = 0; i < d->indicator.count; i++) { d->indicators[i].isLit = False; paintIndicator(app, d->dialogWindow, d->indicators[i]); } d->indicator.current = 0; } XSync(app->dpy, False); } void paintDialog(AppInfo *app) { DialogInfo *d = app->dialog; Drawable draw = d->dialogWindow; int i; XSetForeground(app->dpy, app->fillGC, d->w3.w.background); XFillRectangle(app->dpy, draw, app->fillGC, 0, 0, d->w3.w.width, d->w3.w.height); if (d->w3.shadowThickness > 0) { draw_shaded_rectangle(app->dpy, draw, 0, 0, d->w3.w.width, d->w3.w.height, d->w3.shadowThickness, d->w3.topShadowColor, d->w3.bottomShadowColor); } paintLabel(app, draw, d->label); for (i = 0; i < d->indicator.count; i++) { paintIndicator(app, draw, d->indicators[i]); } paintButton(app, draw, d->okButton); paintButton(app, draw, d->cancelButton); XSync(app->dpy, False); } void performGrab(AppInfo *app, int grabType, char *grabTypeName, Bool shouldGrab, Bool *isGrabbed) { if ((!(shouldGrab)) || (*isGrabbed)) { return; } else if ((GRAB_KEYBOARD != grabType) && (GRAB_POINTER != grabType)) { fprintf(stderr, "%s[%ld]: performGrab: invalid grab type (%d).\n", app->appName, (long) app->pid, grabType); return; } else { int status = GrabInvalidTime; /* keep gcc -Wall from complaining */ unsigned int seconds = 0; int helpful_message = 0; /* keyboard and pointer */ Window grabWindow = app->dialog->dialogWindow; Bool ownerEvents = False; Bool pointerMode = GrabModeAsync; Bool keyboardMode = GrabModeAsync; /* pointer only */ unsigned int eventMask = ButtonPressMask | ButtonReleaseMask; Window confineTo = None; Cursor cursor = None; *isGrabbed = True; if (NULL == grabTypeName) { fprintf(stderr, "%s[%ld]: performGrab: null grab type name.\n", app->appName, (long) app->pid); } if (0 == app->grabFailTimeout) { /* Ensure we try to perform the grab at least once. */ app->grabFailTimeout = 1; } while (seconds < app->grabFailTimeout) { XSync(app->dpy, False); switch (grabType) { case GRAB_KEYBOARD: status = XGrabKeyboard(app->dpy, grabWindow, ownerEvents, pointerMode, keyboardMode, CurrentTime); break; case GRAB_POINTER: status = XGrabPointer(app->dpy, grabWindow, ownerEvents, eventMask, pointerMode, keyboardMode, confineTo, cursor, CurrentTime); break; } XSync(app->dpy, False); if (GrabSuccess == status) { if (helpful_message) { fprintf(stderr, "%s[%ld]: Got %s.\n", app->appName, (long) app->pid, grabTypeName); } break; } if (!helpful_message) { fprintf(stderr, "%s[%ld]: Trying to grab %s ...\n", app->appName, (long) app->pid, grabTypeName); helpful_message = 1; } seconds += app->grabRetryInterval; sleep(app->grabRetryInterval); } if (GrabSuccess != status) { char *reason = "reason unknown"; switch (status) { case AlreadyGrabbed: reason = "someone else already has it"; break; case GrabFrozen: reason = "someone else has frozen it"; break; case GrabInvalidTime: reason = "bad grab time [this shouldn't happen]"; break; case GrabNotViewable: reason = "grab not viewable [this shouldn't happen]"; break; } fprintf(stderr, "%s[%ld]: Could not grab %s (%s)\n", app->appName, (long) app->pid, grabTypeName, reason); exitApp(app, EXIT_STATUS_ERROR); } } } void grabKeyboard(AppInfo *app) { performGrab(app, GRAB_KEYBOARD, "keyboard", app->grabKeyboard, &(app->isKeyboardGrabbed)); } void ungrabKeyboard(AppInfo *app) { if (app->grabKeyboard) { XUngrabKeyboard(app->dpy, CurrentTime); } } void grabPointer(AppInfo *app) { performGrab(app, GRAB_POINTER, "pointer", app->grabPointer, &(app->isPointerGrabbed)); } void ungrabPointer(AppInfo *app) { if (app->grabPointer) { XUngrabPointer(app->dpy, CurrentTime); } } void grabServer(AppInfo *app) { if ((!(app->grabServer)) || (app->isServerGrabbed)) { return; } else { app->isServerGrabbed = True; XSync(app->dpy, False); XGrabServer(app->dpy); XSync(app->dpy, False); } } void ungrabServer(AppInfo *app) { if (app->grabServer) { XUngrabServer(app->dpy); } } void cleanUp(AppInfo *app) { cancelInputTimeout(app); XDestroyWindow(app->dpy, app->dialog->dialogWindow); destroyGCs(app); destroyDialog(app); if (app->buf) { memset(app->buf, 0, app->bufSize); } freeIf(app->buf); ungrabPointer(app); ungrabKeyboard(app); ungrabServer(app); } void exitApp(AppInfo *app, int exitCode) { cleanUp(app); exit(exitCode); } void acceptAction(AppInfo *app) { int status = append_to_buf(&(app->buf), &(app->bufSize), &(app->bufIndex), '\0'); if (APPEND_FAILURE == status) { cleanUp(app); outOfMemory(app, __LINE__); } fputs(app->buf, stdout); fputc('\n', stdout); exitApp(app, EXIT_STATUS_ACCEPT); } void cancelAction(AppInfo *app) { exitApp(app, EXIT_STATUS_CANCEL); } void backspacePassphrase(AppInfo *app) { if (0 >= app->bufIndex) { XBell(app->dpy, 0); return; } (app->bufIndex)--; updateIndicators(app, -1); } void erasePassphrase(AppInfo *app) { if (0 >= app->bufIndex) { XBell(app->dpy, 0); return; } updateIndicators(app, 0); app->bufIndex = 0; } void addToPassphrase(AppInfo *app, char c) { int status = append_to_buf(&(app->buf), &(app->bufSize), &(app->bufIndex), c); if (APPEND_FAILURE == status) { cleanUp(app); outOfMemory(app, __LINE__); } updateIndicators(app, 1); } void handleKeyPress(AppInfo *app, XEvent *event) { char s[2]; int n; if (event->xkey.send_event) { /* Pay no attention to synthetic key events. */ return; } cancelInputTimeout(app); n = XLookupString(&(event->xkey), s, 1, NULL, NULL); if (1 != n) { return; } s[1] = '\0'; switch (s[0]) { case '\010': case '\177': backspacePassphrase(app); break; case '\025': case '\030': erasePassphrase(app); break; case '\012': case '\015': acceptAction(app); break; case '\033': cancelAction(app); break; default: addToPassphrase(app, s[0]); break; } } Bool eventIsInsideButton(AppInfo *app, XEvent *event, ButtonInfo button) { /* 'gcc -Wall' complains about 'app' being an unused parameter. * Tough. We might want to use it later, and then we don't have * to change it in each place it's called. Performance won't suffer. */ int status = False; int x, y; switch(event->type) { case ButtonPress: case ButtonRelease: x = event->xbutton.x; y = event->xbutton.y; break; case MotionNotify: x = event->xmotion.x; y = event->xmotion.y; break; default: return(False); } if ((x >= (button.w3.w.x + button.w3.borderWidth)) && (x < (button.w3.w.x + button.w3.w.width - (2 * button.w3.borderWidth))) && (y >= (button.w3.w.y + button.w3.borderWidth)) && (y < (button.w3.w.y + button.w3.w.height - (2 * button.w3.borderWidth)))) { status = True; } return(status); } void handleButtonPress(AppInfo *app, XEvent *event) { DialogInfo *d = app->dialog; cancelInputTimeout(app); if (event->xbutton.button != Button1) { return; } if (ButtonPress == event->type) { if (eventIsInsideButton(app, event, d->okButton)) { d->pressedButton = OK_BUTTON; d->okButton.pressed = True; paintButton(app, d->dialogWindow, d->okButton); } else if (eventIsInsideButton(app, event, d->cancelButton)) { d->pressedButton = CANCEL_BUTTON; d->cancelButton.pressed = True; paintButton(app, d->dialogWindow, d->cancelButton); } else { d->pressedButton = NO_BUTTON; } } else if (ButtonRelease == event->type) { if (OK_BUTTON == d->pressedButton) { if (eventIsInsideButton(app, event, d->okButton)) { acceptAction(app); } else { if (d->okButton.pressed) { d->okButton.pressed = False; paintButton(app, d->dialogWindow, d->okButton); } } } else if (CANCEL_BUTTON == d->pressedButton) { if (eventIsInsideButton(app, event, d->cancelButton)) { cancelAction(app); } else { if (d->cancelButton.pressed) { d->cancelButton.pressed = False; paintButton(app, d->dialogWindow, d->cancelButton); } } } d->pressedButton = NO_BUTTON; } } void handlePointerMotion(AppInfo *app, XEvent *event) { DialogInfo *d = app->dialog; if (NO_BUTTON == d->pressedButton) { return; } else if (OK_BUTTON == d->pressedButton) { if (eventIsInsideButton(app, event, d->okButton)) { if (!(d->okButton.pressed)) { d->okButton.pressed = True; paintButton(app, d->dialogWindow, d->okButton); } } else { if (d->okButton.pressed) { d->okButton.pressed = False; paintButton(app, d->dialogWindow, d->okButton); } } } else if (CANCEL_BUTTON == d->pressedButton) { if (eventIsInsideButton(app, event, d->cancelButton)) { if (!(d->cancelButton.pressed)) { d->cancelButton.pressed = True; paintButton(app, d->dialogWindow, d->cancelButton); } } else { if (d->cancelButton.pressed) { d->cancelButton.pressed = False; paintButton(app, d->dialogWindow, d->cancelButton); } } } } void handleInputTimeout(XtPointer data, XtIntervalId *timerId) { /* 'gcc -Wall' complains about 'timerId' being an unused parameter. * Tough. Xt forces us to have it here. Like it. */ AppInfo *app = (AppInfo *) data; if (app->inputTimeoutActive) { app->inputTimeoutActive = False; fprintf(stderr, "%s[%ld]: *Yawn*...timed out after %lu seconds.\n", app->appName, (long) app->pid, (app->inputTimeout / 1000)); exitApp(app, EXIT_STATUS_TIMEOUT); } } void cancelInputTimeout(AppInfo *app) { if (app->inputTimeoutActive) { app->inputTimeoutActive = False; XtRemoveTimeOut(app->inputTimeoutTimerId); } } int main(int argc, char **argv) { AppInfo app; XEvent event; XineramaScreenInfo *screens; int nscreens; memset(&app, 0, sizeof(app)); progclass = "SshAskpass"; app.toplevelShell = XtAppInitialize(&(app.appContext), progclass, NULL, 0, &argc, argv, defaults, NULL, 0); app.argc = argc; app.argv = argv; app.dpy = XtDisplay(app.toplevelShell); app.screen = DefaultScreenOfDisplay(app.dpy); app.rootWindow = RootWindowOfScreen(app.screen); app.black = BlackPixel(app.dpy, DefaultScreen(app.dpy)); app.white = WhitePixel(app.dpy, DefaultScreen(app.dpy)); app.colormap = DefaultColormapOfScreen(app.screen); app.resourceDb = XtDatabase(app.dpy); XtGetApplicationNameAndClass(app.dpy, &progname, &progclass); app.appName = progname; app.appClass = progclass; /* For resources.c. */ db = app.resourceDb; /* Seconds after which keyboard/pointer grab fail. */ app.grabFailTimeout = 5; /* Number of seconds to wait between grab attempts. */ app.grabRetryInterval = 1; app.pid = getpid(); { struct rlimit resourceLimit; int status; status = getrlimit(RLIMIT_CORE, &resourceLimit); if (-1 == status) { fprintf(stderr, "%s[%ld]: getrlimit failed (%s)\n", app.appName, (long) app.pid, strerror(errno)); exit(EXIT_STATUS_ERROR); } resourceLimit.rlim_cur = 0; status = setrlimit(RLIMIT_CORE, &resourceLimit); if (-1 == status) { fprintf(stderr, "%s[%ld]: setrlimit failed (%s)\n", app.appName, (long) app.pid, strerror(errno)); exit(EXIT_STATUS_ERROR); } } app.screen_width = WidthOfScreen(app.screen); app.screen_height = HeightOfScreen(app.screen); if (XineramaIsActive(app.dpy) && (screens = XineramaQueryScreens(app.dpy, &nscreens)) != NULL && nscreens) { app.screen_width = screens[0].width; app.screen_height = screens[0].height; XFree(screens); } app.xResolution = app.screen_width * 1000 / WidthMMOfScreen(app.screen); app.yResolution = app.screen_height * 1000 / HeightMMOfScreen(app.screen); createDialog(&app); createGCs(&app); app.eventMask = 0; app.eventMask |= ExposureMask; app.eventMask |= ButtonPressMask; app.eventMask |= ButtonReleaseMask; app.eventMask |= Button1MotionMask; app.eventMask |= KeyPressMask; createDialogWindow(&app); XMapWindow(app.dpy, app.dialog->dialogWindow); if (app.inputTimeout > 0) { app.inputTimeoutActive = True; app.inputTimeoutTimerId = XtAppAddTimeOut(app.appContext, app.inputTimeout, handleInputTimeout, (XtPointer) &app); } while(True) { XtAppNextEvent(app.appContext, &event); switch (event.type) { case Expose: grabServer(&app); grabKeyboard(&app); grabPointer(&app); if (event.xexpose.count) { break; } paintDialog(&app); break; case ButtonPress: case ButtonRelease: handleButtonPress(&app, &event); break; case MotionNotify: handlePointerMotion(&app, &event); case KeyPress: handleKeyPress(&app, &event); break; case ClientMessage: if ((32 == event.xclient.format) && ((unsigned long) event.xclient.data.l[0] == app.wmDeleteWindowAtom)) { cancelAction(&app); } break; default: break; } } fprintf(stderr, "%s[%ld]: This should not happen.\n", app.appName, (long) app.pid); return(EXIT_STATUS_ANOMALY); }