+ dlg_init(&dp);
+
+ for (index = 0; index < lenof(scs.sc); index++) {
+ scs.sc[index].action = SHORTCUT_EMPTY;
+ }
+
+ ctrlbox = ctrl_new_box();
+
+ /*
+ * Preliminary pass over the va_list, to count up the number of
+ * buttons and find out what kinds there are.
+ */
+ ncols = 0;
+ va_start(ap, selectable);
+ min_type = +1;
+ while (va_arg(ap, char *) != NULL) {
+ int type;
+
+ (void) va_arg(ap, int); /* shortcut */
+ type = va_arg(ap, int); /* normal/default/cancel */
+ (void) va_arg(ap, int); /* end value */
+
+ ncols++;
+ if (min_type > type)
+ min_type = type;
+ }
+ va_end(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, selectable);
+ 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;
+
+ /* We always arrange that _some_ button is labelled as
+ * 'iscancel', so that pressing Escape will always cause
+ * win_key_press to do something. The button we choose is
+ * whichever has the smallest type value: this means that real
+ * cancel buttons (labelled -1) will be picked if one is
+ * there, or in cases where the options are yes/no (1,0) then
+ * no will be picked, and if there's only one option (a box
+ * that really is just showing a _message_ and not even asking
+ * a question) then that will be picked. */
+ if (type == min_type)
+ c->button.iscancel = TRUE;
+ }
+ va_end(ap);
+
+ s1 = ctrl_getset(ctrlbox, "x", "", "");
+ textctrl = ctrl_text(s1, msg, HELPCTX(no_help));
+
+ window = our_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(window), title);
+ w0 = layout_ctrls(&dp, &scs, s0, GTK_WINDOW(window));
+ our_dialog_set_action_area(GTK_WINDOW(window), w0);
+ gtk_widget_show(w0);
+ w1 = layout_ctrls(&dp, &scs, s1, GTK_WINDOW(window));
+ gtk_container_set_border_width(GTK_CONTAINER(w1), 10);
+ gtk_widget_set_size_request(w1, minwid+20, -1);
+ our_dialog_add_to_content_area(GTK_WINDOW(window), w1, TRUE, TRUE, 0);
+ gtk_widget_show(w1);
+
+ dp.shortcuts = &scs;
+ dp.lastfocus = NULL;
+ dp.retval = 0;
+ dp.window = window;
+
+ if (selectable) {
+#if GTK_CHECK_VERSION(2,0,0)
+ struct uctrl *uc = dlg_find_byctrl(&dp, textctrl);
+ gtk_label_set_selectable(GTK_LABEL(uc->text), TRUE);
+
+ /*
+ * GTK selectable labels have a habit of selecting their
+ * entire contents when they gain focus. It's ugly to have
+ * text in a message box start up all selected, so we suppress
+ * this by manually selecting none of it - but we must do this
+ * when the widget _already has_ focus, otherwise our work
+ * will be undone when it gains it shortly.
+ */
+ gtk_widget_grab_focus(uc->text);
+ gtk_label_select_region(GTK_LABEL(uc->text), 0, 0);
+#else
+ (void)textctrl; /* placate warning */
+#endif
+ }
+
+ gtk_window_set_modal(GTK_WINDOW(window), TRUE);
+ if (parentwin) {
+ set_transient_window_pos(parentwin, window);
+ gtk_window_set_transient_for(GTK_WINDOW(window),
+ GTK_WINDOW(parentwin));
+ } else
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+ gtk_container_set_focus_child(GTK_CONTAINER(window), NULL);
+ gtk_widget_show(window);
+ gtk_window_set_focus(GTK_WINDOW(window), NULL);
+
+ g_signal_connect(G_OBJECT(window), "destroy",
+ G_CALLBACK(window_destroy), NULL);
+ g_signal_connect(G_OBJECT(window), "key_press_event",
+ G_CALLBACK(win_key_press), &dp);
+
+ gtk_main();
+ post_main();
+
+ dlg_cleanup(&dp);
+ ctrl_free_box(ctrlbox);
+
+ return dp.retval;
+}
+
+int reallyclose(void *frontend)
+{
+ char *title = dupcat(appname, " Exit Confirmation", NULL);
+ int ret = messagebox(GTK_WIDGET(get_window(frontend)),
+ title, "Are you sure you want to close this session?",
+ string_width("Most of the width of the above text"),
+ FALSE,
+ "Yes", 'y', +1, 1,
+ "No", 'n', -1, 0,
+ NULL);
+ sfree(title);
+ return ret;
+}
+
+int verify_ssh_host_key(void *frontend, char *host, int port,
+ const char *keytype, char *keystr, char *fingerprint,
+ void (*callback)(void *ctx, int result), void *ctx)
+{
+ 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 %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 %s 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;