+++ /dev/null
-/*\r
- * osxdlg.m: various PuTTY dialog boxes for OS X.\r
- */\r
-\r
-#import <Cocoa/Cocoa.h>\r
-#include "putty.h"\r
-#include "storage.h"\r
-#include "dialog.h"\r
-#include "osxclass.h"\r
-\r
-/*\r
- * The `ConfigWindow' class is used to start up a new PuTTY\r
- * session.\r
- */\r
-\r
-@class ConfigTree;\r
-@interface ConfigTree : NSObject\r
-{\r
- NSString **paths;\r
- int *levels;\r
- int nitems, itemsize;\r
-}\r
-- (void)addPath:(char *)path;\r
-@end\r
-\r
-@implementation ConfigTree\r
-- (id)init\r
-{\r
- self = [super init];\r
- paths = NULL;\r
- levels = NULL;\r
- nitems = itemsize = 0;\r
- return self;\r
-}\r
-- (void)addPath:(char *)path\r
-{\r
- if (nitems >= itemsize) {\r
- itemsize += 32;\r
- paths = sresize(paths, itemsize, NSString *);\r
- levels = sresize(levels, itemsize, int);\r
- }\r
- paths[nitems] = [[NSString stringWithCString:path] retain];\r
- levels[nitems] = ctrl_path_elements(path) - 1;\r
- nitems++;\r
-}\r
-- (void)dealloc\r
-{\r
- int i;\r
-\r
- for (i = 0; i < nitems; i++)\r
- [paths[i] release];\r
-\r
- sfree(paths);\r
- sfree(levels);\r
-\r
- [super dealloc];\r
-}\r
-- (id)iterateChildren:(int)index ofItem:(id)item count:(int *)count\r
-{\r
- int i, plevel;\r
-\r
- if (item) {\r
- for (i = 0; i < nitems; i++)\r
- if (paths[i] == item)\r
- break;\r
- assert(i < nitems);\r
- plevel = levels[i];\r
- i++;\r
- } else {\r
- i = 0;\r
- plevel = -1;\r
- }\r
-\r
- if (count)\r
- *count = 0;\r
-\r
- while (index > 0) {\r
- if (i >= nitems || levels[i] != plevel+1)\r
- return nil;\r
- if (count)\r
- (*count)++;\r
- do {\r
- i++;\r
- } while (i < nitems && levels[i] > plevel+1);\r
- index--;\r
- }\r
-\r
- return paths[i];\r
-}\r
-- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item\r
-{\r
- return [self iterateChildren:index ofItem:item count:NULL];\r
-}\r
-- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item\r
-{\r
- int count = 0;\r
- /* pass nitems+1 to ensure we run off the end */\r
- [self iterateChildren:nitems+1 ofItem:item count:&count];\r
- return count;\r
-}\r
-- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item\r
-{\r
- return [self outlineView:outlineView numberOfChildrenOfItem:item] > 0;\r
-}\r
-- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item\r
-{\r
- /*\r
- * Trim off all path elements except the last one.\r
- */\r
- NSArray *components = [item componentsSeparatedByString:@"/"];\r
- return [components objectAtIndex:[components count]-1];\r
-}\r
-@end\r
-\r
-@implementation ConfigWindow\r
-- (id)initWithConfig:(Config)aCfg\r
-{\r
- NSScrollView *scrollview;\r
- NSTableColumn *col;\r
- ConfigTree *treedata;\r
- int by = 0, mby = 0;\r
- int wmin = 0;\r
- int hmin = 0;\r
- int panelht = 0;\r
-\r
- ctrlbox = ctrl_new_box();\r
- setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol,\r
- 0 /* protcfginfo */);\r
- unix_setup_config_box(ctrlbox, FALSE /*midsession*/, aCfg.protocol);\r
-\r
- cfg = aCfg; /* structure copy */\r
-\r
- self = [super initWithContentRect:NSMakeRect(0,0,300,300)\r
- styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask |\r
- NSClosableWindowMask)\r
- backing:NSBackingStoreBuffered\r
- defer:YES];\r
- [self setTitle:@"PuTTY Configuration"];\r
-\r
- [self setIgnoresMouseEvents:NO];\r
-\r
- dv = fe_dlg_init(&cfg, self, self, @selector(configBoxFinished:));\r
-\r
- scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(20,20,10,10)];\r
- treeview = [[NSOutlineView alloc] initWithFrame:[scrollview frame]];\r
- [scrollview setBorderType:NSLineBorder];\r
- [scrollview setDocumentView:treeview];\r
- [[self contentView] addSubview:scrollview];\r
- [scrollview setHasVerticalScroller:YES];\r
- [scrollview setAutohidesScrollers:YES];\r
- /* FIXME: the below is untested. Test it then remove this notice. */\r
- [treeview setAllowsColumnReordering:NO];\r
- [treeview setAllowsColumnResizing:NO];\r
- [treeview setAllowsMultipleSelection:NO];\r
- [treeview setAllowsEmptySelection:NO];\r
- [treeview setAllowsColumnSelection:YES];\r
-\r
- treedata = [[[ConfigTree alloc] init] retain];\r
-\r
- col = [[NSTableColumn alloc] initWithIdentifier:nil];\r
- [treeview addTableColumn:col];\r
- [treeview setOutlineTableColumn:col];\r
-\r
- [[treeview headerView] setFrame:NSMakeRect(0,0,0,0)];\r
-\r
- /*\r
- * Create the controls.\r
- */\r
- {\r
- int i;\r
- char *path = NULL;\r
-\r
- for (i = 0; i < ctrlbox->nctrlsets; i++) {\r
- struct controlset *s = ctrlbox->ctrlsets[i];\r
- int mw, mh;\r
-\r
- if (!*s->pathname) {\r
-\r
- create_ctrls(dv, [self contentView], s, &mw, &mh);\r
-\r
- by += 20 + mh;\r
-\r
- if (wmin < mw + 40)\r
- wmin = mw + 40;\r
- } else {\r
- int j = path ? ctrl_path_compare(s->pathname, path) : 0;\r
-\r
- if (j != INT_MAX) { /* add to treeview, start new panel */\r
- char *c;\r
-\r
- /*\r
- * We expect never to find an implicit path\r
- * component. For example, we expect never to\r
- * see A/B/C followed by A/D/E, because that\r
- * would _implicitly_ create A/D. All our path\r
- * prefixes are expected to contain actual\r
- * controls and be selectable in the treeview;\r
- * so we would expect to see A/D _explicitly_\r
- * before encountering A/D/E.\r
- */\r
- assert(j == ctrl_path_elements(s->pathname) - 1);\r
-\r
- c = strrchr(s->pathname, '/');\r
- if (!c)\r
- c = s->pathname;\r
- else\r
- c++;\r
-\r
- [treedata addPath:s->pathname];\r
- path = s->pathname;\r
-\r
- panelht = 0;\r
- }\r
-\r
- create_ctrls(dv, [self contentView], s, &mw, &mh);\r
- if (wmin < mw + 3*20+150)\r
- wmin = mw + 3*20+150;\r
- panelht += mh + 20;\r
- if (hmin < panelht - 20)\r
- hmin = panelht - 20;\r
- }\r
- }\r
- }\r
-\r
- {\r
- int i;\r
- NSRect r;\r
-\r
- [treeview setDataSource:treedata];\r
- for (i = [treeview numberOfRows]; i-- ;)\r
- [treeview expandItem:[treeview itemAtRow:i] expandChildren:YES];\r
-\r
- [treeview sizeToFit];\r
- r = [treeview frame];\r
- if (hmin < r.size.height)\r
- hmin = r.size.height;\r
- }\r
-\r
- [self setContentSize:NSMakeSize(wmin, hmin+60+by)];\r
- [scrollview setFrame:NSMakeRect(20, 40+by, 150, hmin)];\r
- [treeview setDelegate:self];\r
- mby = by;\r
-\r
- /*\r
- * Now place the controls.\r
- */\r
- {\r
- int i;\r
- char *path = NULL;\r
- panelht = 0;\r
-\r
- for (i = 0; i < ctrlbox->nctrlsets; i++) {\r
- struct controlset *s = ctrlbox->ctrlsets[i];\r
-\r
- if (!*s->pathname) {\r
- by -= VSPACING + place_ctrls(dv, s, 20, by, wmin-40);\r
- } else {\r
- if (!path || strcmp(s->pathname, path))\r
- panelht = 0;\r
-\r
- panelht += VSPACING + place_ctrls(dv, s, 2*20+150,\r
- 40+mby+hmin-panelht,\r
- wmin - (3*20+150));\r
-\r
- path = s->pathname;\r
- }\r
- }\r
- }\r
-\r
- select_panel(dv, ctrlbox, [[treeview itemAtRow:0] cString]);\r
-\r
- [treeview reloadData];\r
-\r
- dlg_refresh(NULL, dv);\r
-\r
- [self center]; /* :-) */\r
-\r
- return self;\r
-}\r
-- (void)configBoxFinished:(id)object\r
-{\r
- int ret = [object intValue]; /* it'll be an NSNumber */\r
- if (ret) {\r
- [controller performSelectorOnMainThread:\r
- @selector(newSessionWithConfig:)\r
- withObject:[NSData dataWithBytes:&cfg length:sizeof(cfg)]\r
- waitUntilDone:NO];\r
- }\r
- [self close];\r
-}\r
-- (void)outlineViewSelectionDidChange:(NSNotification *)notification\r
-{\r
- const char *path = [[treeview itemAtRow:[treeview selectedRow]] cString];\r
- select_panel(dv, ctrlbox, path);\r
-}\r
-- (BOOL)outlineView:(NSOutlineView *)outlineView\r
- shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item\r
-{\r
- return NO; /* no editing! */\r
-}\r
-@end\r
-\r
-/* ----------------------------------------------------------------------\r
- * Various special-purpose dialog boxes.\r
- */\r
-\r
-struct appendstate {\r
- void (*callback)(void *ctx, int result);\r
- void *ctx;\r
-};\r
-\r
-static void askappend_callback(void *ctx, int result)\r
-{\r
- struct appendstate *state = (struct appendstate *)ctx;\r
-\r
- state->callback(state->ctx, (result == NSAlertFirstButtonReturn ? 2 :\r
- result == NSAlertSecondButtonReturn ? 1 : 0));\r
- sfree(state);\r
-}\r
-\r
-int askappend(void *frontend, Filename filename,\r
- void (*callback)(void *ctx, int result), void *ctx)\r
-{\r
- static const char msgtemplate[] =\r
- "The session log file \"%s\" already exists. "\r
- "You can overwrite it with a new session log, "\r
- "append your session log to the end of it, "\r
- "or disable session logging for this session.";\r
-\r
- char *text;\r
- SessionWindow *win = (SessionWindow *)frontend;\r
- struct appendstate *state;\r
- NSAlert *alert;\r
-\r
- text = dupprintf(msgtemplate, filename.path);\r
-\r
- state = snew(struct appendstate);\r
- state->callback = callback;\r
- state->ctx = ctx;\r
-\r
- alert = [[NSAlert alloc] init];\r
- [alert setInformativeText:[NSString stringWithCString:text]];\r
- [alert addButtonWithTitle:@"Overwrite"];\r
- [alert addButtonWithTitle:@"Append"];\r
- [alert addButtonWithTitle:@"Disable"];\r
- [win startAlert:alert withCallback:askappend_callback andCtx:state];\r
-\r
- return -1;\r
-}\r
-\r
-struct algstate {\r
- void (*callback)(void *ctx, int result);\r
- void *ctx;\r
-};\r
-\r
-static void askalg_callback(void *ctx, int result)\r
-{\r
- struct algstate *state = (struct algstate *)ctx;\r
-\r
- state->callback(state->ctx, result == NSAlertFirstButtonReturn);\r
- sfree(state);\r
-}\r
-\r
-int askalg(void *frontend, const char *algtype, const char *algname,\r
- void (*callback)(void *ctx, int result), void *ctx)\r
-{\r
- static const char msg[] =\r
- "The first %s supported by the server is "\r
- "%s, which is below the configured warning threshold.\n"\r
- "Continue with connection?";\r
-\r
- char *text;\r
- SessionWindow *win = (SessionWindow *)frontend;\r
- struct algstate *state;\r
- NSAlert *alert;\r
-\r
- text = dupprintf(msg, algtype, algname);\r
-\r
- state = snew(struct algstate);\r
- state->callback = callback;\r
- state->ctx = ctx;\r
-\r
- alert = [[NSAlert alloc] init];\r
- [alert setInformativeText:[NSString stringWithCString:text]];\r
- [alert addButtonWithTitle:@"Yes"];\r
- [alert addButtonWithTitle:@"No"];\r
- [win startAlert:alert withCallback:askalg_callback andCtx:state];\r
-\r
- return -1;\r
-}\r
-\r
-struct hostkeystate {\r
- char *host, *keytype, *keystr;\r
- int port;\r
- void (*callback)(void *ctx, int result);\r
- void *ctx;\r
-};\r
-\r
-static void verify_ssh_host_key_callback(void *ctx, int result)\r
-{\r
- struct hostkeystate *state = (struct hostkeystate *)ctx;\r
-\r
- if (result == NSAlertThirdButtonReturn) /* `Accept' */\r
- store_host_key(state->host, state->port,\r
- state->keytype, state->keystr);\r
- state->callback(state->ctx, result != NSAlertFirstButtonReturn);\r
- sfree(state->host);\r
- sfree(state->keytype);\r
- sfree(state->keystr);\r
- sfree(state);\r
-}\r
-\r
-int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
- char *keystr, char *fingerprint,\r
- void (*callback)(void *ctx, int result), void *ctx)\r
-{\r
- static const char absenttxt[] =\r
- "The server's host key is not cached. You have no guarantee "\r
- "that the server is the computer you think it is.\n"\r
- "The server's %s key fingerprint is:\n"\r
- "%s\n"\r
- "If you trust this host, press \"Accept\" to add the key to "\r
- "PuTTY's cache and carry on connecting.\n"\r
- "If you want to carry on connecting just once, without "\r
- "adding the key to the cache, press \"Connect Once\".\n"\r
- "If you do not trust this host, press \"Cancel\" to abandon the "\r
- "connection.";\r
- static const char wrongtxt[] =\r
- "WARNING - POTENTIAL SECURITY BREACH!\n"\r
- "The server's host key does not match the one PuTTY has "\r
- "cached. This means that either the server administrator "\r
- "has changed the host key, or you have actually connected "\r
- "to another computer pretending to be the server.\n"\r
- "The new %s key fingerprint is:\n"\r
- "%s\n"\r
- "If you were expecting this change and trust the new key, "\r
- "press \"Accept\" to update PuTTY's cache and continue connecting.\n"\r
- "If you want to carry on connecting but without updating "\r
- "the cache, press \"Connect Once\".\n"\r
- "If you want to abandon the connection completely, press "\r
- "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed "\r
- "safe choice.";\r
-\r
- int ret;\r
- char *text;\r
- SessionWindow *win = (SessionWindow *)frontend;\r
- struct hostkeystate *state;\r
- NSAlert *alert;\r
-\r
- /*\r
- * Verify the key.\r
- */\r
- ret = verify_host_key(host, port, keytype, keystr);\r
-\r
- if (ret == 0)\r
- return 1;\r
-\r
- text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint);\r
-\r
- state = snew(struct hostkeystate);\r
- state->callback = callback;\r
- state->ctx = ctx;\r
- state->host = dupstr(host);\r
- state->port = port;\r
- state->keytype = dupstr(keytype);\r
- state->keystr = dupstr(keystr);\r
-\r
- alert = [[NSAlert alloc] init];\r
- [alert setInformativeText:[NSString stringWithCString:text]];\r
- [alert addButtonWithTitle:@"Cancel"];\r
- [alert addButtonWithTitle:@"Connect Once"];\r
- [alert addButtonWithTitle:@"Accept"];\r
- [win startAlert:alert withCallback:verify_ssh_host_key_callback\r
- andCtx:state];\r
-\r
- return -1;\r
-}\r
-\r
-void old_keyfile_warning(void)\r
-{\r
- /*\r
- * This should never happen on OS X. We hope.\r
- */\r
-}\r
-\r
-static void connection_fatal_callback(void *ctx, int result)\r
-{\r
- SessionWindow *win = (SessionWindow *)ctx;\r
-\r
- [win endSession:FALSE];\r
-}\r
-\r
-void connection_fatal(void *frontend, char *p, ...)\r
-{\r
- SessionWindow *win = (SessionWindow *)frontend;\r
- va_list ap;\r
- char *msg;\r
- NSAlert *alert;\r
-\r
- va_start(ap, p);\r
- msg = dupvprintf(p, ap);\r
- va_end(ap);\r
-\r
- alert = [[NSAlert alloc] init];\r
- [alert setInformativeText:[NSString stringWithCString:msg]];\r
- [alert addButtonWithTitle:@"Proceed"];\r
- [win startAlert:alert withCallback:connection_fatal_callback\r
- andCtx:win];\r
-}\r