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-15 00:54:31 +01:00
struct ssheven_console con = { NULL , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , NULL , NULL } ;
2020-07-20 05:14:27 +01:00
struct ssheven_ssh_connection ssh_con = { NULL , NULL , kOTInvalidEndpointRef , NULL , NULL } ;
2020-10-04 03:30:48 +01:00
struct preferences prefs ;
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-10-04 03:30:48 +01:00
int save_prefs ( void )
{
int ok = 1 ;
short foundVRefNum = 0 ;
long foundDirID = 0 ;
FSSpec pref_file ;
//int create_new = 0;
short prefRefNum = 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 , PREFERENCES_FILENAME , & pref_file ) ;
2020-10-04 18:40:19 +01:00
// if the file exists, delete it
if ( e = = noErr ) FSpDelete ( & pref_file ) ;
// and then make it
e = FSpCreate ( & pref_file , creator_type , pref_type , smSystemScript ) ;
if ( e ! = noErr ) ok = 0 ;
2020-10-04 03:30:48 +01:00
}
// open the file
if ( ok )
{
e = FSpOpenDF ( & pref_file , fsRdWrPerm , & prefRefNum ) ;
if ( e ! = noErr ) ok = 0 ;
}
// write prefs to the file
if ( ok )
{
// TODO: choose buffer size more effectively
size_t write_length = 8192 ;
char * output_buffer = malloc ( write_length ) ;
memset ( output_buffer , 0 , write_length ) ;
long int i = snprintf ( output_buffer , write_length , " %d \n %d \n " , prefs . major_version , prefs . minor_version ) ;
i + = snprintf ( output_buffer + i , write_length - i , " %d \n %d \n %d \n %d \n " , ( int ) prefs . auth_type , ( int ) prefs . display_mode , ( int ) prefs . fg_color , ( int ) prefs . bg_color ) ;
2020-09-16 02:33:00 +01:00
2020-10-04 03:30:48 +01:00
snprintf ( output_buffer + i , prefs . hostname [ 0 ] + 1 , " %s " , prefs . hostname + 1 ) ; i + = prefs . hostname [ 0 ] ;
i + = snprintf ( output_buffer + i , write_length - i , " \n " ) ;
snprintf ( output_buffer + i , prefs . username [ 0 ] + 1 , " %s " , prefs . username + 1 ) ; i + = prefs . username [ 0 ] ;
i + = snprintf ( output_buffer + i , write_length - i , " \n " ) ;
snprintf ( output_buffer + i , prefs . port [ 0 ] + 1 , " %s " , prefs . port + 1 ) ; i + = prefs . port [ 0 ] ;
i + = snprintf ( output_buffer + i , write_length - i , " \n " ) ;
2020-10-04 18:40:19 +01:00
if ( prefs . privkey_path & & prefs . privkey_path [ 0 ] ! = ' \0 ' )
{
i + = snprintf ( output_buffer + i , write_length - i , " %s \n %s \n " , prefs . privkey_path , prefs . pubkey_path ) ;
}
else
{
i + = snprintf ( output_buffer + i , write_length - i , " \n \n " ) ;
}
2020-10-04 03:30:48 +01:00
// tell it to write all bytes
long int bytes = i ;
e = FSWrite ( prefRefNum , & bytes , output_buffer ) ;
// FSWrite sets bytes to the actual number of bytes written
if ( e ! = noErr | | ( bytes ! = i ) ) ok = 0 ;
}
// close the file
if ( prefRefNum ! = 0 )
{
e = FSClose ( prefRefNum ) ;
if ( e ! = noErr ) ok = 0 ;
}
return ok ;
}
void init_prefs ( void )
{
// initialize everything to a safe default
prefs . major_version = SSHEVEN_VERSION_MAJOR ;
prefs . minor_version = SSHEVEN_VERSION_MINOR ;
memset ( & ( prefs . hostname ) , 0 , 512 ) ;
memset ( & ( prefs . username ) , 0 , 256 ) ;
memset ( & ( prefs . password ) , 0 , 256 ) ;
memset ( & ( prefs . port ) , 0 , 256 ) ;
// default port: 22
prefs . port [ 0 ] = 2 ;
prefs . port [ 1 ] = ' 2 ' ;
prefs . port [ 2 ] = ' 2 ' ;
prefs . pubkey_path = " " ;
prefs . privkey_path = " " ;
prefs . terminal_string = SSHEVEN_TERMINAL_TYPE ;
prefs . auth_type = USE_PASSWORD ;
prefs . display_mode = COLOR ;
prefs . fg_color = blackColor ;
prefs . bg_color = whiteColor ;
prefs . loaded_from_file = 0 ;
}
void load_prefs ( void )
{
// now try to load preferences from the file
short foundVRefNum = 0 ;
long foundDirID = 0 ;
FSSpec pref_file ;
short prefRefNum = 0 ;
// find the preferences folder on the system disk
OSErr e = FindFolder ( kOnSystemDisk , kPreferencesFolderType , kDontCreateFolder , & foundVRefNum , & foundDirID ) ;
if ( e ! = noErr ) return ;
// make an FSSpec for the preferences file location and check if it exists
// TODO: if I just put PREFERENCES_FILENAME it doesn't work, wtf
e = FSMakeFSSpec ( foundVRefNum , foundDirID , " \ pssheven Preferences " , & pref_file ) ;
if ( e = = fnfErr ) // file not found, nothing to load
{
return ;
}
else if ( e ! = noErr ) return ;
e = FSpOpenDF ( & pref_file , fsCurPerm , & prefRefNum ) ;
if ( e ! = noErr ) return ;
// actually read and parse the file
long int buffer_size = 8192 ;
char * buffer = NULL ;
buffer = malloc ( buffer_size ) ;
prefs . privkey_path = malloc ( 2048 ) ;
prefs . pubkey_path = malloc ( 2048 ) ;
prefs . pubkey_path [ 0 ] = ' \0 ' ;
prefs . privkey_path [ 0 ] = ' \0 ' ;
e = FSRead ( prefRefNum , & buffer_size , buffer ) ;
e = FSClose ( prefRefNum ) ;
// check the version (first two numbers)
int items_got = sscanf ( buffer , " %d \n %d " , & prefs . major_version , & prefs . minor_version ) ;
if ( items_got ! = 2 ) return ;
// only load a prefs file if the saved version number matches ours
if ( ( prefs . major_version = = SSHEVEN_VERSION_MAJOR ) & & ( prefs . minor_version = = SSHEVEN_VERSION_MINOR ) )
{
prefs . loaded_from_file = 1 ;
items_got = sscanf ( buffer , " %d \n %d \n %d \n %d \n %d \n %d \n %255[^ \n ] \n %255[^ \n ] \n %255[^ \n ] \n %[^ \n ] \n %[^ \n ] " , & prefs . major_version , & prefs . minor_version , ( int * ) & prefs . auth_type , ( int * ) & prefs . display_mode , & prefs . fg_color , & prefs . bg_color , prefs . hostname + 1 , prefs . username + 1 , prefs . port + 1 , prefs . privkey_path , prefs . pubkey_path ) ;
// add the size for the pascal strings
prefs . hostname [ 0 ] = ( unsigned char ) strlen ( prefs . hostname + 1 ) ;
prefs . username [ 0 ] = ( unsigned char ) strlen ( prefs . username + 1 ) ;
prefs . port [ 0 ] = ( unsigned char ) strlen ( prefs . port + 1 ) ;
}
if ( buffer ) free ( buffer ) ;
}
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 ) ;
}
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-09-13 01:16:51 +01:00
ssize_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-09-13 01:16:51 +01:00
void 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 )
{
2020-10-04 03:32:51 +01:00
libssh2_channel_send_eof ( ssh_con . channel ) ;
2020-07-17 02:32:31 +01:00
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
OTSndOrderlyDisconnect ( ssh_con . endpoint ) ;
2020-07-17 02:32:31 +01:00
2020-09-13 01:16:51 +01:00
// discard remaining data so we can finish closing the connection
2020-07-17 02:32:31 +01:00
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-09-13 01:16:51 +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-09-13 01:16:51 +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-09-13 01:16:51 +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-09-14 22:26:14 +01:00
int check_network_events ( void )
2020-07-17 02:32:31 +01:00
{
2020-09-14 22:26:14 +01:00
int ok = 1 ;
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
2020-09-14 22:26:14 +01:00
// we always try to read, so ignore this event
2020-07-20 05:14:27 +01:00
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 ( ) ;
2020-09-14 22:26:14 +01:00
ok = 0 ;
2020-07-20 05:14:27 +01:00
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 ( ) ;
2020-09-14 22:26:14 +01:00
ok = 0 ;
2020-07-20 05:14:27 +01:00
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 ( ) ;
2020-09-14 22:26:14 +01:00
ok = 0 ;
2020-07-20 05:14:27 +01:00
break ;
default :
// something weird or irrelevant: ignore it
break ;
}
2020-09-14 22:26:14 +01:00
return ok ;
2020-07-20 05:14:27 +01:00
}
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 ) ;
2020-09-13 01:16:51 +01:00
2020-08-20 20:31:26 +01:00
if ( r < 1 )
{
2020-09-13 01:16:51 +01:00
printf_i ( " Failed to write to channel, 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-10-04 17:19:49 +01:00
int qd_color_to_menu_item ( int qd_color )
{
switch ( qd_color )
{
case blackColor : return 1 ;
case redColor : return 2 ;
case greenColor : return 3 ;
case yellowColor : return 4 ;
case blueColor : return 5 ;
case magentaColor : return 6 ;
case cyanColor : return 7 ;
case whiteColor : return 8 ;
default : return 1 ;
}
}
int menu_item_to_qd_color ( int menu_item )
{
switch ( menu_item )
{
case 1 : return blackColor ;
case 2 : return redColor ;
case 3 : return greenColor ;
case 4 : return yellowColor ;
case 5 : return blueColor ;
case 6 : return magentaColor ;
case 7 : return cyanColor ;
case 8 : return whiteColor ;
default : return 1 ;
}
}
void preferences_window ( void )
{
// modal dialog setup
TEInit ( ) ;
InitDialogs ( NULL ) ;
DialogPtr dlg = GetNewDialog ( DLOG_PREFERENCES , 0 , ( WindowPtr ) - 1 ) ;
InitCursor ( ) ;
// select all text in dialog item 4 (the hostname 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 menu, set to current prefs value
ControlHandle term_type_menu ;
GetDialogItem ( dlg , 6 , & type , & itemH , & box ) ;
term_type_menu = ( ControlHandle ) itemH ;
SetControlValue ( term_type_menu , prefs . display_mode + 1 ) ;
ControlHandle bg_color_menu ;
GetDialogItem ( dlg , 7 , & type , & itemH , & box ) ;
bg_color_menu = ( ControlHandle ) itemH ;
SetControlValue ( bg_color_menu , qd_color_to_menu_item ( prefs . bg_color ) ) ;
ControlHandle fg_color_menu ;
GetDialogItem ( dlg , 8 , & type , & itemH , & box ) ;
fg_color_menu = ( ControlHandle ) itemH ;
SetControlValue ( fg_color_menu , qd_color_to_menu_item ( prefs . fg_color ) ) ;
// let the modalmanager do everything
// stop on ok or cancel
short item ;
do {
ModalDialog ( NULL , & item ) ;
} while ( item ! = 1 & & item ! = 9 ) ;
// save if OK'd
if ( item = = 1 )
{
// read menu values into prefs
prefs . display_mode = GetControlValue ( term_type_menu ) - 1 ;
// TODO: don't save colors, make it take effect immediately
int save_bg = prefs . bg_color ;
int save_fg = prefs . fg_color ;
prefs . bg_color = menu_item_to_qd_color ( GetControlValue ( bg_color_menu ) ) ;
prefs . fg_color = menu_item_to_qd_color ( GetControlValue ( fg_color_menu ) ) ;
save_prefs ( ) ;
prefs . bg_color = save_bg ;
prefs . fg_color = save_fg ;
// TODO: make this actually fix all colors in vterm
update_console_colors ( ) ;
}
// clean it up
DisposeDialog ( dlg ) ;
FlushEvents ( everyEvent , - 1 ) ;
}
2020-09-13 01:16:51 +01:00
// returns 1 if quit selected, else 0
2020-08-06 01:59:49 +01:00
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 :
2020-10-04 17:19:49 +01:00
if ( item = = 1 ) preferences_window ( ) ;
if ( item = = 2 ) exit = 1 ;
2020-08-06 01:59:49 +01:00
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
2020-09-13 01:16:51 +01:00
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 } ;
2020-09-01 03:53:06 +01:00
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)
2020-09-13 01:16:51 +01:00
// GetCaretTime gets the number of ticks between system 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-09-13 01:16:51 +01:00
// handle any GUI events
unsigned char c = 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-06 19:34:42 +01:00
if ( key_to_vterm [ c ] ! = VTERM_KEY_NONE )
2020-09-01 00:36:19 +01:00
{
2020-09-06 19:34:42 +01:00
vterm_keyboard_key ( con . vterm , key_to_vterm [ c ] , VTERM_MOD_NONE ) ;
}
else
{
if ( ' \r ' = = c ) c = ' \n ' ;
ssh_con . send_buffer [ 0 ] = c ;
ssh_write ( ssh_con . send_buffer , 1 ) ;
2020-09-01 00:36:19 +01:00
}
2020-07-17 02:32:31 +01:00
}
2020-09-06 19:34:42 +01:00
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 ;
2020-09-13 01:16: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-09-13 01:16:51 +01:00
printf_i ( " failed to open Open Transport 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-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-09-13 01:16: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-09-13 01:16: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-09-13 01:16: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-09-13 01:16:51 +01:00
printf_i ( " failed to initialize SSH session. \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-09-13 01:16: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
2020-10-04 17:21:01 +01:00
const char * banner = libssh2_session_banner_get ( ssh_con . session ) ;
if ( banner ) printf_i ( " Server banner: %s \r \n " , banner ) ;
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_setup_terminal ( void )
2020-07-17 02:32:31 +01:00
{
int rc = 0 ;
2020-10-04 03:30:48 +01:00
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 ) ) ;
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 ;
2020-09-13 01:16:51 +01:00
long unsigned ticks ;
2020-09-06 01:04:55 +01:00
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 ;
}
2020-09-13 01:16:51 +01:00
break ;
2020-09-06 01:04:55 +01:00
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
2020-09-16 02:17:02 +01:00
int password_dialog ( int dialog_resource )
2020-07-17 02:32:31 +01:00
{
2020-09-13 01:16:51 +01:00
int ret = 1 ;
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 ;
2020-09-16 02:17:02 +01:00
dlog = GetNewDialog ( dialog_resource , 0 , ( WindowPtr ) - 1 ) ;
2020-09-06 01:04:55 +01:00
// 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
2020-09-13 01:16:51 +01:00
if ( 6 = = item ) ret = 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 ) ;
2020-10-04 03:30:48 +01:00
GetDialogItemText ( itemH , ( unsigned char * ) prefs . password ) ;
prefs . auth_type = USE_PASSWORD ;
2020-09-06 01:04:55 +01:00
DisposeDialog ( dlog ) ;
2020-09-13 01:16:51 +01:00
return ret ;
2020-09-06 01:04:55 +01:00
}
2020-09-16 02:17:02 +01:00
// derived from More Files sample code
OSErr
FSpPathFromLocation (
FSSpec * spec , /* The location we want a path for. */
int * length , /* Length of the resulting path. */
Handle * fullPath ) /* Handle to path. */
{
OSErr err ;
FSSpec tempSpec ;
CInfoPBRec pb ;
* fullPath = NULL ;
/*
* Make a copy of the input FSSpec that can be modified .
*/
BlockMoveData ( spec , & tempSpec , sizeof ( FSSpec ) ) ;
if ( tempSpec . parID = = fsRtParID ) {
/*
* The object is a volume . Add a colon to make it a full
* pathname . Allocate a handle for it and we are done .
*/
tempSpec . name [ 0 ] + = 2 ;
tempSpec . name [ tempSpec . name [ 0 ] - 1 ] = ' : ' ;
tempSpec . name [ tempSpec . name [ 0 ] ] = ' \0 ' ;
err = PtrToHand ( & tempSpec . name [ 1 ] , fullPath , tempSpec . name [ 0 ] ) ;
} else {
/*
* The object isn ' t a volume . Is the object a file or a directory ?
*/
pb . dirInfo . ioNamePtr = tempSpec . name ;
pb . dirInfo . ioVRefNum = tempSpec . vRefNum ;
pb . dirInfo . ioDrDirID = tempSpec . parID ;
pb . dirInfo . ioFDirIndex = 0 ;
err = PBGetCatInfoSync ( & pb ) ;
if ( ( err = = noErr ) | | ( err = = fnfErr ) ) {
/*
* If the file doesn ' t currently exist we start over . If the
* directory exists everything will work just fine . Otherwise we
* will just fail later . If the object is a directory , append a
* colon so full pathname ends with colon .
*/
if ( err = = fnfErr ) {
BlockMoveData ( spec , & tempSpec , sizeof ( FSSpec ) ) ;
} else if ( ( pb . hFileInfo . ioFlAttrib & ioDirMask ) ! = 0 ) {
tempSpec . name [ 0 ] + = 1 ;
tempSpec . name [ tempSpec . name [ 0 ] ] = ' : ' ;
}
/*
* Create a new Handle for the object - make it a C string
*/
tempSpec . name [ 0 ] + = 1 ;
tempSpec . name [ tempSpec . name [ 0 ] ] = ' \0 ' ;
err = PtrToHand ( & tempSpec . name [ 1 ] , fullPath , tempSpec . name [ 0 ] ) ;
if ( err = = noErr ) {
/*
* Get the ancestor directory names - loop until we have an
* error or find the root directory .
*/
pb . dirInfo . ioNamePtr = tempSpec . name ;
pb . dirInfo . ioVRefNum = tempSpec . vRefNum ;
pb . dirInfo . ioDrParID = tempSpec . parID ;
do {
pb . dirInfo . ioFDirIndex = - 1 ;
pb . dirInfo . ioDrDirID = pb . dirInfo . ioDrParID ;
err = PBGetCatInfoSync ( & pb ) ;
if ( err = = noErr ) {
/*
* Append colon to directory name and add
* directory name to beginning of fullPath
*/
+ + tempSpec . name [ 0 ] ;
tempSpec . name [ tempSpec . name [ 0 ] ] = ' : ' ;
( void ) Munger ( * fullPath , 0 , NULL , 0 , & tempSpec . name [ 1 ] ,
tempSpec . name [ 0 ] ) ;
err = MemError ( ) ;
}
} while ( ( err = = noErr ) & & ( pb . dirInfo . ioDrDirID ! = fsRtDirID ) ) ;
}
}
}
/*
* On error Dispose the handle , set it to NULL & return the err .
* Otherwise , set the length & return .
*/
if ( err = = noErr ) {
* length = GetHandleSize ( * fullPath ) - 1 ;
} else {
if ( * fullPath ! = NULL ) {
DisposeHandle ( * fullPath ) ;
}
* fullPath = NULL ;
* length = 0 ;
}
return err ;
}
2020-09-14 23:51:29 +01:00
int key_dialog ( void )
{
2020-09-16 02:17:02 +01:00
Handle full_path = NULL ;
int path_length = 0 ;
2020-10-04 03:30:48 +01:00
// if we don't have a saved pubkey path, ask for one
if ( prefs . pubkey_path = = NULL | | prefs . pubkey_path [ 0 ] = = ' \0 ' )
{
NoteAlert ( ALRT_PUBKEY , nil ) ;
StandardFileReply pubkey ;
StandardGetFile ( NULL , 0 , NULL , & pubkey ) ;
FSpPathFromLocation ( & pubkey . sfFile , & path_length , & full_path ) ;
prefs . pubkey_path = malloc ( path_length + 1 ) ;
strncpy ( prefs . pubkey_path , ( char * ) ( * full_path ) , path_length + 1 ) ;
DisposeHandle ( full_path ) ;
// if the user hit cancel, 0
if ( ! pubkey . sfGood ) return 0 ;
}
2020-09-16 02:17:02 +01:00
path_length = 0 ;
full_path = NULL ;
2020-10-04 03:30:48 +01:00
// if we don't have a saved privkey path, ask for one
if ( prefs . privkey_path = = NULL | | prefs . privkey_path [ 0 ] = = ' \0 ' )
{
NoteAlert ( ALRT_PRIVKEY , nil ) ;
StandardFileReply privkey ;
StandardGetFile ( NULL , 0 , NULL , & privkey ) ;
FSpPathFromLocation ( & privkey . sfFile , & path_length , & full_path ) ;
prefs . privkey_path = malloc ( path_length + 1 ) ;
strncpy ( prefs . privkey_path , ( char * ) ( * full_path ) , path_length + 1 ) ;
DisposeHandle ( full_path ) ;
// if the user hit cancel, 0
if ( ! privkey . sfGood ) return 0 ;
}
2020-09-16 02:17:02 +01:00
// get the key decryption password
if ( ! password_dialog ( DLOG_KEY_PASSWORD ) ) return 0 ;
2020-10-04 03:30:48 +01:00
prefs . auth_type = USE_KEY ;
2020-09-16 02:17:02 +01:00
return 1 ;
2020-09-14 23:51:29 +01:00
}
2020-10-04 03:30:48 +01:00
int intro_dialog ( void )
2020-09-06 01:04:55 +01:00
{
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 ( ) ;
2020-09-06 20:35:13 +01:00
// select all text in dialog item 4 (the hostname one)
2020-07-17 02:32:31 +01:00
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 ) ;
2020-09-13 01:16:51 +01:00
SetDialogItem ( dlg , 2 , type , ( Handle ) NewUserItemUPP ( & ButtonFrameProc ) , & box ) ;
2020-07-17 02:32:31 +01:00
2020-10-04 03:30:48 +01:00
// get the handles for each of the text boxes, and load preference data in
2020-07-17 02:32:31 +01:00
ControlHandle address_text_box ;
GetDialogItem ( dlg , 4 , & type , & itemH , & box ) ;
address_text_box = ( ControlHandle ) itemH ;
2020-10-04 03:30:48 +01:00
SetDialogItemText ( ( Handle ) address_text_box , ( ConstStr255Param ) prefs . hostname ) ;
2020-07-17 02:32:31 +01:00
2020-09-06 20:35:13 +01:00
ControlHandle port_text_box ;
GetDialogItem ( dlg , 5 , & type , & itemH , & box ) ;
port_text_box = ( ControlHandle ) itemH ;
2020-10-04 03:30:48 +01:00
SetDialogItemText ( ( Handle ) port_text_box , ( ConstStr255Param ) prefs . port ) ;
2020-09-06 20:35:13 +01:00
2020-07-17 02:32:31 +01:00
ControlHandle username_text_box ;
2020-09-06 20:35:13 +01:00
GetDialogItem ( dlg , 7 , & type , & itemH , & box ) ;
2020-07-17 02:32:31 +01:00
username_text_box = ( ControlHandle ) itemH ;
2020-10-04 03:30:48 +01:00
SetDialogItemText ( ( Handle ) username_text_box , ( ConstStr255Param ) prefs . username ) ;
2020-07-17 02:32:31 +01:00
2020-09-14 23:51:29 +01:00
ControlHandle password_radio ;
GetDialogItem ( dlg , 9 , & type , & itemH , & box ) ;
password_radio = ( ControlHandle ) itemH ;
2020-10-04 03:30:48 +01:00
SetControlValue ( password_radio , 0 ) ;
2020-09-14 23:51:29 +01:00
ControlHandle key_radio ;
GetDialogItem ( dlg , 10 , & type , & itemH , & box ) ;
key_radio = ( ControlHandle ) itemH ;
SetControlValue ( key_radio , 0 ) ;
2020-10-04 03:30:48 +01:00
// recall last-used connection type
if ( prefs . auth_type = = USE_PASSWORD )
{
SetControlValue ( password_radio , 1 ) ;
}
else
{
SetControlValue ( key_radio , 1 ) ;
}
2020-07-17 02:32:31 +01:00
// let the modalmanager do everything
// stop when the connect button is hit
short item ;
do {
ModalDialog ( NULL , & item ) ;
2020-09-14 23:51:29 +01:00
if ( item = = 9 )
{
SetControlValue ( key_radio , 0 ) ;
SetControlValue ( password_radio , 1 ) ;
}
else if ( item = = 10 )
{
SetControlValue ( key_radio , 1 ) ;
SetControlValue ( password_radio , 0 ) ;
}
2020-09-06 20:35:13 +01:00
} while ( item ! = 1 & & item ! = 8 ) ;
2020-07-17 02:32:31 +01:00
// copy the text out of the boxes
2020-10-04 03:30:48 +01:00
GetDialogItemText ( ( Handle ) address_text_box , ( unsigned char * ) prefs . hostname ) ;
GetDialogItemText ( ( Handle ) username_text_box , ( unsigned char * ) prefs . username ) ;
GetDialogItemText ( ( Handle ) port_text_box , ( unsigned char * ) prefs . hostname + prefs . hostname [ 0 ] + 1 ) ;
prefs . hostname [ prefs . hostname [ 0 ] + 1 ] = ' : ' ;
2020-07-17 02:32:31 +01:00
2020-10-04 03:30:48 +01:00
char * port_start = prefs . hostname + prefs . hostname [ 0 ] + 2 ;
prefs . port [ 0 ] = strlen ( port_start ) ;
strncpy ( prefs . port + 1 , port_start , 255 ) ;
2020-09-06 20:35:13 +01:00
2020-09-15 00:55:00 +01:00
int use_password = GetControlValue ( password_radio ) ;
2020-07-17 02:32:31 +01:00
// 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
2020-09-15 01:01:19 +01:00
// if we hit cancel, 0
if ( item = = 8 ) return 0 ;
2020-09-15 00:55:00 +01:00
if ( use_password )
2020-09-14 23:51:29 +01:00
{
2020-09-16 02:17:02 +01:00
return password_dialog ( DLOG_PASSWORD ) ;
2020-09-14 23:51:29 +01:00
}
else
{
return key_dialog ( ) ;
}
2020-07-17 02:32:31 +01:00
}
2020-07-20 05:14:27 +01:00
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-09-13 17:21:17 +01:00
int rc = LIBSSH2_ERROR_NONE ;
2020-07-24 00:46:29 +01:00
2020-09-13 17:21:17 +01:00
// yield until we're given a command
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-10-04 03:30:48 +01:00
ok = init_connection ( prefs . 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-09-13 17:21:17 +01:00
printf_i ( " Authenticating... " ) ; YieldToAnyThread ( ) ;
2020-09-16 02:17:02 +01:00
2020-10-04 03:30:48 +01:00
if ( prefs . auth_type = = USE_PASSWORD )
2020-09-16 02:17:02 +01:00
{
2020-10-04 03:30:48 +01:00
rc = libssh2_userauth_password ( ssh_con . session , prefs . username + 1 , prefs . password + 1 ) ;
2020-09-16 02:17:02 +01:00
}
else
{
rc = libssh2_userauth_publickey_fromfile_ex ( ssh_con . session ,
2020-10-04 03:30:48 +01:00
prefs . username + 1 ,
prefs . username [ 0 ] ,
prefs . pubkey_path ,
prefs . privkey_path ,
prefs . password + 1 ) ;
2020-09-16 02:17:02 +01:00
}
2020-09-13 17:21:17 +01:00
if ( rc = = LIBSSH2_ERROR_NONE )
{
printf_i ( " done. \r \n " ) ;
}
else
{
2020-10-04 18:40:19 +01:00
printf_i ( " failed! \r \n " ) ;
2020-10-04 03:30:48 +01:00
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 )
{
2020-10-04 17:20:43 +01:00
printf_i ( " Username/public key combination invalid! \r \n " ) ; // TODO: have an alert for this
2020-10-04 17:54:59 +01:00
if ( prefs . pubkey_path [ 0 ] ! = ' \0 ' | | prefs . privkey_path [ 0 ] ! = ' \0 ' )
{
prefs . pubkey_path [ 0 ] = ' \0 ' ;
prefs . privkey_path [ 0 ] = ' \0 ' ;
}
2020-10-04 03:30:48 +01:00
}
2020-10-04 17:20:43 +01:00
else printf_i ( " unexpected failure: %s \r \n " , libssh2_error_string ( rc ) ) ;
2020-09-13 17:21:17 +01:00
ok = 0 ;
}
2020-07-24 00:46:29 +01:00
}
2020-07-20 05:14:27 +01:00
2020-10-04 18:40:19 +01:00
save_prefs ( ) ;
2020-10-04 03:30:48 +01:00
2020-09-13 17:21:17 +01:00
// if we logged in, open and set up the tty
2020-07-24 00:46:29 +01:00
if ( ok )
{
2020-10-04 03:32:51 +01:00
libssh2_channel_handle_extended_data2 ( ssh_con . channel , LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE ) ;
2020-09-13 17:21:17 +01:00
ssh_con . channel = libssh2_channel_open_session ( ssh_con . session ) ;
2020-07-24 00:46:29 +01:00
ok = ssh_setup_terminal ( ) ;
YieldToAnyThread ( ) ;
}
2020-07-20 05:14:27 +01:00
2020-09-13 17:21:17 +01:00
// if we failed, close everything and exit
if ( ! ok | | ( read_thread_state ! = OPEN ) )
{
end_connection ( ) ;
return 0 ;
}
2020-07-20 05:14:27 +01:00
2020-09-13 17:21:17 +01:00
// if we connected, allow pasting
2020-08-22 21:23:42 +01:00
void * menu = GetMenuHandle ( MENU_EDIT ) ;
EnableItem ( menu , 5 ) ;
2020-10-04 03:32:51 +01:00
// 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 )
2020-07-20 05:14:27 +01:00
{
2020-09-14 22:26:14 +01:00
if ( check_network_events ( ) ) ssh_read ( ) ;
2020-07-20 05:14:27 +01:00
YieldToAnyThread ( ) ;
}
// if we still have a connection, close it
if ( read_thread_state ! = DONE ) end_connection ( ) ;
2020-09-13 17:21:17 +01:00
// disallow pasting after connection is closed
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-10-04 03:30:48 +01:00
// set default preferences, then load from preferences file if possible
init_prefs ( ) ;
load_prefs ( ) ;
2020-09-20 04:48:41 +01:00
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 "
2020-09-06 01:06:01 +01:00
" |_____/_____/|_| |_| \\ ___| \\ _/ \\ ___|_| |_| \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-10-04 03:30:48 +01:00
if ( prefs . loaded_from_file )
{
printf_i ( " Loaded preferences file. \r \n " ) ;
}
else
{
printf_i ( " Could not load from preferences file. \r \n " ) ;
}
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-10-04 03:30:48 +01:00
ok = intro_dialog ( ) ;
2020-09-20 04:48:41 +01:00
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-09-13 01:16:51 +01:00
printf_i ( " Failed to initialize Open Transport. \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-09-13 01:16:51 +01:00
printf_i ( " Failed to allocate network data 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
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 )
{
2020-09-13 01:16:51 +01:00
printf_i ( " Failed to create network read thread. \r \n " ) ;
2020-07-17 02:32:31 +01:00
ok = 0 ;
}
}
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-13 01:16:51 +01:00
event_loop ( ) ;
2020-06-08 17:53:38 +01:00
2020-09-13 01:16:51 +01:00
// tell the read thread to finish, then let it run to actually do so
2020-07-20 05:14:27 +01:00
read_thread_command = EXIT ;
2020-09-13 01:16:51 +01:00
2020-09-06 01:05:35 +01:00
if ( read_thread_state ! = UNINTIALIZED )
2020-09-13 01:16:51 +01:00
{
2020-09-06 01:05:35 +01:00
while ( read_thread_state ! = DONE )
2020-09-13 01:16:51 +01:00
{
BeginUpdate ( con . win ) ;
draw_screen ( & ( con . win - > portRect ) ) ;
EndUpdate ( con . win ) ;
2020-09-06 01:05:35 +01:00
YieldToAnyThread ( ) ;
2020-09-13 01:16:51 +01:00
}
2020-09-06 01:05:35 +01:00
}
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-10-04 03:30:48 +01:00
if ( prefs . pubkey_path ! = NULL & & prefs . pubkey_path [ 0 ] ! = ' \0 ' ) free ( prefs . pubkey_path ) ;
if ( prefs . privkey_path ! = NULL & & prefs . privkey_path [ 0 ] ! = ' \0 ' ) free ( prefs . privkey_path ) ;
2020-09-16 02:17:02 +01:00
2020-08-29 03:14:43 +01:00
if ( con . vterm ! = NULL ) vterm_free ( con . vterm ) ;
2020-09-13 01:16:51 +01:00
if ( ssh_con . endpoint ! = kOTInvalidEndpointRef )
2020-07-24 00:46:29 +01:00
{
err = OTCancelSynchronousCalls ( ssh_con . endpoint , kOTCanceledErr ) ;
CloseOpenTransport ( ) ;
}
2020-07-17 02:32:31 +01:00
}