diff --git a/be_misc.c b/be_misc.c index 1e8026ea..8c1ff481 100644 --- a/be_misc.c +++ b/be_misc.c @@ -8,7 +8,7 @@ #include "putty.h" #include "network.h" -void backend_socket_log(Frontend *frontend, LogContext *logctx, +void backend_socket_log(Seat *seat, LogContext *logctx, int type, SockAddr *addr, int port, const char *error_msg, int error_code, Conf *conf, int session_started) @@ -43,7 +43,7 @@ void backend_socket_log(Frontend *frontend, LogContext *logctx, if (log_to_term == AUTO) log_to_term = session_started ? FORCE_OFF : FORCE_ON; if (log_to_term == FORCE_ON) - from_backend(frontend, TRUE, msg, len); + seat_stderr(seat, msg, len); msg[len-2] = '\0'; /* remove the \r\n again */ } diff --git a/cmdgen.c b/cmdgen.c index 7e98ef24..a08e7de9 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -92,27 +92,6 @@ static void no_progress(void *param, int action, int phase, int iprogress) { } -void modalfatalbox(const 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 nonfatal(const char *p, ...) -{ - va_list ap; - fprintf(stderr, "ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); -} - /* * Stubs to let everything else link sensibly. */ @@ -775,7 +754,7 @@ int main(int argc, char **argv) */ if (encrypted && load_encrypted) { if (!old_passphrase) { - prompts_t *p = new_prompts(NULL); + prompts_t *p = new_prompts(); int ret; p->to_server = FALSE; p->name = dupstr("SSH key passphrase"); diff --git a/cmdline.c b/cmdline.c index 85586176..cf25e584 100644 --- a/cmdline.c +++ b/cmdline.c @@ -81,8 +81,8 @@ void cmdline_cleanup(void) } while (0) /* - * Similar interface to get_userpass_input(), except that here a -1 - * return means that we aren't capable of processing the prompt and + * Similar interface to seat_get_userpass_input(), except that here a + * -1 return means that we aren't capable of processing the prompt and * someone else should do it. */ int cmdline_get_passwd_input(prompts_t *p) diff --git a/defs.h b/defs.h index f524378e..ec31c246 100644 --- a/defs.h +++ b/defs.h @@ -53,6 +53,9 @@ typedef struct LogContext LogContext; typedef struct LogPolicy LogPolicy; typedef struct LogPolicyVtable LogPolicyVtable; +typedef struct Seat Seat; +typedef struct SeatVtable SeatVtable; + typedef struct Frontend Frontend; typedef struct Ssh Ssh; diff --git a/fuzzterm.c b/fuzzterm.c index d6152661..32608d9f 100644 --- a/fuzzterm.c +++ b/fuzzterm.c @@ -38,9 +38,6 @@ int main(int argc, char **argv) return 0; } -int from_backend(Frontend *frontend, int is_stderr, const void *data, int len) -{ return 0; } - /* functions required by terminal.c */ void request_resize(Frontend *frontend, int x, int y) { } diff --git a/ldisc.c b/ldisc.c index 2225318e..a72adbcb 100644 --- a/ldisc.c +++ b/ldisc.c @@ -24,7 +24,7 @@ static void c_write(Ldisc *ldisc, const void *buf, int len) { - from_backend(ldisc->frontend, 0, buf, len); + seat_stdout(ldisc->seat, buf, len); } static int plen(Ldisc *ldisc, unsigned char c) @@ -77,8 +77,7 @@ static void bsb(Ldisc *ldisc, int n) #define CTRL(x) (x^'@') #define KCTRL(x) ((x^'@') | 0x100) -Ldisc *ldisc_create(Conf *conf, Terminal *term, - Backend *backend, Frontend *frontend) +Ldisc *ldisc_create(Conf *conf, Terminal *term, Backend *backend, Seat *seat) { Ldisc *ldisc = snew(Ldisc); @@ -89,7 +88,7 @@ Ldisc *ldisc_create(Conf *conf, Terminal *term, ldisc->backend = backend; ldisc->term = term; - ldisc->frontend = frontend; + ldisc->seat = seat; ldisc_configure(ldisc, conf); @@ -124,7 +123,7 @@ void ldisc_free(Ldisc *ldisc) void ldisc_echoedit_update(Ldisc *ldisc) { - frontend_echoedit_update(ldisc->frontend, ECHOING, EDITING); + seat_echoedit_update(ldisc->seat, ECHOING, EDITING); } void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, int interactive) diff --git a/ldisc.h b/ldisc.h index e3d043b5..3b5e8249 100644 --- a/ldisc.h +++ b/ldisc.h @@ -11,7 +11,7 @@ struct Ldisc_tag { Terminal *term; Backend *backend; - Frontend *frontend; + Seat *seat; /* * Values cached out of conf. diff --git a/misc.c b/misc.c index 0b9e6c25..18edfb80 100644 --- a/misc.c +++ b/misc.c @@ -216,12 +216,24 @@ char *host_strduptrim(const char *s) return dupstr(s); } -prompts_t *new_prompts(Frontend *frontend) +void seat_connection_fatal(Seat *seat, const char *fmt, ...) +{ + va_list ap; + char *msg; + + va_start(ap, fmt); + msg = dupvprintf(fmt, ap); + va_end(ap); + + seat->vt->connection_fatal(seat, msg); + sfree(msg); /* if we return */ +} + +prompts_t *new_prompts(void) { prompts_t *p = snew(prompts_t); p->prompts = NULL; p->n_prompts = 0; - p->frontend = frontend; p->data = NULL; p->to_server = TRUE; /* to be on the safe side */ p->name = p->instruction = NULL; @@ -1348,3 +1360,31 @@ char *buildinfo(const char *newline) return strbuf_to_str(buf); } + +int nullseat_output( + Seat *seat, int is_stderr, const void *data, int len) { return 0; } +int nullseat_eof(Seat *seat) { return TRUE; } +int nullseat_get_userpass_input( + Seat *seat, prompts_t *p, bufchain *input) { return 0; } +void nullseat_notify_remote_exit(Seat *seat) {} +void nullseat_connection_fatal(Seat *seat, const char *message) {} +void nullseat_update_specials_menu(Seat *seat) {} +char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; } +void nullseat_set_busy_status(Seat *seat, BusyStatus status) {} +int nullseat_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *key_fingerprint, + void (*callback)(void *ctx, int result), void *ctx) { return 0; } +int nullseat_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx) { return 0; } +int nullseat_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx) { return 0; } +int nullseat_is_never_utf8(Seat *seat) { return FALSE; } +int nullseat_is_always_utf8(Seat *seat) { return TRUE; } +void nullseat_echoedit_update(Seat *seat, int echoing, int editing) {} +const char *nullseat_get_x_display(Seat *seat) { return NULL; } +int nullseat_get_windowid(Seat *seat, long *id_out) { return FALSE; } +int nullseat_get_char_cell_size( + Seat *seat, int *width, int *height) { return FALSE; } diff --git a/network.h b/network.h index 9a1b2383..4799a337 100644 --- a/network.h +++ b/network.h @@ -235,7 +235,7 @@ extern Plug *const nullplug; /* * Exports from be_misc.c. */ -void backend_socket_log(Frontend *frontend, LogContext *logctx, +void backend_socket_log(Seat *seat, LogContext *logctx, int type, SockAddr *addr, int port, const char *error_msg, int error_code, Conf *conf, int session_started); diff --git a/pscp.c b/pscp.c index 1b9e7c34..9fc0c3e0 100644 --- a/pscp.c +++ b/pscp.c @@ -61,6 +61,29 @@ const char *const appname = "PSCP"; void ldisc_echoedit_update(Ldisc *ldisc) { } +static int pscp_output(Seat *, int is_stderr, const void *, int); +static int pscp_eof(Seat *); + +static const SeatVtable pscp_seat_vt = { + pscp_output, + pscp_eof, + filexfer_get_userpass_input, + nullseat_notify_remote_exit, + console_connection_fatal, + nullseat_update_specials_menu, + nullseat_get_ttymode, + nullseat_set_busy_status, + console_verify_ssh_host_key, + console_confirm_weak_crypto_primitive, + console_confirm_weak_cached_hostkey, + nullseat_is_never_utf8, + nullseat_echoedit_update, + nullseat_get_x_display, + nullseat_get_windowid, + nullseat_get_char_cell_size, +}; +static Seat pscp_seat[1] = {{ &pscp_seat_vt }}; + static void tell_char(FILE *stream, char c) { fputc(c, stream); @@ -103,56 +126,6 @@ static void tell_user(FILE *stream, const char *fmt, ...) sfree(str2); } -/* - * Print an error message and perform a fatal exit. - */ -void modalfatalbox(const char *fmt, ...) -{ - char *str, *str2; - va_list ap; - va_start(ap, fmt); - str = dupvprintf(fmt, ap); - str2 = dupcat("Fatal: ", str, "\n", NULL); - sfree(str); - va_end(ap); - abandon_stats(); - tell_str(stderr, str2); - sfree(str2); - errs++; - - cleanup_exit(1); -} -void nonfatal(const char *fmt, ...) -{ - char *str, *str2; - va_list ap; - va_start(ap, fmt); - str = dupvprintf(fmt, ap); - str2 = dupcat("Error: ", str, "\n", NULL); - sfree(str); - va_end(ap); - abandon_stats(); - tell_str(stderr, str2); - sfree(str2); - errs++; -} -void connection_fatal(Frontend *frontend, const char *fmt, ...) -{ - char *str, *str2; - va_list ap; - va_start(ap, fmt); - str = dupvprintf(fmt, ap); - str2 = dupcat("Fatal: ", str, "\n", NULL); - sfree(str); - va_end(ap); - abandon_stats(); - tell_str(stderr, str2); - sfree(str2); - errs++; - - cleanup_exit(1); -} - /* * In pscp, all agent requests should be synchronous, so this is a * never-called stub. @@ -168,16 +141,16 @@ void agent_schedule_callback(void (*callback)(void *, void *, int), * is available. * * To do this, we repeatedly call the SSH protocol module, with our - * own trap in from_backend() to catch the data that comes back. We - * do this until we have enough data. + * own pscp_output() function to catch the data that comes back. We do + * this until we have enough data. */ static unsigned char *outptr; /* where to put the data */ static unsigned outlen; /* how much data required */ static unsigned char *pending = NULL; /* any spare data */ static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */ -int from_backend(Frontend *frontend, int is_stderr, - const void *data, int datalen) +static int pscp_output(Seat *seat, int is_stderr, + const void *data, int datalen) { unsigned char *p = (unsigned char *) data; unsigned len = (unsigned) datalen; @@ -215,7 +188,7 @@ int from_backend(Frontend *frontend, int is_stderr, return 0; } -int from_backend_eof(Frontend *frontend) +static int pscp_eof(Seat *seat) { /* * We usually expect to be the party deciding when to close the @@ -224,8 +197,8 @@ int from_backend_eof(Frontend *frontend) * downloading rather than uploading. */ if ((using_sftp || uploading) && !sent_eof) { - connection_fatal(frontend, - "Received unexpected end-of-file from server"); + seat_connection_fatal( + pscp_seat, "Received unexpected end-of-file from server"); } return FALSE; } @@ -325,7 +298,7 @@ static void bump(const char *fmt, ...) * Wait for the reply to a single SFTP request. Parallels the same * function in psftp.c (but isn't centralised into sftp.c because the * latter module handles SFTP only and shouldn't assume that SFTP is - * the only thing going on by calling connection_fatal). + * the only thing going on by calling seat_connection_fatal). */ struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) { @@ -334,13 +307,17 @@ struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) sftp_register(req); pktin = sftp_recv(); - if (pktin == NULL) - connection_fatal(NULL, "did not receive SFTP response packet " - "from server"); + if (pktin == NULL) { + seat_connection_fatal( + pscp_seat, "did not receive SFTP response packet from server"); + } rreq = sftp_find_request(pktin); - if (rreq != req) - connection_fatal(NULL, "unable to understand SFTP response packet " - "from server: %s", fxp_error()); + if (rreq != req) { + seat_connection_fatal( + pscp_seat, + "unable to understand SFTP response packet from server: %s", + fxp_error()); + } return pktin; } @@ -513,7 +490,7 @@ static void do_cmd(char *host, char *user, char *cmd) platform_psftp_pre_conn_setup(); - err = backend_init(&ssh_backend, NULL, &backend, logctx, conf, + err = backend_init(&ssh_backend, pscp_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, diff --git a/psftp.c b/psftp.c index 4caabe76..08f5d5bd 100644 --- a/psftp.c +++ b/psftp.c @@ -38,6 +38,33 @@ static Backend *backend; static Conf *conf; int sent_eof = FALSE; +/* ------------------------------------------------------------ + * Seat vtable. + */ + +static int psftp_output(Seat *, int is_stderr, const void *, int); +static int psftp_eof(Seat *); + +static const SeatVtable psftp_seat_vt = { + psftp_output, + psftp_eof, + filexfer_get_userpass_input, + nullseat_notify_remote_exit, + console_connection_fatal, + nullseat_update_specials_menu, + nullseat_get_ttymode, + nullseat_set_busy_status, + console_verify_ssh_host_key, + console_confirm_weak_crypto_primitive, + console_confirm_weak_cached_hostkey, + nullseat_is_never_utf8, + nullseat_echoedit_update, + nullseat_get_x_display, + nullseat_get_windowid, + nullseat_get_char_cell_size, +}; +static Seat psftp_seat[1] = {{ &psftp_seat_vt }}; + /* ---------------------------------------------------------------------- * Manage sending requests and waiting for replies. */ @@ -48,13 +75,17 @@ struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req) sftp_register(req); pktin = sftp_recv(); - if (pktin == NULL) - connection_fatal(NULL, "did not receive SFTP response packet " - "from server"); + if (pktin == NULL) { + seat_connection_fatal( + psftp_seat, "did not receive SFTP response packet from server"); + } rreq = sftp_find_request(pktin); - if (rreq != req) - connection_fatal(NULL, "unable to understand SFTP response packet " - "from server: %s", fxp_error()); + if (rreq != req) { + seat_connection_fatal( + psftp_seat, + "unable to understand SFTP response packet from server: %s", + fxp_error()); + } return pktin; } @@ -2437,50 +2468,6 @@ int do_sftp(int mode, int modeflags, char *batchfile) static int verbose = 0; -/* - * Print an error message and perform a fatal exit. - */ -void modalfatalbox(const char *fmt, ...) -{ - char *str, *str2; - va_list ap; - va_start(ap, fmt); - str = dupvprintf(fmt, ap); - str2 = dupcat("Fatal: ", str, "\n", NULL); - sfree(str); - va_end(ap); - fputs(str2, stderr); - sfree(str2); - - cleanup_exit(1); -} -void nonfatal(const char *fmt, ...) -{ - char *str, *str2; - va_list ap; - va_start(ap, fmt); - str = dupvprintf(fmt, ap); - str2 = dupcat("Error: ", str, "\n", NULL); - sfree(str); - va_end(ap); - fputs(str2, stderr); - sfree(str2); -} -void connection_fatal(Frontend *frontend, const char *fmt, ...) -{ - char *str, *str2; - va_list ap; - va_start(ap, fmt); - str = dupvprintf(fmt, ap); - str2 = dupcat("Fatal: ", str, "\n", NULL); - sfree(str); - va_end(ap); - fputs(str2, stderr); - sfree(str2); - - cleanup_exit(1); -} - void ldisc_echoedit_update(Ldisc *ldisc) { } /* @@ -2498,7 +2485,7 @@ void agent_schedule_callback(void (*callback)(void *, void *, int), * is available. * * To do this, we repeatedly call the SSH protocol module, with our - * own trap in from_backend() to catch the data that comes back. We + * own psftp_output() function to catch the data that comes back. We * do this until we have enough data. */ @@ -2506,8 +2493,8 @@ static unsigned char *outptr; /* where to put the data */ static unsigned outlen; /* how much data required */ static unsigned char *pending = NULL; /* any spare data */ static unsigned pendlen = 0, pendsize = 0; /* length and phys. size of buffer */ -int from_backend(Frontend *frontend, int is_stderr, - const void *data, int datalen) +static int psftp_output(Seat *seat, int is_stderr, + const void *data, int datalen) { unsigned char *p = (unsigned char *) data; unsigned len = (unsigned) datalen; @@ -2551,7 +2538,8 @@ int from_backend(Frontend *frontend, int is_stderr, return 0; } -int from_backend_eof(Frontend *frontend) + +static int psftp_eof(Seat *seat) { /* * We expect to be the party deciding when to close the @@ -2559,11 +2547,12 @@ int from_backend_eof(Frontend *frontend) * should panic. */ if (!sent_eof) { - connection_fatal(frontend, - "Received unexpected end-of-file from SFTP server"); + seat_connection_fatal( + psftp_seat, "Received unexpected end-of-file from SFTP server"); } return FALSE; } + int sftp_recvdata(char *buf, int len) { outptr = (unsigned char *) buf; @@ -2820,7 +2809,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber) platform_psftp_pre_conn_setup(); - err = backend_init(&ssh_backend, NULL, &backend, logctx, conf, + err = backend_init(&ssh_backend, psftp_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, diff --git a/putty.h b/putty.h index 679e2e00..492d87e1 100644 --- a/putty.h +++ b/putty.h @@ -479,7 +479,7 @@ struct Backend { const BackendVtable *vt; }; struct BackendVtable { - const char *(*init) (Frontend *frontend, Backend **backend_out, + const char *(*init) (Seat *seat, Backend **backend_out, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive); @@ -515,8 +515,8 @@ struct BackendVtable { int default_port; }; -#define backend_init(vt, fe, out, logctx, conf, host, port, rhost, nd, ka) \ - ((vt)->init(fe, out, logctx, conf, host, port, rhost, nd, ka)) +#define backend_init(vt, seat, out, logctx, conf, host, port, rhost, nd, ka) \ + ((vt)->init(seat, out, logctx, conf, host, port, rhost, nd, ka)) #define backend_free(be) ((be)->vt->free(be)) #define backend_reconfig(be, conf) ((be)->vt->reconfig(be, conf)) #define backend_send(be, buf, len) ((be)->vt->send(be, buf, len)) @@ -635,11 +635,10 @@ typedef struct { size_t n_prompts; /* May be zero (in which case display the foregoing, * if any, and return success) */ prompt_t **prompts; - Frontend *frontend; void *data; /* slot for housekeeping data, managed by - * get_userpass_input(); initially NULL */ + * seat_get_userpass_input(); initially NULL */ } prompts_t; -prompts_t *new_prompts(Frontend *frontend); +prompts_t *new_prompts(); void add_prompt(prompts_t *p, char *promptstr, int echo); void prompt_set_result(prompt_t *pr, const char *newstr); void prompt_ensure_result_size(prompt_t *pr, int len); @@ -697,6 +696,306 @@ typedef struct truecolour { enum { ALL_CLIPBOARDS(CLIP_ID) N_CLIPBOARDS }; #undef CLIP_ID +/* Hint from backend to frontend about time-consuming operations, used + * by seat_set_busy_status. Initial state is assumed to be + * BUSY_NOT. */ +typedef enum BusyStatus { + BUSY_NOT, /* Not busy, all user interaction OK */ + BUSY_WAITING, /* Waiting for something; local event loops still + running so some local interaction (e.g. menus) + OK, but network stuff is suspended */ + BUSY_CPU /* Locally busy (e.g. crypto); user interaction + * suspended */ +} BusyStatus; + +/* + * Data type 'Seat', which is an API intended to contain essentially + * everything that a back end might need to talk to its client for: + * session output, password prompts, SSH warnings about host keys and + * weak cryptography, notifications of events like the remote process + * exiting or the GUI specials menu needing an update. + */ +struct Seat { + const struct SeatVtable *vt; +}; +struct SeatVtable { + /* + * Provide output from the remote session. 'is_stderr' indicates + * that the output should be sent to a separate error message + * channel, if the seat has one. But combining both channels into + * one is OK too; that's what terminal-window based seats do. + * + * The return value is the current size of the output backlog. + */ + int (*output)(Seat *seat, int is_stderr, const void *data, int len); + + /* + * Called when the back end wants to indicate that EOF has arrived + * on the server-to-client stream. Returns FALSE to indicate that + * we intend to keep the session open in the other direction, or + * TRUE to indicate that if they're closing so are we. + */ + int (*eof)(Seat *seat); + + /* + * Try to get answers from a set of interactive login prompts. The + * prompts are provided in 'p'; the bufchain 'input' holds the + * data currently outstanding in the session's normal standard- + * input channel. Seats may implement this function by consuming + * data from 'input' (e.g. password prompts in GUI PuTTY, + * displayed in the same terminal as the subsequent session), or + * by doing something entirely different (e.g. directly + * interacting with standard I/O, or putting up a dialog box). + * + * A positive return value means that all prompts have had answers + * filled in. A zero return means that the user performed a + * deliberate 'cancel' UI action. A negative return means that no + * answer can be given yet but please try again later. + * + * (FIXME: it would be nice to distinguish two classes of cancel + * action, so the user could specify 'I want to abandon this + * entire attempt to start a session' or the milder 'I want to + * abandon this particular form of authentication and fall back to + * a different one' - e.g. if you turn out not to be able to + * remember your private key passphrase then perhaps you'd rather + * fall back to password auth rather than aborting the whole + * session.) + * + * (Also FIXME: currently, backends' only response to the 'try + * again later' is to try again when more input data becomes + * available, because they assume that a seat is returning that + * value because it's consuming keyboard input. But a seat that + * handled this function by putting up a dialog box might want to + * put it up non-modally, and therefore would want to proactively + * notify the backend to retry once the dialog went away. So if I + * ever do want to move password prompts into a dialog box, I'll + * want a backend method for sending that notification.) + */ + int (*get_userpass_input)(Seat *seat, prompts_t *p, bufchain *input); + + /* + * Notify the seat that the process running at the other end of + * the connection has finished. + */ + void (*notify_remote_exit)(Seat *seat); + + /* + * Notify the seat that the connection has suffered a fatal error. + */ + void (*connection_fatal)(Seat *seat, const char *message); + + /* + * Notify the seat that the list of special commands available + * from backend_get_specials() has changed, so that it might want + * to call that function to repopulate its menu. + * + * Seats are not expected to call backend_get_specials() + * proactively; they may start by assuming that the backend + * provides no special commands at all, so if the backend does + * provide any, then it should use this notification at startup + * time. Of course 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)(Seat *seat); + + /* + * Get the seat's preferred value for an SSH terminal mode + * setting. Returning NULL indicates no preference (i.e. the SSH + * connection will not attempt to set the mode at all). + * + * The returned value is dynamically allocated, and the caller + * should free it. + */ + char *(*get_ttymode)(Seat *seat, const char *mode); + + /* + * Tell the seat whether the backend is currently doing anything + * CPU-intensive (typically a cryptographic key exchange). See + * BusyStatus enumeration above. + */ + void (*set_busy_status)(Seat *seat, BusyStatus status); + + /* + * Ask the seat whether a given SSH host key should be accepted. + * This may return immediately after checking saved configuration + * or command-line options, or it may have to present a prompt to + * the user and return asynchronously later. + * + * Return values: + * + * - +1 means `key was OK' (either already known or the user just + * approved it) `so continue with the connection' + * + * - 0 means `key was not OK, abandon the connection' + * + * - -1 means `I've initiated enquiries, please wait to be called + * back via the provided function with a result that's either 0 + * or +1'. + */ + int (*verify_ssh_host_key)( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *key_fingerprint, + void (*callback)(void *ctx, int result), void *ctx); + + /* + * Check with the seat whether it's OK to use a cryptographic + * primitive from below the 'warn below this line' threshold in + * the input Conf. Return values are the same as + * verify_ssh_host_key above. + */ + int (*confirm_weak_crypto_primitive)( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx); + + /* + * Variant form of confirm_weak_crypto_primitive, which prints a + * slightly different message but otherwise has the same + * semantics. + * + * This form is used in the case where we're using a host key + * below the warning threshold because that's the best one we have + * cached, but at least one host key algorithm *above* the + * threshold is available that we don't have cached. 'betteralgs' + * lists the better algorithm(s). + */ + int (*confirm_weak_cached_hostkey)( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx); + + /* + * Indicates whether the seat is expecting to interact with the + * user in the UTF-8 character set. (Affects e.g. visual erase + * handling in local line editing.) + */ + int (*is_utf8)(Seat *seat); + + /* + * Notify the seat that the back end, and/or the ldisc between + * them, have changed their idea of whether they currently want + * local echo and/or local line editing enabled. + */ + void (*echoedit_update)(Seat *seat, int echoing, int editing); + + /* + * Return the local X display string relevant to a seat, or NULL + * if there isn't one or if the concept is meaningless. + */ + const char *(*get_x_display)(Seat *seat); + + /* + * Return the X11 id of the X terminal window relevant to a seat, + * by returning TRUE and filling in the output pointer. Return + * FALSE if there isn't one or if the concept is meaningless. + */ + int (*get_windowid)(Seat *seat, long *id_out); + + /* + * Return the pixel size of a terminal character cell. If the + * concept is meaningless or the information is unavailable, + * return FALSE; otherwise fill in the output pointers and return + * TRUE. + */ + int (*get_char_cell_size)(Seat *seat, int *width, int *height); +}; + +#define seat_output(seat, is_stderr, data, len) \ + ((seat)->vt->output(seat, is_stderr, data, len)) +#define seat_eof(seat) \ + ((seat)->vt->eof(seat)) +#define seat_get_userpass_input(seat, p, input) \ + ((seat)->vt->get_userpass_input(seat, p, input)) +#define seat_notify_remote_exit(seat) \ + ((seat)->vt->notify_remote_exit(seat)) +#define seat_update_specials_menu(seat) \ + ((seat)->vt->update_specials_menu(seat)) +#define seat_get_ttymode(seat, mode) \ + ((seat)->vt->get_ttymode(seat, mode)) +#define seat_set_busy_status(seat, status) \ + ((seat)->vt->set_busy_status(seat, status)) +#define seat_verify_ssh_host_key(seat, h, p, typ, str, fp, cb, ctx) \ + ((seat)->vt->verify_ssh_host_key(seat, h, p, typ, str, fp, cb, ctx)) +#define seat_confirm_weak_crypto_primitive(seat, typ, alg, cb, ctx) \ + ((seat)->vt->confirm_weak_crypto_primitive(seat, typ, alg, cb, ctx)) +#define seat_confirm_weak_cached_hostkey(seat, alg, better, cb, ctx) \ + ((seat)->vt->confirm_weak_cached_hostkey(seat, alg, better, cb, ctx)) +#define seat_is_utf8(seat) \ + ((seat)->vt->is_utf8(seat)) +#define seat_echoedit_update(seat, echoing, editing) \ + ((seat)->vt->echoedit_update(seat, echoing, editing)) +#define seat_get_x_display(seat) \ + ((seat)->vt->get_x_display(seat)) +#define seat_get_windowid(seat, out) \ + ((seat)->vt->get_windowid(seat, out)) +#define seat_get_char_cell_size(seat, width, height) \ + ((seat)->vt->get_char_cell_size(seat, width, height)) + +/* Unlike the seat's actual method, the public entry point + * seat_connection_fatal is a wrapper function with a printf-like API, + * defined in misc.c. */ +void seat_connection_fatal(Seat *seat, const char *fmt, ...); + +/* Handy aliases for seat_output which set is_stderr to a fixed value. */ +#define seat_stdout(seat, data, len) \ + seat_output(seat, FALSE, data, len) +#define seat_stderr(seat, data, len) \ + seat_output(seat, TRUE, data, len) + +/* + * Stub methods for seat implementations that want to use the obvious + * null handling for a given method. + * + * These are generally obvious, except for is_utf8, where you might + * plausibly want to return either fixed answer 'no' or 'yes'. + */ +int nullseat_output(Seat *seat, int is_stderr, const void *data, int len); +int nullseat_eof(Seat *seat); +int nullseat_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input); +void nullseat_notify_remote_exit(Seat *seat); +void nullseat_connection_fatal(Seat *seat, const char *message); +void nullseat_update_specials_menu(Seat *seat); +char *nullseat_get_ttymode(Seat *seat, const char *mode); +void nullseat_set_busy_status(Seat *seat, BusyStatus status); +int nullseat_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *key_fingerprint, + void (*callback)(void *ctx, int result), void *ctx); +int nullseat_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx); +int nullseat_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx); +int nullseat_is_never_utf8(Seat *seat); +int nullseat_is_always_utf8(Seat *seat); +void nullseat_echoedit_update(Seat *seat, int echoing, int editing); +const char *nullseat_get_x_display(Seat *seat); +int nullseat_get_windowid(Seat *seat, long *id_out); +int nullseat_get_char_cell_size(Seat *seat, int *width, int *height); + +/* + * Seat functions provided by the platform's console-application + * support module (wincons.c, uxcons.c). + */ + +void console_connection_fatal(Seat *seat, const char *message); +int console_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *key_fingerprint, + void (*callback)(void *ctx, int result), void *ctx); +int console_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx); +int console_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx); + +/* + * Other centralised seat functions. + */ +int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input); + /* * Exports from the front end. */ @@ -721,7 +1020,6 @@ void write_clip(Frontend *frontend, int clipboard, wchar_t *, int *, truecolour *, int, int); void optimised_move(Frontend *frontend, int, int, int); void set_raw_mouse_mode(Frontend *frontend, int); -void connection_fatal(Frontend *frontend, const char *, ...); void nonfatal(const char *, ...); void modalfatalbox(const char *, ...); #ifdef macintosh @@ -730,28 +1028,6 @@ void modalfatalbox(const char *, ...); void do_beep(Frontend *frontend, int); void sys_cursor(Frontend *frontend, int x, int y); void frontend_request_paste(Frontend *frontend, int clipboard); -void frontend_echoedit_update(Frontend *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(Frontend *frontend); -int from_backend(Frontend *frontend, int is_stderr, const void *data, int len); -/* Called when the back end wants to indicate that EOF has arrived on - * the server-to-client stream. Returns FALSE to indicate that we - * intend to keep the session open in the other direction, or TRUE to - * indicate that if they're closing so are we. */ -int from_backend_eof(Frontend *frontend); -void notify_remote_exit(Frontend *frontend); -/* Get a sensible value for a tty mode. NULL return = don't set. - * Otherwise, returned value should be freed by caller. */ -char *get_ttymode(Frontend *frontend, const char *mode); -/* - * >0 = `got all results, carry on' - * 0 = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?) - * <0 = `please call back later with a fuller bufchain' - */ -int get_userpass_input(prompts_t *p, bufchain *input); #define OPTIMISE_IS_SCROLL 1 void set_iconic(Frontend *frontend, int iconic); @@ -763,16 +1039,6 @@ int is_iconic(Frontend *frontend); void get_window_pos(Frontend *frontend, int *x, int *y); void get_window_pixels(Frontend *frontend, int *x, int *y); char *get_window_title(Frontend *frontend, int icon); -/* Hint from backend to frontend about time-consuming operations. - * Initial state is assumed to be BUSY_NOT. */ -enum { - BUSY_NOT, /* Not busy, all user interaction OK */ - BUSY_WAITING, /* Waiting for something; local event loops still running - so some local interaction (e.g. menus) OK, but network - stuff is suspended */ - BUSY_CPU /* Locally busy (e.g. crypto); user interaction suspended */ -}; -void set_busy_status(Frontend *frontend, int status); int frontend_is_utf8(Frontend *frontend); void cleanup_exit(int); @@ -1274,7 +1540,7 @@ extern const struct BackendVtable ssh_backend; /* * Exports from ldisc.c. */ -Ldisc *ldisc_create(Conf *, Terminal *, Backend *, Frontend *); +Ldisc *ldisc_create(Conf *, Terminal *, Backend *, Seat *); void ldisc_configure(Ldisc *, Conf *); void ldisc_free(Ldisc *); void ldisc_send(Ldisc *, const void *buf, int len, int interactive); @@ -1403,38 +1669,11 @@ int wc_unescape(char *output, const char *wildcard); * Exports from frontend (windlg.c etc) */ void pgp_fingerprints(void); -/* - * verify_ssh_host_key() can return one of three values: - * - * - +1 means `key was OK' (either already known or the user just - * approved it) `so continue with the connection' - * - * - 0 means `key was not OK, abandon the connection' - * - * - -1 means `I've initiated enquiries, please wait to be called - * back via the provided function with a result that's either 0 - * or +1'. - */ -int verify_ssh_host_key(Frontend *frontend, char *host, int port, - const char *keytype, char *keystr, char *fingerprint, - void (*callback)(void *ctx, int result), void *ctx); /* * have_ssh_host_key() just returns true if a key of that type is * already cached and false otherwise. */ int have_ssh_host_key(const char *host, int port, const char *keytype); -/* - * askalg and askhk have the same set of return values as - * verify_ssh_host_key. - * - * (askhk is used in the case where we're using a host key below the - * warning threshold because that's all we have cached, but at least - * one acceptable algorithm is available that we don't have cached.) - */ -int askalg(Frontend *frontend, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx); -int askhk(Frontend *frontend, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx); /* * Exports from console frontends (wincons.c, uxcons.c) @@ -1443,6 +1682,10 @@ int askhk(Frontend *frontend, const char *algname, const char *betteralgs, extern int console_batch_mode; int console_get_userpass_input(prompts_t *p); int is_interactive(void); +void console_print_error_msg(const char *prefix, const char *msg); +void console_print_error_msg_fmt_v( + const char *prefix, const char *fmt, va_list ap); +void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...); /* * Exports from printing.c. diff --git a/raw.c b/raw.c index 09cdb1d3..b6fcbe96 100644 --- a/raw.c +++ b/raw.c @@ -15,7 +15,7 @@ struct Raw { Socket *s; int closed_on_socket_error; int bufsize; - Frontend *frontend; + Seat *seat; LogContext *logctx; int sent_console_eof, sent_socket_eof, session_started; @@ -29,7 +29,7 @@ static void raw_size(Backend *be, int width, int height); static void c_write(Raw *raw, const void *buf, int len) { - int backlog = from_backend(raw->frontend, 0, buf, len); + int backlog = seat_stdout(raw->seat, buf, len); sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG); } @@ -37,7 +37,7 @@ static void raw_log(Plug *plug, int type, SockAddr *addr, int port, const char *error_msg, int error_code) { Raw *raw = container_of(plug, Raw, plug); - backend_socket_log(raw->frontend, raw->logctx, type, addr, port, + backend_socket_log(raw->seat, raw->logctx, type, addr, port, error_msg, error_code, raw->conf, raw->session_started); } @@ -51,7 +51,7 @@ static void raw_check_close(Raw *raw) if (raw->s) { sk_close(raw->s); raw->s = NULL; - notify_remote_exit(raw->frontend); + seat_notify_remote_exit(raw->seat); } } } @@ -67,13 +67,13 @@ static void raw_closing(Plug *plug, const char *error_msg, int error_code, sk_close(raw->s); raw->s = NULL; raw->closed_on_socket_error = TRUE; - notify_remote_exit(raw->frontend); + seat_notify_remote_exit(raw->seat); } logevent(raw->logctx, error_msg); - connection_fatal(raw->frontend, "%s", error_msg); + seat_connection_fatal(raw->seat, "%s", error_msg); } else { /* Otherwise, the remote side closed the connection normally. */ - if (!raw->sent_console_eof && from_backend_eof(raw->frontend)) { + if (!raw->sent_console_eof && seat_eof(raw->seat)) { /* * The front end wants us to close the outgoing side of the * connection as soon as we see EOF from the far end. @@ -119,7 +119,7 @@ static const PlugVtable Raw_plugvt = { * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *raw_init(Frontend *frontend, Backend **backend_handle, +static const char *raw_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive) @@ -141,7 +141,7 @@ static const char *raw_init(Frontend *frontend, Backend **backend_handle, raw->session_started = FALSE; raw->conf = conf_copy(conf); - raw->frontend = frontend; + raw->seat = seat; raw->logctx = logctx; addressfamily = conf_get_int(conf, CONF_addressfamily); diff --git a/rlogin.c b/rlogin.c index 34232c83..d91b93cc 100644 --- a/rlogin.c +++ b/rlogin.c @@ -19,7 +19,7 @@ struct Rlogin { int firstbyte; int cansize; int term_width, term_height; - Frontend *frontend; + Seat *seat; LogContext *logctx; Conf *conf; @@ -33,7 +33,7 @@ struct Rlogin { static void c_write(Rlogin *rlogin, const void *buf, int len) { - int backlog = from_backend(rlogin->frontend, 0, buf, len); + int backlog = seat_stdout(rlogin->seat, buf, len); sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG); } @@ -41,7 +41,7 @@ static void rlogin_log(Plug *plug, int type, SockAddr *addr, int port, const char *error_msg, int error_code) { Rlogin *rlogin = container_of(plug, Rlogin, plug); - backend_socket_log(rlogin->frontend, rlogin->logctx, type, addr, port, + backend_socket_log(rlogin->seat, rlogin->logctx, type, addr, port, error_msg, error_code, rlogin->conf, !rlogin->firstbyte); } @@ -62,12 +62,12 @@ static void rlogin_closing(Plug *plug, const char *error_msg, int error_code, rlogin->s = NULL; if (error_msg) rlogin->closed_on_socket_error = TRUE; - notify_remote_exit(rlogin->frontend); + seat_notify_remote_exit(rlogin->seat); } if (error_msg) { /* A socket error has occurred. */ logevent(rlogin->logctx, error_msg); - connection_fatal(rlogin->frontend, "%s", error_msg); + seat_connection_fatal(rlogin->seat, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ } @@ -150,7 +150,7 @@ static const PlugVtable Rlogin_plugvt = { * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *rlogin_init(Frontend *frontend, Backend **backend_handle, +static const char *rlogin_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive) @@ -167,7 +167,7 @@ static const char *rlogin_init(Frontend *frontend, Backend **backend_handle, rlogin->backend.vt = &rlogin_backend; rlogin->s = NULL; rlogin->closed_on_socket_error = FALSE; - rlogin->frontend = frontend; + rlogin->seat = seat; rlogin->logctx = logctx; rlogin->term_width = conf_get_int(conf, CONF_width); rlogin->term_height = conf_get_int(conf, CONF_height); @@ -223,11 +223,11 @@ static const char *rlogin_init(Frontend *frontend, Backend **backend_handle, } else { int ret; - rlogin->prompt = new_prompts(rlogin->frontend); + rlogin->prompt = new_prompts(); rlogin->prompt->to_server = TRUE; rlogin->prompt->name = dupstr("Rlogin login name"); add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE); - ret = get_userpass_input(rlogin->prompt, NULL); + ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, NULL); if (ret >= 0) { rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); } @@ -274,7 +274,7 @@ static int rlogin_send(Backend *be, const char *buf, int len) * We're still prompting for a username, and aren't talking * directly to the network connection yet. */ - int ret = get_userpass_input(rlogin->prompt, &bc); + int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, &bc); if (ret >= 0) { rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); /* that nulls out rlogin->prompt, so then we'll start sending diff --git a/ssh.c b/ssh.c index 69927b65..000bee65 100644 --- a/ssh.c +++ b/ssh.c @@ -31,7 +31,7 @@ struct Ssh { Socket *s; - Frontend *frontend; + Seat *seat; Conf *conf; struct ssh_version_receiver version_receiver; @@ -140,7 +140,7 @@ static void ssh_connect_ppl(Ssh *ssh, PacketProtocolLayer *ppl) { ppl->bpp = ssh->bpp; ppl->user_input = &ssh->user_input; - ppl->frontend = ssh->frontend; + ppl->seat = ssh->seat; ppl->ssh = ssh; ppl->remote_bugs = ssh->remote_bugs; } @@ -282,7 +282,7 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv, ssh->base_layer->selfptr = &ssh->base_layer; ssh_ppl_setup_queues(ssh->base_layer, &ssh->bpp->in_pq, &ssh->bpp->out_pq); - update_specials_menu(ssh->frontend); + seat_update_specials_menu(ssh->seat); ssh->pinger = pinger_new(ssh->conf, &ssh->backend); queue_idempotent_callback(&ssh->bpp->ic_in_raw); @@ -408,7 +408,7 @@ void ssh_remote_error(Ssh *ssh, const char *fmt, ...) ssh_shutdown(ssh); logevent(ssh->logctx, msg); - connection_fatal(ssh->frontend, "%s", msg); + seat_connection_fatal(ssh->seat, "%s", msg); sfree(msg); } } @@ -428,7 +428,7 @@ void ssh_remote_eof(Ssh *ssh, const char *fmt, ...) logevent(ssh->logctx, msg); sfree(msg); - notify_remote_exit(ssh->frontend); + seat_notify_remote_exit(ssh->seat); } else { /* This is responding to EOF after we've already seen some * other reason for terminating the session. */ @@ -448,7 +448,7 @@ void ssh_proto_error(Ssh *ssh, const char *fmt, ...) ssh_initiate_connection_close(ssh); logevent(ssh->logctx, msg); - connection_fatal(ssh->frontend, "%s", msg); + seat_connection_fatal(ssh->seat, "%s", msg); sfree(msg); } } @@ -463,10 +463,10 @@ void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) ssh_initiate_connection_close(ssh); logevent(ssh->logctx, msg); - connection_fatal(ssh->frontend, "%s", msg); + seat_connection_fatal(ssh->seat, "%s", msg); sfree(msg); - notify_remote_exit(ssh->frontend); + seat_notify_remote_exit(ssh->seat); } } @@ -489,7 +489,7 @@ void ssh_user_close(Ssh *ssh, const char *fmt, ...) logevent(ssh->logctx, msg); sfree(msg); - notify_remote_exit(ssh->frontend); + seat_notify_remote_exit(ssh->seat); } } @@ -508,7 +508,7 @@ static void ssh_socket_log(Plug *plug, int type, SockAddr *addr, int port, */ if (!ssh->attempting_connshare) - backend_socket_log(ssh->frontend, ssh->logctx, type, addr, port, + backend_socket_log(ssh->seat, ssh->logctx, type, addr, port, error_msg, error_code, ssh->conf, ssh->session_started); } @@ -665,7 +665,7 @@ static const char *connect_to_host(Ssh *ssh, const char *host, int port, * behave in quite the usual way. */ const char *msg = "Reusing a shared connection to this server.\r\n"; - from_backend(ssh->frontend, TRUE, msg, strlen(msg)); + seat_stderr(ssh->seat, msg, strlen(msg)); } } else { /* @@ -689,7 +689,7 @@ static const char *connect_to_host(Ssh *ssh, const char *host, int port, &ssh->plug, ssh->conf); if ((err = sk_socket_error(ssh->s)) != NULL) { ssh->s = NULL; - notify_remote_exit(ssh->frontend); + seat_notify_remote_exit(ssh->seat); return err; } } @@ -788,7 +788,7 @@ static void ssh_cache_conf_values(Ssh *ssh) * * Returns an error message, or NULL on success. */ -static const char *ssh_init(Frontend *frontend, Backend **backend_handle, +static const char *ssh_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive) @@ -813,7 +813,7 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle, ssh->backend.vt = &ssh_backend; *backend_handle = &ssh->backend; - ssh->frontend = frontend; + ssh->seat = seat; ssh->cl_dummy.logctx = ssh->logctx = logctx; random_ref(); /* do this now - may be needed by sharing setup code */ @@ -1003,8 +1003,8 @@ static void ssh_special(Backend *be, SessionSpecialCode code, int arg) } /* - * This is called when stdout/stderr (the entity to which - * from_backend sends data) manages to clear some backlog. + * This is called when the seat's output channel manages to clear some + * backlog. */ static void ssh_unthrottle(Backend *be, int bufsize) { diff --git a/ssh.h b/ssh.h index ea0d6c75..0ed25b2a 100644 --- a/ssh.h +++ b/ssh.h @@ -1397,7 +1397,7 @@ enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) }; /* Shared function that writes tty modes into a pty request */ void write_ttymodes_to_packet_from_conf( - BinarySink *bs, Frontend *frontend, Conf *conf, + BinarySink *bs, Seat *seat, Conf *conf, int ssh_version, int ospeed, int ispeed); /* Shared system for allocating local SSH channel ids. Expects to be diff --git a/ssh1connection.c b/ssh1connection.c index e89e87f4..e493b513 100644 --- a/ssh1connection.c +++ b/ssh1connection.c @@ -579,8 +579,8 @@ static int ssh1_connection_filter_queue(struct ssh1_connection_state *s) case SSH1_SMSG_STDERR_DATA: data = get_string(pktin); if (!get_err(pktin)) { - int bufsize = from_backend( - s->ppl.frontend, pktin->type == SSH1_SMSG_STDERR_DATA, + int bufsize = seat_output( + s->ppl.seat, pktin->type == SSH1_SMSG_STDERR_DATA, data.ptr, data.len); if (!s->stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { s->stdout_throttling = 1; @@ -706,7 +706,7 @@ static void ssh1_connection_process_queue(PacketProtocolLayer *ppl) put_uint32(pktout, 0); /* width in pixels */ put_uint32(pktout, 0); /* height in pixels */ write_ttymodes_to_packet_from_conf( - BinarySink_UPCAST(pktout), s->ppl.frontend, s->conf, + BinarySink_UPCAST(pktout), s->ppl.seat, s->conf, 1, s->ospeed, s->ispeed); pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh1_connection_pop(s)) != NULL); diff --git a/ssh1login.c b/ssh1login.c index 0b8ea448..08282d5b 100644 --- a/ssh1login.c +++ b/ssh1login.c @@ -279,8 +279,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) "configured list"); return; } else if (s->dlgret < 0) { /* none configured; use standard handling */ - s->dlgret = verify_ssh_host_key( - s->ppl.frontend, s->savedhost, s->savedport, + s->dlgret = seat_verify_ssh_host_key( + s->ppl.seat, s->savedhost, s->savedport, "rsa", keystr, fingerprint, ssh1_login_dialog_callback, s); sfree(keystr); #ifdef FUZZING @@ -359,8 +359,9 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) /* Warn about chosen cipher if necessary. */ if (warn) { - s->dlgret = askalg(s->ppl.frontend, "cipher", cipher_string, - ssh1_login_dialog_callback, s); + s->dlgret = seat_confirm_weak_crypto_primitive( + s->ppl.seat, "cipher", cipher_string, + ssh1_login_dialog_callback, s); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); @@ -434,16 +435,17 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) ppl_logevent(("Successfully started encryption")); if ((s->username = get_remote_username(s->conf)) == NULL) { - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); - s->userpass_ret = get_userpass_input(s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; @@ -691,18 +693,19 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) ppl_printf(("No passphrase required.\r\n")); passphrase = NULL; } else { - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(s->ppl.seat); s->cur_prompt->to_server = FALSE; s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", s->publickey_comment), FALSE); - s->userpass_ret = get_userpass_input(s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; @@ -830,7 +833,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) /* * Otherwise, try various forms of password-like authentication. */ - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(s->ppl.seat); if (conf_get_int(s->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) && @@ -950,12 +953,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) * or CryptoCard exchange if we're doing TIS or CryptoCard * authentication. */ - s->userpass_ret = get_userpass_input(s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; diff --git a/ssh2connection.c b/ssh2connection.c index 16996d43..a5c293ff 100644 --- a/ssh2connection.c +++ b/ssh2connection.c @@ -1689,7 +1689,7 @@ static void ssh2_setup_pty(struct ssh2_channel *c, PktIn *pktin, void *ctx) { strbuf *modebuf = strbuf_new(); write_ttymodes_to_packet_from_conf( - BinarySink_UPCAST(modebuf), cs->ppl.frontend, cs->conf, + BinarySink_UPCAST(modebuf), cs->ppl.seat, cs->conf, 2, s->ospeed, s->ispeed); put_stringsb(pktout, modebuf); } @@ -2224,7 +2224,7 @@ static void mainchan_open_confirmation(Channel *chan) struct ssh2_connection_state *s = mc->connlayer; PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - update_specials_menu(s->ppl.frontend); + seat_update_specials_menu(s->ppl.seat); ppl_logevent(("Opened main channel")); } @@ -2248,7 +2248,7 @@ static int mainchan_send(Channel *chan, int is_stderr, assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); struct ssh2_connection_state *s = mc->connlayer; - return from_backend(s->ppl.frontend, is_stderr, data, length); + return seat_output(s->ppl.seat, is_stderr, data, length); } static void mainchan_send_eof(Channel *chan) @@ -2258,14 +2258,13 @@ static void mainchan_send_eof(Channel *chan) struct ssh2_connection_state *s = mc->connlayer; PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - if (!s->mainchan_eof_sent && - (from_backend_eof(s->ppl.frontend) || s->got_pty)) { + if (!s->mainchan_eof_sent && (seat_eof(s->ppl.seat) || s->got_pty)) { /* - * Either from_backend_eof told us that the front end wants us - * to close the outgoing side of the connection as soon as we - * see EOF from the far end, or else we've unilaterally - * decided to do that because we've allocated a remote pty and - * hence EOF isn't a particularly meaningful concept. + * Either seat_eof told us that the front end wants us to + * close the outgoing side of the connection as soon as we see + * EOF from the far end, or else we've unilaterally decided to + * do that because we've allocated a remote pty and hence EOF + * isn't a particularly meaningful concept. */ sshfwd_write_eof(mc->sc); ppl_logevent(("Sent EOF message")); diff --git a/ssh2transport.c b/ssh2transport.c index 0b1f6506..343d6787 100644 --- a/ssh2transport.c +++ b/ssh2transport.c @@ -1136,9 +1136,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) BinarySource_UPCAST(pktin)->len + 1); if (s->warn_kex) { - s->dlgret = askalg(s->ppl.frontend, "key-exchange algorithm", - s->kex_alg->name, - ssh2_transport_dialog_callback, s); + s->dlgret = seat_confirm_weak_crypto_primitive( + s->ppl.seat, "key-exchange algorithm", s->kex_alg->name, + ssh2_transport_dialog_callback, s); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at kex warning"); @@ -1153,9 +1153,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) /* * Change warning box wording depending on why we chose a * warning-level host key algorithm. If it's because - * that's all we have *cached*, use the askhk mechanism, - * and list the host keys we could usefully cross-certify. - * Otherwise, use askalg for the standard wording. + * that's all we have *cached*, list the host keys we + * could usefully cross-certify. Otherwise, use the same + * standard wording as any other weak crypto primitive. */ betteralgs = NULL; for (j = 0; j < s->n_uncert_hostkeys; j++) { @@ -1184,14 +1184,18 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) } } if (betteralgs) { - s->dlgret = askhk( - s->ppl.frontend, s->hostkey_alg->ssh_id, betteralgs, + /* Use the special warning prompt that lets us provide + * a list of better algorithms */ + s->dlgret = seat_confirm_weak_cached_hostkey( + s->ppl.seat, s->hostkey_alg->ssh_id, betteralgs, ssh2_transport_dialog_callback, s); sfree(betteralgs); } else { - s->dlgret = askalg(s->ppl.frontend, "host key type", - s->hostkey_alg->ssh_id, - ssh2_transport_dialog_callback, s); + /* If none exist, use the more general 'weak crypto' + * warning prompt */ + s->dlgret = seat_confirm_weak_crypto_primitive( + s->ppl.seat, "host key type", s->hostkey_alg->ssh_id, + ssh2_transport_dialog_callback, s); } crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { @@ -1201,10 +1205,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) } if (s->warn_cscipher) { - s->dlgret = askalg(s->ppl.frontend, - "client-to-server cipher", - s->out.cipher->name, - ssh2_transport_dialog_callback, s); + s->dlgret = seat_confirm_weak_crypto_primitive( + s->ppl.seat, "client-to-server cipher", s->out.cipher->name, + ssh2_transport_dialog_callback, s); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); @@ -1213,10 +1216,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) } if (s->warn_sccipher) { - s->dlgret = askalg(s->ppl.frontend, - "server-to-client cipher", - s->in.cipher->name, - ssh2_transport_dialog_callback, s); + s->dlgret = seat_confirm_weak_crypto_primitive( + s->ppl.seat, "server-to-client cipher", s->in.cipher->name, + ssh2_transport_dialog_callback, s); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); @@ -1309,13 +1311,13 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) /* * Now generate and send e for Diffie-Hellman. */ - set_busy_status(s->ppl.frontend, BUSY_CPU); /* this can take a while */ + seat_set_busy_status(s->ppl.seat, BUSY_CPU); s->e = dh_create_e(s->dh_ctx, s->nbits * 2); pktout = ssh_bpp_new_pktout(s->ppl.bpp, s->kex_init_value); put_mp_ssh2(pktout, s->e); pq_push(s->ppl.out_pq, pktout); - set_busy_status(s->ppl.frontend, BUSY_WAITING); /* wait for server */ + seat_set_busy_status(s->ppl.seat, BUSY_WAITING); crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != s->kex_reply_value) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " @@ -1326,7 +1328,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) pktin->type)); return; } - set_busy_status(s->ppl.frontend, BUSY_CPU); /* cogitate */ + seat_set_busy_status(s->ppl.seat, BUSY_CPU); s->hostkeydata = get_string(pktin); s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata); s->f = get_mp_ssh2(pktin); @@ -1349,7 +1351,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) /* We assume everything from now on will be quick, and it might * involve user interaction. */ - set_busy_status(s->ppl.frontend, BUSY_NOT); + seat_set_busy_status(s->ppl.seat, BUSY_NOT); put_stringpl(s->exhash, s->hostkeydata); if (dh_is_gex(s->kex_alg)) { @@ -1505,7 +1507,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) ppl_logevent(("Doing GSSAPI (with Kerberos V5) Diffie-Hellman key " "exchange with hash %s", s->kex_alg->hash->text_name)); /* Now generate e for Diffie-Hellman. */ - set_busy_status(s->ppl.frontend, BUSY_CPU); /* this can take a while */ + seat_set_busy_status(s->ppl.seat, BUSY_CPU); s->e = dh_create_e(s->dh_ctx, s->nbits * 2); if (s->shgss->lib->gsslogmsg) @@ -1664,7 +1666,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) /* We assume everything from now on will be quick, and it might * involve user interaction. */ - set_busy_status(s->ppl.frontend, BUSY_NOT); + seat_set_busy_status(s->ppl.seat, BUSY_NOT); if (!s->hkey) put_stringz(s->exhash, ""); @@ -2021,11 +2023,10 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) "configured list"); return; } else if (s->dlgret < 0) { /* none configured; use standard handling */ - s->dlgret = verify_ssh_host_key(s->ppl.frontend, - s->savedhost, s->savedport, - ssh_key_cache_id(s->hkey), - s->keystr, s->fingerprint, - ssh2_transport_dialog_callback, s); + s->dlgret = seat_verify_ssh_host_key( + s->ppl.seat, s->savedhost, s->savedport, + ssh_key_cache_id(s->hkey), s->keystr, s->fingerprint, + ssh2_transport_dialog_callback, s); #ifdef FUZZING s->dlgret = 1; #endif @@ -2205,7 +2206,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) * Update the specials menu to list the remaining uncertified host * keys. */ - update_specials_menu(s->ppl.frontend); + seat_update_specials_menu(s->ppl.seat); /* * Key exchange is over. Loop straight back round if we have a diff --git a/ssh2userauth.c b/ssh2userauth.c index 1be98be2..10a2d171 100644 --- a/ssh2userauth.c +++ b/ssh2userauth.c @@ -362,16 +362,17 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * it again. */ } else if ((s->username = s->default_username) == NULL) { - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH login name"); add_prompt(s->cur_prompt, dupstr("login as: "), TRUE); - s->userpass_ret = get_userpass_input(s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; @@ -382,7 +383,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) } if (!s->userpass_ret) { /* - * get_userpass_input() failed to get a username. + * seat_get_userpass_input() failed to get a username. * Terminate. */ free_prompts(s->cur_prompt); @@ -453,7 +454,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * * The banner data has been sanitised already by this * point, so we can safely pass it straight to - * from_backend. + * seat_stderr. */ if (bufchain_size(&s->banner) && (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) { @@ -461,7 +462,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) void *data; int len; bufchain_prefix(&s->banner, &data, &len); - from_backend(s->ppl.frontend, TRUE, data, len); + seat_stderr(s->ppl.seat, data, len); bufchain_consume(&s->banner, len); } } @@ -766,20 +767,21 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) /* * Get a passphrase from the user. */ - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(); s->cur_prompt->to_server = FALSE; s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%.100s\": ", s->publickey_comment), FALSE); - s->userpass_ret = get_userpass_input( - s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, + s->ppl.user_input); if (s->userpass_ret >= 0) break; @@ -1089,7 +1091,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) name = get_string(pktin); inst = get_string(pktin); get_string(pktin); /* skip language tag */ - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(); s->cur_prompt->to_server = TRUE; /* @@ -1144,12 +1146,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * Display any instructions, and get the user's * response(s). */ - s->userpass_ret = get_userpass_input(s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; @@ -1213,19 +1216,20 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) s->ppl.bpp->pls->actx = SSH2_PKTCTX_PASSWORD; - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", s->username, s->hostname), FALSE); - s->userpass_ret = get_userpass_input(s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; @@ -1306,7 +1310,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) prompt = get_string(pktin); - s->cur_prompt = new_prompts(s->ppl.frontend); + s->cur_prompt = new_prompts(); s->cur_prompt->to_server = TRUE; s->cur_prompt->name = dupstr("New SSH password"); s->cur_prompt->instruction = mkstr(prompt); @@ -1336,13 +1340,14 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * password twice. */ while (!got_new) { - s->userpass_ret = get_userpass_input( - s->cur_prompt, NULL); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) - s->userpass_ret = get_userpass_input( - s->cur_prompt, s->ppl.user_input); + s->userpass_ret = seat_get_userpass_input( + s->ppl.seat, s->cur_prompt, + s->ppl.user_input); if (s->userpass_ret >= 0) break; diff --git a/sshcommon.c b/sshcommon.c index 16cf9310..6095930a 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -345,7 +345,7 @@ int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof) */ void write_ttymodes_to_packet_from_conf( - BinarySink *bs, Frontend *frontend, Conf *conf, + BinarySink *bs, Seat *seat, Conf *conf, int ssh_version, int ospeed, int ispeed) { int i; @@ -446,7 +446,7 @@ void write_ttymodes_to_packet_from_conf( * mode. */ if (sval[0] == 'A') { - sval = to_free = get_ttymode(frontend, mode->mode); + sval = to_free = seat_get_ttymode(seat, mode->mode); } else if (sval[0] == 'V') { sval++; /* skip the 'V' */ } else { @@ -644,7 +644,7 @@ void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new) ssh_ppl_setup_queues(new, old->in_pq, old->out_pq); new->selfptr = old->selfptr; new->user_input = old->user_input; - new->frontend = old->frontend; + new->seat = old->seat; new->ssh = old->ssh; *new->selfptr = new; @@ -689,7 +689,7 @@ void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text) /* Messages sent via this function are from the SSH layer, not * from the server-side process, so they always have the stderr * flag set. */ - from_backend(ppl->frontend, TRUE, text, strlen(text)); + seat_stderr(ppl->seat, text, strlen(text)); sfree(text); } diff --git a/sshppl.h b/sshppl.h index f637b0e0..2536c0ec 100644 --- a/sshppl.h +++ b/sshppl.h @@ -54,7 +54,7 @@ struct PacketProtocolLayer { /* Logging and error-reporting facilities. */ LogContext *logctx; - void *frontend; /* for dialog boxes etc */ + Seat *seat; /* for dialog boxes, session output etc */ Ssh *ssh; /* for session termination + assorted connection-layer ops */ /* Known bugs in the remote implementation. */ diff --git a/telnet.c b/telnet.c index 76d8a943..bdb24e2f 100644 --- a/telnet.c +++ b/telnet.c @@ -173,7 +173,7 @@ struct Telnet { Socket *s; int closed_on_socket_error; - Frontend *frontend; + Seat *seat; LogContext *logctx; Ldisc *ldisc; int term_width, term_height; @@ -209,7 +209,7 @@ struct Telnet { static void c_write(Telnet *telnet, const void *buf, int len) { int backlog; - backlog = from_backend(telnet->frontend, 0, buf, len); + backlog = seat_stdout(telnet->seat, buf, len); sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG); } @@ -627,7 +627,7 @@ static void telnet_log(Plug *plug, int type, SockAddr *addr, int port, const char *error_msg, int error_code) { Telnet *telnet = container_of(plug, Telnet, plug); - backend_socket_log(telnet->frontend, telnet->logctx, type, addr, port, + backend_socket_log(telnet->seat, telnet->logctx, type, addr, port, error_msg, error_code, telnet->conf, telnet->session_started); } @@ -648,11 +648,11 @@ static void telnet_closing(Plug *plug, const char *error_msg, int error_code, telnet->s = NULL; if (error_msg) telnet->closed_on_socket_error = TRUE; - notify_remote_exit(telnet->frontend); + seat_notify_remote_exit(telnet->seat); } if (error_msg) { logevent(telnet->logctx, error_msg); - connection_fatal(telnet->frontend, "%s", error_msg); + seat_connection_fatal(telnet->seat, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ } @@ -687,7 +687,7 @@ static const PlugVtable Telnet_plugvt = { * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *telnet_init(Frontend *frontend, Backend **backend_handle, +static const char *telnet_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive) @@ -709,7 +709,7 @@ static const char *telnet_init(Frontend *frontend, Backend **backend_handle, telnet->activated = FALSE; telnet->sb_buf = NULL; telnet->sb_size = 0; - telnet->frontend = frontend; + telnet->seat = seat; telnet->logctx = logctx; telnet->term_width = conf_get_int(telnet->conf, CONF_width); telnet->term_height = conf_get_int(telnet->conf, CONF_height); @@ -770,7 +770,7 @@ static const char *telnet_init(Frontend *frontend, Backend **backend_handle, /* * We can send special commands from the start. */ - update_specials_menu(telnet->frontend); + seat_update_specials_menu(telnet->seat); /* * loghost overrides realhost, if specified. diff --git a/terminal.h b/terminal.h index f0727cf2..be3c589f 100644 --- a/terminal.h +++ b/terminal.h @@ -249,12 +249,12 @@ struct terminal_tag { Conf *conf; /* - * from_backend calls term_out, but it can also be called from - * the ldisc if the ldisc is called _within_ term_out. So we - * have to guard against re-entrancy - if from_backend is - * called recursively like this, it will simply add data to the - * end of the buffer term_out is in the process of working - * through. + * GUI implementations of seat_output call term_out, but it can + * also be called from the ldisc if the ldisc is called _within_ + * term_out. So we have to guard against re-entrancy - if + * seat_output is called recursively like this, it will simply add + * data to the end of the buffer term_out is in the process of + * working through. */ int in_term_out; diff --git a/testback.c b/testback.c index 6a89bfc5..b54c682e 100644 --- a/testback.c +++ b/testback.c @@ -32,10 +32,10 @@ #include "putty.h" -static const char *null_init(Frontend *, Backend **, Conf *, const char *, int, - char **, int, int); -static const char *loop_init(Frontend *, Backend **, Conf *, const char *, int, - char **, int, int); +static const char *null_init(Seat *, Backend **, LogContext *, Conf *, + const char *, int, char **, int, int); +static const char *loop_init(Seat *, Backend **, LogContext *, Conf *, + const char *, int, char **, int, int); static void null_free(Backend *); static void loop_free(Backend *); static void null_reconfig(Backend *, Conf *); @@ -68,23 +68,25 @@ const struct BackendVtable loop_backend = { }; struct loop_state { - Frontend *frontend; + Seat *seat; Backend backend; }; -static const char *null_init(Frontend *frontend, Backend **backend_handle, - Conf *conf, const char *host, int port, - char **realhost, int nodelay, int keepalive) { +static const char *null_init(Seat *seat, Backend **backend_handle, + LogContext *logctx, Conf *conf, + const char *host, int port, char **realhost, + int nodelay, int keepalive) { *backend_handle = NULL; return NULL; } -static const char *loop_init(Frontend *frontend, Backend **backend_handle, - Conf *conf, const char *host, int port, - char **realhost, int nodelay, int keepalive) { +static const char *loop_init(Seat *seat, Backend **backend_handle, + LogContext *logctx, Conf *conf, + const char *host, int port, char **realhost, + int nodelay, int keepalive) { struct loop_state *st = snew(struct loop_state); - st->frontend = frontend; + st->seat = seat; *backend_handle = &st->backend; return NULL; } @@ -113,7 +115,7 @@ static int null_send(Backend *be, const char *buf, int len) { static int loop_send(Backend *be, const char *buf, int len) { struct loop_state *st = container_of(be, struct loop_state, backend); - return from_backend(st->frontend, 0, buf, len); + return seat_output(st->seat, 0, buf, len); } static int null_sendbuffer(Backend *be) { @@ -162,10 +164,6 @@ static void null_provide_ldisc (Backend *be, Ldisc *ldisc) { } -static void null_provide_logctx(Backend *be, LogContext *logctx) { - -} - static int null_cfg_info(Backend *be) { return 0; diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index e9d2d6c4..39c91c61 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -3416,7 +3416,7 @@ struct verify_ssh_host_key_result_ctx { char *keystr; void (*callback)(void *callback_ctx, int result); void *callback_ctx; - Frontend *frontend; + Seat *seat; }; static void verify_ssh_host_key_result_callback(void *vctx, int result) @@ -3449,7 +3449,7 @@ static void verify_ssh_host_key_result_callback(void *vctx, int result) * Clean up this context structure, whether or not a result was * ever actually delivered from the dialog box. */ - unregister_dialog(ctx->frontend, DIALOG_SLOT_NETWORK_PROMPT); + unregister_dialog(ctx->seat, DIALOG_SLOT_NETWORK_PROMPT); sfree(ctx->host); sfree(ctx->keytype); @@ -3457,9 +3457,10 @@ static void verify_ssh_host_key_result_callback(void *vctx, int result) sfree(ctx); } -int verify_ssh_host_key(Frontend *frontend, char *host, int port, - const char *keytype, char *keystr, char *fingerprint, - void (*callback)(void *ctx, int result), void *ctx) +int gtk_seat_verify_ssh_host_key( + Seat *seat, const 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 " @@ -3518,13 +3519,13 @@ int verify_ssh_host_key(Frontend *frontend, char *host, int port, result_ctx->port = port; result_ctx->keytype = dupstr(keytype); result_ctx->keystr = dupstr(keystr); - result_ctx->frontend = frontend; + result_ctx->seat = seat; - mainwin = GTK_WIDGET(get_window(frontend)); + mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); msgbox = create_message_box( mainwin, "PuTTY Security Alert", text, string_width(fingerprint), TRUE, &buttons_hostkey, verify_ssh_host_key_result_callback, result_ctx); - register_dialog(frontend, DIALOG_SLOT_NETWORK_PROMPT, msgbox); + register_dialog(seat, DIALOG_SLOT_NETWORK_PROMPT, msgbox); sfree(text); @@ -3534,7 +3535,7 @@ int verify_ssh_host_key(Frontend *frontend, char *host, int port, struct simple_prompt_result_ctx { void (*callback)(void *callback_ctx, int result); void *callback_ctx; - Frontend *frontend; + Seat *seat; enum DialogSlot dialog_slot; }; @@ -3550,7 +3551,7 @@ static void simple_prompt_result_callback(void *vctx, int result) * Clean up this context structure, whether or not a result was * ever actually delivered from the dialog box. */ - unregister_dialog(ctx->frontend, ctx->dialog_slot); + unregister_dialog(ctx->seat, ctx->dialog_slot); sfree(ctx); } @@ -3558,8 +3559,9 @@ static void simple_prompt_result_callback(void *vctx, int result) * Ask whether the selected algorithm is acceptable (since it was * below the configured 'warn' threshold). */ -int askalg(Frontend *frontend, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) +int gtk_seat_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx) { static const char msg[] = "The first %s supported by the server is " @@ -3575,23 +3577,24 @@ int askalg(Frontend *frontend, const char *algtype, const char *algname, result_ctx = snew(struct simple_prompt_result_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; - result_ctx->frontend = frontend; + result_ctx->seat = seat; result_ctx->dialog_slot = DIALOG_SLOT_NETWORK_PROMPT; - mainwin = GTK_WIDGET(get_window(frontend)); + mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); msgbox = create_message_box( mainwin, "PuTTY Security Alert", text, string_width("Reasonably long line of text as a width template"), FALSE, &buttons_yn, simple_prompt_result_callback, result_ctx); - register_dialog(frontend, result_ctx->dialog_slot, msgbox); + register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(text); return -1; /* dialog still in progress */ } -int askhk(Frontend *frontend, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) +int gtk_seat_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx) { static const char msg[] = "The first host key type we have stored for this server\n" @@ -3610,16 +3613,16 @@ int askhk(Frontend *frontend, const char *algname, const char *betteralgs, result_ctx = snew(struct simple_prompt_result_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; - result_ctx->frontend = frontend; + result_ctx->seat = seat; result_ctx->dialog_slot = DIALOG_SLOT_NETWORK_PROMPT; - mainwin = GTK_WIDGET(get_window(frontend)); + mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); msgbox = create_message_box( mainwin, "PuTTY Security Alert", text, string_width("is ecdsa-nistp521, which is below the configured" " warning threshold."), FALSE, &buttons_yn, simple_prompt_result_callback, result_ctx); - register_dialog(frontend, result_ctx->dialog_slot, msgbox); + register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(text); @@ -4048,7 +4051,7 @@ void logevent_dlg(eventlog_stuff *es, const char *string) } } -int gtkdlg_askappend(Frontend *frontend, Filename *filename, +int gtkdlg_askappend(Seat *seat, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = @@ -4076,15 +4079,15 @@ int gtkdlg_askappend(Frontend *frontend, Filename *filename, result_ctx = snew(struct simple_prompt_result_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; - result_ctx->frontend = frontend; + result_ctx->seat = seat; result_ctx->dialog_slot = DIALOG_SLOT_LOGFILE_PROMPT; - mainwin = GTK_WIDGET(get_window(frontend)); + mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); msgbox = create_message_box( mainwin, mbtitle, message, string_width("LINE OF TEXT SUITABLE FOR THE ASKAPPEND WIDTH"), FALSE, &buttons_append, simple_prompt_result_callback, result_ctx); - register_dialog(frontend, result_ctx->dialog_slot, msgbox); + register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(message); sfree(mbtitle); diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 1b56d859..63ae311c 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -152,7 +152,7 @@ struct Frontend { int width, height, scale; int ignore_sbar; int mouseptr_visible; - int busy_status; + BusyStatus busy_status; int alt_keycode; int alt_digits; char *wintitle; @@ -181,6 +181,7 @@ struct Frontend { int system_mod_mask; #endif + Seat seat; LogPolicy logpolicy; }; @@ -222,7 +223,7 @@ static void post_fatal_message_box_toplevel(void *vctx) static void post_fatal_message_box(void *vctx, int result) { Frontend *inst = (Frontend *)vctx; - unregister_dialog(inst, DIALOG_SLOT_CONNECTION_FATAL); + unregister_dialog(&inst->seat, DIALOG_SLOT_CONNECTION_FATAL); queue_toplevel_callback(post_fatal_message_box_toplevel, inst); } @@ -234,7 +235,7 @@ static void common_connfatal_message_box( inst->window, title, msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), FALSE, &buttons_ok, postfn, inst); - register_dialog(inst, DIALOG_SLOT_CONNECTION_FATAL, dialog); + register_dialog(&inst->seat, DIALOG_SLOT_CONNECTION_FATAL, dialog); sfree(title); } @@ -252,22 +253,17 @@ static void connection_fatal_callback(void *vctx) static void post_nonfatal_message_box(void *vctx, int result) { Frontend *inst = (Frontend *)vctx; - unregister_dialog(inst, DIALOG_SLOT_CONNECTION_FATAL); + unregister_dialog(&inst->seat, DIALOG_SLOT_CONNECTION_FATAL); } -void connection_fatal(Frontend *inst, const char *p, ...) +static void gtk_seat_connection_fatal(Seat *seat, const char *msg) { - va_list ap; - char *msg; - va_start(ap, p); - msg = dupvprintf(p, ap); - va_end(ap); + Frontend *inst = container_of(seat, Frontend, seat); if (conf_get_int(inst->conf, CONF_close_on_exit) == FORCE_ON) { fatal_message_box(inst, msg); } else { common_connfatal_message_box(inst, msg, post_nonfatal_message_box); } - sfree(msg); inst->exited = TRUE; /* suppress normal exit handling */ queue_toplevel_callback(connection_fatal_callback, inst); @@ -308,29 +304,29 @@ int platform_default_i(const char *name, int def) return def; } -/* Dummy routine, only required in plink. */ -void frontend_echoedit_update(Frontend *inst, int echo, int edit) -{ -} - -char *get_ttymode(Frontend *inst, const char *mode) +static char *gtk_seat_get_ttymode(Seat *seat, const char *mode) { + Frontend *inst = container_of(seat, Frontend, seat); return term_get_ttymode(inst->term, mode); } -int from_backend(Frontend *inst, int is_stderr, const void *data, int len) +static int gtk_seat_output(Seat *seat, int is_stderr, + const void *data, int len) { + Frontend *inst = container_of(seat, Frontend, seat); return term_data(inst->term, is_stderr, data, len); } -int from_backend_eof(Frontend *inst) +static int gtk_seat_eof(Seat *seat) { + /* Frontend *inst = container_of(seat, Frontend, seat); */ return TRUE; /* do respond to incoming EOF with outgoing */ } -int get_userpass_input(prompts_t *p, bufchain *input) +static int gtk_seat_get_userpass_input(Seat *seat, prompts_t *p, + bufchain *input) { - Frontend *inst = p->frontend; + Frontend *inst = container_of(seat, Frontend, seat); int ret; ret = cmdline_get_passwd_input(p); if (ret == -1) @@ -338,6 +334,51 @@ int get_userpass_input(prompts_t *p, bufchain *input) return ret; } +static int gtk_seat_is_utf8(Seat *seat) +{ + Frontend *inst = container_of(seat, Frontend, seat); + return frontend_is_utf8(inst); +} + +static int gtk_seat_get_char_cell_size(Seat *seat, int *w, int *h) +{ + Frontend *inst = container_of(seat, Frontend, seat); + *w = inst->font_width; + *h = inst->font_height; + return TRUE; +} + +static void gtk_seat_notify_remote_exit(Seat *seat); +static void gtk_seat_update_specials_menu(Seat *seat); +static void gtk_seat_set_busy_status(Seat *seat, BusyStatus status); +static const char *gtk_seat_get_x_display(Seat *seat); +#ifndef NOT_X_WINDOWS +static int gtk_seat_get_windowid(Seat *seat, long *id); +#endif + +static const SeatVtable gtk_seat_vt = { + gtk_seat_output, + gtk_seat_eof, + gtk_seat_get_userpass_input, + gtk_seat_notify_remote_exit, + gtk_seat_connection_fatal, + gtk_seat_update_specials_menu, + gtk_seat_get_ttymode, + gtk_seat_set_busy_status, + gtk_seat_verify_ssh_host_key, + gtk_seat_confirm_weak_crypto_primitive, + gtk_seat_confirm_weak_cached_hostkey, + gtk_seat_is_utf8, + nullseat_echoedit_update, + gtk_seat_get_x_display, +#ifdef NOT_X_WINDOWS + nullseat_get_windowid, +#else + gtk_seat_get_windowid, +#endif + gtk_seat_get_char_cell_size, +}; + static void gtk_eventlog(LogPolicy *lp, const char *string) { Frontend *inst = container_of(lp, Frontend, logpolicy); @@ -348,10 +389,7 @@ static int gtk_askappend(LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { Frontend *inst = container_of(lp, Frontend, logpolicy); - - int gtkdlg_askappend(Frontend *frontend, Filename *filename, - void (*callback)(void *ctx, int result), void *ctx); - return gtkdlg_askappend(inst, filename, callback, ctx); + return gtkdlg_askappend(&inst->seat, filename, callback, ctx); } static void gtk_logging_error(LogPolicy *lp, const char *event) @@ -360,8 +398,8 @@ static void gtk_logging_error(LogPolicy *lp, const char *event) /* Send 'can't open log file' errors to the terminal window. * (Marked as stderr, although terminal.c won't care.) */ - from_backend(inst, 1, event, strlen(event)); - from_backend(inst, 1, "\r\n", 2); + seat_stderr(&inst->seat, event, strlen(event)); + seat_stderr(&inst->seat, "\r\n", 2); } static const LogPolicyVtable gtk_logpolicy_vt = { @@ -370,14 +408,6 @@ static const LogPolicyVtable gtk_logpolicy_vt = { gtk_logging_error, }; -int font_dimension(Frontend *inst, int which) /* 0 for width, 1 for height */ -{ - if (which) - return inst->font_height; - else - return inst->font_width; -} - /* * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT) * into a cooked one (SELECT, EXTEND, PASTE). @@ -402,8 +432,9 @@ static Mouse_Button translate_button(Mouse_Button button) * Return the top-level GtkWindow associated with a particular * front end instance. */ -GtkWidget *get_window(Frontend *inst) +GtkWidget *gtk_seat_get_window(Seat *seat) { + Frontend *inst = container_of(seat, Frontend, seat); return inst->window; } @@ -412,14 +443,20 @@ GtkWidget *get_window(Frontend *inst) * network code wanting to ask an asynchronous user question (e.g. * 'what about this dodgy host key, then?'). */ -void register_dialog(Frontend *inst, enum DialogSlot slot, GtkWidget *dialog) +void register_dialog(Seat *seat, enum DialogSlot slot, GtkWidget *dialog) { + Frontend *inst; + assert(seat->vt == >k_seat_vt); + inst = container_of(seat, Frontend, seat); assert(slot < DIALOG_SLOT_LIMIT); assert(!inst->dialogs[slot]); inst->dialogs[slot] = dialog; } -void unregister_dialog(Frontend *inst, enum DialogSlot slot) +void unregister_dialog(Seat *seat, enum DialogSlot slot) { + Frontend *inst; + assert(seat->vt == >k_seat_vt); + inst = container_of(seat, Frontend, seat); assert(slot < DIALOG_SLOT_LIMIT); assert(inst->dialogs[slot]); inst->dialogs[slot] = NULL; @@ -570,7 +607,7 @@ char *get_window_title(Frontend *inst, int icon) static void warn_on_close_callback(void *vctx, int result) { Frontend *inst = (Frontend *)vctx; - unregister_dialog(inst, DIALOG_SLOT_WARN_ON_CLOSE); + unregister_dialog(&inst->seat, DIALOG_SLOT_WARN_ON_CLOSE); if (result) gtk_widget_destroy(inst->window); } @@ -600,7 +637,7 @@ gint delete_window(GtkWidget *widget, GdkEvent *event, Frontend *inst) "Are you sure you want to close this session?", string_width("Most of the width of the above text"), FALSE, &buttons_yn, warn_on_close_callback, inst); - register_dialog(inst, DIALOG_SLOT_WARN_ON_CLOSE, dialog); + register_dialog(&inst->seat, DIALOG_SLOT_WARN_ON_CLOSE, dialog); sfree(title); } return TRUE; @@ -2435,8 +2472,9 @@ static void exit_callback(void *vctx) } } -void notify_remote_exit(Frontend *inst) +static void gtk_seat_notify_remote_exit(Seat *seat) { + Frontend *inst = container_of(seat, Frontend, seat); queue_toplevel_callback(exit_callback, inst); } @@ -2454,7 +2492,7 @@ static void destroy_inst_connection(Frontend *inst) if (inst->term) term_provide_backend(inst->term, NULL); if (inst->menu) { - update_specials_menu(inst); + seat_update_specials_menu(&inst->seat); gtk_widget_set_sensitive(inst->restartitem, TRUE); } } @@ -2537,8 +2575,9 @@ gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data) return FALSE; } -void set_busy_status(Frontend *inst, int status) +static void gtk_seat_set_busy_status(Seat *seat, BusyStatus status) { + Frontend *inst = container_of(seat, Frontend, seat); inst->busy_status = status; update_mouseptr(inst); } @@ -4190,14 +4229,15 @@ void modalfatalbox(const char *p, ...) exit(1); } -const char *get_x_display(Frontend *frontend) +static const char *gtk_seat_get_x_display(Seat *seat) { return gdk_get_display(); } #ifndef NOT_X_WINDOWS -int get_windowid(Frontend *inst, long *id) +static int gtk_seat_get_windowid(Seat *seat, long *id) { + Frontend *inst = container_of(seat, Frontend, seat); GdkWindow *window = gtk_widget_get_window(inst->area); if (!GDK_IS_X11_WINDOW(window)) return FALSE; @@ -4583,7 +4623,7 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) title, ctx->newconf, 1, inst->backend ? backend_cfg_info(inst->backend) : 0, after_change_settings_dialog, ctx); - register_dialog(inst, DIALOG_SLOT_RECONFIGURE, dialog); + register_dialog(&inst->seat, DIALOG_SLOT_RECONFIGURE, dialog); sfree(title); } @@ -4613,7 +4653,7 @@ static void after_change_settings_dialog(void *vctx, int retval) assert(lenof(ww) == NCFGCOLOURS); - unregister_dialog(inst, DIALOG_SLOT_RECONFIGURE); + unregister_dialog(&inst->seat, DIALOG_SLOT_RECONFIGURE); if (retval) { inst->conf = newconf; @@ -4979,8 +5019,9 @@ void set_window_icon(GtkWidget *window, const char *const *const *icon, static void free_special_cmd(gpointer data) { sfree(data); } -void update_specials_menu(Frontend *inst) +static void gtk_seat_update_specials_menu(Seat *seat) { + Frontend *inst = container_of(seat, Frontend, seat); const SessionSpecial *specials; if (inst->backend) @@ -5055,7 +5096,7 @@ static void start_backend(Frontend *inst) vt = select_backend(inst->conf); - error = backend_init(vt, (void *)inst, &inst->backend, + error = backend_init(vt, &inst->seat, &inst->backend, inst->logctx, inst->conf, conf_get_str(inst->conf, CONF_host), conf_get_int(inst->conf, CONF_port), @@ -5067,7 +5108,7 @@ static void start_backend(Frontend *inst) char *msg = dupprintf("Unable to open connection to %s:\n%s", conf_dest(inst->conf), error); inst->exited = TRUE; - connection_fatal(inst, msg); + seat_connection_fatal(&inst->seat, msg); sfree(msg); return; } @@ -5084,7 +5125,8 @@ static void start_backend(Frontend *inst) term_provide_backend(inst->term, inst->backend); - inst->ldisc = ldisc_create(inst->conf, inst->term, inst->backend, inst); + inst->ldisc = ldisc_create(inst->conf, inst->term, inst->backend, + &inst->seat); gtk_widget_set_sensitive(inst->restartitem, FALSE); } @@ -5139,6 +5181,7 @@ void new_session_window(Conf *conf, const char *geometry_string) #endif inst->drawing_area_setup_needed = TRUE; + inst->seat.vt = >k_seat_vt; inst->logpolicy.vt = >k_logpolicy_vt; #ifndef NOT_X_WINDOWS diff --git a/unix/unix.h b/unix/unix.h index c85c8470..f222928e 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -191,14 +191,8 @@ enum MenuAction { }; void app_menu_action(Frontend *frontend, enum MenuAction); -/* Things pty.c needs from pterm.c */ -const char *get_x_display(Frontend *frontend); -int font_dimension(Frontend *frontend, int which);/* 0 for width, 1 for height */ -int get_windowid(Frontend *frontend, long *id); - /* Things gtkdlg.c needs from pterm.c */ #ifdef MAY_REFER_TO_GTK_IN_HEADERS -GtkWidget *get_window(Frontend *frontend); enum DialogSlot { DIALOG_SLOT_RECONFIGURE, DIALOG_SLOT_NETWORK_PROMPT, @@ -207,8 +201,9 @@ enum DialogSlot { DIALOG_SLOT_CONNECTION_FATAL, DIALOG_SLOT_LIMIT /* must remain last */ }; -void register_dialog(Frontend *frontend, enum DialogSlot slot, GtkWidget *dialog); -void unregister_dialog(Frontend *frontend, enum DialogSlot slot); +GtkWidget *gtk_seat_get_window(Seat *seat); +void register_dialog(Seat *seat, enum DialogSlot slot, GtkWidget *dialog); +void unregister_dialog(Seat *seat, enum DialogSlot slot); #endif /* Things pterm.c needs from gtkdlg.c */ @@ -224,6 +219,18 @@ eventlog_stuff *eventlogstuff_new(void); void eventlogstuff_free(eventlog_stuff *); void showeventlog(eventlog_stuff *estuff, void *parentwin); void logevent_dlg(eventlog_stuff *estuff, const char *string); +int gtkdlg_askappend(Seat *seat, Filename *filename, + void (*callback)(void *ctx, int result), void *ctx); +int gtk_seat_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *fingerprint, + void (*callback)(void *ctx, int result), void *ctx); +int gtk_seat_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx); +int gtk_seat_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx); #ifdef MAY_REFER_TO_GTK_IN_HEADERS struct message_box_button { const char *title; diff --git a/unix/uxcons.c b/unix/uxcons.c index fb75743c..b44d34b8 100644 --- a/unix/uxcons.c +++ b/unix/uxcons.c @@ -61,16 +61,58 @@ void cleanup_exit(int code) exit(code); } -void set_busy_status(Frontend *frontend, int status) +/* + * Various error message and/or fatal exit functions. + */ +void console_print_error_msg(const char *prefix, const char *msg) { + struct termios cf; + premsg(&cf); + fputs(prefix, stderr); + fputs(": ", stderr); + fputs(msg, stderr); + fputc('\n', stderr); + fflush(stderr); + postmsg(&cf); } -void update_specials_menu(Frontend *frontend) +void console_print_error_msg_fmt_v( + const char *prefix, const char *fmt, va_list ap) { + char *msg = dupvprintf(fmt, ap); + console_print_error_msg(prefix, msg); + sfree(msg); } -void notify_remote_exit(Frontend *frontend) +void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v(prefix, fmt, ap); + va_end(ap); +} + +void modalfatalbox(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v("FATAL ERROR", fmt, ap); + va_end(ap); + cleanup_exit(1); +} + +void nonfatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v("ERROR", fmt, ap); + va_end(ap); +} + +void console_connection_fatal(Seat *seat, const char *msg) +{ + console_print_error_msg("FATAL ERROR", msg); + cleanup_exit(1); } void timer_change_notify(unsigned long next) @@ -111,9 +153,10 @@ static int block_and_read(int fd, void *buf, size_t len) return ret; } -int verify_ssh_host_key(Frontend *frontend, char *host, int port, - const char *keytype, char *keystr, char *fingerprint, - void (*callback)(void *ctx, int result), void *ctx) +int console_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *fingerprint, + void (*callback)(void *ctx, int result), void *ctx) { int ret; @@ -217,12 +260,9 @@ int verify_ssh_host_key(Frontend *frontend, char *host, int port, } } -/* - * Ask whether the selected algorithm is acceptable (since it was - * below the configured 'warn' threshold). - */ -int askalg(Frontend *frontend, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) +int console_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx) { static const char msg[] = "The first %s supported by the server is\n" @@ -268,8 +308,9 @@ int askalg(Frontend *frontend, const char *algtype, const char *algname, } } -int askhk(Frontend *frontend, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) +int console_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx) { static const char msg[] = "The first host key type we have stored for this server\n" diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index a3f2356e..06efb0bc 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -23,43 +23,12 @@ SockAddr *unix_sock_addr(const char *path); Socket *new_unix_listener(SockAddr *listenaddr, Plug *plug); -void modalfatalbox(const char *p, ...) +void cmdline_error(const char *fmt, ...) { va_list ap; - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); + va_start(ap, fmt); + console_print_error_msg_fmt_v("pageant", fmt, ap); va_end(ap); - fputc('\n', stderr); - exit(1); -} -void nonfatal(const char *p, ...) -{ - va_list ap; - fprintf(stderr, "ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); -} -void connection_fatal(Frontend *frontend, const char *p, ...) -{ - va_list ap; - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); - exit(1); -} -void cmdline_error(const char *p, ...) -{ - va_list ap; - fprintf(stderr, "pageant: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); exit(1); } @@ -92,8 +61,6 @@ int platform_default_i(const char *name, int def) { return def; } FontSpec *platform_default_fontspec(const char *name) { return fontspec_new(""); } Filename *platform_default_filename(const char *name) { return filename_from_str(""); } char *x_get_default(const char *key) { return NULL; } -int from_backend(Frontend *fe, int is_stderr, const void *data, int datalen) -{ assert(!"only here to satisfy notional call from backend_socket_log"); } /* * Short description of parameters. @@ -338,7 +305,7 @@ enum { static char *askpass_tty(const char *prompt) { int ret; - prompts_t *p = new_prompts(NULL); + prompts_t *p = new_prompts(); p->to_server = FALSE; p->name = dupstr("Pageant passphrase prompt"); add_prompt(p, dupcat(prompt, ": ", (const char *)NULL), FALSE); diff --git a/unix/uxplink.c b/unix/uxplink.c index c26fd43f..6f3e7bea 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -29,63 +29,12 @@ static LogContext *logctx; static struct termios orig_termios; -void modalfatalbox(const char *p, ...) +void cmdline_error(const char *fmt, ...) { - struct termios cf; va_list ap; - premsg(&cf); - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); + va_start(ap, fmt); + console_print_error_msg_fmt_v("plink", fmt, ap); va_end(ap); - fputc('\n', stderr); - postmsg(&cf); - if (logctx) { - log_free(logctx); - logctx = NULL; - } - cleanup_exit(1); -} -void nonfatal(const char *p, ...) -{ - struct termios cf; - va_list ap; - premsg(&cf); - fprintf(stderr, "ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); - postmsg(&cf); -} -void connection_fatal(Frontend *frontend, const char *p, ...) -{ - struct termios cf; - va_list ap; - premsg(&cf); - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); - postmsg(&cf); - if (logctx) { - log_free(logctx); - logctx = NULL; - } - cleanup_exit(1); -} -void cmdline_error(const char *p, ...) -{ - struct termios cf; - va_list ap; - premsg(&cf); - fprintf(stderr, "plink: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); - postmsg(&cf); exit(1); } @@ -132,7 +81,7 @@ int term_ldisc(Terminal *term, int mode) { return FALSE; } -void frontend_echoedit_update(Frontend *frontend, int echo, int edit) +static void plink_echoedit_update(Seat *seat, int echo, int edit) { /* Update stdin read mode to reflect changes in line discipline. */ struct termios mode; @@ -158,7 +107,7 @@ void frontend_echoedit_update(Frontend *frontend, int echo, int edit) mode.c_cc[VMIN] = 1; mode.c_cc[VTIME] = 0; /* FIXME: perhaps what we do with IXON/IXOFF should be an - * argument to frontend_echoedit_update(), to allow + * argument to the echoedit_update() method, to allow * implementation of SSH-2 "xon-xoff" and Rlogin's * equivalent? */ mode.c_iflag &= ~IXON; @@ -190,7 +139,7 @@ static char *get_ttychar(struct termios *t, int index) return dupprintf("^<%d>", c); } -char *get_ttymode(Frontend *frontend, const char *mode) +static char *plink_get_ttymode(Seat *seat, const char *mode) { /* * Propagate appropriate terminal modes from the local terminal, @@ -400,8 +349,7 @@ int try_output(int is_stderr) return bufchain_size(&stdout_data) + bufchain_size(&stderr_data); } -int from_backend(Frontend *frontend, int is_stderr, - const void *data, int len) +static int plink_output(Seat *seat, int is_stderr, const void *data, int len) { if (is_stderr) { bufchain_add(&stderr_data, data, len); @@ -413,7 +361,7 @@ int from_backend(Frontend *frontend, int is_stderr, } } -int from_backend_eof(Frontend *frontend) +static int plink_eof(Seat *seat) { assert(outgoingeof == EOF_NO); outgoingeof = EOF_PENDING; @@ -421,7 +369,7 @@ int from_backend_eof(Frontend *frontend) return FALSE; /* do not respond to incoming EOF with outgoing */ } -int get_userpass_input(prompts_t *p, bufchain *input) +static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); @@ -430,6 +378,26 @@ int get_userpass_input(prompts_t *p, bufchain *input) return ret; } +static const SeatVtable plink_seat_vt = { + plink_output, + plink_eof, + plink_get_userpass_input, + nullseat_notify_remote_exit, + console_connection_fatal, + nullseat_update_specials_menu, + plink_get_ttymode, + nullseat_set_busy_status, + console_verify_ssh_host_key, + console_confirm_weak_crypto_primitive, + console_confirm_weak_cached_hostkey, + nullseat_is_never_utf8, + plink_echoedit_update, + nullseat_get_x_display, + nullseat_get_windowid, + nullseat_get_char_cell_size, +}; +static Seat plink_seat[1] = {{ &plink_seat_vt }}; + /* * Handle data from a local tty in PARMRK format. */ @@ -834,7 +802,7 @@ int main(int argc, char **argv) __AFL_INIT(); #endif - error = backend_init(backvt, NULL, &backend, logctx, conf, + error = backend_init(backvt, plink_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, nodelay, @@ -843,7 +811,7 @@ int main(int argc, char **argv) fprintf(stderr, "Unable to open connection:\n%s\n", error); return 1; } - ldisc_create(conf, NULL, backend, NULL); + ldisc_create(conf, NULL, backend, plink_seat); sfree(realhost); } @@ -854,7 +822,7 @@ int main(int argc, char **argv) */ local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0); atexit(cleanup_termios); - frontend_echoedit_update(NULL, 1, 1); + seat_echoedit_update(plink_seat, 1, 1); sending = FALSE; now = GETTICKCOUNT(); diff --git a/unix/uxpty.c b/unix/uxpty.c index 31f6b880..c80dde6b 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -71,7 +71,7 @@ static int pty_signal_pipe[2] = { -1, -1 }; /* obviously bogus initial val */ struct Pty { Conf *conf; int master_fd, slave_fd; - Frontend *frontend; + Seat *seat; char name[FILENAME_MAX]; pid_t child_pid; int term_width, term_height; @@ -633,7 +633,7 @@ void pty_real_select_result(Pty *pty, int event, int status) perror("read pty master"); exit(1); } else if (ret > 0) { - from_backend(pty->frontend, 0, buf, ret); + seat_stdout(pty->seat, buf, ret); } } else if (event == 2) { /* @@ -675,10 +675,10 @@ void pty_real_select_result(Pty *pty, int event, int status) " %d (%.400s)]\r\n", WTERMSIG(pty->exit_code), strsignal(WTERMSIG(pty->exit_code))); #endif - from_backend(pty->frontend, 0, message, strlen(message)); + seat_stdout(pty->seat, message, strlen(message)); } - notify_remote_exit(pty->frontend); + seat_notify_remote_exit(pty->seat); } } @@ -736,7 +736,7 @@ static void pty_uxsel_setup(Pty *pty) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *pty_init(Frontend *frontend, Backend **backend_handle, +static const char *pty_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive) @@ -760,7 +760,7 @@ static const char *pty_init(Frontend *frontend, Backend **backend_handle, #endif } - pty->frontend = frontend; + pty->seat = seat; pty->backend.vt = &pty_backend; *backend_handle = &pty->backend; @@ -781,7 +781,7 @@ static const char *pty_init(Frontend *frontend, Backend **backend_handle, close(pty_utmp_helper_pipe); /* just let the child process die */ pty_utmp_helper_pipe = -1; } else { - const char *location = get_x_display(pty->frontend); + const char *location = seat_get_x_display(pty->seat); int len = strlen(location)+1, pos = 0; /* +1 to include NUL */ while (pos < len) { int ret = write(pty_utmp_helper_pipe, location+pos, len - pos); @@ -798,7 +798,7 @@ static const char *pty_init(Frontend *frontend, Backend **backend_handle, #endif #ifndef NOT_X_WINDOWS /* for Mac OS X native compilation */ - got_windowid = get_windowid(pty->frontend, &windowid); + got_windowid = seat_get_windowid(pty->seat, &windowid); #endif /* @@ -888,7 +888,7 @@ static const char *pty_init(Frontend *frontend, Backend **backend_handle, * Set the IUTF8 bit iff the character set is UTF-8. */ #ifdef IUTF8 - if (frontend_is_utf8(frontend)) + if (seat_is_utf8(seat)) attrs.c_iflag |= IUTF8; else attrs.c_iflag &= ~IUTF8; @@ -928,7 +928,7 @@ static const char *pty_init(Frontend *frontend, Backend **backend_handle, * terminal to match the display the terminal itself is * on. */ - const char *x_display = get_x_display(pty->frontend); + const char *x_display = seat_get_x_display(pty->seat); char *x_display_env_var = dupprintf("DISPLAY=%s", x_display); putenv(x_display_env_var); /* As above, we don't free this. */ @@ -1150,16 +1150,17 @@ static void pty_size(Backend *be, int width, int height) { Pty *pty = container_of(be, Pty, backend); struct winsize size; + int xpixel = 0, ypixel = 0; pty->term_width = width; pty->term_height = height; + seat_get_char_cell_size(pty->seat, &xpixel, &ypixel); + size.ws_row = (unsigned short)pty->term_height; size.ws_col = (unsigned short)pty->term_width; - size.ws_xpixel = (unsigned short) pty->term_width * - font_dimension(pty->frontend, 0); - size.ws_ypixel = (unsigned short) pty->term_height * - font_dimension(pty->frontend, 1); + size.ws_xpixel = (unsigned short)pty->term_width * xpixel; + size.ws_ypixel = (unsigned short)pty->term_height * ypixel; ioctl(pty->master_fd, TIOCSWINSZ, (void *)&size); return; } diff --git a/unix/uxser.c b/unix/uxser.c index 6c837625..2f9251b7 100644 --- a/unix/uxser.c +++ b/unix/uxser.c @@ -19,7 +19,7 @@ typedef struct Serial Serial; struct Serial { - Frontend *frontend; + Seat *seat; LogContext *logctx; int fd; int finished; @@ -279,7 +279,7 @@ static const char *serial_configure(Serial *serial, Conf *conf) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *serial_init(Frontend *frontend, Backend **backend_handle, +static const char *serial_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive) @@ -292,7 +292,7 @@ static const char *serial_init(Frontend *frontend, Backend **backend_handle, serial->backend.vt = &serial_backend; *backend_handle = &serial->backend; - serial->frontend = frontend; + serial->seat = seat; serial->logctx = logctx; serial->finished = FALSE; serial->inbufsize = 0; @@ -322,7 +322,7 @@ static const char *serial_init(Frontend *frontend, Backend **backend_handle, /* * Specials are always available. */ - update_specials_menu(serial->frontend); + seat_update_specials_menu(serial->seat); return NULL; } @@ -390,7 +390,7 @@ static void serial_select_result(int fd, int event) perror("read serial port"); exit(1); } else if (ret > 0) { - serial->inbufsize = from_backend(serial->frontend, 0, buf, ret); + serial->inbufsize = seat_stdout(serial->seat, buf, ret); serial_uxsel_setup(serial); /* might acquire backlog and freeze */ } } else if (event == 2) { @@ -405,7 +405,7 @@ static void serial_select_result(int fd, int event) serial->finished = TRUE; - notify_remote_exit(serial->frontend); + seat_notify_remote_exit(serial->seat); } } diff --git a/unix/uxsftp.c b/unix/uxsftp.c index a0d5c71e..f6ac2bbb 100644 --- a/unix/uxsftp.c +++ b/unix/uxsftp.c @@ -66,9 +66,7 @@ Filename *platform_default_filename(const char *name) return filename_from_str(""); } -char *get_ttymode(Frontend *frontend, const char *mode) { return NULL; } - -int get_userpass_input(prompts_t *p, bufchain *input) +int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); diff --git a/windows/wincons.c b/windows/wincons.c index 83146968..9a542571 100644 --- a/windows/wincons.c +++ b/windows/wincons.c @@ -28,21 +28,65 @@ void cleanup_exit(int code) exit(code); } -void set_busy_status(Frontend *frontend, int status) +/* + * Various error message and/or fatal exit functions. + */ +void console_print_error_msg(const char *prefix, const char *msg) { + fputs(prefix, stderr); + fputs(": ", stderr); + fputs(msg, stderr); + fputc('\n', stderr); + fflush(stderr); } -void notify_remote_exit(Frontend *frontend) +void console_print_error_msg_fmt_v( + const char *prefix, const char *fmt, va_list ap) { + char *msg = dupvprintf(fmt, ap); + console_print_error_msg(prefix, msg); + sfree(msg); +} + +void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v(prefix, fmt, ap); + va_end(ap); +} + +void modalfatalbox(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v("FATAL ERROR", fmt, ap); + va_end(ap); + cleanup_exit(1); +} + +void nonfatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v("ERROR", fmt, ap); + va_end(ap); +} + +void console_connection_fatal(Seat *seat, const char *msg) +{ + console_print_error_msg("FATAL ERROR", msg); + cleanup_exit(1); } void timer_change_notify(unsigned long next) { } -int verify_ssh_host_key(Frontend *frontend, char *host, int port, - const char *keytype, char *keystr, char *fingerprint, - void (*callback)(void *ctx, int result), void *ctx) +int console_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *fingerprint, + void (*callback)(void *ctx, int result), void *ctx) { int ret; HANDLE hin; @@ -145,16 +189,9 @@ int verify_ssh_host_key(Frontend *frontend, char *host, int port, } } -void update_specials_menu(Frontend *frontend) -{ -} - -/* - * Ask whether the selected algorithm is acceptable (since it was - * below the configured 'warn' threshold). - */ -int askalg(Frontend *frontend, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) +int console_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; DWORD savemode, i; @@ -194,8 +231,9 @@ int askalg(Frontend *frontend, const char *algtype, const char *algname, } } -int askhk(Frontend *frontend, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) +int console_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; DWORD savemode, i; diff --git a/windows/windlg.c b/windows/windlg.c index d0843754..6dd8bf98 100644 --- a/windows/windlg.c +++ b/windows/windlg.c @@ -798,10 +798,11 @@ static void win_gui_eventlog(LogPolicy *lp, const char *string) static void win_gui_logging_error(LogPolicy *lp, const char *event) { + extern Seat win_seat[1]; /* Send 'can't open log file' errors to the terminal window. * (Marked as stderr, although terminal.c won't care.) */ - from_backend(NULL, 1, event, strlen(event)); - from_backend(NULL, 1, "\r\n", 2); + seat_stderr(win_seat, event, strlen(event)); + seat_stderr(win_seat, "\r\n", 2); } void showeventlog(HWND hwnd) @@ -819,9 +820,10 @@ void showabout(HWND hwnd) DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); } -int verify_ssh_host_key(Frontend *frontend, char *host, int port, - const char *keytype, char *keystr, char *fingerprint, - void (*callback)(void *ctx, int result), void *ctx) +int win_seat_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *fingerprint, + void (*callback)(void *ctx, int result), void *ctx) { int ret; @@ -903,8 +905,9 @@ int verify_ssh_host_key(Frontend *frontend, char *host, int port, * Ask whether the selected algorithm is acceptable (since it was * below the configured 'warn' threshold). */ -int askalg(Frontend *frontend, const char *algtype, const char *algname, - void (*callback)(void *ctx, int result), void *ctx) +int win_seat_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx) { static const char mbtitle[] = "%s Security Alert"; static const char msg[] = @@ -928,8 +931,9 @@ int askalg(Frontend *frontend, const char *algtype, const char *algname, return 0; } -int askhk(Frontend *frontend, const char *algname, const char *betteralgs, - void (*callback)(void *ctx, int result), void *ctx) +int win_seat_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx) { static const char mbtitle[] = "%s Security Alert"; static const char msg[] = diff --git a/windows/window.c b/windows/window.c index 5133a0ad..d0320433 100644 --- a/windows/window.c +++ b/windows/window.c @@ -213,7 +213,7 @@ static Mouse_Button lastbtn; static int send_raw_mouse = 0; static int wheel_accumulator = 0; -static int busy_status = BUSY_NOT; +static BusyStatus busy_status = BUSY_NOT; static char *window_name, *icon_name; @@ -230,21 +230,57 @@ static UINT wm_mousewheel = WM_MOUSEWHEEL; const int share_can_be_downstream = TRUE; const int share_can_be_upstream = TRUE; -/* Dummy routine, only required in plink. */ -void frontend_echoedit_update(Frontend *frontend, int echo, int edit) -{ -} - int frontend_is_utf8(Frontend *frontend) { return ucsdata.line_codepage == CP_UTF8; } -char *get_ttymode(Frontend *frontend, const char *mode) +static int win_seat_is_utf8(Seat *seat) +{ + return frontend_is_utf8(NULL); +} + +char *win_seat_get_ttymode(Seat *seat, const char *mode) { return term_get_ttymode(term, mode); } +int win_seat_get_char_cell_size(Seat *seat, int *x, int *y) +{ + *x = font_width; + *y = font_height; + return TRUE; +} + +static int win_seat_output(Seat *seat, int is_stderr, const void *, int); +static int win_seat_eof(Seat *seat); +static int win_seat_get_userpass_input( + Seat *seat, prompts_t *p, bufchain *input); +static void win_seat_notify_remote_exit(Seat *seat); +static void win_seat_connection_fatal(Seat *seat, const char *msg); +static void win_seat_update_specials_menu(Seat *seat); +static void win_seat_set_busy_status(Seat *seat, BusyStatus status); + +static const SeatVtable win_seat_vt = { + win_seat_output, + win_seat_eof, + win_seat_get_userpass_input, + win_seat_notify_remote_exit, + win_seat_connection_fatal, + win_seat_update_specials_menu, + win_seat_get_ttymode, + win_seat_set_busy_status, + win_seat_verify_ssh_host_key, + win_seat_confirm_weak_crypto_primitive, + win_seat_confirm_weak_cached_hostkey, + win_seat_is_utf8, + nullseat_echoedit_update, + nullseat_get_x_display, + nullseat_get_windowid, + win_seat_get_char_cell_size, +}; +Seat win_seat[1] = {{ &win_seat_vt }}; + static void start_backend(void) { const struct BackendVtable *vt; @@ -266,7 +302,7 @@ static void start_backend(void) cleanup_exit(1); } - error = backend_init(vt, NULL, &backend, logctx, conf, + error = backend_init(vt, win_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, @@ -298,7 +334,7 @@ static void start_backend(void) /* * Set up a line discipline. */ - ldisc = ldisc_create(conf, term, backend, NULL); + ldisc = ldisc_create(conf, term, backend, win_seat); /* * Destroy the Restart Session menu item. (This will return @@ -332,7 +368,7 @@ static void close_session(void *ignored_context) backend_free(backend); backend = NULL; term_provide_backend(term, NULL); - update_specials_menu(NULL); + seat_update_specials_menu(win_seat); } /* @@ -945,7 +981,7 @@ static void update_savedsess_menu(void) /* * Update the Special Commands submenu. */ -void update_specials_menu(Frontend *frontend) +static void win_seat_update_specials_menu(Seat *seat) { HMENU new_menu; int i, j; @@ -1051,7 +1087,7 @@ static void update_mouse_pointer(void) } } -void set_busy_status(Frontend *frontend, int status) +static void win_seat_set_busy_status(Seat *seat, BusyStatus status) { busy_status = status; update_mouse_pointer(); @@ -1070,17 +1106,12 @@ void set_raw_mouse_mode(Frontend *frontend, int activate) /* * Print a message box and close the connection. */ -void connection_fatal(Frontend *frontend, const char *fmt, ...) +static void win_seat_connection_fatal(Seat *seat, const char *msg) { - va_list ap; - char *stuff, morestuff[100]; + char title[100]; - va_start(ap, fmt); - stuff = dupvprintf(fmt, ap); - va_end(ap); - sprintf(morestuff, "%.70s Fatal Error", appname); - MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK); - sfree(stuff); + sprintf(title, "%.70s Fatal Error", appname); + MessageBox(hwnd, msg, title, MB_ICONERROR | MB_OK); if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON) PostQuitMessage(1); @@ -1969,7 +2000,7 @@ static int is_alt_pressed(void) static int resizing; -void notify_remote_exit(Frontend *frontend) +static void win_seat_notify_remote_exit(Seat *seat) { int exitcode, close_on_exit; @@ -5907,17 +5938,19 @@ static void flip_full_screen() } } -int from_backend(Frontend *frontend, int is_stderr, const void *data, int len) +static int win_seat_output(Seat *seat, int is_stderr, + const void *data, int len) { return term_data(term, is_stderr, data, len); } -int from_backend_eof(Frontend *frontend) +static int win_seat_eof(Seat *seat) { return TRUE; /* do respond to incoming EOF with outgoing */ } -int get_userpass_input(prompts_t *p, bufchain *input) +static int win_seat_get_userpass_input( + Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); diff --git a/windows/winplink.c b/windows/winplink.c index 2d4b74fa..4c173182 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -22,51 +22,12 @@ struct agent_callback { int len; }; -void modalfatalbox(const char *p, ...) +void cmdline_error(const char *fmt, ...) { va_list ap; - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); + va_start(ap, fmt); + console_print_error_msg_fmt_v("plink", fmt, ap); va_end(ap); - fputc('\n', stderr); - if (logctx) { - log_free(logctx); - logctx = NULL; - } - cleanup_exit(1); -} -void nonfatal(const char *p, ...) -{ - va_list ap; - fprintf(stderr, "ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); -} -void connection_fatal(Frontend *frontend, const char *p, ...) -{ - va_list ap; - fprintf(stderr, "FATAL ERROR: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); - if (logctx) { - log_free(logctx); - logctx = NULL; - } - cleanup_exit(1); -} -void cmdline_error(const char *p, ...) -{ - va_list ap; - fprintf(stderr, "plink: "); - va_start(ap, p); - vfprintf(stderr, p, ap); - va_end(ap); - fputc('\n', stderr); exit(1); } @@ -83,7 +44,7 @@ int term_ldisc(Terminal *term, int mode) { return FALSE; } -void frontend_echoedit_update(Frontend *frontend, int echo, int edit) +static void plink_echoedit_update(Seat *seat, int echo, int edit) { /* Update stdin read mode to reflect changes in line discipline. */ DWORD mode; @@ -100,10 +61,7 @@ void frontend_echoedit_update(Frontend *frontend, int echo, int edit) SetConsoleMode(inhandle, mode); } -char *get_ttymode(Frontend *frontend, const char *mode) { return NULL; } - -int from_backend(Frontend *frontend, int is_stderr, - const void *data, int len) +static int plink_output(Seat *seat, int is_stderr, const void *data, int len) { if (is_stderr) { handle_write(stderr_handle, data, len); @@ -114,13 +72,13 @@ int from_backend(Frontend *frontend, int is_stderr, return handle_backlog(stdout_handle) + handle_backlog(stderr_handle); } -int from_backend_eof(Frontend *frontend) +static int plink_eof(Seat *seat) { handle_write_eof(stdout_handle); return FALSE; /* do not respond to incoming EOF with outgoing */ } -int get_userpass_input(prompts_t *p, bufchain *input) +static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); @@ -129,6 +87,26 @@ int get_userpass_input(prompts_t *p, bufchain *input) return ret; } +static const SeatVtable plink_seat_vt = { + plink_output, + plink_eof, + plink_get_userpass_input, + nullseat_notify_remote_exit, + console_connection_fatal, + nullseat_update_specials_menu, + nullseat_get_ttymode, + nullseat_set_busy_status, + console_verify_ssh_host_key, + console_confirm_weak_crypto_primitive, + console_confirm_weak_cached_hostkey, + nullseat_is_never_utf8, + plink_echoedit_update, + nullseat_get_x_display, + nullseat_get_windowid, + nullseat_get_char_cell_size, +}; +static Seat plink_seat[1] = {{ &plink_seat_vt }}; + static DWORD main_thread_id; void agent_schedule_callback(void (*callback)(void *, void *, int), @@ -476,7 +454,7 @@ int main(int argc, char **argv) int nodelay = conf_get_int(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); - error = backend_init(vt, NULL, &backend, logctx, conf, + error = backend_init(vt, plink_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, nodelay, diff --git a/windows/winser.c b/windows/winser.c index e1f40369..3fba2989 100644 --- a/windows/winser.c +++ b/windows/winser.c @@ -14,7 +14,7 @@ typedef struct Serial Serial; struct Serial { HANDLE port; struct handle *out, *in; - Frontend *frontend; + Seat *seat; LogContext *logctx; int bufsize; long clearbreak_time; @@ -60,15 +60,15 @@ static int serial_gotdata(struct handle *h, void *data, int len) serial_terminate(serial); - notify_remote_exit(serial->frontend); + seat_notify_remote_exit(serial->seat); logevent(serial->logctx, error_msg); - connection_fatal(serial->frontend, "%s", error_msg); + seat_connection_fatal(serial->seat, "%s", error_msg); return 0; /* placate optimiser */ } else { - return from_backend(serial->frontend, 0, data, len); + return seat_stdout(serial->seat, data, len); } } @@ -80,11 +80,11 @@ static void serial_sentdata(struct handle *h, int new_backlog) serial_terminate(serial); - notify_remote_exit(serial->frontend); + seat_notify_remote_exit(serial->seat); logevent(serial->logctx, error_msg); - connection_fatal(serial->frontend, "%s", error_msg); + seat_connection_fatal(serial->seat, "%s", error_msg); } else { serial->bufsize = new_backlog; } @@ -190,7 +190,7 @@ static const char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *serial_init(Frontend *frontend, Backend **backend_handle, +static const char *serial_init(Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, int nodelay, int keepalive) @@ -208,7 +208,7 @@ static const char *serial_init(Frontend *frontend, Backend **backend_handle, serial->backend.vt = &serial_backend; *backend_handle = &serial->backend; - serial->frontend = frontend; + serial->seat = seat; serial->logctx = logctx; serline = conf_get_str(conf, CONF_serline); @@ -265,7 +265,7 @@ static const char *serial_init(Frontend *frontend, Backend **backend_handle, /* * Specials are always available. */ - update_specials_menu(serial->frontend); + seat_update_specials_menu(serial->seat); return NULL; } diff --git a/windows/winsftp.c b/windows/winsftp.c index 48235cd9..6a8aba77 100644 --- a/windows/winsftp.c +++ b/windows/winsftp.c @@ -13,9 +13,7 @@ #include "int64.h" #include "winsecur.h" -char *get_ttymode(Frontend *frontend, const char *mode) { return NULL; } - -int get_userpass_input(prompts_t *p, bufchain *input) +int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); diff --git a/windows/winstuff.h b/windows/winstuff.h index bb64ac07..3708e2e6 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -237,11 +237,25 @@ void quit_help(HWND hwnd); /* * The terminal and logging context are notionally local to the * Windows front end, but they must be shared between window.c and - * windlg.c. Likewise the saved-sessions list. + * windlg.c. Likewise the Seat structure for the Windows GUI. */ GLOBAL Terminal *term; GLOBAL LogContext *logctx; +/* + * GUI seat methods in windlg.c. + */ +int win_seat_verify_ssh_host_key( + Seat *seat, const char *host, int port, + const char *keytype, char *keystr, char *key_fingerprint, + void (*callback)(void *ctx, int result), void *ctx); +int win_seat_confirm_weak_crypto_primitive( + Seat *seat, const char *algtype, const char *algname, + void (*callback)(void *ctx, int result), void *ctx); +int win_seat_confirm_weak_cached_hostkey( + Seat *seat, const char *algname, const char *betteralgs, + void (*callback)(void *ctx, int result), void *ctx); + /* * Windows-specific clipboard helper function shared with windlg.c, * which takes the data string in the system code page instead of