Moved the environment variables config block out of the Telnet panel
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 16 Oct 2004 10:56:54 +0000 (10:56 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 16 Oct 2004 10:56:54 +0000 (10:56 +0000)
into the Connection panel, and implemented support for the SSH2
"env" request. (I haven't yet found a server which accepts this
request, so although I've visually checked the packet log and it
looks OK, I haven't yet been able to do a full end-to-end test.)
Also, the `pty' backend reads this data and does a series of
`putenv' commands before launching the shell or application.

This is mostly because in last week's UTF-8 faffings I got
thoroughly sick of typing `export LANG=en_GB.UTF-8' every time I
started a new testing pterm, and it suddenly occurred to me that
this would be precisely the sort of thing you'd want to have pterm
set up for you, particularly since you can configure it alongside
the translation settings and so you can ensure they match up
properly.

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

config.c
doc/config.but
ssh.c
unix/pty.c

index 8588b23..7898c7a 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1288,6 +1288,36 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                         HELPCTX(connection_username),
                         dlg_stdeditbox_handler, I(offsetof(Config,username)),
                         I(sizeof(((Config *)0)->username)));
+
+           ctrl_text(s, "Environment variables:", HELPCTX(telnet_environ));
+           ctrl_columns(s, 2, 80, 20);
+           ed = (struct environ_data *)
+               ctrl_alloc(b, sizeof(struct environ_data));
+           ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
+                                     HELPCTX(telnet_environ),
+                                     environ_handler, P(ed), P(NULL));
+           ed->varbox->generic.column = 0;
+           ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
+                                     HELPCTX(telnet_environ),
+                                     environ_handler, P(ed), P(NULL));
+           ed->valbox->generic.column = 0;
+           ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
+                                           HELPCTX(telnet_environ),
+                                           environ_handler, P(ed));
+           ed->addbutton->generic.column = 1;
+           ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
+                                           HELPCTX(telnet_environ),
+                                           environ_handler, P(ed));
+           ed->rembutton->generic.column = 1;
+           ctrl_columns(s, 1, 100);
+           ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
+                                      HELPCTX(telnet_environ),
+                                      environ_handler, P(ed));
+           ed->listbox->listbox.height = 3;
+           ed->listbox->listbox.ncols = 2;
+           ed->listbox->listbox.percentages = snewn(2, int);
+           ed->listbox->listbox.percentages[0] = 30;
+           ed->listbox->listbox.percentages[1] = 70;
        }
 
        s = ctrl_getset(b, "Connection", "keepalive",
@@ -1389,40 +1419,6 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
        ctrl_settitle(b, "Connection/Telnet",
                      "Options controlling Telnet connections");
 
-       if (!midsession) {
-           s = ctrl_getset(b, "Connection/Telnet", "data",
-                           "Data to send to the server");
-           ctrl_text(s, "Environment variables:", HELPCTX(telnet_environ));
-           ctrl_columns(s, 2, 80, 20);
-           ed = (struct environ_data *)
-               ctrl_alloc(b, sizeof(struct environ_data));
-           ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
-                                     HELPCTX(telnet_environ),
-                                     environ_handler, P(ed), P(NULL));
-           ed->varbox->generic.column = 0;
-           ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
-                                     HELPCTX(telnet_environ),
-                                     environ_handler, P(ed), P(NULL));
-           ed->valbox->generic.column = 0;
-           ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
-                                           HELPCTX(telnet_environ),
-                                           environ_handler, P(ed));
-           ed->addbutton->generic.column = 1;
-           ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
-                                           HELPCTX(telnet_environ),
-                                           environ_handler, P(ed));
-           ed->rembutton->generic.column = 1;
-           ctrl_columns(s, 1, 100);
-           ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
-                                      HELPCTX(telnet_environ),
-                                      environ_handler, P(ed));
-           ed->listbox->listbox.height = 3;
-           ed->listbox->listbox.ncols = 2;
-           ed->listbox->listbox.percentages = snewn(2, int);
-           ed->listbox->listbox.percentages[0] = 30;
-           ed->listbox->listbox.percentages[1] = 70;
-       }
-
        s = ctrl_getset(b, "Connection/Telnet", "protocol",
                        "Telnet protocol adjustments");
 
index 92b479b..77d9a09 100644 (file)
@@ -1,4 +1,4 @@
-\versionid $Id: config.but,v 1.93 2004/10/13 13:43:11 simon Exp $
+\versionid $Id: config.but,v 1.94 2004/10/16 10:56:54 simon Exp $
 
 \C{config} Configuring PuTTY
 
@@ -1502,6 +1502,30 @@ it explicitly every time. (Some Telnet servers don't support this.)
 
 In this box you can type that user name.
 
+\S{config-environ} Setting environment variables on the server
+
+\cfg{winhelp-topic}{telnet.environ}
+
+The Telnet protocol provides a means for the client to pass
+environment variables to the server. Many Telnet servers have
+stopped supporting this feature due to security flaws, but PuTTY
+still supports it for the benefit of any servers which have found
+other ways around the security problems than just disabling the
+whole mechanism.
+
+Version 2 of the SSH protocol also provides a similar mechanism,
+which is easier to implement without security flaws. Newer SSH2
+servers are more likely to support it than older ones.
+
+This configuration data is not used in the SSHv1, rlogin or raw
+protocols.
+
+To add an environment variable to the list transmitted down the
+connection, you enter the variable name in the \q{Variable} box,
+enter its value in the \q{Value} box, and press the \q{Add} button.
+To remove one from the list, select it in the list box and press
+\q{Remove}.
+
 \S{config-keepalive} Using keepalives to prevent disconnection
 
 \cfg{winhelp-topic}{connection.keepalive}
@@ -1768,23 +1792,6 @@ configuration fields will be ignored.
 The Telnet panel allows you to configure options that only apply to
 Telnet sessions.
 
-\S{config-environ} Setting environment variables on the server
-
-\cfg{winhelp-topic}{telnet.environ}
-
-The Telnet protocol provides a means for the client to pass
-environment variables to the server. Many Telnet servers have
-stopped supporting this feature due to security flaws, but PuTTY
-still supports it for the benefit of any servers which have found
-other ways around the security problems than just disabling the
-whole mechanism.
-
-To add an environment variable to the list transmitted down the
-connection, you enter the variable name in the \q{Variable} box,
-enter its value in the \q{Value} box, and press the \q{Add} button.
-To remove one from the list, select it in the list box and press
-\q{Remove}.
-
 \S{config-oldenviron} \q{Handling of OLD_ENVIRON ambiguity}
 
 \cfg{winhelp-topic}{telnet.oldenviron}
diff --git a/ssh.c b/ssh.c
index 8356a07..69ff174 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -4788,6 +4788,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
        int siglen, retlen, len;
        char *q, *agentreq, *ret;
        int try_send;
+       int num_env, env_left, env_ok;
     };
     crState(do_ssh2_authconn_state);
 
@@ -5954,6 +5955,82 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
     }
 
     /*
+     * Send environment variables.
+     * 
+     * Simplest thing here is to send all the requests at once, and
+     * then wait for a whole bunch of successes or failures.
+     */
+    if (ssh->mainchan && *ssh->cfg.environmt) {
+       char *e = ssh->cfg.environmt;
+       char *var, *varend, *val;
+
+       s->num_env = 0;
+
+       while (*e) {
+           var = e;
+           while (*e && *e != '\t') e++;
+           varend = e;
+           if (*e == '\t') e++;
+           val = e;
+           while (*e) e++;
+           e++;
+
+           ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+           ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
+           ssh2_pkt_addstring(ssh, "env");
+           ssh2_pkt_addbool(ssh, 1);          /* want reply */
+           ssh2_pkt_addstring_start(ssh);
+           ssh2_pkt_addstring_data(ssh, var, varend-var);
+           ssh2_pkt_addstring(ssh, val);
+           ssh2_pkt_send(ssh);
+
+           s->num_env++;
+       }
+
+       logeventf(ssh, "Sent %d environment variables", s->num_env);
+
+       s->env_ok = 0;
+       s->env_left = s->num_env;
+
+       while (s->env_left > 0) {
+           do {
+               crWaitUntilV(ispkt);
+               if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+                   unsigned i = ssh_pkt_getuint32(ssh);
+                   struct ssh_channel *c;
+                   c = find234(ssh->channels, &i, ssh_channelfind);
+                   if (!c)
+                       continue;              /* nonexistent channel */
+                   c->v.v2.remwindow += ssh_pkt_getuint32(ssh);
+               }
+           } while (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+
+           if (ssh->pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+               if (ssh->pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+                   bombout(("Unexpected response to environment request:"
+                            " packet type %d", ssh->pktin.type));
+                   crStopV;
+               }
+           } else {
+               s->env_ok++;
+           }
+
+           s->env_left--;
+       }
+
+       if (s->env_ok == s->num_env) {
+           logevent("All environment variables successfully set");
+       } else if (s->env_ok == 0) {
+           logevent("All environment variables refused");
+           c_write_str(ssh, "Server refused to set environment variables\r\n");
+       } else {
+           logeventf(ssh, "%d environment variables refused",
+                     s->num_env - s->env_ok);
+           c_write_str(ssh, "Server refused to set all environment variables\r\n");
+       }
+    }
+
+    /*
      * Start a shell or a remote command. We may have to attempt
      * this twice if the config data has provided a second choice
      * of command.
index 0be58fb..61b9d03 100644 (file)
@@ -590,6 +590,29 @@ static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
            sprintf(windowid_env_var, "WINDOWID=%ld", windowid);
            putenv(windowid_env_var);
        }
+       {
+           char *e = cfg->environmt;
+           char *var, *varend, *val, *varval;
+           while (*e) {
+               var = e;
+               while (*e && *e != '\t') e++;
+               varend = e;
+               if (*e == '\t') e++;
+               val = e;
+               while (*e) e++;
+               e++;
+
+               varval = dupprintf("%.*s=%s", varend-var, var, val);
+               putenv(varval);
+               /*
+                * We must not free varval, since putenv links it
+                * into the environment _in place_. Weird, but
+                * there we go. Memory usage will be rationalised
+                * as soon as we exec anyway.
+                */
+           }
+       }
+
        /*
         * SIGINT and SIGQUIT may have been set to ignored by our
         * parent, particularly by things like sh -c 'pterm &' and