Having created and used uxsel, it actually turns out to be
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 29 Mar 2003 19:52:50 +0000 (19:52 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 29 Mar 2003 19:52:50 +0000 (19:52 +0000)
practically trivial to put all the pieces together and create a
working prototype of Unix PuTTY! It's missing a lot of things -
notably GUI request boxes for host keys and logfiles and so forth,
the Event Log, mid-session reconfiguration, session loading and
saving, sensible population of the character sets drop-down list and
probably other fiddly little things too - but it will put up a
config box and then create a GUI window containing an SSH connection
to the host you specified, so it's _basically_ there. Woo!

git-svn-id: svn://svn.tartarus.org/sgt/putty@3020 cda61777-01e9-0310-a592-d414129be87e

Recipe
unix/gtkdlg.c
unix/pterm.c
unix/ptermm.c [new file with mode: 0644]
unix/pty.c
unix/uxputty.c [new file with mode: 0644]

diff --git a/Recipe b/Recipe
index ad04c4f..41710dc 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -97,6 +97,9 @@
 GUITERM  = window windlg winctrls terminal sizetip wcwidth unicode ldiscucs
          + logging printing winutils dialog config wincfg tree234
 
+# Config box on Unix.
+UXCFG    = config uxcfg dialog gtkdlg gtkcols gtkpanel
+
 # Non-SSH back ends (putty, puttytel, plink).
 NONSSH   = telnet raw rlogin ldisc
 
@@ -151,6 +154,10 @@ puttygen : [G] puttygen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
 
 pterm    : [X] pterm terminal wcwidth uxucs uxmisc tree234 misc ldisc ldiscucs
          + logging uxprint settings pty uxsel be_none uxstore signal CHARSET
+        + ptermm
+putty    : [X] pterm terminal wcwidth uxucs uxmisc tree234 misc ldisc ldiscucs
+         + logging uxprint settings pty uxsel be_all uxstore signal CHARSET
+        + uxputty NONSSH UXSSH UXMISC logging ux_x11 UXCFG
 
 plink    : [U] uxplink uxcons NONSSH UXSSH be_all logging UXMISC signal ux_x11
 
index 4afabde..328e1d3 100644 (file)
@@ -716,7 +716,7 @@ void dlg_end(void *dlg, int value)
 {
     struct dlgparam *dp = (struct dlgparam *)dlg;
     dp->retval = value;
-    gtk_main_quit();
+    gtk_widget_destroy(dp->window);
 }
 
 void dlg_refresh(union control *ctrl, void *dlg)
@@ -910,7 +910,6 @@ static int listitem_key(GtkWidget *item, GdkEventKey *event, gpointer data,
                  event->keyval==GDK_Page_Up || event->keyval==GDK_KP_Page_Up)
                 ? 2 : 1;
             int i, n;
-            GtkWidget *thisitem;
             GList *children, *chead;
 
             chead = children = gtk_container_children(GTK_CONTAINER(list));
@@ -964,13 +963,13 @@ static int listitem_key(GtkWidget *item, GdkEventKey *event, gpointer data,
 static int listitem_single_key(GtkWidget *item, GdkEventKey *event,
                                gpointer data)
 {
-    listitem_key(item, event, data, FALSE);
+    return listitem_key(item, event, data, FALSE);
 }
 
 static int listitem_multi_key(GtkWidget *item, GdkEventKey *event,
                                  gpointer data)
 {
-    listitem_key(item, event, data, TRUE);
+    return listitem_key(item, event, data, TRUE);
 }
 
 static int listitem_button(GtkWidget *item, GdkEventButton *event,
@@ -1043,7 +1042,7 @@ static void draglist_down(GtkButton *button, gpointer data)
 
 static void filesel_ok(GtkButton *button, gpointer data)
 {
-    struct dlgparam *dp = (struct dlgparam *)data;
+    /* struct dlgparam *dp = (struct dlgparam *)data; */
     gpointer filesel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
     struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(filesel), "user-data");
     char *name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
@@ -1052,7 +1051,7 @@ static void filesel_ok(GtkButton *button, gpointer data)
 
 static void fontsel_ok(GtkButton *button, gpointer data)
 {
-    struct dlgparam *dp = (struct dlgparam *)data;
+    /* struct dlgparam *dp = (struct dlgparam *)data; */
     gpointer fontsel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
     struct uctrl *uc = gtk_object_get_data(GTK_OBJECT(fontsel), "user-data");
     char *name = gtk_font_selection_dialog_get_font_name
@@ -1808,7 +1807,7 @@ int tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
                {
                    GtkWidget *w = dp->treeitems[i];
                    int vis = TRUE;
-                   while (w && GTK_IS_TREE_ITEM(w) || GTK_IS_TREE(w)) {
+                   while (w && (GTK_IS_TREE_ITEM(w) || GTK_IS_TREE(w))) {
                        if (!GTK_WIDGET_VISIBLE(w)) {
                            vis = FALSE;
                            break;
@@ -1825,7 +1824,6 @@ int tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
         gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
                                      "key_press_event");
         if (j >= 0) {
-            gint return_val;
             gtk_signal_emit_by_name(GTK_OBJECT(dp->treeitems[j]), "toggle");
             gtk_widget_grab_focus(dp->treeitems[j]);
         }
@@ -1892,7 +1890,7 @@ void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
        }
 }
 
-int do_config_box(const char *title)
+int do_config_box(const char *title, Config *cfg)
 {
     GtkWidget *window, *hbox, *vbox, *cols, *label,
        *tree, *treescroll, *panels, *panelvbox;
@@ -1901,7 +1899,6 @@ int do_config_box(const char *title)
     char *path;
     GtkTreeItem *treeitemlevels[8];
     GtkTree *treelevels[8];
-    Config cfg;
     struct dlgparam dp;
     struct sesslist sl;
     struct Shortcuts scs;
@@ -1909,7 +1906,7 @@ int do_config_box(const char *title)
     struct selparam *selparams = NULL;
     int nselparams = 0, selparamsize = 0;
 
-    do_defaults(NULL, &cfg);
+    do_defaults(NULL, cfg);
 
     dlg_init(&dp);
 
@@ -2072,7 +2069,7 @@ int do_config_box(const char *title)
         dp.treeitems[index] = selparams[index].treeitem;
     }
 
-    dp.data = &cfg;
+    dp.data = cfg;
     dlg_refresh(NULL, &dp);
 
     dp.shortcuts = &selparams[0].shortcuts;
@@ -2218,8 +2215,9 @@ char *x_get_default(const char *key)
 
 int main(int argc, char **argv)
 {
+    Config cfg;
     gtk_init(&argc, &argv);
-    printf("returned %d\n", do_config_box("PuTTY Configuration"));
+    printf("returned %d\n", do_config_box("PuTTY Configuration", &cfg));
     return 0;
 }
 
index d04b9e3..3eafe14 100644 (file)
@@ -1948,6 +1948,7 @@ int do_cmdline(int argc, char **argv, int do_everything, Config *cfg)
 {
     int err = 0;
     extern char **pty_argv;           /* declared in pty.c */
+    extern int use_pty_argv;
 
     /*
      * Macros to make argument handling easier. Note that because
@@ -2054,7 +2055,7 @@ int do_cmdline(int argc, char **argv, int do_everything, Config *cfg)
                cfg->colours[index][2] = col.blue / 256;
            }
 
-       } else if (!strcmp(p, "-e")) {
+       } else if (use_pty_argv && !strcmp(p, "-e")) {
            /* This option swallows all further arguments. */
            if (!do_everything)
                break;
@@ -2219,9 +2220,10 @@ void uxsel_input_remove(int id) {
     gdk_input_remove(id);
 }
 
-int main(int argc, char **argv)
+int pt_main(int argc, char **argv)
 {
-    extern void pty_pre_init(void);    /* declared in pty.c */
+    extern Backend *select_backend(Config *cfg);
+    extern int cfgbox(Config *cfg);
     struct gui_data *inst;
     int font_charset;
 
@@ -2229,8 +2231,6 @@ int main(int argc, char **argv)
      * it */
     block_signal(SIGCHLD, 1);
 
-    pty_pre_init();
-
     gtk_init(&argc, &argv);
 
     /*
@@ -2246,6 +2246,9 @@ int main(int argc, char **argv)
     if (do_cmdline(argc, argv, 1, &inst->cfg))
        exit(1);                       /* post-defaults, do everything */
 
+    if (!cfgbox(&inst->cfg))
+       exit(0);                       /* config box hit Cancel */
+
     inst->fonts[0] = gdk_font_load(inst->cfg.font.name);
     if (!inst->fonts[0]) {
        fprintf(stderr, "pterm: unable to load font \"%s\"\n",
@@ -2397,9 +2400,13 @@ int main(int argc, char **argv)
 
     uxsel_init();
 
-    inst->back = &pty_backend;
-    inst->back->init((void *)inst->term, &inst->backhandle, &inst->cfg,
-                    NULL, 0, NULL, 0);
+    inst->back = select_backend(&inst->cfg);
+    {
+       char *realhost;                /* FIXME: don't ignore this! */
+       inst->back->init((void *)inst->term, &inst->backhandle, &inst->cfg,
+                        inst->cfg.host, inst->cfg.port, &realhost,
+                        inst->cfg.tcp_nodelay);
+    }
     inst->back->provide_logctx(inst->backhandle, inst->logctx);
 
     term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
diff --git a/unix/ptermm.c b/unix/ptermm.c
new file mode 100644 (file)
index 0000000..4765c54
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * pterm main program.
+ */
+
+#include <stdio.h>
+
+#include "putty.h"
+
+Backend *select_backend(Config *cfg)
+{
+    return &pty_backend;
+}
+
+int cfgbox(Config *cfg)
+{
+    return 1;                         /* no-op in pterm */
+}
+
+int main(int argc, char **argv)
+{
+    extern int pt_main(int argc, char **argv);
+    extern void pty_pre_init(void);    /* declared in pty.c */
+
+    pty_pre_init();
+
+    return pt_main(argc, argv);
+}
index eed8ecf..1c9763a 100644 (file)
@@ -84,6 +84,7 @@ static int pty_exit_code;
 static struct utmp utmp_entry;
 #endif
 char **pty_argv;
+int use_pty_argv = TRUE;
 
 static void pty_close(void);
 
diff --git a/unix/uxputty.c b/unix/uxputty.c
new file mode 100644 (file)
index 0000000..e685608
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Unix PuTTY main program.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "putty.h"
+#include "storage.h"
+
+/*
+ * FIXME: At least some of these functions should be replaced with
+ * GTK GUI error-box-type things.
+ * 
+ * In particular, all the termios-type things must go, and
+ * termios.h should disappear from the above #include list.
+ */
+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;
+    fprintf(stderr, "plink: ");
+    va_start(ap, p);
+    vfprintf(stderr, p, ap);
+    va_end(ap);
+    fputc('\n', stderr);
+    exit(1);
+}
+
+/*
+ * Clean up and exit.
+ */
+void cleanup_exit(int code)
+{
+    /*
+     * Clean up.
+     */
+    sk_cleanup();
+    random_save_seed();
+    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
+ * should invent its own sensible format for codepage names and a
+ * means of enumerating them, and printer_enum needs to be dealt
+ * with somehow or other too.
+ */
+
+char *cp_name(int codepage)
+{
+    return "";
+}
+char *cp_enumerate(int index)
+{
+    return NULL;
+}
+int decode_codepage(char *cp_name)
+{
+    return -2;
+}
+
+printer_enum *printer_start_enum(int *nprinters_ptr) {
+    *nprinters_ptr = 0;
+    return NULL;
+}
+char *printer_get_name(printer_enum *pe, int i) { return NULL;
+}
+void printer_finish_enum(printer_enum *pe) { }
+
+Backend *select_backend(Config *cfg)
+{
+    int i;
+    Backend *back = NULL;
+    for (i = 0; backends[i].backend != NULL; i++)
+       if (backends[i].protocol == cfg->protocol) {
+           back = backends[i].backend;
+           break;
+       }
+    assert(back != NULL);
+    return back;
+}
+
+int cfgbox(Config *cfg)
+{
+    extern int do_config_box(const char *title, Config *cfg);
+    return do_config_box("PuTTY Configuration", cfg);
+}
+
+int main(int argc, char **argv)
+{
+    extern int pt_main(int argc, char **argv);
+    sk_init();
+    flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
+    default_protocol = be_default_protocol;
+    /* Find the appropriate default port. */
+    {
+       int i;
+       default_port = 0; /* illegal */
+       for (i = 0; backends[i].backend != NULL; i++)
+           if (backends[i].protocol == default_protocol) {
+               default_port = backends[i].backend->default_port;
+               break;
+           }
+    }
+    return pt_main(argc, argv);
+}