From 3e2647c6af2a85f2887c4037974849c9b799cd01 Mon Sep 17 00:00:00 2001 From: cy384 Date: Thu, 16 Jul 2020 21:32:31 -0400 Subject: [PATCH] extensive changes to everything implement my own terminal implement my own gui event loop add a first configuration dialog resource --- CMakeLists.txt | 6 +- README.md | 13 +- ssheven-debug.h | 6 +- ssheven.c | 767 ++++++++++++++++++++++++++++++++++++++---------- ssheven.r | 63 ++++ 5 files changed, 693 insertions(+), 162 deletions(-) create mode 100644 ssheven.r diff --git a/CMakeLists.txt b/CMakeLists.txt index a81b3cf..5fde4de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,16 @@ cmake_minimum_required(VERSION 3.9) -add_application(ssheven ssheven.c CONSOLE) +add_application(ssheven ssheven.c ssheven.r) set_target_properties(ssheven PROPERTIES COMPILE_OPTIONS -ffunction-sections) IF(CMAKE_SYSTEM_NAME MATCHES Retro68) # for 68k set_target_properties(ssheven PROPERTIES LINK_FLAGS "-Wl,-gc-sections -Wl,--mac-strip-macsbug -Wl,--mac-segments -Wl,${CMAKE_CURRENT_SOURCE_DIR}/ssheven.segmap") -target_link_libraries(ssheven RetroConsole ssh2 mbedtls mbedx509 mbedcrypto OpenTransportApp OpenTransport OpenTptInet) +target_link_libraries(ssheven ssh2 mbedtls mbedx509 mbedcrypto OpenTransportApp OpenTransport OpenTptInet) ELSE() # for PPC set_target_properties(ssheven PROPERTIES LINK_FLAGS "-Wl,-gc-sections") -target_link_libraries(ssheven RetroConsole ThreadsLib ssh2 mbedtls mbedx509 mbedcrypto OpenTransportAppPPC OpenTransportLib OpenTptInternetLib) +target_link_libraries(ssheven ThreadsLib ssh2 mbedtls mbedx509 mbedcrypto OpenTransportAppPPC OpenTransportLib OpenTptInternetLib) ENDIF() diff --git a/README.md b/README.md index cac23aa..88a6406 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ +![ssheven box](http://www.cy384.com/media/img/ssheven_box_front_small.png) + ssheven ------- A modern SSH client for Mac OS 7/8/9 on m68k and PPC machines. -Project status: can open the SSH connection, channel, send a command, get the response, and cleanly disconnect. See release 0.0.0 for a simple demo. +Project status: as of 0.1.0 (see github releases), an actual SSH client, with a zero-features "vanilla" fixed-size terminal -* encryption libraries: ham-handedly ported and seem to work -* console emulation: nonexistent, planning the use of libvterm for escape codes etc., going to need to make a custom window type -* UI/UX: not yet considered at all +* encryption libraries: ham-handedly ported and fairly functional +* console emulation: very basic, no escape codes or anything yet (to be implemented with libvterm soon) +* UI/UX: it quits when you click the close button! (i.e. basically nothing yet) build ----- @@ -14,7 +16,7 @@ More details to come as functionality is added. Uses Retro68 and cmake. -Requires mbedtls and libssh2, see my (cy384's) ports of those libraries for details. +Requires mbedtls and libssh2, see my (cy384's) ports of those libraries for details. Note that you need to make/install them for both platforms if you want to build for both platforms. * `mkdir build && cd build` * `cmake .. -DCMAKE_TOOLCHAIN_FILE=/your/path/to/Retro68-build/toolchain/powerpc-apple-macos/cmake/retroppc.toolchain.cmake` or `cmake .. -DCMAKE_TOOLCHAIN_FILE=/your/path/to/Retro68-build/toolchain/m68k-apple-macos/cmake/retro68.toolchain.cmake` @@ -23,3 +25,4 @@ Requires mbedtls and libssh2, see my (cy384's) ports of those libraries for deta license ------- Licensed under the BSD 2 clause license, see LICENSE file. + diff --git a/ssheven-debug.h b/ssheven-debug.h index e87daf4..48fa472 100644 --- a/ssheven-debug.h +++ b/ssheven-debug.h @@ -1,6 +1,6 @@ -// handy debugging string conversions +/* handy debugging string conversions */ -// convert libssh2 errors into strings +/* convert libssh2 errors into strings */ const char* libssh2_error_string(int i) { switch (i) @@ -109,7 +109,7 @@ const char* libssh2_error_string(int i) return "libssh2_error_string should never return from here"; } -// convert OT event types into strings +/* convert OT event types into strings */ const char* OT_event_string(int i) { switch (i) diff --git a/ssheven.c b/ssheven.c index ab9bb70..bef0c1c 100644 --- a/ssheven.c +++ b/ssheven.c @@ -6,14 +6,19 @@ */ // retro68 stdio/console library -#include +//#include // open transport #include #include -// mac os threads +// mac os stuff #include +#include +#include +#include +#include +#include // libssh2 #include @@ -22,15 +27,58 @@ #include "ssheven-debug.h" // version string -#define SSHEVEN_VERSION "0.0.1" +#define SSHEVEN_VERSION "0.1.0" -// network buffer size -enum { buffer_size = 4096 }; +// size for recv and send thread buffers +#define BUFFER_SIZE 4096 -// text input buffer size -enum { input_buffer_size = 128 }; +// terminal type to send over ssh, determines features etc. +// "vanilla" supports basically nothing, which is good for us here +#define TERMINAL_TYPE "vanilla" -// event handler to yield whenever we're blocked +// error checking convenience macros +#define OT_CHECK(X) err = (X); if (err != noErr) { print_string("" #X " failed\n"); return; }; +#define SSH_CHECK(X) rc = (X); if (rc != LIBSSH2_ERROR_NONE) { print_string("" #X " failed: "); print_string(libssh2_error_string(rc)); print_string("\n"); return;}; + +// sinful globals +struct ssheven_console +{ + WindowPtr win; + + char data[80][24]; + + int cursor_x; + int cursor_y; +} con = { NULL, {0}, 0, 0 }; + +struct ssheven_ssh_connection +{ + LIBSSH2_CHANNEL* channel; + LIBSSH2_SESSION* session; + + EndpointRef endpoint; + + char* recv_buffer; + char* send_buffer; +} ssh_con = { NULL, NULL, kOTInvalidEndpointRef, NULL, NULL }; + +enum { wait, read, exit } read_thread_state = wait; + +// borrowed from Retro68 sample code +// draws the "default" indicator around a button +pascal void ButtonFrameProc(DialogRef dlg, DialogItemIndex itemNo) +{ + DialogItemType type; + Handle itemH; + Rect box; + + GetDialogItem(dlg, 1, &type, &itemH, &box); + InsetRect(&box, -4, -4); + PenSize(3, 3); + FrameRoundRect(&box, 16, 16); +} + +// event handler for a thread to yield when blocked static pascal void yield_notifier(void* contextPtr, OTEventCode code, OTResult result, void* cookie) { switch (code) @@ -44,61 +92,407 @@ static pascal void yield_notifier(void* contextPtr, OTEventCode code, OTResult r } } -void get_line(char* buffer) +void draw_char(int x, int y, Rect* r, char c) { - int i = 0; - char c; + TextFont(kFontIDMonaco); + TextSize(9); + TextFace(normal); - while (i < input_buffer_size - 1) - { - c = getc(stdin); - if (c != '\n') buffer[i++] = c; else break; - } + int cell_height = 12; + int cell_width = CharWidth('M'); - buffer[i] = '\0'; - return; + MoveTo(r->left + x * cell_width + 2, r->top + ((y+1) * cell_height) - 2); + DrawChar(c); } -void do_ssh_connection(char* hostname, char* username, char* password, char* command) +void draw_screen(Rect* r) +{ + EraseRect(&(con.win->portRect)); + for (int x = 0; x < 80; x++) + for (int y = 0; y < 24; y++) + draw_char(x, y, r, con.data[x][y]); +} + +char itoc[] = {'0','1', '2','3','4','5','6','7','8','9'}; + +void ruler(Rect* r) +{ + for (int x = 0; x < 80; x++) + for (int y = 0; y < 24; y++) + draw_char(x, y, r, itoc[x%10]); +} + +void bump_up_line() +{ + for (int y = 0; y < 23; y++) + { + for (int x = 0; x < 80; x++) + { + con.data[x][y] = con.data[x][y+1]; + } + } + + for (int x = 0; x < 80; x++) con.data[x][23] = ' '; +} + +int is_printable(char c) +{ + if (c >= 32 && c <= 126) return 1; else return 0; +} + +void print_char(char c) +{ + // backspace + if ('\b' == c) + { + // erase current location + con.data[con.cursor_x][con.cursor_y] = ' '; + + // wrap back to the previous line if possible and necessary + if (con.cursor_x == 0 && con.cursor_y != 0) + { + con.cursor_x = 79; + con.cursor_y--; + } + // otherwise just move back a spot + else if (con.cursor_x > 0) + { + con.cursor_x--; + } + + return; + } + + // got a bell, give em a system beep (value of 30 recommended by docs) + if ('\a' == c) SysBeep(30); + + if ('\n' == c) + { + con.cursor_y++; + con.cursor_x = 0; + } + + if (is_printable(c)) + { + con.data[con.cursor_x][con.cursor_y] = c; + con.cursor_x++; + } + + if (con.cursor_x == 80) + { + con.cursor_x = 0; + con.cursor_y++; + } + + if (con.cursor_y == 24) + { + bump_up_line(); + con.cursor_y = 23; + } +} + +void print_string(const char* c) +{ + for (int i = 0; i < strlen(c); i++) + { + print_char(c[i]); + } +} + +#include +void set_window_title(WindowPtr w, const char* c_name) +{ + Str255 pascal_name; + strncpy((char *) &pascal_name[1], c_name, 255); + pascal_name[0] = strlen(c_name); + + SetWTitle(w, pascal_name); +} + +void ssh_read(void) +{ + // read from the channel + int rc = libssh2_channel_read(ssh_con.channel, ssh_con.recv_buffer, BUFFER_SIZE); + + if (rc == 0) return; + + if (rc > 0) + { + for(int i = 0; i < rc; ++i) print_char(ssh_con.recv_buffer[i]); + InvalRect(&(con.win->portRect)); + //print_string("\n"); + } + else + { + print_string("channel read error: "); + print_string(libssh2_error_string(rc)); + print_string("\n"); + } +} + + +void end_connection(void) +{ + OSStatus err = noErr; + + if (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 + OT_CHECK(OTSndOrderlyDisconnect(ssh_con.endpoint)); + + // get and 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: + print_string("unexpected OTLook result while closing: "); + print_string(OT_event_string(result)); + print_string("\n"); + break; + } + + // release endpoint + err = OTUnbind(ssh_con.endpoint); + if (err != noErr) print_string("OTUnbind failed\n"); + + err = OTCloseProvider(ssh_con.endpoint); + if (err != noErr) print_string("OTCloseProvider failed\n"); + } +} + +void event_loop(void) +{ + int exit_event_loop = 0; + OTResult look_result = 0; + OSStatus err = noErr; + + do + { + // system task yields to run drivers and other stuff! + //SystemTask(); // don't need to call if we use waitnextevent + //Idle(); + EventRecord event; + WindowPtr eventWin; + +/* + while(!GetNextEvent(everyEvent, &event)) + { + SystemTask(); + YieldToAnyThread(); + //Idle(); + } +*/ + + // alternately we can use: + long int ct = GetCaretTime(); // should probably make this a smaller number, but eh. + + // wait for some length of time to get an event + // runs the loop every time we timeout waiting for a mac event + while (!WaitNextEvent(everyEvent, &event, ct, NULL)) + { + // check if we have any new network events + look_result = OTLook(ssh_con.endpoint); + + switch (look_result) + { + case T_DATA: + case T_EXDATA: + // got data + ssh_read(); + break; + + case T_RESET: + // connection reset? close it/give up + end_connection(); + break; + + case T_DISCONNECT: + // got disconnected + OTRcvDisconnect(ssh_con.endpoint, nil); + end_connection(); + 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(); + break; + + default: + // something weird or irrelevant: ignore it + break; + } + + // let any other threads run before we wait for events again + YieldToAnyThread(); + } + + // handle any mac gui events + char c = 0; + switch(event.what) + { + // TODO: don't redraw the whole screen, just do needed region + case updateEvt: + eventWin = (WindowPtr)event.message; + BeginUpdate(eventWin); + draw_screen(&(eventWin->portRect)); + EndUpdate(eventWin); + break; + + case keyDown: + case autoKey: // autokey means we're repeating a held down key event + c = event.message & charCodeMask; + if (c) + { + if ('\r' == c) c = '\n'; + ssh_con.send_buffer[0] = c; + libssh2_channel_write(ssh_con.channel, ssh_con.send_buffer, 1); + } + + case mouseDown: + switch(FindWindow(event.where, &eventWin)) + { + case inDrag: + // allow the window to be dragged anywhere on any monitor + // hmmm... which of these is better??? + DragWindow(eventWin, event.where, &(*(GetGrayRgn()))->rgnBBox); + // DragWindow(eventWin, event.where, &(*qd.thePort->visRgn)->rgnBBox); + break; + + case inGrow: + { + //don't allow resize right now + break; + /*long growResult = GrowWindow(eventWin, event.where, &window_limits); + SizeWindow(eventWin, growResult & 0xFFFF, growResult >> 16, true); + EraseRect(&(eventWin->portRect)); + InvalRect(&(eventWin->portRect));*/ + } + break; + + case inGoAway: + { + if (TrackGoAway(eventWin, event.where)) + exit_event_loop = 1; + } + break; + + case inMenuBar: + break; + + case inSysWindow: + // is this system6 relevant only??? + SystemClick(&event, eventWin); + break; + + case inContent: + break; + } + break; + } + } while (!exit_event_loop); +} + +void console_setup(void) +{ + TextFont(kFontIDMonaco); + TextSize(9); + TextFace(normal); + + int cell_height = 12; + int cell_width = CharWidth('M'); + + Rect initial_window_bounds = qd.screenBits.bounds; + InsetRect(&initial_window_bounds, 20, 20); + initial_window_bounds.top += 40; + + initial_window_bounds.bottom = initial_window_bounds.top + cell_height * 24 + 2; + initial_window_bounds.right = initial_window_bounds.left + cell_width * 80 + 4; + + // limits on window size changes: + // top = min vertical + // bottom = max vertical + // left = min horizontal + // right = max horizontal + //Rect window_limits = { .top = 100, .bottom = 200, .left = 100, .right = 200 }; + + ConstStr255Param title = "\pssheven " SSHEVEN_VERSION; + + WindowPtr win = NewWindow(NULL, &initial_window_bounds, title, true, noGrowDocProc, (WindowPtr)-1, true, 0); + + Rect portRect = win->portRect; + + SetPort(win); + EraseRect(&portRect); + + int exit_main_loop = 0; + + con.win = win; + memset(con.data, ' ', sizeof(char) * 24*80); + + con.cursor_x = 0; + con.cursor_y = 0; +} + +void init_connection(char* hostname) { - // libssh2 vars - LIBSSH2_CHANNEL* channel; - LIBSSH2_SESSION* session; int rc; // OT vars OSStatus err = noErr; - char* buffer = NULL; - EndpointRef endpoint = kOTInvalidEndpointRef; TCall sndCall; DNSAddress hostDNSAddress; OSStatus result; - OTFlags ot_flags; - - // allocate buffer - buffer = OTAllocMem(buffer_size); - if (buffer == NULL) - { - printf("failed to allocate OT buffer\n"); - return; - } // open TCP endpoint - endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err); - if (err != noErr) + ssh_con.endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err); + + if (err != noErr || ssh_con.endpoint == kOTInvalidEndpointRef) { - printf("failed to open TCP endpoint\n"); - if (buffer != NULL) OTFreeMem(buffer); + print_string("failed to open OT TCP endpoint\n"); return; } - #define OT_CHECK(X) err = (X); if (err != noErr) { printf("" #X " failed: %d\n", err); goto OT_cleanup; }; + OT_CHECK(OTSetSynchronous(ssh_con.endpoint)); + OT_CHECK(OTSetNonBlocking(ssh_con.endpoint)); + OT_CHECK(OTUseSyncIdleEvents(ssh_con.endpoint, false)); - OT_CHECK(OTSetSynchronous(endpoint)); - OT_CHECK(OTSetBlocking(endpoint)); - OT_CHECK(OTInstallNotifier(endpoint, yield_notifier, nil)); - OT_CHECK(OTUseSyncIdleEvents(endpoint, true)); - OT_CHECK(OTBind(endpoint, nil, nil)); + OT_CHECK(OTBind(ssh_con.endpoint, nil, nil)); // set up address struct, do the DNS lookup, and connect OTMemzero(&sndCall, sizeof(TCall)); @@ -106,139 +500,210 @@ void do_ssh_connection(char* hostname, char* username, char* password, char* com sndCall.addr.buf = (UInt8 *) &hostDNSAddress; sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostname); - OT_CHECK(OTConnect(endpoint, &sndCall, nil)); + OT_CHECK(OTConnect(ssh_con.endpoint, &sndCall, nil)); - printf("OT setup done, endpoint: %d, (should not be %d for libssh2," - " should not be %d for OT)\n", (int) endpoint, - (int) LIBSSH2_INVALID_SOCKET, (int) kOTInvalidEndpointRef); - - #define SSH_CHECK(X) rc = (X); if (rc != LIBSSH2_ERROR_NONE) { printf("" #X "failed: %s\n", libssh2_error_string(rc)); goto libssh2_cleanup; }; + print_string("OT setup done\n"); // init libssh2 SSH_CHECK(libssh2_init(0)); - session = libssh2_session_init(); - if (session == 0) + ssh_con.session = libssh2_session_init(); + if (ssh_con.session == 0) { - printf("failed to open SSH session\n"); - goto libssh2_cleanup; + print_string("failed to initialize SSH library\n"); + return; } - SSH_CHECK(libssh2_session_handshake(session, endpoint)); - - SSH_CHECK(libssh2_userauth_password(session, username, password)); - - channel = libssh2_channel_open_session(session); - printf("channel open: %d\n", channel); - - SSH_CHECK(libssh2_channel_exec(channel, command)); - - // read from the channel - rc = libssh2_channel_read(channel, buffer, buffer_size); - if (rc > 0) - { - printf("got %d bytes:\n", rc); - for(int i = 0; i < rc; ++i) printf("%c", buffer[i]); - printf("\n"); - } - else - { - printf("channel read error: %s\n", libssh2_error_string(rc)); - } - - libssh2_cleanup: - - libssh2_channel_close(channel); - libssh2_channel_free(channel); - libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); - libssh2_session_free(session); - libssh2_exit(); - - // request to close the TCP connection - OT_CHECK(OTSndOrderlyDisconnect(endpoint)); - - // get and discard remaining data so we can finish closing the connection - rc = 1; - while (rc != kOTLookErr) - { - rc = OTRcv(endpoint, buffer, 1, &ot_flags); - } - - // finish closing the TCP connection - result = OTLook(endpoint); - switch (result) - { - case T_DISCONNECT: - OTRcvDisconnect(endpoint, nil); - break; - - case T_ORDREL: - err = OTRcvOrderlyDisconnect(endpoint); - if (err == noErr) - { - err = OTSndOrderlyDisconnect(endpoint); - } - break; - - default: - printf("unexpected OTLook result while closing: %s\n", OT_event_string(result)); - break; - } - - - - OT_cleanup: - - // release endpoint - err = OTUnbind(endpoint); - if (err != noErr) printf("OTUnbind failed: %d\n", err); - - err = OTCloseProvider(endpoint); - if (err != noErr) printf("OTCloseProvider failed: %d\n", err); - - // if we have a buffer, release it - if (buffer != nil) OTFreeMem(buffer); + SSH_CHECK(libssh2_session_handshake(ssh_con.session, ssh_con.endpoint)); return; } +void ssh_password_auth(char* username, char* password) +{ + OSStatus err = noErr; + int rc = 1; + + SSH_CHECK(libssh2_userauth_password(ssh_con.session, username, password)); + ssh_con.channel = libssh2_channel_open_session(ssh_con.session); +} + +void ssh_setup_terminal(void) +{ + int rc = 0; + + SSH_CHECK(libssh2_channel_request_pty(ssh_con.channel, TERMINAL_TYPE)); + SSH_CHECK(libssh2_channel_shell(ssh_con.channel)); +} + +// TODO: unused +void* read_thread(void* arg) +{ + while(1) + { + switch(read_thread_state) + { + case wait: + break; + + case read: + ssh_read(); + InvalRect(&(con.win->portRect)); + break; + + case exit: + return 0; + break; + } + + YieldToAnyThread(); + } +} + +void intro_dialog(char* hostname, char* username, char* password) +{ + + // modal dialog setup + TEInit(); + InitDialogs(NULL); + DialogPtr dlg = GetNewDialog(128,0,(WindowPtr)-1); + InitCursor(); + + // select all text in dialog item 4 (the hostname+port one) + SelectDialogItemText(dlg, 4, 0, 32767); + + 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); + + // get the handles for each of the text boxes + ControlHandle address_text_box; + GetDialogItem(dlg, 4, &type, &itemH, &box); + address_text_box = (ControlHandle)itemH; + + ControlHandle username_text_box; + GetDialogItem(dlg, 6, &type, &itemH, &box); + username_text_box = (ControlHandle)itemH; + + ControlHandle password_text_box; + GetDialogItem(dlg, 8, &type, &itemH, &box); + password_text_box = (ControlHandle)itemH; + + // let the modalmanager do everything + // stop when the connect button is hit + short item; + do { + ModalDialog(NULL, &item); + } while(item != 1); + + // copy the text out of the boxes + GetDialogItemText((Handle)address_text_box, hostname); + GetDialogItemText((Handle)username_text_box, username); + GetDialogItemText((Handle)password_text_box, password); + + // clean it up + CloseDialog(dlg); + FlushEvents(everyEvent, -1); +} + int main(int argc, char** argv) { - char hostname[input_buffer_size] = {0}; - char username[input_buffer_size] = {0}; - char password[input_buffer_size] = {0}; - char command[input_buffer_size] = {0}; + char hostname[256] = {0}; + char username[256] = {0}; + char password[256] = {0}; - printf("WARNING: this is a prototype with a bad RNG and no host key checks," - " do not use over untrusted networks or with untrusted SSH servers!\n\n"); + OSStatus err = noErr; - printf("ssheven by cy384 version " SSHEVEN_VERSION "\n\n"); + // expands the application heap to its maximum requested size + // supposedly good for performance + // also required before creating threads! + MaxApplZone(); - printf("enter a host:port >"); fflush(stdout); - get_line(hostname); + // general gui setup + InitGraf(&qd.thePort); + InitFonts(); + InitWindows(); + InitMenus(); - printf("enter a username >"); fflush(stdout); - get_line(username); + intro_dialog(hostname, username, password); - printf("enter a password >"); fflush(stdout); - get_line(password); + console_setup(); - printf("enter a command >"); fflush(stdout); - get_line(command); + char* logo = " _____ _____ _ _\n" + " / ____/ ____| | | |\n" + " | (___| (___ | |__| | _____ _____ _ __\n" + " \\___ \\\\___ \\| __ |/ _ \\ \\ / / _ \\ '_ \\\n" + " ____) |___) | | | | __/\\ V / __/ | | |\n" + " |_____/_____/|_| |_|\\___| \\_/ \\___|_| |_|\n"; + + print_string(logo); + print_string("by cy384, version " SSHEVEN_VERSION "\n"); + + int ok = 1; if (InitOpenTransport() != noErr) { - printf("failed to initialize OT\n"); - return 0; + print_string("failed to initialize OT\n"); + ok = 0; } - do_ssh_connection(hostname, username, password, command); + if (ok) + { + ssh_con.recv_buffer = OTAllocMem(BUFFER_SIZE); + ssh_con.send_buffer = OTAllocMem(BUFFER_SIZE); + + if (ssh_con.recv_buffer == NULL || ssh_con.send_buffer == NULL) + { + print_string("failed to allocate network buffers\n"); + ok = 0; + } + } + +/* + read_thread_state = wait; + int read_thread_result = 0; + ThreadID read_thread_id = 0; + if (ok) + { + err = NewThread(kCooperativeThread, read_thread, NULL, 0, kCreateIfNeeded, NULL, &read_thread_id); + + if (err < 0) + { + ok = 0; + print_string("failed to create network read thread\n"); + } + } +*/ + + if (ok) + { + // those strings are pascal strings, so we skip the first char + init_connection(hostname+1); + + ssh_password_auth(username+1, password+1); + ssh_setup_terminal(); + + read_thread_state = read; + } + + event_loop(); + +/* + read_thread_state = exit; + OTCancelSynchronousCalls(ssh_con.endpoint, kOTCanceledErr); + YieldToThread(read_thread_id); +// err = DisposeThread(read_thread_id, (void*)&read_thread_result, 0); + err = DisposeThread(read_thread_id, NULL, 0); +*/ + + end_connection(); + + if (ssh_con.recv_buffer != NULL) OTFreeMem(ssh_con.recv_buffer); + if (ssh_con.send_buffer != NULL) OTFreeMem(ssh_con.send_buffer); CloseOpenTransport(); - - printf("\n(enter to exit)\n"); - getchar(); - - return 0; } - diff --git a/ssheven.r b/ssheven.r new file mode 100644 index 0000000..b3cfc04 --- /dev/null +++ b/ssheven.r @@ -0,0 +1,63 @@ +#include "Dialogs.r" + +resource 'DLOG' (128) { + { 50, 100, 240, 420 }, + dBoxProc, + visible, + noGoAway, + 0, + 128, + "", + centerMainScreen +}; + +resource 'DITL' (128) { + { + { 190-10-20, 320-10-80, 190-10, 320-10 }, + Button { enabled, "Connect" }; + + { 190-10-20-5, 320-10-80-5, 190-10+5, 320-10+5 }, + UserItem { enabled }; + + { 10, 10, 30, 310 }, + StaticText { enabled, "Address with port" }; + + { 35, 10, 51, 310 }, + EditText { enabled, "10.0.2.2:22" }; + + { 60, 10, 80, 310 }, + StaticText { enabled, "Username" }; + + { 85, 10, 101, 310 }, + EditText { enabled, "" }; + + { 110, 10, 130, 310 }, + StaticText { enabled, "Password" }; + + { 135, 10, 151, 310 }, + EditText { enabled, "" }; + } +}; + +#include "Processes.r" + +resource 'SIZE' (-1) { + reserved, + acceptSuspendResumeEvents, + reserved, + canBackground, + doesActivateOnFGSwitch, + backgroundAndForeground, + dontGetFrontClicks, + ignoreChildDiedEvents, + is32BitCompatible, + notHighLevelEventAware, + onlyLocalHLEvents, + notStationeryAware, + dontUseTextEditServices, + reserved, + reserved, + reserved, + 1024 * 1024, + 1024 * 1024 +};