From 5bf9955dd5524e9b564f4d520e863b2509a56f89 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 31 Mar 2003 11:21:07 +0000 Subject: [PATCH] Checkin of last night's work on GTK message boxes. Unix PuTTY now has proper GUI prompts for host keys etc, so it should now be usable without a controlling tty. git-svn-id: svn://svn.tartarus.org/sgt/putty@3028 cda61777-01e9-0310-a592-d414129be87e --- unix/gtkdlg.c | 302 ++++++++++++++++++++++++++++++++++++++++++--------------- unix/pterm.c | 11 +++ unix/unix.h | 3 + unix/uxputty.c | 223 +++++++++++++----------------------------- 4 files changed, 302 insertions(+), 237 deletions(-) diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 328e1d35..f5fe5d49 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,7 @@ #endif #include "putty.h" +#include "storage.h" #include "dialog.h" #include "tree234.h" @@ -147,6 +149,8 @@ static void dlg_init(struct dlgparam *dp) dp->byctrl = newtree234(uctrl_cmp_byctrl); dp->bywidget = newtree234(uctrl_cmp_bywidget); dp->coloursel_result.ok = FALSE; + dp->treeitems = NULL; + dp->window = dp->cancelbutton = dp->currtreeitem = NULL; } static void dlg_cleanup(struct dlgparam *dp) @@ -2117,108 +2121,246 @@ int do_config_box(const char *title, Config *cfg) return dp.retval; } -/* ====================================================================== - * Below here is a stub main program which allows the dialog box - * code to be compiled and tested with a minimal amount of the rest - * of PuTTY. - */ +static void messagebox_handler(union control *ctrl, void *dlg, + void *data, int event) +{ + if (event == EVENT_ACTION) + dlg_end(dlg, ctrl->generic.context.i); +} +int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) +{ + GtkWidget *window, *w0, *w1; + struct controlbox *ctrlbox; + struct controlset *s0, *s1; + union control *c; + struct dlgparam dp; + struct Shortcuts scs; + int index, ncols; + va_list ap; -#ifdef TESTMODE + dlg_init(&dp); -/* Compile command for testing: + for (index = 0; index < lenof(scs.sc); index++) { + scs.sc[index].action = SHORTCUT_EMPTY; + } -gcc -g -o gtkdlg gtk{dlg,cols,panel}.c ../{config,dialog,settings}.c \ -../{misc,tree234,be_none}.c ux{store,misc,print,cfg}.c \ --I. -I.. -I../charset -DTESTMODE `gtk-config --cflags --libs` + ctrlbox = ctrl_new_box(); - */ + ncols = 0; + va_start(ap, minwid); + while (va_arg(ap, char *) != NULL) { + ncols++; + (void) va_arg(ap, int); /* shortcut */ + (void) va_arg(ap, int); /* normal/default/cancel */ + (void) va_arg(ap, int); /* end value */ + } + va_end(ap); -void modalfatalbox(char *p, ...) -{ - va_list ap; - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); + s0 = ctrl_getset(ctrlbox, "", "", ""); + c = ctrl_columns(s0, 2, 50, 50); + c->columns.ncols = s0->ncolumns = ncols; + c->columns.percentages = sresize(c->columns.percentages, ncols, int); + for (index = 0; index < ncols; index++) + c->columns.percentages[index] = (index+1)*100/ncols - index*100/ncols; + va_start(ap, minwid); + index = 0; + while (1) { + char *title = va_arg(ap, char *); + int shortcut, type, value; + if (title == NULL) + break; + shortcut = va_arg(ap, int); + type = va_arg(ap, int); + value = va_arg(ap, int); + c = ctrl_pushbutton(s0, title, shortcut, HELPCTX(no_help), + messagebox_handler, I(value)); + c->generic.column = index++; + if (type > 0) + c->button.isdefault = TRUE; + else if (type < 0) + c->button.iscancel = TRUE; + } va_end(ap); - fputc('\n', stderr); - exit(1); -} -char *cp_name(int codepage) -{ - return (codepage == 123 ? "testing123" : - codepage == 234 ? "testing234" : - codepage == 345 ? "testing345" : - "unknown"); -} + s1 = ctrl_getset(ctrlbox, "x", "", ""); + ctrl_text(s1, msg, HELPCTX(no_help)); -char *cp_enumerate(int index) -{ - return (index == 0 ? "testing123" : - index == 1 ? "testing234" : - NULL); -} + window = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(window), title); + w0 = layout_ctrls(&dp, &scs, s0, 0, GTK_WINDOW(window)); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->action_area), + w0, TRUE, TRUE, 0); + gtk_widget_show(w0); + w1 = layout_ctrls(&dp, &scs, s1, 0, GTK_WINDOW(window)); + gtk_container_set_border_width(GTK_CONTAINER(w1), 10); + gtk_widget_set_usize(w1, minwid+20, -1); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), + w1, TRUE, TRUE, 0); + gtk_widget_show(w1); -int decode_codepage(char *cp_name) -{ - return (!strcmp(cp_name, "testing123") ? 123 : - !strcmp(cp_name, "testing234") ? 234 : - !strcmp(cp_name, "testing345") ? 345 : - -2); -} + dp.shortcuts = &scs; + dp.lastfocus = NULL; + dp.retval = 0; + dp.window = window; -struct printer_enum_tag { int dummy; } printer_test; + gtk_window_set_modal(GTK_WINDOW(window), TRUE); + if (parentwin) { + gint x, y, w, h, dx, dy; + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_NONE); + gdk_window_get_origin(parentwin->window, &x, &y); + gdk_window_get_size(parentwin->window, &w, &h); + dx = x + w/4; + dy = y + h/4; + gtk_widget_set_uposition(GTK_WIDGET(window), dx, dy); + gtk_window_set_transient_for(GTK_WINDOW(window), + GTK_WINDOW(parentwin)); + } else + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_widget_show(window); -printer_enum *printer_start_enum(int *nprinters_ptr) { - *nprinters_ptr = 2; - return &printer_test; -} -char *printer_get_name(printer_enum *pe, int i) { - return (i==0 ? "lpr" : i==1 ? "lpr -Pfoobar" : NULL); -} -void printer_finish_enum(printer_enum *pe) { } + gtk_signal_connect(GTK_OBJECT(window), "destroy", + GTK_SIGNAL_FUNC(window_destroy), NULL); + gtk_signal_connect(GTK_OBJECT(window), "key_press_event", + GTK_SIGNAL_FUNC(win_key_press), &dp); -char *platform_default_s(const char *name) -{ - return NULL; + gtk_main(); + + dlg_cleanup(&dp); + ctrl_free_box(ctrlbox); + + return dp.retval; } -int platform_default_i(const char *name, int def) -{ - return def; +static int string_width(char *text) +{ + GtkWidget *label = gtk_label_new(text); + GtkRequisition req; + gtk_widget_size_request(label, &req); + gtk_widget_unref(label); + return req.width; +} + +void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, + char *keystr, char *fingerprint) +{ + static const char absenttxt[] = + "The server's host key is not cached. You have no guarantee " + "that the server is the computer you think it is.\n" + "The server's key fingerprint is:\n" + "%s\n" + "If you trust this host, press \"Accept\" to add the key to " + "PuTTY's cache and carry on connecting.\n" + "If you want to carry on connecting just once, without " + "adding the key to the cache, press \"Connect Once\".\n" + "If you do not trust this host, press \"Cancel\" to abandon the " + "connection."; + static const char wrongtxt[] = + "WARNING - POTENTIAL SECURITY BREACH!\n" + "The server's host key does not match the one PuTTY has " + "cached. This means that either the server administrator " + "has changed the host key, or you have actually connected " + "to another computer pretending to be the server.\n" + "The new key fingerprint is:\n" + "%s\n" + "If you were expecting this change and trust the new key, " + "press \"Accept\" to update PuTTY's cache and continue connecting.\n" + "If you want to carry on connecting but without updating " + "the cache, press \"Connect Once\".\n" + "If you want to abandon the connection completely, press " + "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed " + "safe choice."; + char *text; + int ret; + + /* + * Verify the key. + */ + ret = verify_host_key(host, port, keytype, keystr); + + if (ret == 0) /* success - key matched OK */ + return; + + text = dupprintf((ret == 2 ? wrongtxt : absenttxt), fingerprint); + + ret = messagebox(GTK_WIDGET(get_window(frontend)), + "PuTTY Security Alert", text, + string_width(fingerprint), + "Accept", 'a', 0, 2, + "Connect Once", 'o', 0, 1, + "Cancel", 'c', -1, 0, + NULL); + + sfree(text); + + if (ret == 0) + cleanup_exit(0); + else if (ret == 2) + store_host_key(host, port, keytype, keystr); } -FontSpec platform_default_fontspec(const char *name) -{ - FontSpec ret; - if (!strcmp(name, "Font")) - strcpy(ret.name, "fixed"); - else - *ret.name = '\0'; - return ret; +/* + * Ask whether the selected cipher is acceptable (since it was + * below the configured 'warn' threshold). + * cs: 0 = both ways, 1 = client->server, 2 = server->client + */ +void askcipher(void *frontend, char *ciphername, int cs) +{ + static const char msg[] = + "The first %scipher supported by the server is " + "%s, which is below the configured warning threshold.\n" + "Continue with connection?"; + char *text; + int ret; + + text = dupprintf(msg, (cs == 0) ? "" : + (cs == 1) ? "client-to-server " : "server-to-client ", + ciphername); + ret = messagebox(GTK_WIDGET(get_window(frontend)), + "PuTTY Security Alert", text, + string_width("Continue with connection?"), + "Yes", 'y', 0, 1, + "No", 'n', 0, 0, + NULL); + sfree(text); + + if (ret) { + return; + } else { + cleanup_exit(0); + } } -Filename platform_default_filename(const char *name) +void old_keyfile_warning(void) { - Filename ret; - if (!strcmp(name, "LogFileName")) - strcpy(ret.path, "putty.log"); - else - *ret.path = '\0'; - return ret; + /* + * This should never happen on Unix. We hope. + */ } -char *x_get_default(const char *key) +void fatalbox(char *p, ...) { - return NULL; + va_list ap; + char *msg; + va_start(ap, p); + msg = dupvprintf(p, ap); + va_end(ap); + messagebox(NULL, "PuTTY Fatal Error", msg, + string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), + "OK", 'o', 1, 1, NULL); + sfree(msg); + cleanup_exit(1); } - -int main(int argc, char **argv) +void connection_fatal(void *frontend, char *p, ...) { - Config cfg; - gtk_init(&argc, &argv); - printf("returned %d\n", do_config_box("PuTTY Configuration", &cfg)); - return 0; + va_list ap; + char *msg; + va_start(ap, p); + msg = dupvprintf(p, ap); + va_end(ap); + messagebox(GTK_WIDGET(get_window(frontend)), + "PuTTY Fatal Error", msg, + string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), + "OK", 'o', 1, 1, NULL); + sfree(msg); + cleanup_exit(1); } - -#endif diff --git a/unix/pterm.c b/unix/pterm.c index 3eafe14a..122605e3 100644 --- a/unix/pterm.c +++ b/unix/pterm.c @@ -184,6 +184,17 @@ static Mouse_Button translate_button(Mouse_Button button) } /* + * Return the top-level GtkWindow associated with a particular + * front end instance. + */ +void *get_window(void *frontend) +{ + Terminal *term = (Terminal *)frontend; + struct gui_data *inst = (struct gui_data *)term->frontend; + return inst->window; +} + +/* * Minimise or restore the window in response to a server-side * request. */ diff --git a/unix/unix.h b/unix/unix.h index 16156c93..63d7ff02 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -55,6 +55,9 @@ char *get_x_display(void *frontend); int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */ long get_windowid(void *frontend); +/* Things gtkdlg.c needs from pterm.c */ +void *get_window(void *frontend); /* void * to avoid depending on gtk.h */ + /* Things uxstore.c needs from pterm.c */ char *x_get_default(const char *key); diff --git a/unix/uxputty.c b/unix/uxputty.c index e6856086..5b7c580a 100644 --- a/unix/uxputty.c +++ b/unix/uxputty.c @@ -3,40 +3,81 @@ */ #include +#include #include -#include #include #include "putty.h" #include "storage.h" /* - * FIXME: At least some of these functions should be replaced with - * GTK GUI error-box-type things. + * TODO: * - * In particular, all the termios-type things must go, and - * termios.h should disappear from the above #include list. + * - Arrange for the window title not to be `pterm'. + * + * - Fix command-line parsing to be more PuTTYlike and not so + * ptermy - in particular non-option arguments should be + * hostname and port in the obvious way. + * + * - Session loading and saving; current thinking says the best + * way is to have a subdir .putty/sessions containing files + * whose names are actually munged saved session names. + * + * - libcharset enumeration. + * + * - fix the printer enum (I think the sensible thing is simply to + * have uxcfg.c remove the drop-down list completely, since you + * can't sensibly provide an enumerated list of lpr commands!). + * + * - Ctrl+right-click for a context menu (also in Windows for + * consistency, I think). This should contain pretty much + * everything in the Windows PuTTY menu, and a subset of that in + * pterm: + * + * - Telnet special commands (not in pterm :-) + * + * - Event Log (this means we must implement the Event Log; not + * in pterm) + * + * - New Session and Duplicate Session (perhaps in pterm, in fact?!) + * + Duplicate Session will be fun, since we must work out + * how to pass the config data through. + * + In fact this should be easier on Unix, since fork() is + * available so we need not even exec (this also saves us + * the trouble of scrabbling around trying to find our own + * binary). Possible scenario: respond to Duplicate + * Session by forking. Parent continues as before; child + * unceremoniously frees all extant resources (backend, + * terminal, ldisc, frontend etc) and then _longjmps_ (I + * kid you not) back to a point in pt_main() which causes + * it to go back round to the point of opening a new + * terminal window and a new backend. + * + A tricky bit here is how to free everything without + * also _destroying_ things - calling GTK to free up + * existing widgets is liable to send destroy messages to + * the X server, which won't go down too well with the + * parent process. exec() is a much cleaner solution to + * this bit, but requires us to invent some ghastly IPC as + * we did in Windows PuTTY. + * + Arrgh! Also, this won't work in pterm since we'll + * already have dropped privileges by this point, so we + * can't get another pty. Sigh. Looks like exec has to be + * the way forward then :-/ + * + * - Saved Sessions submenu (not in pterm of course) + * + * - Change Settings + * + we must also implement mid-session reconfig in pterm.c. + * + note this also requires config.c and uxcfg.c to be able + * to get hold of the application name. + * + * - Copy All to Clipboard (for what that's worth) + * + * - Clear Scrollback and Reset Terminal + * + * - About (and uxcfg.c must also supply the about box) */ -void fatalbox(char *p, ...) -{ - va_list ap; - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); - cleanup_exit(1); -} -void connection_fatal(void *frontend, char *p, ...) -{ - va_list ap; - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); - cleanup_exit(1); -} + void cmdline_error(char *p, ...) { va_list ap; @@ -61,138 +102,6 @@ void cleanup_exit(int code) exit(code); } -void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, - char *keystr, char *fingerprint) -{ - int ret; - - static const char absentmsg[] = - "The server's host key is not cached. You have no guarantee\n" - "that the server is the computer you think it is.\n" - "The server's key fingerprint is:\n" - "%s\n" - "If you trust this host, enter \"y\" to add the key to\n" - "PuTTY's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, enter \"n\".\n" - "If you do not trust this host, press Return to abandon the\n" - "connection.\n" - "Store key in cache? (y/n) "; - - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached. This means that either the server administrator\n" - "has changed the host key, or you have actually connected\n" - "to another computer pretending to be the server.\n" - "The new key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "enter \"y\" to update PuTTY's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, enter \"n\".\n" - "If you want to abandon the connection completely, press\n" - "Return to cancel. Pressing Return is the ONLY guaranteed\n" - "safe choice.\n" - "Update cached key? (y/n, Return cancels connection) "; - - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - /* - * Verify the key. - */ - ret = verify_host_key(host, port, keytype, keystr); - - if (ret == 0) /* success - key matched OK */ - return; - - if (ret == 2) { /* key was different */ - fprintf(stderr, wrongmsg, fingerprint); - fflush(stderr); - } - if (ret == 1) { /* key was absent */ - fprintf(stderr, absentmsg, fingerprint); - fflush(stderr); - } - - { - struct termios oldmode, newmode; - tcgetattr(0, &oldmode); - newmode = oldmode; - newmode.c_lflag |= ECHO | ISIG | ICANON; - tcsetattr(0, TCSANOW, &newmode); - line[0] = '\0'; - read(0, line, sizeof(line) - 1); - tcsetattr(0, TCSANOW, &oldmode); - } - - if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') { - if (line[0] == 'y' || line[0] == 'Y') - store_host_key(host, port, keytype, keystr); - } else { - fprintf(stderr, abandoned); - cleanup_exit(0); - } -} - -/* - * Ask whether the selected cipher is acceptable (since it was - * below the configured 'warn' threshold). - * cs: 0 = both ways, 1 = client->server, 2 = server->client - */ -void askcipher(void *frontend, char *ciphername, int cs) -{ - static const char msg[] = - "The first %scipher supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Continue with connection? (y/n) "; - static const char abandoned[] = "Connection abandoned.\n"; - - char line[32]; - - fprintf(stderr, msg, - (cs == 0) ? "" : - (cs == 1) ? "client-to-server " : "server-to-client ", - ciphername); - fflush(stderr); - - { - struct termios oldmode, newmode; - tcgetattr(0, &oldmode); - newmode = oldmode; - newmode.c_lflag |= ECHO | ISIG | ICANON; - tcsetattr(0, TCSANOW, &newmode); - line[0] = '\0'; - read(0, line, sizeof(line) - 1); - tcsetattr(0, TCSANOW, &oldmode); - } - - if (line[0] == 'y' || line[0] == 'Y') { - return; - } else { - fprintf(stderr, abandoned); - cleanup_exit(0); - } -} - -void old_keyfile_warning(void) -{ - static const char message[] = - "You are loading an SSH 2 private key which has an\n" - "old version of the file format. This means your key\n" - "file is not fully tamperproof. Future versions of\n" - "PuTTY may stop supporting this private key format,\n" - "so we recommend you convert your key to the new\n" - "format.\n" - "\n" - "Once the key is loaded into PuTTYgen, you can perform\n" - "this conversion simply by saving it again.\n"; - - fputs(message, stderr); -} - /* * Another bunch of temporary stub functions. These ones will want * removing by means of implementing them properly: libcharset -- 2.11.0