Remove core SSH client code. Refactor message handler to handle pipe for STDOUT. Refactor key and clipboard handlers to handle pipe for STDIN.

This commit is contained in:
Michael Jumper 2013-05-17 20:20:51 -07:00
parent 791da3dc81
commit ec845a812a
3 changed files with 79 additions and 175 deletions

View File

@ -48,21 +48,44 @@
#include "terminal.h"
#include "cursor.h"
/**
* SSH-specific client data.
*/
typedef struct ssh_guac_client_data {
ssh_session session;
ssh_channel term_channel;
/**
* The terminal which will render all output from the SSH client.
*/
guac_terminal* term;
/**
* The current contents of the clipboard.
*/
char* clipboard_data;
char password[1024];
int password_length;
/**
* Whether the control key is currently being held down.
*/
int mod_ctrl;
/**
* The current mouse button state.
*/
int mouse_mask;
/**
* Pipe which will be used to provide STDOUT to the SSH client.
*/
int stdout_pipe_fd[2];
/**
* Pipe which will be used to provide STDIN to the SSH client.
*/
int stdin_pipe_fd[2];
/**
* The cached I-bar cursor.
*/
@ -80,7 +103,5 @@ typedef struct ssh_guac_client_data {
} ssh_guac_client_data;
int ssh_guac_client_auth(guac_client* client, const char* password);
#endif

View File

@ -38,12 +38,12 @@
#include <stdlib.h>
#include <string.h>
#include <libssh/libssh.h>
#include <unistd.h>
#include <guacamole/socket.h>
#include <guacamole/protocol.h>
#include <guacamole/client.h>
#include <guacamole/error.h>
#include "ssh_client.h"
#include "ssh_handlers.h"
@ -59,53 +59,6 @@ const char* GUAC_CLIENT_ARGS[] = {
NULL
};
int ssh_guac_client_password_key_handler(guac_client* client, int keysym, int pressed) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
/* If key pressed */
if (pressed) {
/* If simple ASCII key */
if (keysym >= 0x00 && keysym <= 0xFF) {
/* Add to password */
client_data->password[client_data->password_length++] = keysym;
guac_terminal_write(client_data->term, "*", 1);
guac_terminal_display_flush(client_data->term->display);
guac_socket_flush(client->socket);
}
else if (keysym == 0xFF08) {
if (client_data->password_length > 0) {
client_data->password_length--;
/* Backspace */
guac_terminal_write(client_data->term, "\x08\x1B[K", 4);
guac_terminal_display_flush(client_data->term->display);
guac_socket_flush(client->socket);
}
}
else if (keysym == 0xFF0D) {
/* Finish password */
client_data->password[client_data->password_length] = '\0';
/* Clear screen */
guac_terminal_write(client_data->term, "\x1B[2J\x1B[1;1H", 10);
guac_terminal_display_flush(client_data->term->display);
guac_socket_flush(client->socket);
return ssh_guac_client_auth(client, client_data->password);
}
}
return 0;
}
int guac_client_init(guac_client* client, int argc, char** argv) {
guac_socket* socket = client->socket;
@ -135,111 +88,44 @@ int guac_client_init(guac_client* client, int argc, char** argv) {
guac_socket_flush(socket);
/* Open SSH session */
client_data->session = ssh_new();
if (client_data->session == NULL) {
guac_protocol_send_error(socket, "Unable to create SSH session.");
guac_socket_flush(socket);
/* Open STDOUT pipe */
if (pipe(client_data->stdout_pipe_fd)) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Unable to open pipe for STDOUT";
return 1;
}
/* Set session options */
ssh_options_set(client_data->session, SSH_OPTIONS_HOST, argv[0]);
ssh_options_set(client_data->session, SSH_OPTIONS_USER, argv[1]);
/* Redirect STDOUT to pipe */
if (dup2(client_data->stdout_pipe_fd[1], STDOUT_FILENO) < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Unable redirect STDOUT";
return 1;
}
/* Connect */
if (ssh_connect(client_data->session) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to connect via SSH.");
guac_socket_flush(socket);
/* Open STDIN pipe */
if (pipe(client_data->stdin_pipe_fd)) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Unable to open pipe for STDIN";
return 1;
}
/* Redirect STDIN to pipe */
if (dup2(client_data->stdin_pipe_fd[0], STDIN_FILENO) < 0) {
guac_error = GUAC_STATUS_SEE_ERRNO;
guac_error_message = "Unable redirect STDIN";
return 1;
}
/* Set basic handlers */
client->free_handler = ssh_guac_client_free_handler;
client->size_handler = ssh_guac_client_size_handler;
/* If password provided, authenticate now */
if (argv[2][0] != '\0')
return ssh_guac_client_auth(client, argv[2]);
/* Otherwise, prompt for password */
else {
client_data->password_length = 0;
guac_terminal_write(client_data->term, "Password: ", 10);
guac_terminal_display_flush(client_data->term->display);
guac_socket_flush(client->socket);
client->key_handler = ssh_guac_client_password_key_handler;
}
/* Success */
return 0;
}
int ssh_guac_client_auth(guac_client* client, const char* password) {
guac_socket* socket = client->socket;
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
guac_terminal* term = client_data->term;
/* Authenticate */
if (ssh_userauth_password(client_data->session, NULL, password) != SSH_AUTH_SUCCESS) {
guac_protocol_send_error(socket, "SSH auth failed.");
guac_socket_flush(socket);
return 1;
}
/* Open channel for terminal */
client_data->term_channel = channel_new(client_data->session);
if (client_data->term_channel == NULL) {
guac_protocol_send_error(socket, "Unable to open channel.");
guac_socket_flush(socket);
return 1;
}
/* Open session for channel */
if (channel_open_session(client_data->term_channel) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to open channel session.");
guac_socket_flush(socket);
return 1;
}
/* Request PTY */
if (channel_request_pty(client_data->term_channel) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to allocate PTY for channel.");
guac_socket_flush(socket);
return 1;
}
/* Request PTY size */
if (channel_change_pty_size(client_data->term_channel, term->term_width, term->term_height) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to change PTY size.");
guac_socket_flush(socket);
return 1;
}
/* Request shell */
if (channel_request_shell(client_data->term_channel) != SSH_OK) {
guac_protocol_send_error(socket, "Unable to associate shell with PTY.");
guac_socket_flush(socket);
return 1;
}
guac_client_log_info(client, "SSH connection successful.");
/* Set handlers */
client->handle_messages = ssh_guac_client_handle_messages;
client->mouse_handler = ssh_guac_client_mouse_handler;
client->key_handler = ssh_guac_client_key_handler;
client->clipboard_handler = ssh_guac_client_clipboard_handler;
client->key_handler = ssh_guac_client_key_handler;
client->mouse_handler = ssh_guac_client_mouse_handler;
client->size_handler = ssh_guac_client_size_handler;
client->free_handler = ssh_guac_client_free_handler;
/* Success */
return 0;
}

View File

@ -41,14 +41,13 @@
#include <stdlib.h>
#include <string.h>
#include <libssh/libssh.h>
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <guacamole/socket.h>
#include <guacamole/protocol.h>
#include <guacamole/client.h>
#include <guacamole/error.h>
#include "ssh_handlers.h"
#include "ssh_client.h"
@ -61,42 +60,32 @@ int ssh_guac_client_handle_messages(guac_client* client) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
char buffer[8192];
ssh_channel channels[2], out_channels[2];
int ret_val;
int fd = client_data->stdout_pipe_fd[0];
struct timeval timeout;
fd_set fds;
int ssh_fd;
/* Channels to read */
channels[0] = client_data->term_channel;
channels[1] = NULL;
/* Get SSH file descriptor */
ssh_fd = ssh_get_fd(client_data->session);
/* Build fd_set */
FD_ZERO(&fds);
FD_SET(ssh_fd, &fds);
FD_SET(fd, &fds);
/* Time to wait */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
/* Wait for data to be available */
if (ssh_select(channels, out_channels, ssh_fd+1, &fds, &timeout)
== SSH_OK) {
ret_val = select(fd+1, &fds, NULL, NULL, &timeout);
if (ret_val > 0) {
int bytes_read = 0;
/* Lock terminal access */
pthread_mutex_lock(&(client_data->term->lock));
/* While data available, write to terminal */
while (channel_is_open(client_data->term_channel)
&& !channel_is_eof(client_data->term_channel)
&& (bytes_read = channel_read_nonblocking(client_data->term_channel, buffer, sizeof(buffer), 0)) > 0) {
/* Read data, write to terminal */
if ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) {
if (guac_terminal_write(client_data->term, buffer, bytes_read)
|| guac_socket_flush(socket))
if (guac_terminal_write(client_data->term, buffer, bytes_read))
return 1;
}
@ -118,6 +107,11 @@ int ssh_guac_client_handle_messages(guac_client* client) {
pthread_mutex_unlock(&(client_data->term->lock));
}
else if (ret_val < 0) {
guac_error_message = "Error waiting for pipe";
guac_error = GUAC_STATUS_SEE_ERRNO;
return 1;
}
return 0;
@ -164,7 +158,7 @@ int ssh_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
int length = strlen(client_data->clipboard_data);
if (length)
return channel_write(client_data->term_channel,
return write(client_data->stdin_pipe_fd[1],
client_data->clipboard_data, length);
}
@ -234,6 +228,9 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
ssh_guac_client_data* client_data = (ssh_guac_client_data*) client->data;
guac_terminal* term = client_data->term;
/* Get write end of STDIN pipe */
int fd = client_data->stdin_pipe_fd[1];
/* Hide mouse cursor if not already hidden */
if (client_data->current_cursor != client_data->blank_cursor) {
pthread_mutex_lock(&(term->lock));
@ -275,7 +272,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
else
return 0;
return channel_write(client_data->term_channel, &data, 1);
return write(fd, &data, 1);
}
@ -286,7 +283,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
char data[5];
length = guac_terminal_encode_utf8(keysym & 0xFFFF, data);
return channel_write(client_data->term_channel, data, length);
return write(fd, data, length);
}
@ -309,7 +306,7 @@ int ssh_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
/* Ignore other keys */
else return 0;
return channel_write(client_data->term_channel, data, length);
return write(fd, data, length);
}
}
@ -335,8 +332,8 @@ int ssh_guac_client_size_handler(guac_client* client, int width, int height) {
/* Resize terminal */
guac_terminal_resize(terminal, columns, rows);
channel_change_pty_size(guac_client_data->term_channel,
terminal->term_width, terminal->term_height);
/* FIXME: Make resize call to SSH thread */
/* Reset scroll region */
terminal->scroll_end = rows - 1;