#include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #define DISPLAY_VARIABLE "DISPLAY" #define RESOURCES_MAX_LENGTH 20000L #define CANCEL_STRING "Cancel" /* 'none' must be the last one in this enum, starting from zero */ typedef enum { background, hilight, shadow, text, dark_led, light_led, none } color_type; /* X data structures */ Display *display; Window main_window, cancel_button; XrmDatabase database; XModifierKeymap *modifiers; XFontStruct *font_struct; Colormap map; Font font; /* Default values for some resources */ char *font_spec = "-*-times-bold-r-*-*-*-140-*-*-*-*-iso8859-1"; char *font_fall_back = "-*-*-*-r-*-*-*-*-*-*-*-*-iso8859-1"; char *background_color_name = "gray80"; char *foreground_color_name = "black"; char *prompt = "Please enter your authentication passphrase:"; /* Color tables */ long color[none]; GC gc[none]; /* Geometry */ int w_width, w_height, w_x, w_y; int led_width = 20; int led_height = 10; int led_i_height = 8; int led_i_width = 18; int relief = 4; int margin = 4; int leds; int b_width, b_height; /* User interfacing */ char phrase[256]; char *ppointer = phrase; int cancel_pressed = 0; int exiting = 0; int next_led = 0; int leds_x, leds_y; int *led_state; void fatal(char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); exit(1); } /* Open connection to the X display. */ void open_display(void) { char *display_name; display_name = getenv(DISPLAY_VARIABLE); if (!display_name) fatal("Display not set, cannot open display."); display = XOpenDisplay(display_name); if (!display) fatal("Cannot open display \"%s\".\n", display_name); } /* Close the display after freeing data structures. */ void close_display(void) { XFreeModifiermap(modifiers); XUnloadFont(display, font); xfree(led_state); XCloseDisplay(display); } /* Create the graphics contexts. Color version. */ void create_GCs(void) { int i; XGCValues values; values.function = GXcopy; values.font = font; values.fill_style = FillSolid; values.background = color[0]; for (i = 0; i < none; i++) { values.foreground = color[i]; gc[i] = XCreateGC(display, main_window, GCForeground | GCBackground | GCFillStyle | GCFunction | GCFont, &values); } } /* Create the graphics contexts. B/W version. */ void create_GCs_bw(void) { int i; XGCValues values; values.function = GXcopy; values.font = font; values.fill_style = FillSolid; values.background = WhitePixel(display, DefaultScreen(display)); for (i = 0; i < none; i++) { values.foreground = ((i == text || i == shadow || i == dark_led) ? BlackPixel(display, DefaultScreen(display)) : WhitePixel(display, DefaultScreen(display))); gc[i] = XCreateGC(display, main_window, GCForeground | GCBackground | GCFillStyle | GCFunction | GCFont, &values); } } /* Try to allocate the colors we would like to use. If any of the allocations fails, revert to black'n'white mode. */ void allocate_colors(void) { XColor xcolor, exact_color; int red, green, blue; map = DefaultColormap(display, DefaultScreen(display)); if(!XAllocNamedColor(display, map, background_color_name, &xcolor, &exact_color)) goto revert; color[background] = xcolor.pixel; exact_color.flags = DoRed | DoGreen | DoBlue; red = exact_color.red; blue = exact_color.blue; green = exact_color.green; red += 16384; if (red > 65535) red = 65535; blue += 16384; if (blue > 65535) blue = 65535; green += 16384; if (green > 65535) green = 65535; exact_color.red = red; exact_color.blue = blue; exact_color.green = green; if(!XAllocColor(display, map, &exact_color)) goto revert; color[hilight] = exact_color.pixel; red -= 32768; if (red < 0) red = 0; blue -= 32768; if (blue < 0) blue = 0; green -= 32768; if (green < 0) green = 0; exact_color.red = red; exact_color.blue = blue; exact_color.green = green; if (!XAllocColor(display, map, &exact_color)) goto revert; color[shadow] = exact_color.pixel; if (!XAllocNamedColor(display, map, foreground_color_name, &xcolor, &exact_color)) goto revert; color[text] = xcolor.pixel; exact_color.red = 0x9000; exact_color.green = 0xd000; exact_color.blue = 0x9000; if(!XAllocColor(display, map, &exact_color)) goto revert; color[light_led] = exact_color.pixel; exact_color.red = 0x0; exact_color.green = 0x0; exact_color.blue = 0x8000; if(!XAllocColor(display, map, &exact_color)) goto revert; color[dark_led] = exact_color.pixel; XSetWindowColormap(display, main_window, map); create_GCs(); return; revert: /* switch to black'n'white mode */ create_GCs_bw(); } /* Calculate dimensions of a button containing 'string' inside it. */ void button_dimensions(char *string, int *width, int *height) { XCharStruct overall; int dir, asc, desc; XTextExtents(font_struct, string, strlen(string), &dir, &asc, &desc, &overall); *width = overall.width + 4 + 2 * margin; *height = overall.ascent + overall.descent + 1 + 4 + 2*margin; } /* Open windows (the dialog box and the cancel button). */ void open_windows(void) { int i; int scrap; XSetWindowAttributes attributes; XSizeHints *hints = XAllocSizeHints(); attributes.backing_store = WhenMapped; attributes.cursor = XCreateFontCursor(display, XC_X_cursor); main_window = XCreateWindow(display, DefaultRootWindow(display), w_x, w_y, w_width, w_height, 0, CopyFromParent, InputOutput, CopyFromParent, CWCursor | CWBackingStore, &attributes); hints->flags = PPosition | PSize | PMinSize | PMaxSize | PWinGravity; hints->x = w_x; hints->y = w_y; hints->width = w_width; hints->height = w_height; hints->min_width = hints->max_width = w_width; hints->min_height = hints->max_height = w_height; hints->win_gravity = CenterGravity; XSetWMNormalHints(display, main_window, hints); XStoreName(display, main_window, "SSH Authentication Passphrase Request"); /* This is a dialog box, I think. */ XSetTransientForHint(display, main_window, DefaultRootWindow(display)); XFree(hints); cancel_button = XCreateWindow(display, main_window, w_width - relief - margin - b_width, w_height - relief - margin - b_height, b_width, b_height, 0, CopyFromParent, InputOutput, CopyFromParent, CWCursor | CWBackingStore, &attributes); leds = (w_width - relief * 2 - margin * 2 + (led_width - led_i_width)) / led_width; scrap = (w_width - relief * 2 - margin * 2 + (led_width - led_i_width)) % led_width; leds_x = relief + margin + scrap/2; led_state = xmalloc(sizeof(int) * leds); for (i = 0; i < leds; i++) led_state[i] = 0; attributes.cursor = XCreateFontCursor(display, XC_top_left_arrow); XChangeWindowAttributes(display, cancel_button, CWCursor | CWBackingStore, &attributes); } /* Map the windows. */ void map_windows(void) { XMapRaised(display, main_window); XMapWindow(display, cancel_button); } /* Load the font wanted. */ void get_font(void) { font_struct = XLoadQueryFont(display, font_spec); if (!font_struct) { font_struct = XLoadQueryFont(display, font_fall_back); if (!font_struct) fatal ("Cannot load any font."); } font = font_struct->fid; } /* Calculate correct geometry for the main window. */ void compute_dimensions(void) { Status status; Window root; int dummy, d_width, d_height; int dir, asc, desc; XCharStruct overall; XTextExtents(font_struct, prompt, strlen(prompt), &dir, &asc, &desc, &overall); button_dimensions(CANCEL_STRING, &b_width, &b_height); w_width = XTextWidth(font_struct, prompt, strlen(prompt)); if (b_width > w_width) w_width = b_width; w_width += relief * 2 + margin * 2; w_height = asc + desc - 1 + margin * 2 + b_height + led_height; w_height += relief * 2 + margin * 2; status = XGetGeometry(display, DefaultRootWindow(display), &root, &dummy, &dummy, (unsigned int *)&d_width, (unsigned int *)&d_height, (unsigned int *)&dummy, (unsigned int *)&dummy); w_x = (d_width - w_width) / 2; w_y = (d_height - w_height) / 2; } /* An auxiliary function for getting a resource from the xrm. */ void get_resource(char *name, char *class, char **where) { char *type; XrmValue value; if (XrmGetResource(database, name, class, &type, &value) == True) *where = value.addr; } /* Read and get some resources. */ void read_resources(void) { Status err; unsigned char *prop_return; unsigned long nitems_return, bytes_return; int format_return; Atom atom_return; /* Read the current database in. */ database = NULL; XrmInitialize(); err = XGetWindowProperty(display, DefaultRootWindow(display), XA_RESOURCE_MANAGER, 0L, RESOURCES_MAX_LENGTH, False, XA_STRING, &atom_return, &format_return, &nitems_return, &bytes_return, &prop_return); if (err == Success) { if (prop_return != NULL) { database = XrmGetStringDatabase((char *) prop_return); XFree(prop_return); } } else { fatal("XGetWindowProperty failed."); } if (!database) return; get_resource("ssh.askpass.font", "SSH.AskPass.Font", &font_spec); get_resource("ssh.askpass.background","SSH.AskPass.Background", &background_color_name); get_resource("ssh.askpass.foreground","SSH.AskPass.Foreground", &foreground_color_name); get_resource("ssh.askpass.prompt","SSH.AskPass.Prompt", &prompt); } /* A function to print a string. Return the y-coordinate of the bottom of the string's bounding box. */ int put_string(int x, int y, char *string, int color, Window window) { XCharStruct overall; int dir, asc, desc; XTextExtents(font_struct, string, strlen(string), &dir, &asc, &desc, &overall); y += overall.ascent; XDrawString(display, window, gc[color], x, y, string, strlen(string)); return (y + overall.descent); } /* Draw a raised/sunken box. */ void relief_box(int x, int y, int a, int b, int left, int right, int depth, int fill, Window window) { int i; for (i = 0; i < depth; i++) { XDrawLine(display, window, gc[left], i + x, i + y, i + x, b - i); XDrawLine(display, window, gc[left], i + x, i + y, a - i, i + y); XDrawLine(display, window, gc[right], a - i, b - i, i + x, b - i); XDrawLine(display, window, gc[right], a - i, b - i, a - i, i + y); } if (fill != none) XFillRectangle(display, window, gc[fill], i + x, i + y, a - x - 2*i + 1, b - y - 2*i + 1); } /* Draw a specific led. */ void draw_led(int no, int on) { relief_box(leds_x + led_width * no, leds_y, leds_x + led_width * no + led_i_width, leds_y + led_height, shadow, hilight, 2, on ? light_led : dark_led, main_window); } /* Draw a button with certain string inside. */ void draw_button(char *string, Window window, int width, int height, int pushed) { if (!pushed) { relief_box(0, 0, width - 1, height - 1, hilight, shadow, 2, background, window); put_string(1 + margin, 1 + margin, string, text, window); } else { relief_box(2, 2, width - 1, height - 1, shadow, hilight, 2, background, window); relief_box(0, 0, width - 1, height - 1, shadow, hilight, 2, none, window); put_string(3 + margin, 3 + margin, string, text, window); } } /* Draw the dialog box and the cancel button. */ void draw_it(void) { int y; int i; relief_box(0, 0, w_width - 1, w_height - 1, hilight, shadow, relief, background, main_window); y = put_string(relief + margin, relief + margin, prompt, text, main_window); leds_y = y + margin; for (i = 0; i < leds; i ++) { draw_led(i, led_state[i]); } draw_button(CANCEL_STRING, cancel_button, b_width, b_height, 0); } /* Handle different X events. */ void button_press(XButtonEvent *event) { if (event->subwindow != cancel_button) return; draw_button(CANCEL_STRING, cancel_button, b_width, b_height, 1); cancel_pressed = 1; } void button_release(XButtonEvent *event) { if (event->subwindow != cancel_button) { if (cancel_pressed) { draw_button(CANCEL_STRING, cancel_button, b_width, b_height, 0); cancel_pressed = 0; } } else { if (cancel_pressed) exiting = 2; } } void advance_leds(void) { led_state[next_led] ^= 1; draw_led(next_led, led_state[next_led]); next_led = (next_led + 1) % leds; } void backward_leds(void) { next_led--; if (next_led == -1) next_led += leds; led_state[next_led] ^= 1; draw_led(next_led, led_state[next_led]); } /* Handle key press. */ void key_press(XKeyEvent *event) { char buf[100]; char *ptr = buf; int in_buf; KeySym sym; in_buf = XLookupString(event, buf, 100, &sym, NULL); if ((sym != NoSymbol) && (sym & (0xff)) == sym) { while(in_buf-- > 0) { if ((ppointer - phrase) < 255) { *ppointer++ = *ptr++; advance_leds(); } } } else /* Some special symbol. */ { switch(sym) { /* These cause the last character to be eaten. */ case XK_Delete: case XK_BackSpace: if (ppointer != phrase) { ppointer--; backward_leds(); } break; /* These cause the event loop to terminate. */ case XK_Return: case XK_Linefeed: case XK_KP_Enter: exiting = 1; return; case XK_Escape: case XK_Cancel: exiting = 2; return; } } } /* Read the modifiers mapping. */ void check_keyboard(void) { modifiers = XGetModifierMapping(display); } /* Event loop. */ void event_loop(void) { int focused = 0; XEvent event; XSelectInput(display, main_window, ButtonPressMask | VisibilityChangeMask | StructureNotifyMask | ExposureMask | ButtonReleaseMask | KeyPressMask); /* Windows must be mapped not before XSelectInput, so that the mapping notify will certainly arrive to our event loop. */ map_windows(); while(exiting == 0) { XNextEvent(display, &event); switch (event.type) { case ButtonPress: button_press(&event.xbutton); break; case ButtonRelease: button_release(&event.xbutton); break; case KeyPress: key_press(&event.xkey); break; case Expose: if (event.xexpose.count == 0) { draw_it(); if (!focused) { XSetInputFocus(display, main_window, RevertToPointerRoot, CurrentTime); if (XGrabKeyboard(display, main_window, True, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess ) fatal ("Cannot grab keyboard."); focused = 1; } } break; } } if (focused) { XUngrabKeyboard(display, CurrentTime); } switch (exiting) { case 1: *ppointer = 0; printf("%s\n", phrase); break; default: break; } } int main(int argc, char **argv) { open_display(); read_resources(); /* If we get just one argument, use it as the prompt. */ /* This must be called not before than read_resources() if we wish to override the default from the resources database. */ if (argc == 2) prompt = argv[1]; get_font(); compute_dimensions(); open_windows(); allocate_colors(); check_keyboard(); event_loop(); close_display(); return 0; }