OSDN Git Service

Add PuTTY 0.61 to contrib directory.
[ffftp/ffftp.git] / contrib / putty / MACOSX / OSXDLG.M
diff --git a/contrib/putty/MACOSX/OSXDLG.M b/contrib/putty/MACOSX/OSXDLG.M
new file mode 100644 (file)
index 0000000..9502914
--- /dev/null
@@ -0,0 +1,509 @@
+/*\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