extensive changes to everything

implement my own terminal
implement my own gui event loop
add a first configuration dialog resource
This commit is contained in:
cy384 2020-07-16 21:32:31 -04:00
parent 19a4995d08
commit 3e2647c6af
5 changed files with 693 additions and 162 deletions

View File

@ -1,16 +1,16 @@
cmake_minimum_required(VERSION 3.9) 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) set_target_properties(ssheven PROPERTIES COMPILE_OPTIONS -ffunction-sections)
IF(CMAKE_SYSTEM_NAME MATCHES Retro68) IF(CMAKE_SYSTEM_NAME MATCHES Retro68)
# for 68k # 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") 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() ELSE()
# for PPC # for PPC
set_target_properties(ssheven PROPERTIES LINK_FLAGS "-Wl,-gc-sections") 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() ENDIF()

View File

@ -1,12 +1,14 @@
![ssheven box](http://www.cy384.com/media/img/ssheven_box_front_small.png)
ssheven ssheven
------- -------
A modern SSH client for Mac OS 7/8/9 on m68k and PPC machines. 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 * encryption libraries: ham-handedly ported and fairly functional
* console emulation: nonexistent, planning the use of libvterm for escape codes etc., going to need to make a custom window type * console emulation: very basic, no escape codes or anything yet (to be implemented with libvterm soon)
* UI/UX: not yet considered at all * UI/UX: it quits when you click the close button! (i.e. basically nothing yet)
build build
----- -----
@ -14,7 +16,7 @@ More details to come as functionality is added.
Uses Retro68 and cmake. 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` * `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` * `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 license
------- -------
Licensed under the BSD 2 clause license, see LICENSE file. Licensed under the BSD 2 clause license, see LICENSE file.

View File

@ -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) const char* libssh2_error_string(int i)
{ {
switch (i) switch (i)
@ -109,7 +109,7 @@ const char* libssh2_error_string(int i)
return "libssh2_error_string should never return from here"; 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) const char* OT_event_string(int i)
{ {
switch (i) switch (i)

763
ssheven.c
View File

@ -6,14 +6,19 @@
*/ */
// retro68 stdio/console library // retro68 stdio/console library
#include <stdio.h> //#include <stdio.h>
// open transport // open transport
#include <OpenTransport.h> #include <OpenTransport.h>
#include <OpenTptInternet.h> #include <OpenTptInternet.h>
// mac os threads // mac os stuff
#include <Threads.h> #include <Threads.h>
#include <MacMemory.h>
#include <Quickdraw.h>
#include <Fonts.h>
#include <Windows.h>
#include <Sound.h>
// libssh2 // libssh2
#include <libssh2.h> #include <libssh2.h>
@ -22,15 +27,58 @@
#include "ssheven-debug.h" #include "ssheven-debug.h"
// version string // version string
#define SSHEVEN_VERSION "0.0.1" #define SSHEVEN_VERSION "0.1.0"
// network buffer size // size for recv and send thread buffers
enum { buffer_size = 4096 }; #define BUFFER_SIZE 4096
// text input buffer size // terminal type to send over ssh, determines features etc.
enum { input_buffer_size = 128 }; // "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) static pascal void yield_notifier(void* contextPtr, OTEventCode code, OTResult result, void* cookie)
{ {
switch (code) 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; TextFont(kFontIDMonaco);
char c; TextSize(9);
TextFace(normal);
while (i < input_buffer_size - 1) int cell_height = 12;
{ int cell_width = CharWidth('M');
c = getc(stdin);
if (c != '\n') buffer[i++] = c; else break; MoveTo(r->left + x * cell_width + 2, r->top + ((y+1) * cell_height) - 2);
DrawChar(c);
}
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--;
} }
buffer[i] = '\0';
return; return;
} }
void do_ssh_connection(char* hostname, char* username, char* password, char* command) // 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 <string.h>
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; int rc;
// OT vars // OT vars
OSStatus err = noErr; OSStatus err = noErr;
char* buffer = NULL;
EndpointRef endpoint = kOTInvalidEndpointRef;
TCall sndCall; TCall sndCall;
DNSAddress hostDNSAddress; DNSAddress hostDNSAddress;
OSStatus result; 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 // open TCP endpoint
endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err); ssh_con.endpoint = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err);
if (err != noErr)
if (err != noErr || ssh_con.endpoint == kOTInvalidEndpointRef)
{ {
printf("failed to open TCP endpoint\n"); print_string("failed to open OT TCP endpoint\n");
if (buffer != NULL) OTFreeMem(buffer);
return; 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(OTBind(ssh_con.endpoint, nil, nil));
OT_CHECK(OTSetBlocking(endpoint));
OT_CHECK(OTInstallNotifier(endpoint, yield_notifier, nil));
OT_CHECK(OTUseSyncIdleEvents(endpoint, true));
OT_CHECK(OTBind(endpoint, nil, nil));
// set up address struct, do the DNS lookup, and connect // set up address struct, do the DNS lookup, and connect
OTMemzero(&sndCall, sizeof(TCall)); 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.buf = (UInt8 *) &hostDNSAddress;
sndCall.addr.len = OTInitDNSAddress(&hostDNSAddress, (char *) hostname); 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," print_string("OT setup done\n");
" 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; };
// init libssh2 // init libssh2
SSH_CHECK(libssh2_init(0)); SSH_CHECK(libssh2_init(0));
session = libssh2_session_init(); ssh_con.session = libssh2_session_init();
if (session == 0) if (ssh_con.session == 0)
{ {
printf("failed to open SSH session\n"); print_string("failed to initialize SSH library\n");
goto libssh2_cleanup; return;
} }
SSH_CHECK(libssh2_session_handshake(session, endpoint)); SSH_CHECK(libssh2_session_handshake(ssh_con.session, ssh_con.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);
return; 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) int main(int argc, char** argv)
{ {
char hostname[input_buffer_size] = {0}; char hostname[256] = {0};
char username[input_buffer_size] = {0}; char username[256] = {0};
char password[input_buffer_size] = {0}; char password[256] = {0};
char command[input_buffer_size] = {0};
printf("WARNING: this is a prototype with a bad RNG and no host key checks," OSStatus err = noErr;
" do not use over untrusted networks or with untrusted SSH servers!\n\n");
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); // general gui setup
get_line(hostname); InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
printf("enter a username >"); fflush(stdout); intro_dialog(hostname, username, password);
get_line(username);
printf("enter a password >"); fflush(stdout); console_setup();
get_line(password);
printf("enter a command >"); fflush(stdout); char* logo = " _____ _____ _ _\n"
get_line(command); " / ____/ ____| | | |\n"
" | (___| (___ | |__| | _____ _____ _ __\n"
" \\___ \\\\___ \\| __ |/ _ \\ \\ / / _ \\ '_ \\\n"
" ____) |___) | | | | __/\\ V / __/ | | |\n"
" |_____/_____/|_| |_|\\___| \\_/ \\___|_| |_|\n";
print_string(logo);
print_string("by cy384, version " SSHEVEN_VERSION "\n");
int ok = 1;
if (InitOpenTransport() != noErr) if (InitOpenTransport() != noErr)
{ {
printf("failed to initialize OT\n"); print_string("failed to initialize OT\n");
return 0; 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(); CloseOpenTransport();
printf("\n(enter to exit)\n");
getchar();
return 0;
} }

63
ssheven.r Normal file
View File

@ -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
};