save and load preferences

This commit is contained in:
cy384 2020-10-03 22:30:48 -04:00
parent e97e30d3fc
commit 7b2112f107
3 changed files with 298 additions and 104 deletions

View File

@ -70,6 +70,23 @@ void check_cursor(void)
} }
} }
// convert Quickdraw colors into vterm's ANSI color indexes
int qd2idx(int qdc)
{
switch (qdc)
{
case blackColor: return 0;
case redColor: return 1;
case greenColor: return 2;
case yellowColor: return 3;
case blueColor: return 4;
case magentaColor: return 5;
case cyanColor: return 6;
case whiteColor: return 7;
default: return 0;
}
}
// convert vterm's ANSI color indexes into Quickdraw colors // convert vterm's ANSI color indexes into Quickdraw colors
inline int idx2qd(VTermColor c) inline int idx2qd(VTermColor c)
{ {
@ -142,8 +159,8 @@ void draw_screen(Rect* r)
TextFont(kFontIDMonaco); TextFont(kFontIDMonaco);
TextSize(9); TextSize(9);
TextFace(normal); TextFace(normal);
qd.thePort->bkColor = whiteColor; qd.thePort->bkColor = prefs.bg_color;
qd.thePort->fgColor = blackColor; qd.thePort->fgColor = prefs.fg_color;
EraseRect(r); EraseRect(r);
@ -443,10 +460,10 @@ void console_setup(void)
vterm_state_reset(vtermstate, 1); vterm_state_reset(vtermstate, 1);
VTermColor fg = { .type = VTERM_COLOR_INDEXED }; VTermColor fg = { .type = VTERM_COLOR_INDEXED };
fg.indexed.idx = 0; // ANSI black fg.indexed.idx = qd2idx(prefs.fg_color);
VTermColor bg = { .type = VTERM_COLOR_INDEXED }; VTermColor bg = { .type = VTERM_COLOR_INDEXED };
bg.indexed.idx = 7; // ANSI white bg.indexed.idx = qd2idx(prefs.bg_color);
vterm_state_set_default_colors(vtermstate, &fg, &bg); vterm_state_set_default_colors(vtermstate, &fg, &bg);

347
ssheven.c
View File

@ -18,19 +18,170 @@
// sinful globals // sinful globals
struct ssheven_console con = { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 1, NULL, NULL }; struct ssheven_console con = { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 1, NULL, NULL };
struct ssheven_ssh_connection ssh_con = { NULL, NULL, kOTInvalidEndpointRef, NULL, NULL }; struct ssheven_ssh_connection ssh_con = { NULL, NULL, kOTInvalidEndpointRef, NULL, NULL };
struct preferences prefs;
enum { WAIT, READ, EXIT } read_thread_command = WAIT; enum { WAIT, READ, EXIT } read_thread_command = WAIT;
enum { UNINTIALIZED, OPEN, CLEANUP, DONE } read_thread_state = UNINTIALIZED; enum { UNINTIALIZED, OPEN, CLEANUP, DONE } read_thread_state = UNINTIALIZED;
enum { KEY_LOGIN, PASSWORD_LOGIN } login_type = PASSWORD_LOGIN;
// pascal strings int save_prefs(void)
char hostname[512] = {0}; {
char username[256] = {0}; int ok = 1;
char password[256] = {0}; short foundVRefNum = 0;
long foundDirID = 0;
FSSpec pref_file;
//int create_new = 0;
short prefRefNum = 0;
// malloc'd c strings OSType pref_type = 'SH7p';
char* pubkey_path = NULL; OSType creator_type = 'SSH7';
char* privkey_path = NULL;
// 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);
if (e == fnfErr) // file doesn't exist, but is a valid path
{
// so make the file
e = FSpCreate(&pref_file, creator_type, pref_type, smSystemScript);
if (e != noErr) ok = 0;
}
else if (e != noErr) ok = 0;
}
// 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);
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");
i += snprintf(output_buffer+i, write_length-i, "%s\n%s\n", prefs.privkey_path, prefs.pubkey_path);
// 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);
}
// borrowed from Retro68 sample code // borrowed from Retro68 sample code
// draws the "default" indicator around a button // draws the "default" indicator around a button
@ -470,7 +621,7 @@ int ssh_setup_terminal(void)
{ {
int rc = 0; int rc = 0;
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)); 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));
SSH_CHECK(libssh2_channel_shell(ssh_con.channel)); SSH_CHECK(libssh2_channel_shell(ssh_con.channel));
return 1; return 1;
@ -572,8 +723,8 @@ int password_dialog(int dialog_resource)
// read out of the hidden text box // read out of the hidden text box
GetDialogItem(dlog, 5, &type, &itemH, &box); GetDialogItem(dlog, 5, &type, &itemH, &box);
GetDialogItemText(itemH, (unsigned char*)password); GetDialogItemText(itemH, (unsigned char*)prefs.password);
login_type = PASSWORD_LOGIN; prefs.auth_type = USE_PASSWORD;
DisposeDialog(dlog); DisposeDialog(dlog);
@ -689,41 +840,47 @@ int key_dialog(void)
Handle full_path = NULL; Handle full_path = NULL;
int path_length = 0; int path_length = 0;
// get public key path // if we don't have a saved pubkey path, ask for one
NoteAlert(ALRT_PUBKEY, nil); if (prefs.pubkey_path == NULL || prefs.pubkey_path[0] == '\0')
StandardFileReply pubkey; {
StandardGetFile(NULL, 0, NULL, &pubkey); NoteAlert(ALRT_PUBKEY, nil);
FSpPathFromLocation(&pubkey.sfFile, &path_length, &full_path); StandardFileReply pubkey;
pubkey_path = malloc(path_length+1); StandardGetFile(NULL, 0, NULL, &pubkey);
strncpy(pubkey_path, (char*)(*full_path), path_length+1); FSpPathFromLocation(&pubkey.sfFile, &path_length, &full_path);
DisposeHandle(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;
}
path_length = 0; path_length = 0;
full_path = NULL; full_path = NULL;
// if the user hit cancel, 0 // if we don't have a saved privkey path, ask for one
if (!pubkey.sfGood) return 0; 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);
// get private key path // if the user hit cancel, 0
NoteAlert(ALRT_PRIVKEY, nil); if (!privkey.sfGood) return 0;
StandardFileReply privkey; }
StandardGetFile(NULL, 0, NULL, &privkey);
FSpPathFromLocation(&privkey.sfFile, &path_length, &full_path);
privkey_path = malloc(path_length+1);
strncpy(privkey_path, (char*)(*full_path), path_length+1);
DisposeHandle(full_path);
// if the user hit cancel, 0
if (!privkey.sfGood) return 0;
// get the key decryption password // get the key decryption password
if (!password_dialog(DLOG_KEY_PASSWORD)) return 0; if (!password_dialog(DLOG_KEY_PASSWORD)) return 0;
login_type = KEY_LOGIN; prefs.auth_type = USE_KEY;
return 1; return 1;
} }
int intro_dialog(char* hostname, char* username, char* password) int intro_dialog(void)
{ {
// modal dialog setup // modal dialog setup
TEInit(); TEInit();
@ -742,29 +899,42 @@ int intro_dialog(char* hostname, char* username, char* password)
GetDialogItem(dlg, 2, &type, &itemH, &box); GetDialogItem(dlg, 2, &type, &itemH, &box);
SetDialogItem(dlg, 2, type, (Handle)NewUserItemUPP(&ButtonFrameProc), &box); SetDialogItem(dlg, 2, type, (Handle)NewUserItemUPP(&ButtonFrameProc), &box);
// get the handles for each of the text boxes // get the handles for each of the text boxes, and load preference data in
ControlHandle address_text_box; ControlHandle address_text_box;
GetDialogItem(dlg, 4, &type, &itemH, &box); GetDialogItem(dlg, 4, &type, &itemH, &box);
address_text_box = (ControlHandle)itemH; address_text_box = (ControlHandle)itemH;
SetDialogItemText((Handle)address_text_box, (ConstStr255Param)prefs.hostname);
ControlHandle port_text_box; ControlHandle port_text_box;
GetDialogItem(dlg, 5, &type, &itemH, &box); GetDialogItem(dlg, 5, &type, &itemH, &box);
port_text_box = (ControlHandle)itemH; port_text_box = (ControlHandle)itemH;
SetDialogItemText((Handle)port_text_box, (ConstStr255Param)prefs.port);
ControlHandle username_text_box; ControlHandle username_text_box;
GetDialogItem(dlg, 7, &type, &itemH, &box); GetDialogItem(dlg, 7, &type, &itemH, &box);
username_text_box = (ControlHandle)itemH; username_text_box = (ControlHandle)itemH;
SetDialogItemText((Handle)username_text_box, (ConstStr255Param)prefs.username);
ControlHandle password_radio; ControlHandle password_radio;
GetDialogItem(dlg, 9, &type, &itemH, &box); GetDialogItem(dlg, 9, &type, &itemH, &box);
password_radio = (ControlHandle)itemH; password_radio = (ControlHandle)itemH;
SetControlValue(password_radio, 1); SetControlValue(password_radio, 0);
ControlHandle key_radio; ControlHandle key_radio;
GetDialogItem(dlg, 10, &type, &itemH, &box); GetDialogItem(dlg, 10, &type, &itemH, &box);
key_radio = (ControlHandle)itemH; key_radio = (ControlHandle)itemH;
SetControlValue(key_radio, 0); SetControlValue(key_radio, 0);
// recall last-used connection type
if (prefs.auth_type == USE_PASSWORD)
{
SetControlValue(password_radio, 1);
}
else
{
SetControlValue(key_radio, 1);
}
// let the modalmanager do everything // let the modalmanager do everything
// stop when the connect button is hit // stop when the connect button is hit
short item; short item;
@ -783,12 +953,15 @@ int intro_dialog(char* hostname, char* username, char* password)
} while(item != 1 && item != 8); } while(item != 1 && item != 8);
// copy the text out of the boxes // copy the text out of the boxes
GetDialogItemText((Handle)address_text_box, (unsigned char *)hostname); GetDialogItemText((Handle)address_text_box, (unsigned char *)prefs.hostname);
GetDialogItemText((Handle)username_text_box, (unsigned char *)username); GetDialogItemText((Handle)username_text_box, (unsigned char *)prefs.username);
// splice the port number onto the hostname (n.b. they're pascal strings) GetDialogItemText((Handle)port_text_box, (unsigned char *)prefs.hostname+prefs.hostname[0]+1);
GetDialogItemText((Handle)port_text_box, (unsigned char *)hostname+hostname[0]+1); prefs.hostname[prefs.hostname[0]+1] = ':';
hostname[hostname[0]+1] = ':';
char* port_start = prefs.hostname+prefs.hostname[0] + 2;
prefs.port[0] = strlen(port_start);
strncpy(prefs.port+1, port_start, 255);
int use_password = GetControlValue(password_radio); int use_password = GetControlValue(password_radio);
@ -823,25 +996,25 @@ void* read_thread(void* arg)
} }
// connect and log in // connect and log in
ok = init_connection(hostname+1); ok = init_connection(prefs.hostname+1);
YieldToAnyThread(); YieldToAnyThread();
if (ok) if (ok)
{ {
printf_i("Authenticating... "); YieldToAnyThread(); printf_i("Authenticating... "); YieldToAnyThread();
if (login_type == PASSWORD_LOGIN) if (prefs.auth_type == USE_PASSWORD)
{ {
rc = libssh2_userauth_password(ssh_con.session, username+1, password+1); rc = libssh2_userauth_password(ssh_con.session, prefs.username+1, prefs.password+1);
} }
else else
{ {
rc = libssh2_userauth_publickey_fromfile_ex(ssh_con.session, rc = libssh2_userauth_publickey_fromfile_ex(ssh_con.session,
username+1, prefs.username+1,
username[0], prefs.username[0],
pubkey_path, prefs.pubkey_path,
privkey_path, prefs.privkey_path,
password+1); prefs.password+1);
} }
if (rc == LIBSSH2_ERROR_NONE) if (rc == LIBSSH2_ERROR_NONE)
@ -850,13 +1023,20 @@ void* read_thread(void* arg)
} }
else else
{ {
if (rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED && login_type == PASSWORD_LOGIN) StopAlert(ALRT_PW_FAIL, nil); if (rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED && prefs.auth_type == USE_PASSWORD) StopAlert(ALRT_PW_FAIL, nil);
if (rc == LIBSSH2_ERROR_FILE) StopAlert(ALRT_FILE_FAIL, nil); else if (rc == LIBSSH2_ERROR_FILE) StopAlert(ALRT_FILE_FAIL, nil);
printf_i("failure: %s\r\n", libssh2_error_string(rc)); else if (rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
{
printf_i("Invalid key files!\r\n"); // TODO: have an alert for this
}
else printf_i("failure: %s\r\n", libssh2_error_string(rc));
ok = 0; ok = 0;
} }
} }
// if we logged in, save the connection preferences, since we know they're ok
if (ok) save_prefs();
// if we logged in, open and set up the tty // if we logged in, open and set up the tty
if (ok) if (ok)
{ {
@ -994,48 +1174,6 @@ int safety_checks(void)
return 1; return 1;
} }
int save_preferences(void)
{
int ok = 1;
short foundVRefNum = 0;
long foundDirID = 0;
FSSpec pref_file;
int create_new = 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);
if (e == fnfErr) // file doesn't exist, but is a valid path
{
create_new = 1;
}
else if (e != noErr) ok = 0;
}
if (ok && create_new)
{
e = FSpCreate(&pref_file, creator_type, pref_type, smSystemScript);
if (e != noErr) ok = 0;
}
// TODO: actually save anything
return ok;
}
void load_preferences(void)
{
// TODO: actually load anything
}
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
OSStatus err = noErr; OSStatus err = noErr;
@ -1049,7 +1187,9 @@ int main(int argc, char** argv)
MoreMasters(); MoreMasters();
MoreMasters(); MoreMasters();
load_preferences(); // set default preferences, then load from preferences file if possible
init_prefs();
load_prefs();
// general gui setup // general gui setup
InitGraf(&qd.thePort); InitGraf(&qd.thePort);
@ -1091,6 +1231,15 @@ int main(int argc, char** argv)
printf_i("Running in 68k mode.\r\n"); printf_i("Running in 68k mode.\r\n");
#endif #endif
if (prefs.loaded_from_file)
{
printf_i("Loaded preferences file.\r\n");
}
else
{
printf_i("Could not load from preferences file.\r\n");
}
BeginUpdate(con.win); BeginUpdate(con.win);
draw_screen(&(con.win->portRect)); draw_screen(&(con.win->portRect));
EndUpdate(con.win); EndUpdate(con.win);
@ -1103,9 +1252,7 @@ int main(int argc, char** argv)
draw_screen(&(con.win->portRect)); draw_screen(&(con.win->portRect));
EndUpdate(con.win); EndUpdate(con.win);
ok = intro_dialog(hostname, username, password); ok = intro_dialog();
if (ok) save_preferences();
if (!ok) printf_i("Cancelled, not connecting.\r\n"); if (!ok) printf_i("Cancelled, not connecting.\r\n");
@ -1168,8 +1315,8 @@ int main(int argc, char** argv)
if (ssh_con.recv_buffer != NULL) OTFreeMem(ssh_con.recv_buffer); if (ssh_con.recv_buffer != NULL) OTFreeMem(ssh_con.recv_buffer);
if (ssh_con.send_buffer != NULL) OTFreeMem(ssh_con.send_buffer); if (ssh_con.send_buffer != NULL) OTFreeMem(ssh_con.send_buffer);
if (pubkey_path != NULL) free(pubkey_path); if (prefs.pubkey_path != NULL && prefs.pubkey_path[0] != '\0') free(prefs.pubkey_path);
if (privkey_path != NULL) free(privkey_path); if (prefs.privkey_path != NULL && prefs.privkey_path[0] != '\0') free(prefs.privkey_path);
if (con.vterm != NULL) vterm_free(con.vterm); if (con.vterm != NULL) vterm_free(con.vterm);

View File

@ -36,6 +36,8 @@
#include <vterm.h> #include <vterm.h>
#include <vterm_keycodes.h> #include <vterm_keycodes.h>
#include <stdio.h>
// sinful globals // sinful globals
struct ssheven_console struct ssheven_console
{ {
@ -76,3 +78,31 @@ extern struct ssheven_ssh_connection ssh_con;
extern char key_to_vterm[256]; extern char key_to_vterm[256];
void ssh_write(char* buf, size_t len); void ssh_write(char* buf, size_t len);
struct preferences
{
int major_version;
int minor_version;
int loaded_from_file;
// pascal strings
char hostname[512]; // of the form: "hostname:portnumber", size is first only
char username[256];
char password[256];
char port[256];
// malloc'd c strings
char* pubkey_path;
char* privkey_path;
const char* terminal_string;
enum { USE_KEY, USE_PASSWORD } auth_type;
enum { FASTEST, MONOCHROME, COLOR } display_mode;
int fg_color;
int bg_color;
};
extern struct preferences prefs;