diff --git a/CMakeLists.txt b/CMakeLists.txt index bfaf1e1..ea16ae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,14 @@ +# ssheven +# +# Copyright (c) 20201 by cy384 +# See LICENSE file for details + cmake_minimum_required(VERSION 3.9) -add_application(ssheven CREATOR "SSH7" ssheven.c ssheven-console.c ssheven.r) +add_application(ssheven CREATOR "SSH7" ssheven.c ssheven-console.c ssheven.r ssheven-net.c ssheven-debug.c) IF(CMAKE_SYSTEM_NAME MATCHES Retro68) # for 68k -# add "-mcpu=68020" once we figure out retro68 bug set_target_properties(ssheven PROPERTIES COMPILE_FLAGS "-ffunction-sections -mcpu=68020 -O3 -Wall -Wextra -Wno-unused-parameter") set_target_properties(ssheven PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-segments -Wl,${CMAKE_CURRENT_SOURCE_DIR}/ssheven.segmap") target_link_libraries(ssheven ssh2 mbedtls mbedx509 mbedcrypto OpenTransportApp OpenTransport OpenTptInet vterm retrocrt) diff --git a/ssheven-console.c b/ssheven-console.c index 47d777a..37cd6c0 100644 --- a/ssheven-console.c +++ b/ssheven-console.c @@ -7,6 +7,11 @@ #include "ssheven.h" #include "ssheven-console.h" +#include "ssheven-net.h" + +#include + +#include char key_to_vterm[256] = { VTERM_KEY_NONE }; @@ -47,6 +52,19 @@ void setup_key_translation(void) //key_to_vterm[0] = VTERM_KEY_KP_EQUAL; } +Rect cell_rect(int x, int y, Rect bounds) +{ + Rect r = { (short) (bounds.top + y * con.cell_height), (short) (bounds.left + x * con.cell_width + 2), + (short) (bounds.top + (y+1) * con.cell_height), (short) (bounds.left + (x+1) * con.cell_width + 2) }; + + return r; +} + +void print_string(const char* c) +{ + vterm_input_write(con.vterm, c, strlen(c)); +} + inline void draw_char(int x, int y, Rect* r, char c) { MoveTo(r->left + x * con.cell_width + 2, r->top + ((y+1) * con.cell_height) - 2); @@ -129,26 +147,6 @@ inline int idx2qd(VTermColor c) } } -// closely inspired by the retro68 console library -void draw_screen(Rect* r) -{ - switch (prefs.display_mode) - { - case FASTEST: - draw_screen_fast(r); - break; - case MONOCHROME: - draw_screen_mono(r); - break; - case COLOR: - draw_screen_color(r); - break; - default: - draw_screen_color(r); - break; - } -} - // p is in window local coordinates void mouse_click(Point p, bool click) { @@ -398,6 +396,25 @@ void draw_screen_mono(Rect* r) con.win->clipRgn = old; } +void draw_screen(Rect* r) +{ + switch (prefs.display_mode) + { + case FASTEST: + draw_screen_fast(r); + break; + case MONOCHROME: + draw_screen_mono(r); + break; + case COLOR: + draw_screen_color(r); + break; + default: + draw_screen_color(r); + break; + } +} + void ruler(Rect* r) { char itoc[] = {'0','1','2','3','4','5','6','7','8','9'}; @@ -443,11 +460,6 @@ void print_int(int d) print_string(buffer+i+1-negative); } -void print_string(const char* c) -{ - vterm_input_write(con.vterm, c, strlen(c)); -} - void printf_i(const char* str, ...) { va_list args; @@ -486,15 +498,6 @@ void printf_i(const char* str, ...) va_end(args); } -void set_window_title(WindowPtr w, const char* c_name) -{ - Str255 pascal_name; - strncpy((char *) &pascal_name[1], c_name, 254); - pascal_name[0] = strlen(c_name); - - SetWTitle(w, pascal_name); -} - int bell(void* user) { SysBeep(30); @@ -517,14 +520,6 @@ int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) return 1; } -Rect cell_rect(int x, int y, Rect bounds) -{ - Rect r = { (short) (bounds.top + y * con.cell_height), (short) (bounds.left + x * con.cell_width + 2), - (short) (bounds.top + (y+1) * con.cell_height), (short) (bounds.left + (x+1) * con.cell_width + 2) }; - - return r; -} - int damage(VTermRect rect, void *user) { Rect topleft = cell_rect(rect.start_col, rect.start_row, (con.win->portRect)); diff --git a/ssheven-console.h b/ssheven-console.h index b66ba72..1695c99 100644 --- a/ssheven-console.h +++ b/ssheven-console.h @@ -7,35 +7,14 @@ #pragma once -#include "ssheven.h" -#include +#include "MacTypes.h" void console_setup(void); -void draw_char(int x, int y, Rect* r, char c); void draw_screen(Rect* r); -void draw_screen_fast(Rect* r); -void draw_screen_mono(Rect* r); -void draw_screen_color(Rect* r); - -void bump_up_line(); - -int is_printable(char c); - -void print_char(char c); -void print_string(const char* c); -void print_int(int d); - void printf_i(const char* c, ...); -void set_window_title(WindowPtr w, const char* c_name); - -void ruler(Rect* r); - -Rect cell_rect(int x, int y, Rect bounds); - -void toggle_cursor(void); void check_cursor(void); void mouse_click(Point p, bool click); diff --git a/ssheven-debug.c b/ssheven-debug.c index 4d5b7a8..1793293 100644 --- a/ssheven-debug.c +++ b/ssheven-debug.c @@ -1,11 +1,14 @@ /* * ssheven * - * Copyright (c) 2020 by cy384 + * Copyright (c) 2021 by cy384 * See LICENSE file for details */ -/* handy debugging string conversions */ +#include +#include + +#include /* convert libssh2 errors into strings */ const char* libssh2_error_string(int i) diff --git a/ssheven-debug.h b/ssheven-debug.h new file mode 100644 index 0000000..4c811b0 --- /dev/null +++ b/ssheven-debug.h @@ -0,0 +1,12 @@ +/* + * ssheven + * + * Copyright (c) 2021 by cy384 + * See LICENSE file for details + */ + +#define OT_CHECK(X) err = (X); if (err != noErr) { printf_i("" #X " failed\r\n"); return 0; }; +#define SSH_CHECK(X) rc = (X); if (rc != LIBSSH2_ERROR_NONE) { printf_i("" #X " failed: %s\r\n", libssh2_error_string(rc)); return 0;}; + +const char* libssh2_error_string(int i); +const char* OT_event_string(int i); diff --git a/ssheven-net.c b/ssheven-net.c new file mode 100644 index 0000000..8628407 --- /dev/null +++ b/ssheven-net.c @@ -0,0 +1,619 @@ +/* + * ssheven + * + * Copyright (c) 2021 by cy384 + * See LICENSE file for details + */ + +#include "ssheven.h" +#include "ssheven-net.h" +#include "ssheven-console.h" +#include "ssheven-debug.h" + +#include +#include +#include + +#include + +void ssh_write(char* buf, size_t len) +{ + if (read_thread_state == OPEN && read_thread_command != EXIT) + { + int r = libssh2_channel_write(ssh_con.channel, buf, len); + + if (r < 1) + { + printf_i("Failed to write to channel, closing connection.\r\n"); + read_thread_command = EXIT; + } + } +} + +// read from the channel and print to console +void ssh_read(void) +{ + ssize_t rc = libssh2_channel_read(ssh_con.channel, ssh_con.recv_buffer, SSHEVEN_BUFFER_SIZE); + + if (rc == 0) return; + + if (rc <= 0) + { + printf_i("channel read error: %s\r\n", libssh2_error_string(rc)); + read_thread_command = EXIT; + } + + while (rc > 0) + { + rc -= vterm_input_write(con.vterm, ssh_con.recv_buffer, rc); + } +} + +void end_connection(void) +{ + read_thread_state = CLEANUP; + + OSStatus err = noErr; + + if (ssh_con.channel) + { + libssh2_channel_send_eof(ssh_con.channel); + libssh2_channel_close(ssh_con.channel); + libssh2_channel_free(ssh_con.channel); + libssh2_session_disconnect(ssh_con.session, "Normal Shutdown, Thank you for playing"); + libssh2_session_free(ssh_con.session); + } + + libssh2_exit(); + + if (ssh_con.endpoint != kOTInvalidEndpointRef) + { + // request to close the TCP connection + OTSndOrderlyDisconnect(ssh_con.endpoint); + + // discard remaining data so we can finish closing the connection + int rc = 1; + OTFlags ot_flags; + while (rc != kOTLookErr) + { + rc = OTRcv(ssh_con.endpoint, ssh_con.recv_buffer, 1, &ot_flags); + } + + // finish closing the TCP connection + OSStatus result = OTLook(ssh_con.endpoint); + + switch (result) + { + case T_DISCONNECT: + OTRcvDisconnect(ssh_con.endpoint, nil); + break; + + case T_ORDREL: + err = OTRcvOrderlyDisconnect(ssh_con.endpoint); + if (err == noErr) + { + err = OTSndOrderlyDisconnect(ssh_con.endpoint); + } + break; + + default: + printf_i("Unexpected OTLook result while closing: %s\r\n", OT_event_string(result)); + break; + } + + // release endpoint + err = OTUnbind(ssh_con.endpoint); + if (err != noErr) printf_i("OTUnbind failed.\r\n"); + + err = OTCloseProvider(ssh_con.endpoint); + if (err != noErr) printf_i("OTCloseProvider failed.\r\n"); + } + + read_thread_state = DONE; +} + + +int check_network_events(void) +{ + int ok = 1; + OSStatus err = noErr; + + // check if we have any new network events + OTResult look_result = OTLook(ssh_con.endpoint); + + switch (look_result) + { + case T_DATA: + case T_EXDATA: + // got data + // we always try to read, so ignore this event + break; + + case T_RESET: + // connection reset? close it/give up + end_connection(); + ok = 0; + break; + + case T_DISCONNECT: + // got disconnected + OTRcvDisconnect(ssh_con.endpoint, nil); + end_connection(); + ok = 0; + break; + + case T_ORDREL: + // nice tcp disconnect requested by remote + err = OTRcvOrderlyDisconnect(ssh_con.endpoint); + if (err == noErr) + { + err = OTSndOrderlyDisconnect(ssh_con.endpoint); + } + end_connection(); + ok = 0; + break; + + default: + // something weird or irrelevant: ignore it + break; + } + + return ok; +} + +int ssh_setup_terminal(void) +{ + int rc = 0; + + SSH_CHECK(libssh2_channel_request_pty_ex(ssh_con.channel, prefs.terminal_string, (strlen(prefs.terminal_string)), NULL, 0, con.size_x, con.size_y, 0, 0)); + SSH_CHECK(libssh2_channel_shell(ssh_con.channel)); + + read_thread_state = OPEN; + + return 1; +} + +// returns base64 sha256 hash of key as a malloc'd pascal string +char* host_hash(void) +{ + size_t length = 0; + char* human_readable = malloc(66); + memset(human_readable, 0, 66); + const char* host_key_hash = NULL; + + host_key_hash = libssh2_hostkey_hash(ssh_con.session, LIBSSH2_HOSTKEY_HASH_SHA256); + mbedtls_base64_encode((unsigned char*)human_readable+1, 64, &length, (unsigned const char*)host_key_hash, 32); + + human_readable[0] = (unsigned char)length; + + return human_readable; +} + +char* known_hosts_full_path(int* found) +{ + int ok = 1; + short foundVRefNum = 0; + long foundDirID = 0; + FSSpec known_hosts_file; + *found = 0; + + OSType pref_type = 'SH7p'; + OSType creator_type = 'SSH7'; + + // find the preferences folder on the system disk, create folder if needed + OSErr e = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID); + if (e != noErr) ok = 0; + + // make an FSSpec for the new file we want to make + if (ok) + { + e = FSMakeFSSpec(foundVRefNum, foundDirID, "\pknown_hosts", &known_hosts_file); + + // if the file exists, we found it else make an empty one + if (e == noErr) *found = 1; + else if (e == noErr) e = FSpCreate(&known_hosts_file, creator_type, pref_type, smSystemScript); + + if (e != noErr) ok = 0; + } + + Handle full_path_handle; + int path_length; + + FSpPathFromLocation(&known_hosts_file, &path_length, &full_path_handle); + char* full_path = malloc(path_length+1); + strncpy(full_path, (char*)(*full_path_handle), path_length+1); + DisposeHandle(full_path_handle); + + return full_path; +} + +int known_hosts(void) +{ + int safe_to_connect = 1; + int recognized_key = 0; + int known_hosts_file_exists = 0; + + char* known_hosts_file_path = known_hosts_full_path(&known_hosts_file_exists); + char* hash_string = NULL; + + size_t key_len = 0; + int key_type = 0; + const char* host_key = libssh2_session_hostkey(ssh_con.session, &key_len, &key_type); + + LIBSSH2_KNOWNHOSTS* known_hosts = libssh2_knownhost_init(ssh_con.session); + + if (known_hosts_file_exists) + { + // load known hosts file + + int e = libssh2_knownhost_readfile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if (e < 0) + { + printf_i("Failed to load known hosts file: %s\r\n", libssh2_error_string(e)); + } + } + else + { + printf_i("No known hosts file found.\r\n"); + } + + if (safe_to_connect) + { + // hostnames need to be either plain or of the format "[host]:port" + prefs.hostname[prefs.hostname[0]+1] = '\0'; + int e = libssh2_knownhost_check(known_hosts, prefs.hostname+1, host_key, key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, NULL); + prefs.hostname[prefs.hostname[0]+1] = ':'; + + switch (e) + { + case LIBSSH2_KNOWNHOST_CHECK_FAILURE: + printf_i("Failed to check known hosts against server public key!\r\n"); + safe_to_connect = 0; + break; + case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: + printf_i("No matching host found.\r\n"); + break; + case LIBSSH2_KNOWNHOST_CHECK_MATCH: + //printf_i("Host and key match found in known hosts.\r\n"); + recognized_key = 1; + break; + case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: + printf_i("WARNING! Host found in known hosts but key doesn't match!\r\n"); + safe_to_connect = 0; + break; + default: + printf_i("Unexpected error while checking known-hosts: %d\r\n", e); + safe_to_connect = 0; + break; + } + } + + hash_string = host_hash(); + //printf_i("Host key hash (SHA256): %s\r\n", hash_string+1); + + // ask the user to confirm if we're seeing a new host+key combo + if (safe_to_connect && !recognized_key) + { + // ask the user if the key is OK + DialogPtr dlg = GetNewDialog(DLOG_NEW_HOST, 0, (WindowPtr)-1); + + DialogItemType type; + Handle itemH; + Rect box; + + // draw default button indicator around the connect button + GetDialogItem(dlg, 2, &type, &itemH, &box); + SetDialogItem(dlg, 2, type, (Handle)NewUserItemUPP(&ButtonFrameProc), &box); + + // write the hash string into the dialog window + ControlHandle hash_text_box; + GetDialogItem(dlg, 4, &type, &itemH, &box); + hash_text_box = (ControlHandle)itemH; + SetDialogItemText((Handle)hash_text_box, (ConstStr255Param)hash_string); + + // let the modalmanager do everything + // stop on reject or accept + short item; + do { + ModalDialog(NULL, &item); + } while(item != 1 && item != 5); + + // reject if user hit reject + if (item == 1) + { + safe_to_connect = 0; + } + + DisposeDialog(dlg); + FlushEvents(everyEvent, -1); + + printf_i("Saving host and key... "); + + int save_type = 0; + save_type |= LIBSSH2_KNOWNHOST_TYPE_PLAIN; + save_type |= LIBSSH2_KNOWNHOST_KEYENC_RAW; + + switch (key_type) + { + default: + __attribute__ ((fallthrough)); + case LIBSSH2_HOSTKEY_TYPE_UNKNOWN: + save_type |= LIBSSH2_KNOWNHOST_KEY_UNKNOWN; + break; + case LIBSSH2_HOSTKEY_TYPE_RSA: + save_type |= LIBSSH2_KNOWNHOST_KEY_SSHRSA; + break; + case LIBSSH2_HOSTKEY_TYPE_DSS: + save_type |= LIBSSH2_KNOWNHOST_KEY_SSHDSS; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521; + break; + case LIBSSH2_HOSTKEY_TYPE_ED25519: + save_type |= LIBSSH2_KNOWNHOST_KEY_ED25519; + break; + } + + // hostnames need to be either plain or of the format "[host]:port" + prefs.hostname[prefs.hostname[0]+1] = '\0'; + int e = libssh2_knownhost_addc(known_hosts, prefs.hostname+1, NULL, host_key, key_len, NULL, 0, save_type, NULL); + prefs.hostname[prefs.hostname[0]+1] = ':'; + + if (e != 0) printf_i("failed to add to known hosts: %s\r\n", libssh2_error_string(e)); + else + { + e = libssh2_knownhost_writefile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if (e != 0) printf_i("failed to save known hosts file: %s\r\n", libssh2_error_string(e)); + else printf_i("done.\r\n"); + } + } + + free(hash_string); + + libssh2_knownhost_free(known_hosts); + + free(known_hosts_file_path); + + return safe_to_connect; +} + + +ssize_t network_recv_callback(libssh2_socket_t sock, void *buffer, + size_t length, int flags, void **abstract) +{ + OTResult ret = kOTNoDataErr; + OTFlags ot_flags = 0; + + if (length == 0) return 0; + + // in non-blocking mode, returns instantly always + ret = OTRcv(ssh_con.endpoint, buffer, length, &ot_flags); + + // if we got bytes, return them + if (ret >= 0) return ret; + + // if no data, let other threads run, then tell caller to call again + if (ret == kOTNoDataErr && read_thread_command != EXIT) + { + YieldToAnyThread(); + return -EAGAIN; + } + + // if we got anything other than data or nothing, return an error + if (ret != kOTNoDataErr) return -1; + + return -1; +} + +ssize_t network_send_callback(libssh2_socket_t sock, const void *buffer, + size_t length, int flags, void **abstract) +{ + int ret = -1; + + ret = OTSnd(ssh_con.endpoint, (void*) buffer, length, 0); + + // TODO FIXME handle cases better, i.e. translate error cases + if (ret == kOTLookErr) + { + OTResult lookresult = OTLook(ssh_con.endpoint); + //printf("kOTLookErr, reason: %ld\n", lookresult); + + switch (lookresult) + { + default: + //printf("what?\n"); + ret = -1; + break; + } + } + + return (ssize_t) ret; +} + +void ssh_end_msg_callback(LIBSSH2_SESSION* session, int reason, const char *message, + int message_len, const char *language, int language_len, + void **abstract) +{ + printf_i("got a disconnect msg\r\n"); +} + +int init_connection(char* hostname) +{ + int rc; + + // OT vars + OSStatus err = noErr; + TCall sndCall; + DNSAddress hostDNSAddress; + + + // open TCP endpoint + ssh_con.endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err); + + if (err != noErr || ssh_con.endpoint == kOTInvalidEndpointRef) + { + printf_i("Failed to open Open Transport TCP endpoint.\r\n"); + return 0; + } + + OT_CHECK(OTSetSynchronous(ssh_con.endpoint)); + OT_CHECK(OTSetBlocking(ssh_con.endpoint)); + OT_CHECK(OTUseSyncIdleEvents(ssh_con.endpoint, false)); + + OT_CHECK(OTBind(ssh_con.endpoint, nil, nil)); + + OT_CHECK(OTSetNonBlocking(ssh_con.endpoint)); + + // set up address struct, do the DNS lookup, and connect + OTMemzero(&sndCall, sizeof(TCall)); + + sndCall.addr.buf = (UInt8 *) &hostDNSAddress; + sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostname); + + printf_i("Connecting endpoint... "); YieldToAnyThread(); + OT_CHECK(OTConnect(ssh_con.endpoint, &sndCall, nil)); + printf_i("done.\r\n"); YieldToAnyThread(); + + // init libssh2 + SSH_CHECK(libssh2_init(0)); + YieldToAnyThread(); + + ssh_con.session = libssh2_session_init(); + if (ssh_con.session == 0) + { + printf_i("Failed to initialize SSH session.\r\n"); + return 0; + } + YieldToAnyThread(); + + // register callbacks + libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_SEND, network_send_callback); + libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_RECV, network_recv_callback); + libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_DISCONNECT, network_recv_callback); + + long s = TickCount(); + printf_i("Beginning SSH session handshake... "); YieldToAnyThread(); + SSH_CHECK(libssh2_session_handshake(ssh_con.session, 0)); + + printf_i("done. (%d ticks)\r\n", TickCount() - s); YieldToAnyThread(); + + //const char* banner = libssh2_session_banner_get(ssh_con.session); + //if (banner) printf_i("Server banner: %s\r\n", banner); + + return 1; +} + +void* read_thread(void* arg) +{ + int ok = 1; + int rc = LIBSSH2_ERROR_NONE; + + // yield until we're given a command + while (read_thread_command == WAIT) YieldToAnyThread(); + + if (read_thread_command == EXIT) + { + return 0; + } + + // connect + ok = init_connection(prefs.hostname+1); + YieldToAnyThread(); + + // check the server pub key vs. known hosts + if (ok) + { + ok = known_hosts(); + if (!ok) printf_i("Rejected server public key!\r\n"); + } + + // actually log in + if (ok) + { + printf_i("Authenticating... "); YieldToAnyThread(); + + if (prefs.auth_type == USE_PASSWORD) + { + rc = libssh2_userauth_password(ssh_con.session, prefs.username+1, prefs.password+1); + } + else + { + rc = libssh2_userauth_publickey_fromfile_ex(ssh_con.session, + prefs.username+1, + prefs.username[0], + prefs.pubkey_path, + prefs.privkey_path, + prefs.password+1); + } + + if (rc == LIBSSH2_ERROR_NONE) + { + printf_i("done.\r\n"); + } + else + { + printf_i("failed!\r\n"); + if (rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED && prefs.auth_type == USE_PASSWORD) StopAlert(ALRT_PW_FAIL, nil); + else if (rc == LIBSSH2_ERROR_FILE) StopAlert(ALRT_FILE_FAIL, nil); + else if (rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED) + { + printf_i("Username/public key combination invalid!\r\n"); // TODO: have an alert for this + if (prefs.pubkey_path[0] != '\0' || prefs.privkey_path[0] != '\0') + { + prefs.pubkey_path[0] = '\0'; + prefs.privkey_path[0] = '\0'; + } + } + else printf_i("unexpected failure: %s\r\n", libssh2_error_string(rc)); + ok = 0; + } + } + + save_prefs(); + + // if we logged in, open and set up the tty + if (ok) + { + libssh2_channel_handle_extended_data2(ssh_con.channel, LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE); + ssh_con.channel = libssh2_channel_open_session(ssh_con.session); + ok = ssh_setup_terminal(); + YieldToAnyThread(); + } + + // if we failed, close everything and exit + if (!ok || (read_thread_state != OPEN)) + { + end_connection(); + return 0; + } + + // if we connected, allow pasting + void* menu = GetMenuHandle(MENU_EDIT); + EnableItem(menu, 5); + + // read until failure, command to EXIT, or remote EOF + while (read_thread_command == READ && read_thread_state == OPEN && libssh2_channel_eof(ssh_con.channel) == 0) + { + if (check_network_events()) ssh_read(); + YieldToAnyThread(); + } + + if (libssh2_channel_eof(ssh_con.channel)) + { + printf_i("(disconnected by server)"); + } + + // if we still have a connection, close it + if (read_thread_state != DONE) end_connection(); + + // disallow pasting after connection is closed + DisableItem(menu, 5); + + return 0; +} diff --git a/ssheven-net.h b/ssheven-net.h new file mode 100644 index 0000000..e8fc17b --- /dev/null +++ b/ssheven-net.h @@ -0,0 +1,11 @@ +/* + * ssheven + * + * Copyright (c) 2021 by cy384 + * See LICENSE file for details + */ + +#pragma once + +void ssh_write(char* buf, size_t len); +void* read_thread(void* arg); diff --git a/ssheven.c b/ssheven.c index 68490ea..02c73dc 100644 --- a/ssheven.c +++ b/ssheven.c @@ -7,27 +7,45 @@ #include "ssheven.h" #include "ssheven-console.h" +#include "ssheven-net.h" +#include "ssheven-debug.h" -// functions to convert error and status codes to strings -#include "ssheven-debug.c" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -// error checking convenience macros -#define OT_CHECK(X) err = (X); if (err != noErr) { printf_i("" #X " failed\r\n"); return 0; }; -#define SSH_CHECK(X) rc = (X); if (rc != LIBSSH2_ERROR_NONE) { printf_i("" #X " failed: %s\r\n", libssh2_error_string(rc)); return 0;}; +#include // sinful globals struct ssheven_console con = { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 1, NULL, NULL }; struct ssheven_ssh_connection ssh_con = { NULL, NULL, kOTInvalidEndpointRef, NULL, NULL }; struct preferences prefs; -enum { WAIT, READ, EXIT } read_thread_command = WAIT; -enum { UNINTIALIZED, OPEN, CLEANUP, DONE } read_thread_state = UNINTIALIZED; +enum THREAD_COMMAND read_thread_command = WAIT; +enum THREAD_STATE read_thread_state = UNINTIALIZED; -// TODO: put this somewhere else // contains a mapping of apple virtual keycode to ascii control code // currently only for A-Z +// probably only works for US english keyboard layouts? const uint8_t virtual_keycode_to_control_ascii[255] = {1, 19, 4, 6, 8, 7, 26, 24, 3, 22, 255, 2, 17, 23, 5, 18, 25, 20, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 15, 21, 255, 9, 16, 255, 12, 10, 255, 11, 255, 255, 255, 255, 14, 13, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; +void set_window_title(WindowPtr w, const char* c_name) +{ + Str255 pascal_name; + strncpy((char *) &pascal_name[1], c_name, 254); + pascal_name[0] = strlen(c_name); + + SetWTitle(w, pascal_name); +} + void set_terminal_string(void) { /* @@ -61,7 +79,6 @@ int save_prefs(void) short foundVRefNum = 0; long foundDirID = 0; FSSpec pref_file; - //int create_new = 0; short prefRefNum = 0; OSType pref_type = 'SH7p'; @@ -245,136 +262,6 @@ pascal void ButtonFrameProc(DialogRef dlg, DialogItemIndex itemNo) FrameRoundRect(&box, 16, 16); } -// read from the channel and print to console -void ssh_read(void) -{ - ssize_t rc = libssh2_channel_read(ssh_con.channel, ssh_con.recv_buffer, SSHEVEN_BUFFER_SIZE); - - if (rc == 0) return; - - if (rc <= 0) - { - printf_i("channel read error: %s\r\n", libssh2_error_string(rc)); - read_thread_command = EXIT; - } - - while (rc > 0) - { - rc -= vterm_input_write(con.vterm, ssh_con.recv_buffer, rc); - } -} - -void end_connection(void) -{ - read_thread_state = CLEANUP; - - OSStatus err = noErr; - - if (ssh_con.channel) - { - libssh2_channel_send_eof(ssh_con.channel); - libssh2_channel_close(ssh_con.channel); - libssh2_channel_free(ssh_con.channel); - libssh2_session_disconnect(ssh_con.session, "Normal Shutdown, Thank you for playing"); - libssh2_session_free(ssh_con.session); - } - - libssh2_exit(); - - if (ssh_con.endpoint != kOTInvalidEndpointRef) - { - // request to close the TCP connection - OTSndOrderlyDisconnect(ssh_con.endpoint); - - // discard remaining data so we can finish closing the connection - int rc = 1; - OTFlags ot_flags; - while (rc != kOTLookErr) - { - rc = OTRcv(ssh_con.endpoint, ssh_con.recv_buffer, 1, &ot_flags); - } - - // finish closing the TCP connection - OSStatus result = OTLook(ssh_con.endpoint); - - switch (result) - { - case T_DISCONNECT: - OTRcvDisconnect(ssh_con.endpoint, nil); - break; - - case T_ORDREL: - err = OTRcvOrderlyDisconnect(ssh_con.endpoint); - if (err == noErr) - { - err = OTSndOrderlyDisconnect(ssh_con.endpoint); - } - break; - - default: - printf_i("Unexpected OTLook result while closing: %s\r\n", OT_event_string(result)); - break; - } - - // release endpoint - err = OTUnbind(ssh_con.endpoint); - if (err != noErr) printf_i("OTUnbind failed.\r\n"); - - err = OTCloseProvider(ssh_con.endpoint); - if (err != noErr) printf_i("OTCloseProvider failed.\r\n"); - } - - read_thread_state = DONE; -} - -int check_network_events(void) -{ - int ok = 1; - OSStatus err = noErr; - - // check if we have any new network events - OTResult look_result = OTLook(ssh_con.endpoint); - - switch (look_result) - { - case T_DATA: - case T_EXDATA: - // got data - // we always try to read, so ignore this event - break; - - case T_RESET: - // connection reset? close it/give up - end_connection(); - ok = 0; - break; - - case T_DISCONNECT: - // got disconnected - OTRcvDisconnect(ssh_con.endpoint, nil); - end_connection(); - ok = 0; - break; - - case T_ORDREL: - // nice tcp disconnect requested by remote - err = OTRcvOrderlyDisconnect(ssh_con.endpoint); - if (err == noErr) - { - err = OTSndOrderlyDisconnect(ssh_con.endpoint); - } - end_connection(); - ok = 0; - break; - - default: - // something weird or irrelevant: ignore it - break; - } - - return ok; -} - void display_about_box(void) { DialogRef about = GetNewDialog(DLOG_ABOUT, 0, (WindowPtr)-1); @@ -388,20 +275,6 @@ void display_about_box(void) DisposeWindow(about); } -void ssh_write(char* buf, size_t len) -{ - if (read_thread_state == OPEN && read_thread_command != EXIT) - { - int r = libssh2_channel_write(ssh_con.channel, buf, len); - - if (r < 1) - { - printf_i("Failed to write to channel, closing connection.\r\n"); - read_thread_command = EXIT; - } - } -} - void ssh_paste(void) { // GetScrap requires a handle, not a raw buffer @@ -751,151 +624,6 @@ void event_loop(void) } while (!exit_event_loop); } -#include -ssize_t network_recv_callback(libssh2_socket_t sock, void *buffer, - size_t length, int flags, void **abstract) -{ - OTResult ret = kOTNoDataErr; - OTFlags ot_flags = 0; - - if (length == 0) return 0; - - // in non-blocking mode, returns instantly always - ret = OTRcv(ssh_con.endpoint, buffer, length, &ot_flags); - - // if we got bytes, return them - if (ret >= 0) return ret; - - // if no data, let other threads run, then tell caller to call again - if (ret == kOTNoDataErr && read_thread_command != EXIT) - { - YieldToAnyThread(); - return -EAGAIN; - } - - // if we got anything other than data or nothing, return an error - if (ret != kOTNoDataErr) return -1; - - return -1; -} - -ssize_t network_send_callback(libssh2_socket_t sock, const void *buffer, - size_t length, int flags, void **abstract) -{ - int ret = -1; - - ret = OTSnd(ssh_con.endpoint, (void*) buffer, length, 0); - - // TODO FIXME handle cases better, i.e. translate error cases - if (ret == kOTLookErr) - { - OTResult lookresult = OTLook(ssh_con.endpoint); - //printf("kOTLookErr, reason: %ld\n", lookresult); - - switch (lookresult) - { - default: - //printf("what?\n"); - ret = -1; - break; - } - } - - return (ssize_t) ret; -} - -void ssh_end_msg_callback(LIBSSH2_SESSION* session, int reason, const char *message, - int message_len, const char *language, int language_len, - void **abstract) -{ - printf_i("got a disconnect msg\r\n"); -} - -int init_connection(char* hostname) -{ - int rc; - - // OT vars - OSStatus err = noErr; - TCall sndCall; - DNSAddress hostDNSAddress; - - printf_i("Opening and configuring endpoint... "); YieldToAnyThread(); - - // open TCP endpoint - ssh_con.endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err); - - if (err != noErr || ssh_con.endpoint == kOTInvalidEndpointRef) - { - printf_i("failed to open Open Transport TCP endpoint.\r\n"); - return 0; - } - - OT_CHECK(OTSetSynchronous(ssh_con.endpoint)); - OT_CHECK(OTSetBlocking(ssh_con.endpoint)); - OT_CHECK(OTUseSyncIdleEvents(ssh_con.endpoint, false)); - - OT_CHECK(OTBind(ssh_con.endpoint, nil, nil)); - - OT_CHECK(OTSetNonBlocking(ssh_con.endpoint)); - - printf_i("done.\r\n"); YieldToAnyThread(); - - // set up address struct, do the DNS lookup, and connect - OTMemzero(&sndCall, sizeof(TCall)); - - sndCall.addr.buf = (UInt8 *) &hostDNSAddress; - sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostname); - - printf_i("Connecting endpoint... "); YieldToAnyThread(); - OT_CHECK(OTConnect(ssh_con.endpoint, &sndCall, nil)); - - printf_i("done.\r\n"); YieldToAnyThread(); - - printf_i("Initializing SSH... "); YieldToAnyThread(); - // init libssh2 - SSH_CHECK(libssh2_init(0)); - - printf_i("done.\r\n"); YieldToAnyThread(); - - printf_i("Opening SSH session... "); YieldToAnyThread(); - ssh_con.session = libssh2_session_init(); - if (ssh_con.session == 0) - { - printf_i("failed to initialize SSH session.\r\n"); - return 0; - } - printf_i("done.\r\n"); YieldToAnyThread(); - - // register callbacks - libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_SEND, network_send_callback); - libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_RECV, network_recv_callback); - libssh2_session_callback_set(ssh_con.session, LIBSSH2_CALLBACK_DISCONNECT, network_recv_callback); - - long s = TickCount(); - printf_i("Beginning SSH session handshake... "); YieldToAnyThread(); - SSH_CHECK(libssh2_session_handshake(ssh_con.session, 0)); - - printf_i("done. (%d ticks)\r\n", TickCount() - s); YieldToAnyThread(); - - const char* banner = libssh2_session_banner_get(ssh_con.session); - if (banner) printf_i("Server banner: %s\r\n", banner); - - return 1; -} - -int ssh_setup_terminal(void) -{ - int rc = 0; - - SSH_CHECK(libssh2_channel_request_pty_ex(ssh_con.channel, prefs.terminal_string, (strlen(prefs.terminal_string)), NULL, 0, con.size_x, con.size_y, 0, 0)); - SSH_CHECK(libssh2_channel_shell(ssh_con.channel)); - - read_thread_state = OPEN; - - return 1; -} - // from the ATS password sample code pascal Boolean TwoItemFilter(DialogPtr dlog, EventRecord *event, short *itemHit) { @@ -970,8 +698,7 @@ pascal Boolean TwoItemFilter(DialogPtr dlog, EventRecord *event, short *itemHit) int password_dialog(int dialog_resource) { int ret = 1; - // n.b. dialog strings can't be longer than this, so no overflow risk - //static char password[256]; + DialogPtr dlog; Handle itemH; short item; @@ -1251,325 +978,6 @@ int intro_dialog(void) } } -// returns base64 sha256 hash of key as a malloc'd pascal string -char* host_hash(void) -{ - size_t length = 0; - char* human_readable = malloc(66); - memset(human_readable, 0, 66); - const char* host_key_hash = NULL; - - host_key_hash = libssh2_hostkey_hash(ssh_con.session, LIBSSH2_HOSTKEY_HASH_SHA256); - mbedtls_base64_encode((unsigned char*)human_readable+1, 64, &length, (unsigned const char*)host_key_hash, 32); - - human_readable[0] = (unsigned char)length; - - return human_readable; -} - -char* known_hosts_full_path(int* found) -{ - int ok = 1; - short foundVRefNum = 0; - long foundDirID = 0; - FSSpec known_hosts_file; - *found = 0; - - OSType pref_type = 'SH7p'; - OSType creator_type = 'SSH7'; - - // find the preferences folder on the system disk, create folder if needed - OSErr e = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID); - if (e != noErr) ok = 0; - - // make an FSSpec for the new file we want to make - if (ok) - { - e = FSMakeFSSpec(foundVRefNum, foundDirID, "\pknown_hosts", &known_hosts_file); - - // if the file exists, we found it else make an empty one - if (e == noErr) *found = 1; - else if (e == noErr) e = FSpCreate(&known_hosts_file, creator_type, pref_type, smSystemScript); - - if (e != noErr) ok = 0; - } - - Handle full_path_handle; - int path_length; - - FSpPathFromLocation(&known_hosts_file, &path_length, &full_path_handle); - char* full_path = malloc(path_length+1); - strncpy(full_path, (char*)(*full_path_handle), path_length+1); - DisposeHandle(full_path_handle); - - return full_path; -} - -int known_hosts(void) -{ - int safe_to_connect = 1; - int recognized_key = 0; - int known_hosts_file_exists = 0; - - char* known_hosts_file_path = known_hosts_full_path(&known_hosts_file_exists); - char* hash_string = NULL; - - size_t key_len = 0; - int key_type = 0; - const char* host_key = libssh2_session_hostkey(ssh_con.session, &key_len, &key_type); - - LIBSSH2_KNOWNHOSTS* known_hosts = libssh2_knownhost_init(ssh_con.session); - - if (known_hosts_file_exists) - { - // load known hosts file - printf_i("Loading known hosts... "); - - int e = libssh2_knownhost_readfile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if (e >= 0) - { - printf_i("got %d.\r\n", e); - } - else - { - printf_i("failed: %s\r\n", libssh2_error_string(e)); - } - } - else - { - printf_i("No known hosts file found.\r\n"); - } - - if (safe_to_connect) - { - // hostnames need to be either plain or of the format "[host]:port" - prefs.hostname[prefs.hostname[0]+1] = '\0'; - int e = libssh2_knownhost_check(known_hosts, prefs.hostname+1, host_key, key_len, LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, NULL); - prefs.hostname[prefs.hostname[0]+1] = ':'; - - switch (e) - { - case LIBSSH2_KNOWNHOST_CHECK_FAILURE: - printf_i("Failed to check known hosts against server public key!\r\n"); - safe_to_connect = 0; - break; - case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND: - printf_i("No matching host found.\r\n"); - break; - case LIBSSH2_KNOWNHOST_CHECK_MATCH: - printf_i("Host and key match found in known hosts.\r\n"); - recognized_key = 1; - break; - case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: - printf_i("Host found in known hosts but key doesn't match!\r\n"); - safe_to_connect = 0; - break; - default: - printf_i("Unexpected error while checking known-hosts: %d\r\n", e); - safe_to_connect = 0; - break; - } - } - - hash_string = host_hash(); - //printf_i("Host key hash (SHA256): %s\r\n", hash_string+1); - - // ask the user to confirm if we're seeing a new host+key combo - if (safe_to_connect && !recognized_key) - { - // ask the user if the key is OK - DialogPtr dlg = GetNewDialog(DLOG_NEW_HOST, 0, (WindowPtr)-1); - - DialogItemType type; - Handle itemH; - Rect box; - - // draw default button indicator around the connect button - GetDialogItem(dlg, 2, &type, &itemH, &box); - SetDialogItem(dlg, 2, type, (Handle)NewUserItemUPP(&ButtonFrameProc), &box); - - // write the hash string into the dialog window - ControlHandle hash_text_box; - GetDialogItem(dlg, 4, &type, &itemH, &box); - hash_text_box = (ControlHandle)itemH; - SetDialogItemText((Handle)hash_text_box, (ConstStr255Param)hash_string); - - // let the modalmanager do everything - // stop on reject or accept - short item; - do { - ModalDialog(NULL, &item); - } while(item != 1 && item != 5); - - // reject if user hit reject - if (item == 1) - { - safe_to_connect = 0; - } - - DisposeDialog(dlg); - FlushEvents(everyEvent, -1); - - printf_i("Saving host and key... "); - - int save_type = 0; - save_type |= LIBSSH2_KNOWNHOST_TYPE_PLAIN; - save_type |= LIBSSH2_KNOWNHOST_KEYENC_RAW; - - switch (key_type) - { - default: - __attribute__ ((fallthrough)); - case LIBSSH2_HOSTKEY_TYPE_UNKNOWN: - save_type |= LIBSSH2_KNOWNHOST_KEY_UNKNOWN; - break; - case LIBSSH2_HOSTKEY_TYPE_RSA: - save_type |= LIBSSH2_KNOWNHOST_KEY_SSHRSA; - break; - case LIBSSH2_HOSTKEY_TYPE_DSS: - save_type |= LIBSSH2_KNOWNHOST_KEY_SSHDSS; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: - save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: - save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: - save_type |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521; - break; - case LIBSSH2_HOSTKEY_TYPE_ED25519: - save_type |= LIBSSH2_KNOWNHOST_KEY_ED25519; - break; - } - - // hostnames need to be either plain or of the format "[host]:port" - prefs.hostname[prefs.hostname[0]+1] = '\0'; - int e = libssh2_knownhost_addc(known_hosts, prefs.hostname+1, NULL, host_key, key_len, NULL, 0, save_type, NULL); - prefs.hostname[prefs.hostname[0]+1] = ':'; - - if (e != 0) printf_i("failed to add to known hosts: %s\r\n", libssh2_error_string(e)); - else - { - e = libssh2_knownhost_writefile(known_hosts, known_hosts_file_path, LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if (e != 0) printf_i("failed to save known hosts file: %s\r\n", libssh2_error_string(e)); - else printf_i("done.\r\n"); - } - } - - free(hash_string); - - libssh2_knownhost_free(known_hosts); - - free(known_hosts_file_path); - - return safe_to_connect; -} - -void* read_thread(void* arg) -{ - int ok = 1; - int rc = LIBSSH2_ERROR_NONE; - - // yield until we're given a command - while (read_thread_command == WAIT) YieldToAnyThread(); - - if (read_thread_command == EXIT) - { - return 0; - } - - // connect - ok = init_connection(prefs.hostname+1); - YieldToAnyThread(); - - // check the server pub key vs. known hosts - if (ok) - { - ok = known_hosts(); - if (!ok) printf_i("Rejected server public key!\r\n"); - } - - // actually log in - if (ok) - { - printf_i("Authenticating... "); YieldToAnyThread(); - - if (prefs.auth_type == USE_PASSWORD) - { - rc = libssh2_userauth_password(ssh_con.session, prefs.username+1, prefs.password+1); - } - else - { - rc = libssh2_userauth_publickey_fromfile_ex(ssh_con.session, - prefs.username+1, - prefs.username[0], - prefs.pubkey_path, - prefs.privkey_path, - prefs.password+1); - } - - if (rc == LIBSSH2_ERROR_NONE) - { - printf_i("done.\r\n"); - } - else - { - printf_i("failed!\r\n"); - if (rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED && prefs.auth_type == USE_PASSWORD) StopAlert(ALRT_PW_FAIL, nil); - else if (rc == LIBSSH2_ERROR_FILE) StopAlert(ALRT_FILE_FAIL, nil); - else if (rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED) - { - printf_i("Username/public key combination invalid!\r\n"); // TODO: have an alert for this - if (prefs.pubkey_path[0] != '\0' || prefs.privkey_path[0] != '\0') - { - prefs.pubkey_path[0] = '\0'; - prefs.privkey_path[0] = '\0'; - } - } - else printf_i("unexpected failure: %s\r\n", libssh2_error_string(rc)); - ok = 0; - } - } - - save_prefs(); - - // if we logged in, open and set up the tty - if (ok) - { - libssh2_channel_handle_extended_data2(ssh_con.channel, LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE); - ssh_con.channel = libssh2_channel_open_session(ssh_con.session); - ok = ssh_setup_terminal(); - YieldToAnyThread(); - } - - // if we failed, close everything and exit - if (!ok || (read_thread_state != OPEN)) - { - end_connection(); - return 0; - } - - // if we connected, allow pasting - void* menu = GetMenuHandle(MENU_EDIT); - EnableItem(menu, 5); - - // read until failure, command to EXIT, or remote EOF - while (read_thread_command == READ && read_thread_state == OPEN && libssh2_channel_eof(ssh_con.channel) == 0) - { - if (check_network_events()) ssh_read(); - YieldToAnyThread(); - } - - // if we still have a connection, close it - if (read_thread_state != DONE) end_connection(); - - // disallow pasting after connection is closed - DisableItem(menu, 5); - - return 0; -} - int safety_checks(void) { OSStatus err = noErr; @@ -1623,12 +1031,12 @@ int safety_checks(void) printf_i(" Attempting to continue anyway.\r\n"); } - NumVersion* ot_version = (NumVersion*) &open_transport_new_version; + /*NumVersion* ot_version = (NumVersion*) &open_transport_new_version; printf_i("Detected Open Transport version: %d.%d.%d\r\n", (int)ot_version->majorRev, (int)((ot_version->minorAndBugRev & 0xF0) >> 4), - (int)(ot_version->minorAndBugRev & 0x0F)); + (int)(ot_version->minorAndBugRev & 0x0F));*/ // check for CPU type and display warning if it's going to be too slow long int cpu_type = 0; @@ -1729,22 +1137,13 @@ int main(int argc, char** argv) printf_i("Running in 68k mode.\r\n"); #endif - if (prefs.loaded_from_file) - { - printf_i("Loaded preferences file.\r\n"); - } - else - { - printf_i("Could not load from preferences file.\r\n"); - } - BeginUpdate(con.win); draw_screen(&(con.win->portRect)); EndUpdate(con.win); int ok = 1; - if (!safety_checks()) return 0; + ok = safety_checks(); BeginUpdate(con.win); draw_screen(&(con.win->portRect)); diff --git a/ssheven.h b/ssheven.h index 0984792..ec01ec1 100644 --- a/ssheven.h +++ b/ssheven.h @@ -7,38 +7,17 @@ #pragma once -// open transport #include #include - -// mac os stuff -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -// libssh2 #include -// ssheven constants -#include "ssheven-constants.r" - -// libvterm #include #include -#include - -#include +#include "ssheven-constants.r" // sinful globals struct ssheven_console @@ -77,10 +56,6 @@ struct ssheven_ssh_connection extern struct ssheven_ssh_connection ssh_con; -extern char key_to_vterm[256]; - -void ssh_write(char* buf, size_t len); - struct preferences { int major_version; @@ -110,3 +85,18 @@ struct preferences }; extern struct preferences prefs; + +extern char key_to_vterm[256]; + +enum THREAD_COMMAND { WAIT, READ, EXIT }; +enum THREAD_STATE { UNINTIALIZED, OPEN, CLEANUP, DONE }; + +extern enum THREAD_COMMAND read_thread_command; +extern enum THREAD_STATE read_thread_state; + +int save_prefs(void); +void set_window_title(WindowPtr w, const char* c_name); + +OSErr FSpPathFromLocation(FSSpec* spec, int* length, Handle* fullPath); + +pascal void ButtonFrameProc(DialogRef dlg, DialogItemIndex itemNo);