mirror of https://github.com/cy384/ssheven.git
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:
parent
19a4995d08
commit
3e2647c6af
|
@ -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()
|
||||||
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
767
ssheven.c
767
ssheven.c
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[i] = '\0';
|
MoveTo(r->left + x * cell_width + 2, r->top + ((y+1) * cell_height) - 2);
|
||||||
return;
|
DrawChar(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_ssh_connection(char* hostname, char* username, char* password, char* command)
|
void draw_screen(Rect* r)
|
||||||
|
{
|
||||||
|
EraseRect(&(con.win->portRect));
|
||||||
|
for (int x = 0; x < 80; x++)
|
||||||
|
for (int y = 0; y < 24; y++)
|
||||||
|
draw_char(x, y, r, con.data[x][y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
char itoc[] = {'0','1', '2','3','4','5','6','7','8','9'};
|
||||||
|
|
||||||
|
void ruler(Rect* r)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 80; x++)
|
||||||
|
for (int y = 0; y < 24; y++)
|
||||||
|
draw_char(x, y, r, itoc[x%10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bump_up_line()
|
||||||
|
{
|
||||||
|
for (int y = 0; y < 23; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 80; x++)
|
||||||
|
{
|
||||||
|
con.data[x][y] = con.data[x][y+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = 0; x < 80; x++) con.data[x][23] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_printable(char c)
|
||||||
|
{
|
||||||
|
if (c >= 32 && c <= 126) return 1; else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_char(char c)
|
||||||
|
{
|
||||||
|
// backspace
|
||||||
|
if ('\b' == c)
|
||||||
|
{
|
||||||
|
// erase current location
|
||||||
|
con.data[con.cursor_x][con.cursor_y] = ' ';
|
||||||
|
|
||||||
|
// wrap back to the previous line if possible and necessary
|
||||||
|
if (con.cursor_x == 0 && con.cursor_y != 0)
|
||||||
|
{
|
||||||
|
con.cursor_x = 79;
|
||||||
|
con.cursor_y--;
|
||||||
|
}
|
||||||
|
// otherwise just move back a spot
|
||||||
|
else if (con.cursor_x > 0)
|
||||||
|
{
|
||||||
|
con.cursor_x--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// got a bell, give em a system beep (value of 30 recommended by docs)
|
||||||
|
if ('\a' == c) SysBeep(30);
|
||||||
|
|
||||||
|
if ('\n' == c)
|
||||||
|
{
|
||||||
|
con.cursor_y++;
|
||||||
|
con.cursor_x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_printable(c))
|
||||||
|
{
|
||||||
|
con.data[con.cursor_x][con.cursor_y] = c;
|
||||||
|
con.cursor_x++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (con.cursor_x == 80)
|
||||||
|
{
|
||||||
|
con.cursor_x = 0;
|
||||||
|
con.cursor_y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (con.cursor_y == 24)
|
||||||
|
{
|
||||||
|
bump_up_line();
|
||||||
|
con.cursor_y = 23;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_string(const char* c)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < strlen(c); i++)
|
||||||
|
{
|
||||||
|
print_char(c[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
Loading…
Reference in New Issue