2020-06-08 17:53:38 +01:00
/*
* ssheven
*
* Copyright ( c ) 2020 by cy384 < cy384 @ cy384 . com >
* See LICENSE file for details
*/
2020-07-20 05:14:27 +01:00
# include "ssheven.h"
# include "ssheven-console.h"
2020-06-14 04:33:25 +01:00
2020-06-26 01:59:08 +01:00
// functions to convert error and status codes to strings
2020-07-20 05:14:27 +01:00
# include "ssheven-debug.c"
2020-06-19 23:58:20 +01:00
2020-07-17 02:32:31 +01:00
// error checking convenience macros
2020-08-29 03:14:43 +01:00
# 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;};
2020-07-17 02:32:31 +01:00
// sinful globals
2020-09-01 03:53:06 +01:00
struct ssheven_console con = { NULL , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , NULL , NULL } ;
2020-07-20 05:14:27 +01:00
struct ssheven_ssh_connection ssh_con = { NULL , NULL , kOTInvalidEndpointRef , NULL , NULL } ;
2020-07-17 02:32:31 +01:00
2020-07-20 05:14:27 +01:00
enum { WAIT , READ , EXIT } read_thread_command = WAIT ;
2020-09-06 01:05:35 +01:00
enum { UNINTIALIZED , OPEN , CLEANUP , DONE } read_thread_state = UNINTIALIZED ;
2020-07-17 02:32:31 +01:00
2020-07-20 05:14:27 +01:00
char hostname [ 256 ] = { 0 } ;
char username [ 256 ] = { 0 } ;
char password [ 256 ] = { 0 } ;
2020-07-17 02:32:31 +01:00
// 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
2020-06-14 04:33:25 +01:00
static pascal void yield_notifier ( void * contextPtr , OTEventCode code , OTResult result , void * cookie )
{
switch ( code )
{
case kOTSyncIdleEvent :
YieldToAnyThread ( ) ;
break ;
default :
break ;
}
}
2020-07-20 05:14:27 +01:00
// read from the channel and print to console
2020-07-17 02:32:31 +01:00
void ssh_read ( void )
{
2020-08-29 03:14:43 +01:00
size_t rc = libssh2_channel_read ( ssh_con . channel , ssh_con . recv_buffer , SSHEVEN_BUFFER_SIZE ) ;
2020-07-17 02:32:31 +01:00
if ( rc = = 0 ) return ;
2020-08-29 03:14:43 +01:00
if ( rc < = 0 )
2020-06-14 04:33:25 +01:00
{
2020-08-29 03:14:43 +01:00
printf_i ( " channel read error: %s \r \n " , libssh2_error_string ( rc ) ) ;
2020-06-19 02:21:24 +01:00
}
2020-08-29 03:14:43 +01:00
while ( rc > 0 )
2020-06-19 02:21:24 +01:00
{
2020-08-29 03:14:43 +01:00
rc - = vterm_input_write ( con . vterm , ssh_con . recv_buffer , rc ) ;
2020-06-14 04:33:25 +01:00
}
2020-07-20 05:14:27 +01:00
}
2020-06-14 04:33:25 +01:00
2020-07-24 00:46:29 +01:00
int end_connection ( void )
2020-07-17 02:32:31 +01:00
{
2020-07-20 05:14:27 +01:00
read_thread_state = CLEANUP ;
2020-07-17 02:32:31 +01:00
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 ) ;
}
2020-06-14 04:33:25 +01:00
2020-07-17 02:32:31 +01:00
libssh2_exit ( ) ;
2020-06-16 01:51:36 +01:00
2020-07-17 02:32:31 +01:00
if ( ssh_con . endpoint ! = kOTInvalidEndpointRef )
2020-06-19 02:21:24 +01:00
{
2020-07-17 02:32:31 +01:00
// request to close the TCP connection
2020-07-24 00:46:29 +01:00
//OT_CHECK(OTSndOrderlyDisconnect(ssh_con.endpoint));
OTSndOrderlyDisconnect ( ssh_con . endpoint ) ;
2020-07-17 02:32:31 +01:00
// 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 :
2020-08-29 03:14:43 +01:00
printf_i ( " unexpected OTLook result while closing: %s \r \n " , OT_event_string ( result ) ) ;
2020-07-17 02:32:31 +01:00
break ;
}
// release endpoint
err = OTUnbind ( ssh_con . endpoint ) ;
2020-08-29 03:14:43 +01:00
if ( err ! = noErr ) printf_i ( " OTUnbind failed \r \n " ) ;
2020-07-17 02:32:31 +01:00
err = OTCloseProvider ( ssh_con . endpoint ) ;
2020-08-29 03:14:43 +01:00
if ( err ! = noErr ) printf_i ( " OTCloseProvider failed \r \n " ) ;
2020-06-19 02:21:24 +01:00
}
2020-07-20 05:14:27 +01:00
read_thread_state = DONE ;
2020-07-17 02:32:31 +01:00
}
2020-07-20 05:14:27 +01:00
void check_network_events ( void )
2020-07-17 02:32:31 +01:00
{
OSStatus err = noErr ;
2020-06-16 01:51:36 +01:00
2020-07-20 05:14:27 +01:00
// check if we have any new network events
OTResult look_result = OTLook ( ssh_con . endpoint ) ;
switch ( look_result )
2020-06-19 02:21:24 +01:00
{
2020-07-20 05:14:27 +01:00
case T_DATA :
case T_EXDATA :
// got data
ssh_read ( ) ;
break ;
2020-07-17 02:32:31 +01:00
2020-07-20 05:14:27 +01:00
case T_RESET :
// connection reset? close it/give up
end_connection ( ) ;
break ;
2020-06-14 04:33:25 +01:00
2020-07-20 05:14:27 +01:00
case T_DISCONNECT :
// got disconnected
OTRcvDisconnect ( ssh_con . endpoint , nil ) ;
end_connection ( ) ;
break ;
2020-07-17 02:32:31 +01:00
2020-07-20 05:14:27 +01:00
case T_ORDREL :
// nice tcp disconnect requested by remote
err = OTRcvOrderlyDisconnect ( ssh_con . endpoint ) ;
if ( err = = noErr )
2020-06-26 02:06:19 +01:00
{
2020-07-20 05:14:27 +01:00
err = OTSndOrderlyDisconnect ( ssh_con . endpoint ) ;
2020-06-26 02:06:19 +01:00
}
2020-07-20 05:14:27 +01:00
end_connection ( ) ;
break ;
default :
// something weird or irrelevant: ignore it
break ;
}
return ;
}
2020-08-06 01:59:49 +01:00
void display_about_box ( void )
{
DialogRef about = GetNewDialog ( DLOG_ABOUT , 0 , ( WindowPtr ) - 1 ) ;
UpdateDialog ( about , about - > visRgn ) ;
while ( ! Button ( ) ) YieldToAnyThread ( ) ;
while ( Button ( ) ) YieldToAnyThread ( ) ;
FlushEvents ( everyEvent , 0 ) ;
DisposeWindow ( about ) ;
}
2020-09-01 00:25:46 +01:00
void ssh_write ( char * buf , size_t len )
2020-08-20 20:31:26 +01:00
{
if ( read_thread_state = = OPEN & & read_thread_command ! = EXIT )
{
int r = libssh2_channel_write ( ssh_con . channel , buf , len ) ;
if ( r < 1 )
{
2020-08-29 03:14:43 +01:00
printf_i ( " failed to write to channel! \r \n " ) ;
printf_i ( " closing connection! \r \n " ) ;
2020-08-20 20:31:26 +01:00
read_thread_command = EXIT ;
}
}
}
void ssh_paste ( void )
{
// GetScrap requires a handle, not a raw buffer
// it will increase the size of the handle if needed
Handle buf = NewHandle ( 256 ) ;
int r = GetScrap ( buf , ' TEXT ' , 0 ) ;
if ( r > 0 )
{
ssh_write ( * buf , r ) ;
}
DisposeHandle ( buf ) ;
}
2020-08-06 01:59:49 +01:00
/* returns 1 if quit selected, else 0 */
int process_menu_select ( int32_t result )
{
int exit = 0 ;
int16_t menu = ( result & 0xFFFF0000 ) > > 16 ;
int16_t item = ( result & 0x0000FFFF ) ;
Str255 name ;
switch ( menu )
{
case MENU_APPLE :
if ( item = = 1 )
{
display_about_box ( ) ;
}
else
{
GetMenuItemText ( GetMenuHandle ( menu ) , item , name ) ;
OpenDeskAcc ( name ) ;
}
break ;
case MENU_FILE :
if ( item = = 1 ) exit = 1 ;
break ;
2020-08-20 20:31:26 +01:00
case MENU_EDIT :
if ( item = = 5 ) ssh_paste ( ) ;
break ;
2020-08-06 01:59:49 +01:00
default :
break ;
}
HiliteMenu ( 0 ) ;
return exit ;
}
2020-09-01 03:53:06 +01:00
void resize_con_window ( WindowPtr eventWin , EventRecord event )
{
// TODO: put this somewhere else
// limits on window size
// top = min vertical
// bottom = max vertical
// left = min horizontal
// right = max horizontal
Rect window_limits = { . top = con . cell_height * 2 + 2 , . bottom = con . cell_height * 100 + 2 , . left = con . cell_width * 10 + 4 , . right = con . cell_width * 200 + 4 } ;
long growResult = GrowWindow ( eventWin , event . where , & window_limits ) ;
2020-09-01 17:11:04 +01:00
2020-09-01 03:53:06 +01:00
if ( growResult ! = 0 )
{
int height = growResult > > 16 ;
int width = growResult & 0xFFFF ;
// 'snap' to a size that won't have extra pixels not in a cell
int next_height = height - ( ( height - 2 ) % con . cell_height ) ;
int next_width = width - ( ( width - 4 ) % con . cell_width ) ;
SizeWindow ( eventWin , next_width , next_height , true ) ;
2020-09-01 17:11:04 +01:00
EraseRect ( & ( con . win - > portRect ) ) ;
InvalRect ( & ( con . win - > portRect ) ) ;
2020-09-01 03:53:06 +01:00
con . size_x = ( next_width - 4 ) / con . cell_width ;
con . size_y = ( next_height - 2 ) / con . cell_height ;
vterm_set_size ( con . vterm , con . size_y , con . size_x ) ;
libssh2_channel_request_pty_size ( ssh_con . channel , con . size_x , con . size_y ) ;
}
}
2020-07-20 05:14:27 +01:00
void event_loop ( void )
{
int exit_event_loop = 0 ;
EventRecord event ;
WindowPtr eventWin ;
// maximum length of time to sleep (in ticks)
// GetCaretTime gets the number of ticks between caret on/off time
2020-07-24 19:47:07 +01:00
long int sleep_time = GetCaretTime ( ) / 4 ;
2020-07-20 05:14:27 +01:00
do
{
// wait to get a GUI event
while ( ! WaitNextEvent ( everyEvent , & event , sleep_time , NULL ) )
{
// timed out without any GUI events
2020-08-22 02:29:34 +01:00
// toggle our cursor if needed
check_cursor ( ) ;
// then let any other threads run before we wait for events again
2020-07-17 02:32:31 +01:00
YieldToAnyThread ( ) ;
}
2020-08-22 02:29:34 +01:00
// might need to toggle our cursor even if we got an event
check_cursor ( ) ;
2020-07-17 02:32:31 +01:00
// handle any mac gui events
char c = 0 ;
2020-08-06 03:30:32 +01:00
int r = 0 ;
2020-07-17 02:32:31 +01:00
switch ( event . what )
{
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 ;
2020-08-20 19:56:59 +01:00
// if we have a key and command and it's not autorepeating
if ( c & & ( event . modifiers & cmdKey ) & & event . what ! = autoKey )
2020-08-18 03:29:30 +01:00
{
2020-08-20 19:56:59 +01:00
switch ( c )
{
case ' q ' :
exit_event_loop = 1 ;
break ;
2020-08-20 20:31:26 +01:00
case ' v ' :
ssh_paste ( ) ;
break ;
2020-08-20 19:56:59 +01:00
default :
break ;
}
2020-08-18 03:29:30 +01:00
}
2020-08-20 19:56:59 +01:00
else if ( c )
2020-07-17 02:32:31 +01:00
{
2020-09-01 00:36:19 +01:00
switch ( c )
{
case kLeftArrowCharCode :
vterm_keyboard_key ( con . vterm , VTERM_KEY_LEFT , VTERM_MOD_NONE ) ;
break ;
case kRightArrowCharCode :
vterm_keyboard_key ( con . vterm , VTERM_KEY_RIGHT , VTERM_MOD_NONE ) ;
break ;
case kUpArrowCharCode :
vterm_keyboard_key ( con . vterm , VTERM_KEY_UP , VTERM_MOD_NONE ) ;
break ;
case kDownArrowCharCode :
vterm_keyboard_key ( con . vterm , VTERM_KEY_DOWN , VTERM_MOD_NONE ) ;
break ;
default :
if ( ' \r ' = = c ) c = ' \n ' ;
ssh_con . send_buffer [ 0 ] = c ;
ssh_write ( ssh_con . send_buffer , 1 ) ;
break ;
}
2020-07-17 02:32:31 +01:00
}
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 :
2020-09-01 03:53:06 +01:00
resize_con_window ( eventWin , event ) ;
2020-07-17 02:32:31 +01:00
break ;
case inGoAway :
{
if ( TrackGoAway ( eventWin , event . where ) )
exit_event_loop = 1 ;
}
break ;
case inMenuBar :
2020-08-06 01:59:49 +01:00
exit_event_loop = process_menu_select ( MenuSelect ( event . where ) ) ;
2020-07-17 02:32:31 +01:00
break ;
case inSysWindow :
// is this system6 relevant only???
SystemClick ( & event , eventWin ) ;
break ;
case inContent :
break ;
}
break ;
}
} while ( ! exit_event_loop ) ;
}
2020-07-24 00:46:29 +01:00
int init_connection ( char * hostname )
2020-07-17 02:32:31 +01:00
{
int rc ;
// OT vars
OSStatus err = noErr ;
TCall sndCall ;
DNSAddress hostDNSAddress ;
OSStatus result ;
2020-07-26 15:13:51 +01:00
printf_i ( " opening and configuring endpoint... " ) ; YieldToAnyThread ( ) ;
2020-07-20 05:14:27 +01:00
2020-07-17 02:32:31 +01:00
// open TCP endpoint
ssh_con . endpoint = OTOpenEndpoint ( OTCreateConfiguration ( kTCPName ) , 0 , nil , & err ) ;
if ( err ! = noErr | | ssh_con . endpoint = = kOTInvalidEndpointRef )
{
2020-08-29 03:14:43 +01:00
printf_i ( " failed to open OT TCP endpoint \r \n " ) ;
2020-07-24 00:46:29 +01:00
return 0 ;
2020-06-19 02:21:24 +01:00
}
2020-06-14 04:33:25 +01:00
2020-07-17 02:32:31 +01:00
OT_CHECK ( OTSetSynchronous ( ssh_con . endpoint ) ) ;
2020-07-24 00:46:29 +01:00
OT_CHECK ( OTSetBlocking ( ssh_con . endpoint ) ) ;
2020-07-17 02:32:31 +01:00
OT_CHECK ( OTUseSyncIdleEvents ( ssh_con . endpoint , false ) ) ;
2020-06-26 02:06:19 +01:00
2020-07-24 00:46:29 +01:00
2020-07-17 02:32:31 +01:00
OT_CHECK ( OTBind ( ssh_con . endpoint , nil , nil ) ) ;
2020-06-26 02:06:19 +01:00
2020-07-24 00:46:29 +01:00
OT_CHECK ( OTSetNonBlocking ( ssh_con . endpoint ) ) ;
2020-08-29 03:14:43 +01:00
printf_i ( " done. \r \n " ) ; YieldToAnyThread ( ) ;
2020-07-20 05:14:27 +01:00
2020-07-17 02:32:31 +01:00
// 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 ) ;
2020-06-19 23:58:20 +01:00
2020-07-26 15:13:51 +01:00
printf_i ( " connecting endpoint... " ) ; YieldToAnyThread ( ) ;
2020-07-17 02:32:31 +01:00
OT_CHECK ( OTConnect ( ssh_con . endpoint , & sndCall , nil ) ) ;
2020-06-14 04:33:25 +01:00
2020-08-29 03:14:43 +01:00
printf_i ( " done. \r \n " ) ; YieldToAnyThread ( ) ;
2020-06-14 04:33:25 +01:00
2020-07-26 15:13:51 +01:00
printf_i ( " initializing SSH... " ) ; YieldToAnyThread ( ) ;
2020-07-17 02:32:31 +01:00
// init libssh2
SSH_CHECK ( libssh2_init ( 0 ) ) ;
2020-08-29 03:14:43 +01:00
printf_i ( " done. \r \n " ) ; YieldToAnyThread ( ) ;
2020-07-20 05:14:27 +01:00
2020-07-26 15:13:51 +01:00
printf_i ( " opening SSH session... " ) ; YieldToAnyThread ( ) ;
2020-07-17 02:32:31 +01:00
ssh_con . session = libssh2_session_init ( ) ;
if ( ssh_con . session = = 0 )
{
2020-08-29 03:14:43 +01:00
printf_i ( " failed to initialize SSH library \r \n " ) ;
2020-07-24 00:46:29 +01:00
return 0 ;
2020-07-17 02:32:31 +01:00
}
2020-08-29 03:14:43 +01:00
printf_i ( " done. \r \n " ) ; YieldToAnyThread ( ) ;
2020-07-17 02:32:31 +01:00
2020-08-06 03:30:32 +01:00
long s = TickCount ( ) ;
2020-07-26 15:13:51 +01:00
printf_i ( " beginning SSH session handshake... " ) ; YieldToAnyThread ( ) ;
2020-07-17 02:32:31 +01:00
SSH_CHECK ( libssh2_session_handshake ( ssh_con . session , ssh_con . endpoint ) ) ;
2020-06-14 04:33:25 +01:00
2020-08-29 03:14:43 +01:00
printf_i ( " done. (%d ticks) \r \n " , TickCount ( ) - s ) ; YieldToAnyThread ( ) ;
2020-07-20 05:14:27 +01:00
read_thread_state = OPEN ;
2020-07-24 00:46:29 +01:00
return 1 ;
2020-06-14 04:33:25 +01:00
}
2020-07-24 00:46:29 +01:00
int ssh_password_auth ( char * username , char * password )
2020-07-17 02:32:31 +01:00
{
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 ) ;
2020-07-24 00:46:29 +01:00
return 1 ;
2020-07-17 02:32:31 +01:00
}
2020-07-24 00:46:29 +01:00
int ssh_setup_terminal ( void )
2020-07-17 02:32:31 +01:00
{
int rc = 0 ;
2020-09-01 03:53:06 +01:00
SSH_CHECK ( libssh2_channel_request_pty_ex ( ssh_con . channel , SSHEVEN_TERMINAL_TYPE , ( strlen ( SSHEVEN_TERMINAL_TYPE ) ) , NULL , 0 , con . size_x , con . size_y , 0 , 0 ) ) ;
2020-07-17 02:32:31 +01:00
SSH_CHECK ( libssh2_channel_shell ( ssh_con . channel ) ) ;
2020-07-24 00:46:29 +01:00
return 1 ;
2020-07-17 02:32:31 +01:00
}
2020-09-06 01:04:55 +01:00
// from the ATS password sample code
pascal Boolean TwoItemFilter ( DialogPtr dlog , EventRecord * event , short * itemHit )
{
DialogPtr evtDlog ;
short selStart , selEnd ;
long ticks ;
Handle itemH ;
DialogItemType type ;
Rect box ;
// TODO: this should be declared somewhere? include it?
int kVisualDelay = 8 ;
if ( event - > what = = keyDown | | event - > what = = autoKey )
{
char c = event - > message & charCodeMask ;
switch ( c )
{
case kEnterCharCode : // select the ok button
case kReturnCharCode : // we have to manually blink it...
case kLineFeedCharCode :
GetDialogItem ( dlog , 1 , & type , & itemH , & box ) ;
HiliteControl ( ( ControlHandle ) ( itemH ) , kControlButtonPart ) ;
Delay ( kVisualDelay , & ticks ) ;
HiliteControl ( ( ControlHandle ) ( itemH ) , 1 ) ;
* itemHit = 1 ;
return true ;
case kTabCharCode : // cancel out tab events
event - > what = nullEvent ;
return false ;
case kEscapeCharCode : // hit cancel on esc or cmd-period
case ' . ' :
if ( ( event - > modifiers & cmdKey ) | | c = = kEscapeCharCode )
{
GetDialogItem ( dlog , 6 , & type , & itemH , & box ) ;
HiliteControl ( ( ControlHandle ) ( itemH ) , kControlButtonPart ) ;
Delay ( kVisualDelay , & ticks ) ;
HiliteControl ( ( ControlHandle ) ( itemH ) , 6 ) ;
* itemHit = 6 ;
return true ;
}
case kLeftArrowCharCode :
case kRightArrowCharCode :
case kUpArrowCharCode :
case kDownArrowCharCode :
case kHomeCharCode :
case kEndCharCode :
return false ; // don't handle them
default : // TODO: this is dumb and assumes everything else is a valid text character
selStart = ( * * ( ( DialogPeek ) dlog ) - > textH ) . selStart ; // Get the selection in the visible item
selEnd = ( * * ( ( DialogPeek ) dlog ) - > textH ) . selEnd ;
SelectDialogItemText ( dlog , 5 , selStart , selEnd ) ; // Select text in invisible item
DialogSelect ( event , & evtDlog , itemHit ) ; // Input key
SelectDialogItemText ( dlog , 4 , selStart , selEnd ) ; // Select same area in visible item
if ( ( event - > message & charCodeMask ) ! = kBackspaceCharCode ) // If it's not a backspace (backspace is the only key that can affect both the text and the selection- thus we need to process it in both fields, but not change it for the hidden field.
event - > message = 0xa5 ; // Replace with character to use (the bullet)
DialogSelect ( event , & evtDlog , itemHit ) ; // Put in fake character
return true ;
}
}
return false ; // pass on other (non-keypress) events
}
// from the ATS password sample code
// 1 for ok, 0 for cancel
int password_dialog ( void )
2020-07-17 02:32:31 +01:00
{
2020-09-06 01:04:55 +01:00
// n.b. dialog strings can't be longer than this, so no overflow risk
//static char password[256];
DialogPtr dlog ;
Handle itemH ;
short item ;
Rect box ;
DialogItemType type ;
dlog = GetNewDialog ( DLOG_PASSWORD , 0 , ( WindowPtr ) - 1 ) ;
// draw default button indicator around the connect button
GetDialogItem ( dlog , 2 , & type , & itemH , & box ) ;
SetDialogItem ( dlog , 2 , type , ( Handle ) NewUserItemUPP ( & ButtonFrameProc ) , & box ) ;
do {
ModalDialog ( NewModalFilterUPP ( TwoItemFilter ) , & item ) ;
} while ( item ! = 1 & & item ! = 6 ) ; // until OK or cancel
if ( 6 = = item ) return 0 ;
2020-07-17 02:32:31 +01:00
2020-09-06 01:04:55 +01:00
// read out of the hidden text box
GetDialogItem ( dlog , 5 , & type , & itemH , & box ) ;
GetDialogItemText ( itemH , password ) ;
DisposeDialog ( dlog ) ;
return 1 ;
}
int intro_dialog ( char * hostname , char * username , char * password )
{
2020-07-17 02:32:31 +01:00
// modal dialog setup
TEInit ( ) ;
InitDialogs ( NULL ) ;
2020-08-06 01:59:49 +01:00
DialogPtr dlg = GetNewDialog ( DLOG_CONNECT , 0 , ( WindowPtr ) - 1 ) ;
2020-07-17 02:32:31 +01:00
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 ;
// let the modalmanager do everything
// stop when the connect button is hit
short item ;
do {
ModalDialog ( NULL , & item ) ;
2020-09-06 01:04:55 +01:00
} while ( item ! = 1 & & item ! = 7 ) ;
2020-07-17 02:32:31 +01:00
// copy the text out of the boxes
GetDialogItemText ( ( Handle ) address_text_box , hostname ) ;
GetDialogItemText ( ( Handle ) username_text_box , username ) ;
// clean it up
2020-09-06 01:04:55 +01:00
DisposeDialog ( dlg ) ;
2020-07-17 02:32:31 +01:00
FlushEvents ( everyEvent , - 1 ) ;
2020-07-20 20:30:56 +01:00
// if we hit cancel, 0
2020-09-06 01:04:55 +01:00
if ( item = = 7 ) return 0 ;
return password_dialog ( ) ;
2020-07-17 02:32:31 +01:00
}
2020-07-20 05:14:27 +01:00
//enum { WAIT, READ, EXIT } read_thread_command = WAIT;
//enum { UNITIALIZED, OPEN, CLEANUP, DONE } read_thread_state = UNITIALIZED;
// TODO: threads
void * read_thread ( void * arg )
2020-06-08 17:53:38 +01:00
{
2020-07-24 00:46:29 +01:00
int ok = 1 ;
2020-07-20 05:14:27 +01:00
while ( read_thread_command = = WAIT ) YieldToAnyThread ( ) ;
if ( read_thread_command = = EXIT )
{
return 0 ;
}
// connect and log in
2020-07-24 00:46:29 +01:00
ok = init_connection ( hostname + 1 ) ;
2020-07-20 05:14:27 +01:00
YieldToAnyThread ( ) ;
2020-07-17 02:32:31 +01:00
2020-07-24 00:46:29 +01:00
if ( ok )
{
2020-07-26 15:13:51 +01:00
printf_i ( " authenticating... " ) ; YieldToAnyThread ( ) ;
2020-07-24 00:46:29 +01:00
ok = ssh_password_auth ( username + 1 , password + 1 ) ;
2020-08-29 03:14:43 +01:00
printf_i ( " done. \r \n " ) ; YieldToAnyThread ( ) ;
2020-07-24 00:46:29 +01:00
}
2020-07-20 05:14:27 +01:00
2020-07-24 00:46:29 +01:00
if ( ok )
{
ok = ssh_setup_terminal ( ) ;
YieldToAnyThread ( ) ;
}
2020-07-20 05:14:27 +01:00
// if we failed, exit
if ( read_thread_state ! = OPEN ) return 0 ;
2020-08-22 21:23:42 +01:00
// allow pasting
void * menu = GetMenuHandle ( MENU_EDIT ) ;
EnableItem ( menu , 5 ) ;
2020-07-20 05:14:27 +01:00
// loop as long until we've failed or are asked to EXIT
while ( read_thread_command = = READ & & read_thread_state = = OPEN )
{
check_network_events ( ) ;
YieldToAnyThread ( ) ;
}
// if we still have a connection, close it
if ( read_thread_state ! = DONE ) end_connection ( ) ;
2020-08-22 21:23:42 +01:00
DisableItem ( menu , 5 ) ;
2020-07-20 05:14:27 +01:00
return 0 ;
}
2020-07-24 00:46:29 +01:00
int safety_checks ( void )
{
2020-08-29 03:14:43 +01:00
OSStatus err = noErr ;
2020-07-24 00:46:29 +01:00
// check for thread manager
2020-07-25 23:57:03 +01:00
long int thread_manager_gestalt = 0 ;
2020-07-24 00:46:29 +01:00
err = Gestalt ( gestaltThreadMgrAttr , & thread_manager_gestalt ) ;
// bit one is prescence of thread manager
if ( err ! = noErr | | ( thread_manager_gestalt & ( 1 < < gestaltThreadMgrPresent ) ) = = 0 )
{
2020-07-24 00:51:33 +01:00
StopAlert ( ALRT_TM , nil ) ;
2020-08-29 03:14:43 +01:00
printf_i ( " Thread Manager not available! \r \n " ) ;
2020-07-24 00:46:29 +01:00
return 0 ;
}
// check for Open Transport
// for some reason, the docs say you shouldn't check for OT via the gestalt
// in an application, and should just try to init, but checking seems more
// user-friendly, so...
long int open_transport_any_version = 0 ;
long int open_transport_new_version = 0 ;
err = Gestalt ( gestaltOpenTpt , & open_transport_any_version ) ;
if ( err ! = noErr )
{
2020-08-29 03:14:43 +01:00
printf_i ( " Failed to check for Open Transport! \r \n " ) ;
2020-07-24 00:46:29 +01:00
return 0 ;
}
err = Gestalt ( gestaltOpenTptVersions , & open_transport_new_version ) ;
if ( err ! = noErr )
{
2020-08-29 03:14:43 +01:00
printf_i ( " Failed to check for Open Transport! \r \n " ) ;
2020-07-24 00:46:29 +01:00
return 0 ;
}
if ( open_transport_any_version = = 0 & & open_transport_new_version = = 0 )
{
2020-08-29 03:14:43 +01:00
printf_i ( " Open Transport required but not found! \r \n " ) ;
2020-07-24 00:46:29 +01:00
StopAlert ( ALRT_OT , nil ) ;
return 0 ;
}
if ( open_transport_any_version ! = 0 & & open_transport_new_version = = 0 )
{
2020-07-26 15:13:51 +01:00
printf_i ( " Early version of Open Transport detected! " ) ;
2020-08-29 03:14:43 +01:00
printf_i ( " Attempting to continue anyway. \r \n " ) ;
2020-07-24 00:46:29 +01:00
}
NumVersion * ot_version = ( NumVersion * ) & open_transport_new_version ;
2020-08-29 03:14:43 +01:00
printf_i ( " Detected Open Transport version: %d.%d.%d \r \n " ,
2020-07-26 15:13:51 +01:00
( int ) ot_version - > majorRev ,
( int ) ( ( ot_version - > minorAndBugRev & 0xF0 ) > > 4 ) ,
( int ) ( ot_version - > minorAndBugRev & 0x0F ) ) ;
2020-07-24 00:46:29 +01:00
2020-07-25 23:57:03 +01:00
// check for CPU type and display warning if it's going to be too slow
long int cpu_type = 0 ;
int cpu_slow = 0 ;
int cpu_bad = 0 ;
err = Gestalt ( gestaltNativeCPUtype , & cpu_type ) ;
if ( err ! = noErr | | cpu_type = = 0 )
{
// earlier than 7.5, need to use other gestalt
err = Gestalt ( gestaltProcessorType , & cpu_type ) ;
if ( err ! = noErr | | cpu_type = = 0 )
{
2020-07-26 15:13:51 +01:00
cpu_slow = 1 ;
2020-08-29 03:14:43 +01:00
printf_i ( " Failed to detect CPU type, continuing anyway. \r \n " ) ;
2020-07-25 23:57:03 +01:00
}
else
{
if ( cpu_type < = gestalt68010 ) cpu_bad = 1 ;
if ( cpu_type < = gestalt68030 ) cpu_slow = 1 ;
}
}
else
{
if ( cpu_type < = gestaltCPU68010 ) cpu_bad = 1 ;
if ( cpu_type < = gestaltCPU68030 ) cpu_slow = 1 ;
}
2020-07-26 15:13:51 +01:00
// if we don't have at least a 68020, stop
2020-07-25 23:57:03 +01:00
if ( cpu_bad )
{
StopAlert ( ALRT_CPU_BAD , nil ) ;
return 0 ;
}
2020-07-26 15:13:51 +01:00
// if we don't have at least a 68040, warn
2020-07-25 23:57:03 +01:00
if ( cpu_slow )
{
CautionAlert ( ALRT_CPU_SLOW , nil ) ;
}
2020-07-24 00:46:29 +01:00
return 1 ;
}
2020-07-20 05:14:27 +01:00
int main ( int argc , char * * argv )
{
2020-07-17 02:32:31 +01:00
OSStatus err = noErr ;
2020-06-19 23:58:20 +01:00
2020-07-17 02:32:31 +01:00
// expands the application heap to its maximum requested size
// supposedly good for performance
// also required before creating threads!
MaxApplZone ( ) ;
2020-06-19 23:58:20 +01:00
2020-08-20 20:31:26 +01:00
// "Call the MoreMasters procedure several times at the beginning of your program"
MoreMasters ( ) ;
MoreMasters ( ) ;
2020-07-17 02:32:31 +01:00
// general gui setup
InitGraf ( & qd . thePort ) ;
InitFonts ( ) ;
InitWindows ( ) ;
InitMenus ( ) ;
2020-06-19 23:58:20 +01:00
2020-08-20 19:53:01 +01:00
void * menu = GetNewMBar ( MBAR_SSHEVEN ) ;
2020-08-06 01:59:49 +01:00
SetMenuBar ( menu ) ;
AppendResMenu ( GetMenuHandle ( MENU_APPLE ) , ' DRVR ' ) ;
2020-08-13 16:21:05 +01:00
2020-08-20 20:31:26 +01:00
// disable stuff in edit menu until we implement it
2020-08-13 16:21:05 +01:00
menu = GetMenuHandle ( MENU_EDIT ) ;
2020-08-20 20:31:26 +01:00
DisableItem ( menu , 1 ) ;
DisableItem ( menu , 3 ) ;
DisableItem ( menu , 4 ) ;
2020-08-22 21:23:42 +01:00
DisableItem ( menu , 5 ) ;
2020-08-20 20:31:26 +01:00
DisableItem ( menu , 6 ) ;
DisableItem ( menu , 7 ) ;
DisableItem ( menu , 9 ) ;
2020-08-13 16:21:05 +01:00
2020-08-06 01:59:49 +01:00
DrawMenuBar ( ) ;
2020-07-17 02:32:31 +01:00
console_setup ( ) ;
2020-06-19 23:58:20 +01:00
2020-08-29 03:14:43 +01:00
char * logo = " _____ _____ _ _ \r \n "
" / ____/ ____| | | | \r \n "
" | (___| (___ | |__| | _____ _____ _ __ \r \n "
" \\ ___ \\ \\ ___ \\ | __ |/ _ \\ \\ / / _ \\ '_ \\ \r \n "
" ____) |___) | | | | __/ \\ V / __/ | | | \r \n "
" |_____/_____/|_| |_| \\ ___| \\ _/ \\ ___|_| |_|] \r \n " ;
2020-06-19 23:58:20 +01:00
2020-07-26 15:13:51 +01:00
printf_i ( logo ) ;
2020-08-29 03:14:43 +01:00
printf_i ( " by cy384, version " SSHEVEN_VERSION " \r \n " ) ;
# if defined(__ppc__)
printf_i ( " Running in PPC mode. \r \n " ) ;
# else
printf_i ( " Running in 68k mode. \r \n " ) ;
# endif
2020-07-17 02:32:31 +01:00
2020-07-20 05:14:27 +01:00
BeginUpdate ( con . win ) ;
draw_screen ( & ( con . win - > portRect ) ) ;
EndUpdate ( con . win ) ;
2020-07-17 02:32:31 +01:00
int ok = 1 ;
2020-06-14 04:33:25 +01:00
2020-07-24 00:46:29 +01:00
if ( ! safety_checks ( ) ) return 0 ;
2020-07-24 00:51:33 +01:00
BeginUpdate ( con . win ) ;
draw_screen ( & ( con . win - > portRect ) ) ;
EndUpdate ( con . win ) ;
2020-07-24 00:46:29 +01:00
if ( ! intro_dialog ( hostname , username , password ) ) ok = 0 ;
2020-08-29 03:14:43 +01:00
if ( ! ok ) printf_i ( " Cancelled, not connecting. \r \n " ) ;
2020-07-24 00:46:29 +01:00
if ( ok )
2020-06-16 01:51:36 +01:00
{
2020-07-24 00:46:29 +01:00
if ( InitOpenTransport ( ) ! = noErr )
{
2020-08-29 03:14:43 +01:00
printf_i ( " failed to initialize OT \r \n " ) ;
2020-07-24 00:46:29 +01:00
ok = 0 ;
}
2020-06-16 01:51:36 +01:00
}
2020-07-17 02:32:31 +01:00
if ( ok )
{
2020-07-20 23:14:57 +01:00
ssh_con . recv_buffer = OTAllocMem ( SSHEVEN_BUFFER_SIZE ) ;
ssh_con . send_buffer = OTAllocMem ( SSHEVEN_BUFFER_SIZE ) ;
2020-07-17 02:32:31 +01:00
if ( ssh_con . recv_buffer = = NULL | | ssh_con . send_buffer = = NULL )
{
2020-08-29 03:14:43 +01:00
printf_i ( " failed to allocate network buffers \r \n " ) ;
2020-07-17 02:32:31 +01:00
ok = 0 ;
}
}
2020-06-08 17:53:38 +01:00
2020-07-20 05:14:27 +01:00
// create the network read/print thread
read_thread_command = WAIT ;
2020-07-17 02:32:31 +01:00
int read_thread_result = 0 ;
ThreadID read_thread_id = 0 ;
2020-07-20 05:14:27 +01:00
2020-07-17 02:32:31 +01:00
if ( ok )
{
2020-07-24 00:46:29 +01:00
err = NewThread ( kCooperativeThread , read_thread , NULL , 100000 , kCreateIfNeeded , NULL , & read_thread_id ) ;
2020-06-16 01:51:36 +01:00
2020-07-17 02:32:31 +01:00
if ( err < 0 )
{
ok = 0 ;
2020-08-29 03:14:43 +01:00
printf_i ( " failed to create network read thread \r \n " ) ;
2020-07-17 02:32:31 +01:00
}
}
2020-07-20 05:14:27 +01:00
// if we got the thread, tell it to begin operation
if ( ok ) read_thread_command = READ ;
2020-07-17 02:32:31 +01:00
2020-07-20 05:14:27 +01:00
// procede into our main event loop
2020-09-06 01:05:35 +01:00
if ( ok ) event_loop ( ) ;
2020-06-08 17:53:38 +01:00
2020-07-20 05:14:27 +01:00
// tell the read thread to quit, then let it run to actually do so
read_thread_command = EXIT ;
2020-09-06 01:05:35 +01:00
if ( read_thread_state ! = UNINTIALIZED )
while ( read_thread_state ! = DONE )
YieldToAnyThread ( ) ;
2020-07-20 05:14:27 +01:00
//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);
2020-07-17 02:32:31 +01:00
2020-09-06 01:05:35 +01:00
if ( ok )
{
BeginUpdate ( con . win ) ;
draw_screen ( & ( con . win - > portRect ) ) ;
EndUpdate ( con . win ) ;
}
2020-07-17 02:32:31 +01:00
if ( ssh_con . recv_buffer ! = NULL ) OTFreeMem ( ssh_con . recv_buffer ) ;
if ( ssh_con . send_buffer ! = NULL ) OTFreeMem ( ssh_con . send_buffer ) ;
2020-08-29 03:14:43 +01:00
if ( con . vterm ! = NULL ) vterm_free ( con . vterm ) ;
2020-07-24 00:46:29 +01:00
if ( ok )
{
err = OTCancelSynchronousCalls ( ssh_con . endpoint , kOTCanceledErr ) ;
CloseOpenTransport ( ) ;
}
2020-07-17 02:32:31 +01:00
}