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",
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");
-\versionid $Id: config.but,v 1.92.2.1 2004/10/24 13:21:11 simon Exp $
+\versionid $Id: config.but,v 1.92.2.2 2004/10/24 13:29:02 simon Exp $
\C{config} Configuring PuTTY
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}
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}
\dd Remote command is SSH subsystem (SSH-2 only).
+\dt \cw{-N}
+
+\dd Don't start a remote command or shell at all (SSH-2 only).
+
\S{plink-manpage-more-information} MORE INFORMATION
For more information on plink, it's probably best to go and look at
-\versionid $Id: plink.but,v 1.25 2004/08/19 13:14:55 jacob Exp $
+\versionid $Id: plink.but,v 1.25.2.1 2004/10/24 13:29:02 simon Exp $
\C{plink} Using the command-line connection tool Plink
\c -C enable compression
\c -i key private key file for authentication
\c -s remote command is an SSH subsystem (SSH-2 only)
+\c -N don't start a shell/command (SSH-2 only)
Once this works, you are ready to use Plink.
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
+ printf(" -N don't start a shell/command (SSH-2 only)\n");
exit(1);
}
/* translations */
int vtmode;
char line_codepage[128];
+ int utf8_override;
int xlat_capslockcyr;
/* X11 forwarding */
int x11_forward;
void request_paste(void *frontend);
void frontend_keypress(void *frontend);
void ldisc_update(void *frontend, int echo, int edit);
+/* It's the backend's responsibility to invoke this at the start of a
+ * connection, if necessary; it can also invoke it later if the set of
+ * special commands changes. It does not need to invoke it at session
+ * shutdown. */
void update_specials_menu(void *frontend);
int from_backend(void *frontend, int is_stderr, const char *data, int len);
#define OPTIMISE_IS_SCROLL 1
write_setting_s(sesskey, buf, buf2);
}
write_setting_s(sesskey, "LineCodePage", cfg->line_codepage);
+ write_setting_i(sesskey, "UTF8Override", cfg->utf8_override);
write_setting_s(sesskey, "Printer", cfg->printer);
write_setting_i(sesskey, "CapsLockCyr", cfg->xlat_capslockcyr);
write_setting_i(sesskey, "ScrollBar", cfg->scrollbar);
*/
gpps(sesskey, "LineCodePage", "", cfg->line_codepage,
sizeof(cfg->line_codepage));
+ gppi(sesskey, "UTF8Override", 1, &cfg->utf8_override);
gpps(sesskey, "Printer", "", cfg->printer, sizeof(cfg->printer));
gppi (sesskey, "CapsLockCyr", 0, &cfg->xlat_capslockcyr);
gppi(sesskey, "ScrollBar", 1, &cfg->scrollbar);
int siglen, retlen, len;
char *q, *agentreq, *ret;
int try_send;
+ int num_env, env_left, env_ok;
};
crState(do_ssh2_authconn_state);
ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(ssh);
bufchain_init(&ssh->mainchan->v.v2.outbuffer);
add234(ssh->channels, ssh->mainchan);
+ update_specials_menu(ssh->frontend);
logevent("Opened channel for session");
} else
ssh->mainchan = NULL;
ssh->editing = ssh->echoing = 1;
}
+ /*
+ * 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
/* Do pre-close processing on the channel. */
switch (c->type) {
case CHAN_MAINSESSION:
- break; /* nothing to see here, move along */
+ ssh->mainchan = NULL;
+ update_specials_menu(ssh->frontend);
+ break;
case CHAN_X11:
if (c->u.x11.s != NULL)
x11_close(c->u.x11.s);
*/
static const struct telnet_special *ssh_get_specials(void *handle)
{
+ static const struct telnet_special ignore_special[] = {
+ {"IGNORE message", TS_NOP},
+ };
+ static const struct telnet_special ssh2_session_specials[] = {
+ {"", 0},
+ {"Break", TS_BRK}
+ /* XXX we should also support signals */
+ };
+ static const struct telnet_special specials_end[] = {
+ {NULL, 0}
+ };
+ static struct telnet_special ssh_specials[lenof(ignore_special) +
+ lenof(ssh2_session_specials) +
+ lenof(specials_end)];
Ssh ssh = (Ssh) handle;
+ int i = 0;
+#define ADD_SPECIALS(name) \
+ do { \
+ assert((i + lenof(name)) <= lenof(ssh_specials)); \
+ memcpy(&ssh_specials[i], name, sizeof name); \
+ i += lenof(name); \
+ } while(0)
if (ssh->version == 1) {
- static const struct telnet_special ssh1_specials[] = {
- {"IGNORE message", TS_NOP},
- {NULL, 0}
- };
- return ssh1_specials;
+ /* Don't bother offering IGNORE if we've decided the remote
+ * won't cope with it, since we wouldn't bother sending it if
+ * asked anyway. */
+ if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
+ ADD_SPECIALS(ignore_special);
} else if (ssh->version == 2) {
- static const struct telnet_special ssh2_specials[] = {
- {"Break", TS_BRK},
- {"IGNORE message", TS_NOP},
- {NULL, 0}
- };
- return ssh2_specials;
- } else
+ /* XXX add rekey, when implemented */
+ ADD_SPECIALS(ignore_special);
+ if (ssh->mainchan)
+ ADD_SPECIALS(ssh2_session_specials);
+ } /* else we're not ready yet */
+
+ if (i) {
+ ADD_SPECIALS(specials_end);
+ return ssh_specials;
+ } else {
return NULL;
+ }
+#undef ADD_SPECIALS
}
/*
*/
telnet->in_synch = FALSE;
+ /*
+ * We can send special commands from the start.
+ */
+ update_specials_menu(telnet->frontend);
+
return NULL;
}
inst->font_width = gdk_char_width(inst->fonts[0], ' ');
inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
- inst->direct_to_font = init_ucs(&inst->ucsdata,
- inst->cfg.line_codepage, font_charset,
+ inst->direct_to_font = init_ucs(&inst->ucsdata, inst->cfg.line_codepage,
+ inst->cfg.utf8_override, font_charset,
inst->cfg.vtmode);
}
sfree(title);
}
inst->back->provide_logctx(inst->backhandle, inst->logctx);
- update_specials_menu(inst);
term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
inst->specialsitem1 = menuitem;
MKMENUITEM(NULL, NULL);
inst->specialsitem2 = menuitem;
+ gtk_widget_hide(inst->specialsitem1);
+ gtk_widget_hide(inst->specialsitem2);
MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem);
MKMENUITEM("Reset Terminal", reset_terminal_menuitem);
MKMENUITEM("Copy All", copy_all_menuitem);
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
* Exports from unicode.c.
*/
struct unicode_data;
-int init_ucs(struct unicode_data *ucsdata,
- char *line_codepage, int font_charset, int vtmode);
+int init_ucs(struct unicode_data *ucsdata, char *line_codepage,
+ int utf8_override, int font_charset, int vtmode);
/*
* Spare function exported directly from uxnet.c.
HELPCTX(no_help), dlg_stdeditbox_handler,
I(offsetof(Config,shadowboldoffset)), I(-1));
+ /*
+ * Markus Kuhn feels, not totally unreasonably, that it's good
+ * for all applications to shift into UTF-8 mode if they notice
+ * that they've been started with a LANG setting dictating it,
+ * so that people don't have to keep remembering a separate
+ * UTF-8 option for every application they use. Therefore,
+ * here's an override option in the Translation panel.
+ */
+ s = ctrl_getset(b, "Window/Translation", "trans",
+ "Character set translation on received data");
+ ctrl_checkbox(s, "Override with UTF-8 if locale says so", 'l',
+ HELPCTX(translation_utf8_override),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,utf8_override)));
+
/*
* Unix supports a local-command proxy. This also means we must
* adjust the text on the `Telnet command' control.
printf(" -C enable compression\n");
printf(" -i key private key file for authentication\n");
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
+ printf(" -N don't start a shell/command (SSH-2 only)\n");
exit(1);
}
/*
* Return value is TRUE if pterm is to run in direct-to-font mode.
*/
-int init_ucs(struct unicode_data *ucsdata,
- char *linecharset, int font_charset, int vtmode)
+int init_ucs(struct unicode_data *ucsdata, char *linecharset,
+ int utf8_override, int font_charset, int vtmode)
{
int i, ret = 0;
ucsdata->font_codepage = -1;
/*
- * line_codepage should be decoded from the specification in
- * cfg.
+ * If utf8_override is set and the POSIX locale settings
+ * dictate a UTF-8 character set, then just go straight for
+ * UTF-8.
*/
- ucsdata->line_codepage = decode_codepage(linecharset);
+ ucsdata->line_codepage = CS_NONE;
+ if (utf8_override) {
+ const char *s;
+ if (((s = getenv("LC_ALL")) && *s) ||
+ ((s = getenv("LC_CTYPE")) && *s) ||
+ ((s = getenv("LANG")) && *s)) {
+ if (strstr(s, "UTF-8"))
+ ucsdata->line_codepage = CS_UTF8;
+ }
+ }
+
+ /*
+ * Failing that, line_codepage should be decoded from the
+ * specification in cfg.
+ */
+ if (ucsdata->line_codepage == CS_NONE)
+ ucsdata->line_codepage = decode_codepage(linecharset);
/*
* If line_codepage is _still_ CS_NONE, we assume we're using
SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
}
- start_backend();
-
/*
* Prepare the mouse handler.
*/
}
}
- update_specials_menu(NULL);
+ start_backend();
/*
* Set up the initial input locale.