Initial checkin: beta 0.43
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 8 Jan 1999 13:02:13 +0000 (13:02 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 8 Jan 1999 13:02:13 +0000 (13:02 +0000)
git-svn-id: svn://svn.tartarus.org/sgt/putty@11 cda61777-01e9-0310-a592-d414129be87e

20 files changed:
Makefile [new file with mode: 0644]
misc.c [new file with mode: 0644]
noise.c [new file with mode: 0644]
putty.h [new file with mode: 0644]
putty.ico [new file with mode: 0644]
resource.h [new file with mode: 0644]
ssh.c [new file with mode: 0644]
ssh.h [new file with mode: 0644]
sshcrc.c [new file with mode: 0644]
sshdes.c [new file with mode: 0644]
sshmd5.c [new file with mode: 0644]
sshrand.c [new file with mode: 0644]
sshrsa.c [new file with mode: 0644]
sshsha.c [new file with mode: 0644]
telnet.c [new file with mode: 0644]
terminal.c [new file with mode: 0644]
win_res.h [new file with mode: 0644]
win_res.rc [new file with mode: 0644]
windlg.c [new file with mode: 0644]
window.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..86611be
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,56 @@
+# Makefile for PuTTY. Use `FWHACK=/DFWHACK' to cause the firewall hack
+# to be built in. (requires rebuild of ssh.obj only)
+
+CFLAGS = /nologo /W3 /YX /O2 /Yd /D_X86_ /D_WINDOWS /DDEBUG /ML /Fd
+
+.c.obj:
+       cl $(FWHACK) $(CFLAGS) /c $*.c
+
+OBJS1 = window.obj windlg.obj terminal.obj telnet.obj misc.obj noise.obj
+OBJS2 = ssh.obj sshcrc.obj sshdes.obj sshmd5.obj sshrsa.obj sshrand.obj
+OBJS3 = sshsha.obj
+RESRC = win_res.res
+LIBS1 = advapi32.lib user32.lib gdi32.lib
+LIBS2 = wsock32.lib comctl32.lib comdlg32.lib
+
+putty.exe: $(OBJS1) $(OBJS2) $(OBJS3) $(RESRC) link.rsp
+       link /debug -out:putty.exe @link.rsp
+
+puttyd.exe: $(OBJS1) $(OBJS2) $(OBJS3) $(RESRC) link.rsp
+       link /debug -out:puttyd.exe @link.rsp
+
+link.rsp: makefile
+       echo /nologo /subsystem:windows > link.rsp
+       echo $(OBJS1) >> link.rsp
+       echo $(OBJS2) >> link.rsp
+       echo $(OBJS3) >> link.rsp
+       echo $(RESRC) >> link.rsp
+       echo $(LIBS1) >> link.rsp
+       echo $(LIBS2) >> link.rsp
+
+window.obj: window.c putty.h win_res.h
+windlg.obj: windlg.c putty.h ssh.h win_res.h
+terminal.obj: terminal.c putty.h
+telnet.obj: telnet.c putty.h
+misc.obj: misc.c putty.h
+noise.obj: noise.c putty.h ssh.h
+ssh.obj: ssh.c ssh.h putty.h
+sshcrc.obj: sshcrc.c ssh.h
+sshdes.obj: sshdes.c ssh.h
+sshmd5.obj: sshmd5.c ssh.h
+sshrsa.obj: sshrsa.c ssh.h
+sshsha.obj: sshsha.c ssh.h
+sshrand.obj: sshrand.c ssh.h
+
+win_res.res: win_res.rc win_res.h putty.ico
+       rc $(FWHACK) -r win_res.rc
+
+clean:
+       del *.obj
+       del *.exe
+       del *.res
+       del *.pch
+       del *.aps
+       del *.ilk
+       del *.pdb
+       del link.rsp
diff --git a/misc.c b/misc.c
new file mode 100644 (file)
index 0000000..370eea0
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,67 @@
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "putty.h"
+
+/* My own versions of malloc, realloc and free. Because I want malloc and
+ * realloc to bomb out and exit the program if they run out of memory,
+ * realloc to reliably call malloc if passed a NULL pointer, and free
+ * to reliably do nothing if passed a NULL pointer. Of course we can also
+ * put trace printouts in, if we need to. */
+
+#ifdef MALLOC_LOG
+static FILE *fp = NULL;
+
+void mlog(char *file, int line) {
+    if (!fp)
+       fp = fopen("putty_mem.log", "w");
+    if (fp)
+       fprintf (fp, "%s:%d: ", file, line);
+}
+#endif
+
+void *safemalloc(size_t size) {
+    void *p = malloc (size);
+    if (!p) {
+       MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error",
+                  MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
+       exit(1);
+    }
+#ifdef MALLOC_LOG
+    if (fp)
+       fprintf(fp, "malloc(%d) returns %p\n", size, p);
+#endif
+    return p;
+}
+
+void *saferealloc(void *ptr, size_t size) {
+    void *p;
+    if (!ptr)
+       p = malloc (size);
+    else
+       p = realloc (ptr, size);
+    if (!p) {
+       MessageBox(NULL, "Out of memory!", "PuTTY Fatal Error",
+                  MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
+       exit(1);
+    }
+#ifdef MALLOC_LOG
+    if (fp)
+       fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p);
+#endif
+    return p;
+}
+
+void safefree(void *ptr) {
+    if (ptr) {
+#ifdef MALLOC_LOG
+       if (fp)
+           fprintf(fp, "free(%p)\n", ptr);
+#endif
+       free (ptr);
+    }
+#ifdef MALLOC_LOG
+    else if (fp)
+       fprintf(fp, "freeing null pointer - no action taken\n");
+#endif
+}
diff --git a/noise.c b/noise.c
new file mode 100644 (file)
index 0000000..cf9bba5
--- /dev/null
+++ b/noise.c
@@ -0,0 +1,148 @@
+/*
+ * Noise generation for PuTTY's cryptographic random number
+ * generator.
+ */
+
+#include <windows.h>
+#include <stdio.h>
+
+#include "putty.h"
+#include "ssh.h"
+
+static char seedpath[2*MAX_PATH+10] = "\0";
+
+/*
+ * Find the random seed file path and store it in `seedpath'.
+ */
+static void get_seedpath(void) {
+    HKEY rkey;
+    DWORD type, size;
+
+    size = sizeof(seedpath);
+
+    if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey)==ERROR_SUCCESS) {
+       int ret = RegQueryValueEx(rkey, "RandSeedFile",
+                                 0, &type, seedpath, &size);
+       if (ret != ERROR_SUCCESS || type != REG_SZ)
+           seedpath[0] = '\0';
+       RegCloseKey(rkey);
+    } else
+       seedpath[0] = '\0';
+
+    if (!seedpath[0]) {
+       int len, ret;
+
+       len = GetEnvironmentVariable("HOMEDRIVE", seedpath, sizeof(seedpath));
+       ret = GetEnvironmentVariable("HOMEPATH", seedpath+len,
+                                     sizeof(seedpath)-len);
+       if (ret == 0) {                /* probably win95; store in \WINDOWS */
+           GetWindowsDirectory(seedpath, sizeof(seedpath));
+           len = strlen(seedpath);
+       } else
+           len += ret;
+       strcpy(seedpath+len, "\\PUTTY.RND");
+    }
+}
+
+/*
+ * This function is called once, at PuTTY startup, and will do some
+ * seriously silly things like listing directories and getting disk
+ * free space and a process snapshot.
+ */
+
+void noise_get_heavy(void (*func) (void *, int)) {
+    HANDLE srch;
+    HANDLE seedf;
+    WIN32_FIND_DATA finddata;
+    char winpath[MAX_PATH+3];
+
+    GetWindowsDirectory(winpath, sizeof(winpath));
+    strcat(winpath, "\\*");
+    srch = FindFirstFile(winpath, &finddata);
+    if (srch != INVALID_HANDLE_VALUE) {
+       do {
+           func(&finddata, sizeof(finddata));
+       } while (FindNextFile(srch, &finddata));
+       FindClose(srch);
+    }
+
+    if (!seedpath[0])
+       get_seedpath();
+
+    seedf = CreateFile(seedpath, GENERIC_READ,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                      NULL, OPEN_EXISTING, 0, NULL);
+
+    if (seedf) {
+       while (1) {
+           char buf[1024];
+           DWORD len;
+
+           if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
+               func(buf, len);
+           else
+               break;
+       }
+       CloseHandle(seedf);
+    }
+}
+
+void random_save_seed(void) {
+    HANDLE seedf;
+
+    if (!seedpath[0])
+       get_seedpath();
+
+    seedf = CreateFile(seedpath, GENERIC_WRITE, 0,
+                      NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (seedf) {
+       int len;
+       DWORD lenwritten;
+       void *data;
+
+       random_get_savedata(&data, &len);
+       WriteFile(seedf, data, len, &lenwritten, NULL);
+       CloseHandle(seedf);
+    }
+}
+
+/*
+ * This function is called every time the random pool needs
+ * stirring, and will acquire the system time in all available
+ * forms and the battery status.
+ */
+void noise_get_light(void (*func) (void *, int)) {
+    SYSTEMTIME systime;
+    DWORD adjust[2];
+    BOOL rubbish;
+    SYSTEM_POWER_STATUS pwrstat;
+
+    GetSystemTime(&systime);
+    func(&systime, sizeof(systime));
+
+    GetSystemTimeAdjustment(&adjust[0], &adjust[1], &rubbish);
+    func(&adjust, sizeof(adjust));
+
+    if (GetSystemPowerStatus(&pwrstat))
+       func(&pwrstat, sizeof(pwrstat));
+}
+
+/*
+ * This function is called on every keypress or mouse move, and
+ * will add the current Windows time and performance monitor
+ * counter to the noise pool. It gets the scan code or mouse
+ * position passed in.
+ */
+void noise_ultralight(DWORD data) {
+    DWORD wintime;
+    LARGE_INTEGER perftime;
+
+    random_add_noise(&data, sizeof(DWORD));
+
+    wintime = GetTickCount();
+    random_add_noise(&wintime, sizeof(DWORD));
+
+    if (QueryPerformanceCounter(&perftime))
+       random_add_noise(&perftime, sizeof(perftime));
+}
diff --git a/putty.h b/putty.h
new file mode 100644 (file)
index 0000000..c570392
--- /dev/null
+++ b/putty.h
@@ -0,0 +1,252 @@
+#ifndef PUTTY_PUTTY_H
+#define PUTTY_PUTTY_H
+
+#define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY"
+
+/*
+ * Global variables. Most modules declare these `extern', but
+ * window.c will do `#define PUTTY_DO_GLOBALS' before including this
+ * module, and so will get them properly defined.
+ */
+#ifdef PUTTY_DO_GLOBALS
+#define GLOBAL
+#else
+#define GLOBAL extern
+#endif
+
+#define ATTR_ACTCURS 0x80000000UL      /* active cursor (block) */
+#define ATTR_PASCURS 0x40000000UL      /* passive cursor (box) */
+#define ATTR_INVALID 0x20000000UL
+#define ATTR_WRAPPED 0x10000000UL
+
+#define ATTR_ASCII   0x00000000UL      /* normal ASCII charset ESC ( B */
+#define ATTR_GBCHR   0x00100000UL      /* UK variant   charset ESC ( A */
+#define ATTR_LINEDRW 0x00200000UL      /* line drawing charset ESC ( 0 */
+
+#define ATTR_BOLD    0x00000100UL
+#define ATTR_UNDER   0x00000200UL
+#define ATTR_REVERSE 0x00000400UL
+#define ATTR_FGMASK  0x0000F000UL
+#define ATTR_BGMASK  0x000F0000UL
+#define ATTR_FGSHIFT 12
+#define ATTR_BGSHIFT 16
+
+#define ATTR_DEFAULT 0x00098000UL
+#define ATTR_DEFFG   0x00008000UL
+#define ATTR_DEFBG   0x00090000UL
+#define ATTR_CUR_XOR 0x000BA000UL
+#define ERASE_CHAR   (ATTR_DEFAULT | ' ')
+#define ATTR_MASK    0xFFFFFF00UL
+#define CHAR_MASK    0x000000FFUL
+
+typedef HDC Context;
+#define SEL_NL { 13, 10 }
+
+GLOBAL int rows, cols, savelines;
+
+GLOBAL int font_width, font_height;
+
+#define INBUF_SIZE 2048
+#define INBUF_MASK (INBUF_SIZE-1)
+GLOBAL unsigned char inbuf[INBUF_SIZE];
+GLOBAL int inbuf_head, inbuf_reap;
+
+#define OUTBUF_SIZE 2048
+#define OUTBUF_MASK (OUTBUF_SIZE-1)
+GLOBAL unsigned char outbuf[OUTBUF_SIZE];
+GLOBAL int outbuf_head, outbuf_reap;
+
+GLOBAL int has_focus;
+
+GLOBAL int app_cursor_keys, app_keypad_keys;
+
+#define WM_NETEVENT  (WM_USER + 1)
+
+typedef enum {
+    TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,
+    TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF
+} Telnet_Special;
+
+typedef enum {
+    MB_NOTHING, MB_SELECT, MB_EXTEND, MB_PASTE
+} Mouse_Button;
+
+typedef enum {
+    MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE
+} Mouse_Action;
+
+typedef enum {
+    VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN
+} VT_Mode;
+
+typedef struct {
+    char *(*init) (HWND hwnd, char *host, int port, char **realhost);
+    int (*msg) (WPARAM wParam, LPARAM lParam);
+    void (*send) (char *buf, int len);
+    void (*size) (void);
+    void (*special) (Telnet_Special code);
+} Backend;
+
+GLOBAL Backend *back;
+
+typedef struct {
+    /* Basic options */
+    char host[512];
+    int port;
+    enum { PROT_TELNET, PROT_SSH } protocol;
+    int close_on_exit;
+    /* Telnet options */
+    char termtype[32];
+    char termspeed[32];
+    char environ[1024];                       /* VAR\tvalue\0VAR\tvalue\0\0 */
+    char username[32];
+    int rfc_environ;
+    /* Keyboard options */
+    int bksp_is_delete;
+    int rxvt_homeend;
+    int linux_funkeys;
+    int app_cursor;
+    int app_keypad;
+    /* Terminal options */
+    int savelines;
+    int dec_om;
+    int wrap_mode;
+    int win_name_always;
+    int width, height;
+    char font[64];
+    int fontisbold;
+    int fontheight;
+    VT_Mode vtmode;
+    /* Colour options */
+    int try_palette;
+    int bold_colour;
+    unsigned char colours[22][3];
+    /* Selection options */
+    int mouse_is_xterm;
+    short wordness[256];
+} Config;
+
+GLOBAL Config cfg;
+
+/*
+ * Exports from window.c.
+ */
+void request_resize (int, int);
+void do_text (Context, int, int, char *, int, unsigned long);
+void set_title (char *);
+void set_icon (char *);
+void set_sbar (int, int, int);
+Context get_ctx();
+void free_ctx (Context);
+void palette_set (int, int, int, int);
+void palette_reset (void);
+void write_clip (void *, int);
+void get_clip (void **, int *);
+void optimised_move (int, int, int);
+void fatalbox (char *, ...);
+void beep (void);
+#define OPTIMISE_IS_SCROLL 1
+
+/*
+ * Exports from noise.c.
+ */
+void noise_get_heavy(void (*func) (void *, int));
+void noise_get_light(void (*func) (void *, int));
+void noise_ultralight(DWORD data);
+void random_save_seed(void);
+
+/*
+ * Exports from windlg.c.
+ */
+int do_config (void);
+int do_reconfig (HWND);
+void do_defaults (char *);
+void lognegot (char *);
+void shownegot (HWND);
+void showabout (HWND);
+void verify_ssh_host_key(char *host, struct RSAKey *key);
+
+/*
+ * Exports from terminal.c.
+ */
+
+void term_init (void);
+void term_size (int, int, int);
+void term_out (void);
+void term_paint (Context, int, int, int, int);
+void term_scroll (int, int);
+void term_pwron (void);
+void term_clrsb (void);
+void term_mouse (Mouse_Button, Mouse_Action, int, int);
+void term_deselect (void);
+void term_update (void);
+void term_invalidate(void);
+
+/*
+ * Exports from telnet.c.
+ */
+
+Backend telnet_backend;
+
+/*
+ * Exports from ssh.c.
+ */
+
+Backend ssh_backend;
+
+/*
+ * Exports from sshrand.c.
+ */
+
+void random_add_noise(void *noise, int length);
+void random_init(void);
+int random_byte(void);
+void random_get_savedata(void **data, int *len);
+
+/*
+ * Exports from misc.c.
+ */
+
+/* #define MALLOC_LOG  do this if you suspect putty of leaking memory */
+#ifdef MALLOC_LOG
+#define smalloc(z) (mlog(__FILE__,__LINE__), safemalloc(z))
+#define srealloc(y,z) (mlog(__FILE__,__LINE__), saferealloc(y,z))
+#define sfree(z) (mlog(__FILE__,__LINE__), safefree(z))
+void mlog(char *, int);
+#else
+#define smalloc safemalloc
+#define srealloc saferealloc
+#define sfree safefree
+#endif
+
+void *safemalloc(size_t);
+void *saferealloc(void *, size_t);
+void safefree(void *);
+
+/*
+ * A debug system.
+ */
+#ifdef DEBUG
+#include <stdarg.h>
+#define debug(x) (dprintf x)
+static void dprintf(char *fmt, ...) {
+    char buf[2048];
+    DWORD dw;
+    va_list ap;
+    static int gotconsole = 0;
+
+    if (!gotconsole) {
+       AllocConsole();
+       gotconsole = 1;
+    }
+
+    va_start(ap, fmt);
+    vsprintf(buf, fmt, ap);
+    WriteFile (GetStdHandle(STD_OUTPUT_HANDLE), buf, strlen(buf), &dw, NULL);
+    va_end(ap);
+}
+#else
+#define debug(x)
+#endif
+
+#endif
diff --git a/putty.ico b/putty.ico
new file mode 100644 (file)
index 0000000..c0c7cfc
Binary files /dev/null and b/putty.ico differ
diff --git a/resource.h b/resource.h
new file mode 100644 (file)
index 0000000..07d5008
--- /dev/null
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by win_res.rc
+//
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        101
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1000
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/ssh.c b/ssh.c
new file mode 100644 (file)
index 0000000..f80055c
--- /dev/null
+++ b/ssh.c
@@ -0,0 +1,694 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <winsock.h>
+
+#include "putty.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#include "ssh.h"
+
+/* Coroutine mechanics for the sillier bits of the code */
+#define crBegin1       static int crLine = 0;
+#define crBegin2       switch(crLine) { case 0:;
+#define crBegin                crBegin1; crBegin2;
+#define crFinish(z)    } crLine = 0; return (z)
+#define crFinishV      } crLine = 0; return
+#define crReturn(z)    \
+       do {\
+           crLine=__LINE__; return (z); case __LINE__:;\
+       } while (0)
+#define crReturnV      \
+       do {\
+           crLine=__LINE__; return; case __LINE__:;\
+       } while (0)
+#define crStop(z)      do{ crLine = 0; return (z); }while(0)
+#define crStopV                do{ crLine = 0; return; }while(0)
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+static SOCKET s = INVALID_SOCKET;
+
+static unsigned char session_key[32];
+static struct ssh_cipher *cipher = NULL;
+
+static char *savedhost;
+
+static enum {
+    SSH_STATE_BEFORE_SIZE,
+    SSH_STATE_INTERMED,
+    SSH_STATE_SESSION
+} ssh_state = SSH_STATE_BEFORE_SIZE;
+
+static int size_needed = FALSE;
+
+static void s_write (char *buf, int len) {
+    while (len > 0) {
+       int i = send (s, buf, len, 0);
+       if (i > 0)
+           len -= i, buf += i;
+    }
+}
+
+static int s_read (char *buf, int len) {
+    int ret = 0;
+    while (len > 0) {
+       int i = recv (s, buf, len, 0);
+       if (i > 0)
+           len -= i, buf += i, ret += i;
+       else
+           return i;
+    }
+    return ret;
+}
+
+static void c_write (char *buf, int len) {
+    while (len--) {
+       int new_head = (inbuf_head + 1) & INBUF_MASK;
+       int c = (unsigned char) *buf;
+       if (new_head != inbuf_reap) {
+           inbuf[inbuf_head] = *buf++;
+           inbuf_head = new_head;
+       }
+    }
+}
+
+struct Packet {
+    long length;
+    int type;
+    unsigned long crc;
+    unsigned char *data;
+    unsigned char *body;
+    long maxlen;
+};
+
+static struct Packet pktin = { 0, 0, 0, NULL, 0 };
+static struct Packet pktout = { 0, 0, 0, NULL, 0 };
+
+static void ssh_protocol(unsigned char *in, int inlen, int ispkt);
+static void ssh_size(void);
+
+static void ssh_gotdata(unsigned char *data, int datalen) {
+    static long len, biglen, to_read;
+    static unsigned char c, *p;
+    static int i, pad;
+    static char padding[8];
+    static unsigned char word[4];
+
+    crBegin;
+    while (1) {
+       for (i = len = 0; i < 4; i++) {
+           while (datalen == 0)
+               crReturnV;
+           len = (len << 8) + *data;
+           data++, datalen--;
+       }
+
+       pad = 8 - (len%8);
+
+       biglen = len + pad;
+
+       len -= 5;                      /* type and CRC */
+
+       pktin.length = len;
+       if (pktin.maxlen < biglen) {
+           pktin.maxlen = biglen;
+           pktin.data = (pktin.data == NULL ? malloc(biglen) :
+                       realloc(pktin.data, biglen));
+           if (!pktin.data)
+               fatalbox("Out of memory");
+       }
+
+       p = pktin.data, to_read = biglen;
+       while (to_read > 0) {
+           static int chunk;
+           chunk = to_read;
+           while (datalen == 0)
+               crReturnV;
+           if (chunk > datalen)
+               chunk = datalen;
+           memcpy(p, data, chunk);
+           data += chunk;
+           datalen -= chunk;
+           p += chunk;
+           to_read -= chunk;
+       }
+
+       if (cipher)
+           cipher->decrypt(pktin.data, biglen);
+
+       pktin.type = pktin.data[pad];
+       pktin.body = pktin.data+pad+1;
+
+       if (pktin.type == 36) {        /* SSH_MSG_DEBUG */
+           /* FIXME: log it */
+       } else
+           ssh_protocol(NULL, 0, 1);
+    }
+    crFinishV;
+}
+
+static void s_wrpkt_start(int type, int len) {
+    int pad, biglen;
+
+    len += 5;                         /* type and CRC */
+    pad = 8 - (len%8);
+    biglen = len + pad;
+
+    pktout.length = len-5;
+    if (pktout.maxlen < biglen) {
+       pktout.maxlen = biglen;
+       pktout.data = (pktout.data == NULL ? malloc(biglen) :
+                      realloc(pktout.data, biglen));
+       if (!pktout.data)
+           fatalbox("Out of memory");
+    }
+
+    pktout.type = type;
+    pktout.body = pktout.data+4+pad+1;
+}
+
+static void s_wrpkt(void) {
+    int pad, len, biglen, i;
+    unsigned long crc;
+
+    len = pktout.length + 5;          /* type and CRC */
+    pad = 8 - (len%8);
+    biglen = len + pad;
+
+    pktout.body[-1] = pktout.type;
+    for (i=0; i<pad; i++)
+       pktout.data[i+4] = random_byte();
+    crc = crc32(pktout.data+4, biglen-4);
+
+    pktout.data[biglen+0] = (unsigned char) ((crc >> 24) & 0xFF);
+    pktout.data[biglen+1] = (unsigned char) ((crc >> 16) & 0xFF);
+    pktout.data[biglen+2] = (unsigned char) ((crc >> 8) & 0xFF);
+    pktout.data[biglen+3] = (unsigned char) (crc & 0xFF);
+
+    pktout.data[0] = (len >> 24) & 0xFF;
+    pktout.data[1] = (len >> 16) & 0xFF;
+    pktout.data[2] = (len >> 8) & 0xFF;
+    pktout.data[3] = len & 0xFF;
+
+    if (cipher)
+       cipher->encrypt(pktout.data+4, biglen);
+
+    s_write(pktout.data, biglen+4);
+}
+
+static int do_ssh_init(void) {
+    char c;
+    char version[10];
+    char vstring[40];
+    int i;
+
+#ifdef FWHACK
+    i = 0;
+    while (s_read(&c, 1) == 1) {
+       if (c == 'S' && i < 2) i++;
+       else if (c == 'S' && i == 2) i = 2;
+       else if (c == 'H' && i == 2) break;
+       else i = 0;
+    }
+#else
+    if (s_read(&c,1) != 1 || c != 'S') return 0;
+    if (s_read(&c,1) != 1 || c != 'S') return 0;
+    if (s_read(&c,1) != 1 || c != 'H') return 0;
+#endif
+    if (s_read(&c,1) != 1 || c != '-') return 0;
+    i = 0;
+    while (1) {
+       if (s_read(&c,1) != 1)
+           return 0;
+       if (i >= 0) {
+           if (c == '-') {
+               version[i] = '\0';
+               i = -1;
+           } else if (i < sizeof(version)-1)
+               version[i++] = c;
+       }
+       else if (c == '\n')
+           break;
+    }
+
+    sprintf(vstring, "SSH-%s-7.7.7\n",
+           (strcmp(version, "1.5") <= 0 ? version : "1.5"));
+    s_write(vstring, strlen(vstring));
+}
+
+static void ssh_protocol(unsigned char *in, int inlen, int ispkt) {
+    int i, j, len;
+    unsigned char session_id[16];
+    unsigned char *rsabuf, *keystr1, *keystr2;
+    unsigned char cookie[8];
+    struct RSAKey servkey, hostkey;
+    struct MD5Context md5c;
+
+    extern struct ssh_cipher ssh_3des;
+
+    crBegin;
+
+    random_init();
+
+    while (!ispkt)
+       crReturnV;
+
+    if (pktin.type != 2)
+       fatalbox("Public key packet not received");
+
+    memcpy(cookie, pktin.body, 8);
+
+    MD5Init(&md5c);
+
+    i = makekey(pktin.body+8, &servkey, &keystr1);
+
+    j = makekey(pktin.body+8+i, &hostkey, &keystr2);
+
+    MD5Update(&md5c, keystr2, hostkey.bytes);
+    MD5Update(&md5c, keystr1, servkey.bytes);
+    MD5Update(&md5c, pktin.body, 8);
+
+    MD5Final(session_id, &md5c);
+
+    for (i=0; i<32; i++)
+       session_key[i] = random_byte();
+
+    len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
+
+    rsabuf = malloc(len);
+    if (!rsabuf)
+       fatalbox("Out of memory");
+
+    verify_ssh_host_key(savedhost, &hostkey);
+
+    for (i=0; i<32; i++) {
+       rsabuf[i] = session_key[i];
+       if (i < 16)
+           rsabuf[i] ^= session_id[i];
+    }
+
+    if (hostkey.bytes > servkey.bytes) {
+       rsaencrypt(rsabuf, 32, &servkey);
+       rsaencrypt(rsabuf, servkey.bytes, &hostkey);
+    } else {
+       rsaencrypt(rsabuf, 32, &hostkey);
+       rsaencrypt(rsabuf, hostkey.bytes, &servkey);
+    }
+
+    s_wrpkt_start(3, len+15);
+    pktout.body[0] = 3;                       /* SSH_CIPHER_3DES */
+    memcpy(pktout.body+1, cookie, 8);
+    pktout.body[9] = (len*8) >> 8;
+    pktout.body[10] = (len*8) & 0xFF;
+    memcpy(pktout.body+11, rsabuf, len);
+    pktout.body[len+11] = pktout.body[len+12] = 0;   /* protocol flags */
+    pktout.body[len+13] = pktout.body[len+14] = 0;
+    s_wrpkt();
+
+    free(rsabuf);
+
+    cipher = &ssh_3des;
+    cipher->sesskey(session_key);
+
+    do { crReturnV; } while (!ispkt);
+
+    if (pktin.type != 14)
+       fatalbox("Encryption not successfully enabled");
+
+    fflush(stdout);
+    {
+       static char username[100];
+       static int pos = 0;
+       static char c;
+       if (!*cfg.username) {
+           c_write("login as: ", 10);
+           while (pos >= 0) {
+               do { crReturnV; } while (ispkt);
+               while (inlen--) switch (c = *in++) {
+                 case 10: case 13:
+                   username[pos] = 0;
+                   pos = -1;
+                   break;
+                 case 8: case 127:
+                   if (pos > 0) {
+                       c_write("\b \b", 3);
+                       pos--;
+                   }
+                   break;
+                 case 21: case 27:
+                   while (pos > 0) {
+                       c_write("\b \b", 3);
+                       pos--;
+                   }
+                   break;
+                 case 3: case 4:
+                   random_save_seed();
+                   exit(0);
+                   break;
+                 default:
+                   if (c >= ' ' && c <= '~' && pos < 40) {
+                       username[pos++] = c;
+                       c_write(&c, 1);
+                   }
+                   break;
+               }
+           }
+           c_write("\r\n", 2);
+           username[strcspn(username, "\n\r")] = '\0';
+       } else {
+           char stuff[200];
+           strncpy(username, cfg.username, 99);
+           username[99] = '\0';
+           sprintf(stuff, "Sent username \"%s\".\r\n", username);
+           c_write(stuff, strlen(stuff));
+       }
+       s_wrpkt_start(4, 4+strlen(username));
+       pktout.body[0] = pktout.body[1] = pktout.body[2] = 0;
+       pktout.body[3] = strlen(username);
+       memcpy(pktout.body+4, username, strlen(username));
+       s_wrpkt();
+    }
+
+    do { crReturnV; } while (!ispkt);
+
+    while (pktin.type == 15) {
+       static char password[100];
+       static int pos;
+       static char c;
+       c_write("password: ", 10);
+       pos = 0;
+       while (pos >= 0) {
+           do { crReturnV; } while (ispkt);
+           while (inlen--) switch (c = *in++) {
+             case 10: case 13:
+               password[pos] = 0;
+               pos = -1;
+               break;
+             case 8: case 127:
+               if (pos > 0)
+                   pos--;
+               break;
+             case 21: case 27:
+               pos = 0;
+               break;
+             case 3: case 4:
+               random_save_seed();
+               exit(0);
+               break;
+             default:
+               if (c >= ' ' && c <= '~' && pos < 40)
+                   password[pos++] = c;
+               break;
+           }
+       }
+       c_write("\r\n", 2);
+       s_wrpkt_start(9, 4+strlen(password));
+       pktout.body[0] = pktout.body[1] = pktout.body[2] = 0;
+       pktout.body[3] = strlen(password);
+       memcpy(pktout.body+4, password, strlen(password));
+       s_wrpkt();
+       memset(password, 0, strlen(password));
+       do { crReturnV; } while (!ispkt);
+       if (pktin.type == 15) {
+           c_write("Access denied\r\n", 15);
+       } else if (pktin.type != 14) {
+           fatalbox("Strange packet received, type %d", pktin.type);
+       }
+    }
+
+    i = strlen(cfg.termtype);
+    s_wrpkt_start(10, i+5*4+1);
+    pktout.body[0] = (i >> 24) & 0xFF;
+    pktout.body[1] = (i >> 16) & 0xFF;
+    pktout.body[2] = (i >> 8) & 0xFF;
+    pktout.body[3] = i & 0xFF;
+    memcpy(pktout.body+4, cfg.termtype, i);
+    i += 4;
+    pktout.body[i++] = (rows >> 24) & 0xFF;
+    pktout.body[i++] = (rows >> 16) & 0xFF;
+    pktout.body[i++] = (rows >> 8) & 0xFF;
+    pktout.body[i++] = rows & 0xFF;
+    pktout.body[i++] = (cols >> 24) & 0xFF;
+    pktout.body[i++] = (cols >> 16) & 0xFF;
+    pktout.body[i++] = (cols >> 8) & 0xFF;
+    pktout.body[i++] = cols & 0xFF;
+    memset(pktout.body+i, 0, 9);       /* 0 pixwidth, 0 pixheight, 0.b endofopt */
+    s_wrpkt();
+    ssh_state = SSH_STATE_INTERMED;
+    do { crReturnV; } while (!ispkt);
+    if (pktin.type != 14 && pktin.type != 15) {
+       fatalbox("Protocol confusion");
+    } else if (pktin.type == 15) {
+       c_write("Server refused to allocate pty\r\n", 32);
+    }
+
+    s_wrpkt_start(12, 0);
+    s_wrpkt();
+
+    ssh_state = SSH_STATE_SESSION;
+    if (size_needed)
+       ssh_size();
+
+    while (1) {
+       crReturnV;
+       if (ispkt) {
+           if (pktin.type == 17 || pktin.type == 18) {
+               long len = 0;
+               for (i = 0; i < 4; i++)
+                   len = (len << 8) + pktin.body[i];
+               c_write(pktin.body+4, len);
+           } else if (pktin.type == 1) {
+               /* SSH_MSG_DISCONNECT: do nothing */
+           } else if (pktin.type == 14) {
+               /* SSH_MSG_SUCCESS: may be from EXEC_SHELL on some servers */
+           } else if (pktin.type == 15) {
+               /* SSH_MSG_FAILURE: may be from EXEC_SHELL on some servers
+                * if no pty is available or in other odd cases. Ignore */
+           } else if (pktin.type == 20) {
+               /* EXITSTATUS */
+               s_wrpkt_start(33, 0);
+               s_wrpkt();
+           } else {
+               fatalbox("Strange packet received: type %d", pktin.type);
+           }
+       } else {
+           s_wrpkt_start(16, 4+inlen);
+           pktout.body[0] = (inlen >> 24) & 0xFF;
+           pktout.body[1] = (inlen >> 16) & 0xFF;
+           pktout.body[2] = (inlen >> 8) & 0xFF;
+           pktout.body[3] = inlen & 0xFF;
+           memcpy(pktout.body+4, in, inlen);
+           s_wrpkt();
+       }
+    }
+
+    crFinishV;
+}
+
+/*
+ * Called to set up the connection. Will arrange for WM_NETEVENT
+ * messages to be passed to the specified window, whose window
+ * procedure should then call telnet_msg().
+ *
+ * Returns an error message, or NULL on success.
+ *
+ * Also places the canonical host name into `realhost'.
+ */
+static char *ssh_init (HWND hwnd, char *host, int port, char **realhost) {
+    SOCKADDR_IN addr;
+    struct hostent *h;
+    unsigned long a;
+#ifdef FWHACK
+    char *FWhost;
+    int FWport;
+#endif
+
+    savedhost = malloc(1+strlen(host));
+    if (!savedhost)
+       fatalbox("Out of memory");
+    strcpy(savedhost, host);
+
+#ifdef FWHACK
+    FWhost = host;
+    FWport = port;
+    host = FWSTR;
+    port = 23;
+#endif
+
+    /*
+     * Try to find host.
+     */
+    if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) {
+       if ( (h = gethostbyname(host)) == NULL)
+           switch (WSAGetLastError()) {
+             case WSAENETDOWN: return "Network is down";
+             case WSAHOST_NOT_FOUND: case WSANO_DATA:
+               return "Host does not exist";
+             case WSATRY_AGAIN: return "Host not found";
+             default: return "gethostbyname: unknown error";
+           }
+       memcpy (&a, h->h_addr, sizeof(a));
+       *realhost = h->h_name;
+    } else
+       *realhost = host;
+#ifdef FWHACK
+    *realhost = FWhost;
+#endif
+    a = ntohl(a);
+
+    if (port < 0)
+       port = 22;                     /* default ssh port */
+
+    /*
+     * Open socket.
+     */
+    s = socket(AF_INET, SOCK_STREAM, 0);
+    if (s == INVALID_SOCKET)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         case WSAEAFNOSUPPORT: return "TCP/IP support not present";
+         default: return "socket(): unknown error";
+       }
+
+    /*
+     * Bind to local address.
+     */
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    addr.sin_port = htons(0);
+    if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         default: return "bind(): unknown error";
+       }
+
+    /*
+     * Connect to remote address.
+     */
+    addr.sin_addr.s_addr = htonl(a);
+    addr.sin_port = htons((short)port);
+    if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         case WSAECONNREFUSED: return "Connection refused";
+         case WSAENETUNREACH: return "Network is unreachable";
+         case WSAEHOSTUNREACH: return "No route to host";
+         default: return "connect(): unknown error";
+       }
+
+#ifdef FWHACK
+    send(s, "connect ", 8, 0);
+    send(s, FWhost, strlen(FWhost), 0);
+    {
+       char buf[20];
+       sprintf(buf, " %d\n", FWport);
+       send (s, buf, strlen(buf), 0);
+    }
+#endif
+
+    if (!do_ssh_init())
+       return "Protocol initialisation error";
+
+    if (WSAAsyncSelect (s, hwnd, WM_NETEVENT, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         default: return "WSAAsyncSelect(): unknown error";
+       }
+
+    return NULL;
+}
+
+/*
+ * Process a WM_NETEVENT message. Will return 0 if the connection
+ * has closed, or <0 for a socket error.
+ */
+static int ssh_msg (WPARAM wParam, LPARAM lParam) {
+    int ret;
+    char buf[256];
+
+    if (s == INVALID_SOCKET)          /* how the hell did we get here?! */
+       return -5000;
+
+    if (WSAGETSELECTERROR(lParam) != 0)
+       return -WSAGETSELECTERROR(lParam);
+
+    switch (WSAGETSELECTEVENT(lParam)) {
+      case FD_READ:
+       ret = recv(s, buf, sizeof(buf), 0);
+       if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
+           return 1;
+       if (ret < 0)                   /* any _other_ error */
+           return -10000-WSAGetLastError();
+       if (ret == 0) {
+           s = INVALID_SOCKET;
+           return 0;                  /* can't happen, in theory */
+       }
+       ssh_gotdata (buf, ret);
+       return 1;
+      case FD_CLOSE:
+       s = INVALID_SOCKET;
+       return 0;
+    }
+    return 1;                         /* shouldn't happen, but WTF */
+}
+
+/*
+ * Called to send data down the Telnet connection.
+ */
+static void ssh_send (char *buf, int len) {
+    if (s == INVALID_SOCKET)
+       return;
+
+    ssh_protocol(buf, len, 0);
+}
+
+/*
+ * Called to set the size of the window from Telnet's POV.
+ */
+static void ssh_size(void) {
+    switch (ssh_state) {
+      case SSH_STATE_BEFORE_SIZE:
+       break;                         /* do nothing */
+      case SSH_STATE_INTERMED:
+       size_needed = TRUE;            /* buffer for later */
+       break;
+      case SSH_STATE_SESSION:
+       s_wrpkt_start(11, 16);
+       pktout.body[0] = (rows >> 24) & 0xFF;
+       pktout.body[1] = (rows >> 16) & 0xFF;
+       pktout.body[2] = (rows >> 8) & 0xFF;
+       pktout.body[3] = rows & 0xFF;
+       pktout.body[4] = (cols >> 24) & 0xFF;
+       pktout.body[5] = (cols >> 16) & 0xFF;
+       pktout.body[6] = (cols >> 8) & 0xFF;
+       pktout.body[7] = cols & 0xFF;
+       memset(pktout.body+8, 0, 8);
+       s_wrpkt();
+    }
+}
+
+/*
+ * (Send Telnet special codes)
+ */
+static void ssh_special (Telnet_Special code) {
+    /* do nothing */
+}
+
+Backend ssh_backend = {
+    ssh_init,
+    ssh_msg,
+    ssh_send,
+    ssh_size,
+    ssh_special
+};
diff --git a/ssh.h b/ssh.h
new file mode 100644 (file)
index 0000000..260d062
--- /dev/null
+++ b/ssh.h
@@ -0,0 +1,39 @@
+struct RSAKey {
+    int bits;
+    int bytes;
+    void *modulus;
+    void *exponent;
+};
+
+int makekey(unsigned char *data, struct RSAKey *result,
+           unsigned char **keystr);
+void rsaencrypt(unsigned char *data, int length, struct RSAKey *key);
+int rsastr_len(struct RSAKey *key);
+void rsastr_fmt(char *str, struct RSAKey *key);
+
+typedef unsigned int word32;
+typedef unsigned int uint32;
+
+unsigned long crc32(const unsigned char *s, unsigned int len);
+
+struct MD5Context {
+        uint32 buf[4];
+        uint32 bits[2];
+        unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+               unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+struct ssh_cipher {
+    void (*sesskey)(unsigned char *key);
+    void (*encrypt)(unsigned char *blk, int len);
+    void (*decrypt)(unsigned char *blk, int len);
+};
+
+void SHATransform(word32 *digest, word32 *data);
+
+int random_byte(void);
+void random_add_noise(void *noise, int length);
diff --git a/sshcrc.c b/sshcrc.c
new file mode 100644 (file)
index 0000000..946da7b
--- /dev/null
+++ b/sshcrc.c
@@ -0,0 +1,111 @@
+  /* ============================================================= */
+  /*  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or       */
+  /*  code or tables extracted from it, as desired without restriction.     */
+  /*                                                                        */
+  /*  First, the polynomial itself and its table of feedback terms.  The    */
+  /*  polynomial is                                                         */
+  /*  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0   */
+  /*                                                                        */
+  /*  Note that we take it "backwards" and put the highest-order term in    */
+  /*  the lowest-order bit.  The X^32 term is "implied"; the LSB is the     */
+  /*  X^31 term, etc.  The X^0 term (usually shown as "+1") results in      */
+  /*  the MSB being 1.                                                      */
+  /*                                                                        */
+  /*  Note that the usual hardware shift register implementation, which     */
+  /*  is what we're using (we're merely optimizing it by doing eight-bit    */
+  /*  chunks at a time) shifts bits into the lowest-order term.  In our     */
+  /*  implementation, that means shifting towards the right.  Why do we     */
+  /*  do it this way?  Because the calculated CRC must be transmitted in    */
+  /*  order from highest-order term to lowest-order term.  UARTs transmit   */
+  /*  characters in order from LSB to MSB.  By storing the CRC this way,    */
+  /*  we hand it to the UART in the order low-byte to high-byte; the UART   */
+  /*  sends each low-bit to hight-bit; and the result is transmission bit   */
+  /*  by bit from highest- to lowest-order term without requiring any bit   */
+  /*  shuffling on our part.  Reception works similarly.                    */
+  /*                                                                        */
+  /*  The feedback terms table consists of 256, 32-bit entries.  Notes:     */
+  /*                                                                        */
+  /*      The table can be generated at runtime if desired; code to do so   */
+  /*      is shown later.  It might not be obvious, but the feedback        */
+  /*      terms simply represent the results of eight shift/xor opera-      */
+  /*      tions for all combinations of data and CRC register values.       */
+  /*                                                                        */
+  /*      The values must be right-shifted by eight bits by the "updcrc"    */
+  /*      logic; the shift must be unsigned (bring in zeroes).  On some     */
+  /*      hardware you could probably optimize the shift in assembler by    */
+  /*      using byte-swap instructions.                                     */
+  /*      polynomial $edb88320                                              */
+  /*                                                                        */
+  /*  --------------------------------------------------------------------  */
+
+static unsigned long crc32_tab[] = {
+      0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+      0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+      0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+      0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+      0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+      0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+      0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+      0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+      0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+      0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+      0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+      0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+      0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+      0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+      0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+      0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+      0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+      0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+      0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+      0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+      0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+      0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+      0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+      0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+      0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+      0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+      0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+      0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+      0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+      0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+      0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+      0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+      0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+      0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+      0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+      0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+      0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+      0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+      0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+      0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+      0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+      0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+      0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+      0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+      0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+      0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+      0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+      0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+      0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+      0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+      0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+      0x2d02ef8dL
+   };
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+unsigned long crc32(const unsigned char *s, unsigned int len)
+{
+  unsigned int i;
+  unsigned long crc32val;
+  
+  crc32val = 0;
+  for (i = 0;  i < len;  i ++)
+    {
+      crc32val =
+       crc32_tab[(crc32val ^ s[i]) & 0xff] ^
+         (crc32val >> 8);
+    }
+  return crc32val;
+}
diff --git a/sshdes.c b/sshdes.c
new file mode 100644 (file)
index 0000000..897457a
--- /dev/null
+++ b/sshdes.c
@@ -0,0 +1,768 @@
+#include <assert.h>
+#include "ssh.h"
+
+/*
+
+DES implementation; 1995 Tatu Ylonen <ylo@cs.hut.fi>
+
+This implementation is derived from libdes-3.06, which is copyright
+(c) 1993 Eric Young, and distributed under the GNU GPL or the ARTISTIC licence
+(at the user's option).  The original distribution can be found e.g. from
+ftp://ftp.dsi.unimi.it/pub/security/crypt/libdes/libdes-3.06.tar.gz.
+
+This implementation is distributed under the same terms.  See
+libdes-README, libdes-ARTISTIC, and libdes-COPYING for more
+information.
+
+*/
+
+/*
+ * $Id: sshdes.c,v 1.1 1999/01/08 13:02:12 simon Exp $
+ * $Log: sshdes.c,v $
+ * Revision 1.1  1999/01/08 13:02:12  simon
+ * Initial checkin: beta 0.43
+ *
+ * Revision 1.1.1.1  1996/02/18 21:38:11  ylo
+ *     Imported ssh-1.2.13.
+ *
+ * Revision 1.2  1995/07/13  01:22:57  ylo
+ *     Added cvs log.
+ *
+ * $Endlog$
+ */
+
+typedef struct
+{
+  word32 key_schedule[32];
+} DESContext;
+
+/* Sets the des key for the context.  Initializes the context.  The least
+   significant bit of each byte of the key is ignored as parity. */
+static void des_set_key(unsigned char *key, DESContext *ks);
+
+/* Encrypts 32 bits in l,r, and stores the result in output[0] and output[1].
+   Performs encryption if encrypt is non-zero, and decryption if it is zero.
+   The key context must have been initialized previously with des_set_key. */
+static void des_encrypt(word32 l, word32 r, word32 *output, DESContext *ks,
+                       int encrypt);
+
+/* Encrypts len bytes from src to dest in CBC modes.  Len must be a multiple
+   of 8.  iv will be modified at end to a value suitable for continuing
+   encryption. */
+static void des_cbc_encrypt(DESContext *ks, unsigned char *iv, unsigned char *dest,
+                    const unsigned char *src, unsigned int len);
+
+/* Decrypts len bytes from src to dest in CBC modes.  Len must be a multiple
+   of 8.  iv will be modified at end to a value suitable for continuing
+   decryption. */
+static void des_cbc_decrypt(DESContext *ks, unsigned char *iv, unsigned char *dest,
+                    const unsigned char *src, unsigned int len);
+
+/* Encrypts in CBC mode using triple-DES. */
+static void des_3cbc_encrypt(DESContext *ks1, unsigned char *iv1, 
+                     DESContext *ks2, unsigned char *iv2,
+                     DESContext *ks3, unsigned char *iv3,
+                     unsigned char *dest, const unsigned char *src,
+                     unsigned int len);
+
+/* Decrypts in CBC mode using triple-DES. */
+static void des_3cbc_decrypt(DESContext *ks1, unsigned char *iv1,
+                     DESContext *ks2, unsigned char *iv2,
+                     DESContext *ks3, unsigned char *iv3,
+                     unsigned char *dest, const unsigned char *src,
+                     unsigned int len);
+
+#define GET_32BIT_LSB_FIRST(cp) \
+  (((unsigned long)(unsigned char)(cp)[0]) | \
+  ((unsigned long)(unsigned char)(cp)[1] << 8) | \
+  ((unsigned long)(unsigned char)(cp)[2] << 16) | \
+  ((unsigned long)(unsigned char)(cp)[3] << 24))
+
+#define PUT_32BIT_LSB_FIRST(cp, value) do { \
+  (cp)[0] = (value); \
+  (cp)[1] = (value) >> 8; \
+  (cp)[2] = (value) >> 16; \
+  (cp)[3] = (value) >> 24; } while (0)
+
+/*
+
+DES implementation; 1995 Tatu Ylonen <ylo@cs.hut.fi>
+
+This implementation is derived from libdes-3.06, which is copyright
+(c) 1993 Eric Young, and distributed under the GNU GPL or the ARTISTIC licence
+(at the user's option).  The original distribution can be found e.g. from
+ftp://ftp.dsi.unimi.it/pub/security/crypt/libdes/libdes-3.06.tar.gz.
+
+This implementation is distributed under the same terms.  See
+libdes-README, libdes-ARTISTIC, and libdes-COPYING for more
+information.
+
+A description of the DES algorithm can be found in every modern book on
+cryptography and data security, including the following:
+
+  Bruce Schneier: Applied Cryptography.  John Wiley & Sons, 1994.
+
+  Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to 
+    Computer Security.  Prentice-Hall, 1989.
+
+  Man Young Rhee: Cryptography and Secure Data Communications.  McGraw-Hill, 
+    1994.
+
+*/
+
+/*
+ * $Id: sshdes.c,v 1.1 1999/01/08 13:02:12 simon Exp $
+ * $Log: sshdes.c,v $
+ * Revision 1.1  1999/01/08 13:02:12  simon
+ * Initial checkin: beta 0.43
+ *
+ * Revision 1.1.1.1  1996/02/18 21:38:11  ylo
+ *     Imported ssh-1.2.13.
+ *
+ * Revision 1.2  1995/07/13  01:22:25  ylo
+ *     Added cvs log.
+ *
+ * $Endlog$
+ */
+
+/* Table for key generation.  This used to be in sk.h. */
+/* Copyright (C) 1993 Eric Young - see README for more details */
+static const word32 des_skb[8][64]={
+/* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+{ 0x00000000,0x00000010,0x20000000,0x20000010,
+0x00010000,0x00010010,0x20010000,0x20010010,
+0x00000800,0x00000810,0x20000800,0x20000810,
+0x00010800,0x00010810,0x20010800,0x20010810,
+0x00000020,0x00000030,0x20000020,0x20000030,
+0x00010020,0x00010030,0x20010020,0x20010030,
+0x00000820,0x00000830,0x20000820,0x20000830,
+0x00010820,0x00010830,0x20010820,0x20010830,
+0x00080000,0x00080010,0x20080000,0x20080010,
+0x00090000,0x00090010,0x20090000,0x20090010,
+0x00080800,0x00080810,0x20080800,0x20080810,
+0x00090800,0x00090810,0x20090800,0x20090810,
+0x00080020,0x00080030,0x20080020,0x20080030,
+0x00090020,0x00090030,0x20090020,0x20090030,
+0x00080820,0x00080830,0x20080820,0x20080830,
+0x00090820,0x00090830,0x20090820,0x20090830 },
+/* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
+{ 0x00000000,0x02000000,0x00002000,0x02002000,
+0x00200000,0x02200000,0x00202000,0x02202000,
+0x00000004,0x02000004,0x00002004,0x02002004,
+0x00200004,0x02200004,0x00202004,0x02202004,
+0x00000400,0x02000400,0x00002400,0x02002400,
+0x00200400,0x02200400,0x00202400,0x02202400,
+0x00000404,0x02000404,0x00002404,0x02002404,
+0x00200404,0x02200404,0x00202404,0x02202404,
+0x10000000,0x12000000,0x10002000,0x12002000,
+0x10200000,0x12200000,0x10202000,0x12202000,
+0x10000004,0x12000004,0x10002004,0x12002004,
+0x10200004,0x12200004,0x10202004,0x12202004,
+0x10000400,0x12000400,0x10002400,0x12002400,
+0x10200400,0x12200400,0x10202400,0x12202400,
+0x10000404,0x12000404,0x10002404,0x12002404,
+0x10200404,0x12200404,0x10202404,0x12202404 },
+/* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
+{ 0x00000000,0x00000001,0x00040000,0x00040001,
+0x01000000,0x01000001,0x01040000,0x01040001,
+0x00000002,0x00000003,0x00040002,0x00040003,
+0x01000002,0x01000003,0x01040002,0x01040003,
+0x00000200,0x00000201,0x00040200,0x00040201,
+0x01000200,0x01000201,0x01040200,0x01040201,
+0x00000202,0x00000203,0x00040202,0x00040203,
+0x01000202,0x01000203,0x01040202,0x01040203,
+0x08000000,0x08000001,0x08040000,0x08040001,
+0x09000000,0x09000001,0x09040000,0x09040001,
+0x08000002,0x08000003,0x08040002,0x08040003,
+0x09000002,0x09000003,0x09040002,0x09040003,
+0x08000200,0x08000201,0x08040200,0x08040201,
+0x09000200,0x09000201,0x09040200,0x09040201,
+0x08000202,0x08000203,0x08040202,0x08040203,
+0x09000202,0x09000203,0x09040202,0x09040203 },
+/* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
+{ 0x00000000,0x00100000,0x00000100,0x00100100,
+0x00000008,0x00100008,0x00000108,0x00100108,
+0x00001000,0x00101000,0x00001100,0x00101100,
+0x00001008,0x00101008,0x00001108,0x00101108,
+0x04000000,0x04100000,0x04000100,0x04100100,
+0x04000008,0x04100008,0x04000108,0x04100108,
+0x04001000,0x04101000,0x04001100,0x04101100,
+0x04001008,0x04101008,0x04001108,0x04101108,
+0x00020000,0x00120000,0x00020100,0x00120100,
+0x00020008,0x00120008,0x00020108,0x00120108,
+0x00021000,0x00121000,0x00021100,0x00121100,
+0x00021008,0x00121008,0x00021108,0x00121108,
+0x04020000,0x04120000,0x04020100,0x04120100,
+0x04020008,0x04120008,0x04020108,0x04120108,
+0x04021000,0x04121000,0x04021100,0x04121100,
+0x04021008,0x04121008,0x04021108,0x04121108 },
+/* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
+{ 0x00000000,0x10000000,0x00010000,0x10010000,
+0x00000004,0x10000004,0x00010004,0x10010004,
+0x20000000,0x30000000,0x20010000,0x30010000,
+0x20000004,0x30000004,0x20010004,0x30010004,
+0x00100000,0x10100000,0x00110000,0x10110000,
+0x00100004,0x10100004,0x00110004,0x10110004,
+0x20100000,0x30100000,0x20110000,0x30110000,
+0x20100004,0x30100004,0x20110004,0x30110004,
+0x00001000,0x10001000,0x00011000,0x10011000,
+0x00001004,0x10001004,0x00011004,0x10011004,
+0x20001000,0x30001000,0x20011000,0x30011000,
+0x20001004,0x30001004,0x20011004,0x30011004,
+0x00101000,0x10101000,0x00111000,0x10111000,
+0x00101004,0x10101004,0x00111004,0x10111004,
+0x20101000,0x30101000,0x20111000,0x30111000,
+0x20101004,0x30101004,0x20111004,0x30111004 },
+/* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
+{ 0x00000000,0x08000000,0x00000008,0x08000008,
+0x00000400,0x08000400,0x00000408,0x08000408,
+0x00020000,0x08020000,0x00020008,0x08020008,
+0x00020400,0x08020400,0x00020408,0x08020408,
+0x00000001,0x08000001,0x00000009,0x08000009,
+0x00000401,0x08000401,0x00000409,0x08000409,
+0x00020001,0x08020001,0x00020009,0x08020009,
+0x00020401,0x08020401,0x00020409,0x08020409,
+0x02000000,0x0A000000,0x02000008,0x0A000008,
+0x02000400,0x0A000400,0x02000408,0x0A000408,
+0x02020000,0x0A020000,0x02020008,0x0A020008,
+0x02020400,0x0A020400,0x02020408,0x0A020408,
+0x02000001,0x0A000001,0x02000009,0x0A000009,
+0x02000401,0x0A000401,0x02000409,0x0A000409,
+0x02020001,0x0A020001,0x02020009,0x0A020009,
+0x02020401,0x0A020401,0x02020409,0x0A020409 },
+/* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
+{ 0x00000000,0x00000100,0x00080000,0x00080100,
+0x01000000,0x01000100,0x01080000,0x01080100,
+0x00000010,0x00000110,0x00080010,0x00080110,
+0x01000010,0x01000110,0x01080010,0x01080110,
+0x00200000,0x00200100,0x00280000,0x00280100,
+0x01200000,0x01200100,0x01280000,0x01280100,
+0x00200010,0x00200110,0x00280010,0x00280110,
+0x01200010,0x01200110,0x01280010,0x01280110,
+0x00000200,0x00000300,0x00080200,0x00080300,
+0x01000200,0x01000300,0x01080200,0x01080300,
+0x00000210,0x00000310,0x00080210,0x00080310,
+0x01000210,0x01000310,0x01080210,0x01080310,
+0x00200200,0x00200300,0x00280200,0x00280300,
+0x01200200,0x01200300,0x01280200,0x01280300,
+0x00200210,0x00200310,0x00280210,0x00280310,
+0x01200210,0x01200310,0x01280210,0x01280310 },
+/* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
+{ 0x00000000,0x04000000,0x00040000,0x04040000,
+0x00000002,0x04000002,0x00040002,0x04040002,
+0x00002000,0x04002000,0x00042000,0x04042000,
+0x00002002,0x04002002,0x00042002,0x04042002,
+0x00000020,0x04000020,0x00040020,0x04040020,
+0x00000022,0x04000022,0x00040022,0x04040022,
+0x00002020,0x04002020,0x00042020,0x04042020,
+0x00002022,0x04002022,0x00042022,0x04042022,
+0x00000800,0x04000800,0x00040800,0x04040800,
+0x00000802,0x04000802,0x00040802,0x04040802,
+0x00002800,0x04002800,0x00042800,0x04042800,
+0x00002802,0x04002802,0x00042802,0x04042802,
+0x00000820,0x04000820,0x00040820,0x04040820,
+0x00000822,0x04000822,0x00040822,0x04040822,
+0x00002820,0x04002820,0x00042820,0x04042820,
+0x00002822,0x04002822,0x00042822,0x04042822 }
+};
+
+/* Tables used for executing des.  This used to be in spr.h. */
+/* Copyright (C) 1993 Eric Young - see README for more details */
+static const word32 des_SPtrans[8][64]={
+/* nibble 0 */
+{ 0x00820200, 0x00020000, 0x80800000, 0x80820200,
+0x00800000, 0x80020200, 0x80020000, 0x80800000,
+0x80020200, 0x00820200, 0x00820000, 0x80000200,
+0x80800200, 0x00800000, 0x00000000, 0x80020000,
+0x00020000, 0x80000000, 0x00800200, 0x00020200,
+0x80820200, 0x00820000, 0x80000200, 0x00800200,
+0x80000000, 0x00000200, 0x00020200, 0x80820000,
+0x00000200, 0x80800200, 0x80820000, 0x00000000,
+0x00000000, 0x80820200, 0x00800200, 0x80020000,
+0x00820200, 0x00020000, 0x80000200, 0x00800200,
+0x80820000, 0x00000200, 0x00020200, 0x80800000,
+0x80020200, 0x80000000, 0x80800000, 0x00820000,
+0x80820200, 0x00020200, 0x00820000, 0x80800200,
+0x00800000, 0x80000200, 0x80020000, 0x00000000,
+0x00020000, 0x00800000, 0x80800200, 0x00820200,
+0x80000000, 0x80820000, 0x00000200, 0x80020200 },
+
+/* nibble 1 */
+{ 0x10042004, 0x00000000, 0x00042000, 0x10040000,
+0x10000004, 0x00002004, 0x10002000, 0x00042000,
+0x00002000, 0x10040004, 0x00000004, 0x10002000,
+0x00040004, 0x10042000, 0x10040000, 0x00000004,
+0x00040000, 0x10002004, 0x10040004, 0x00002000,
+0x00042004, 0x10000000, 0x00000000, 0x00040004,
+0x10002004, 0x00042004, 0x10042000, 0x10000004,
+0x10000000, 0x00040000, 0x00002004, 0x10042004,
+0x00040004, 0x10042000, 0x10002000, 0x00042004,
+0x10042004, 0x00040004, 0x10000004, 0x00000000,
+0x10000000, 0x00002004, 0x00040000, 0x10040004,
+0x00002000, 0x10000000, 0x00042004, 0x10002004,
+0x10042000, 0x00002000, 0x00000000, 0x10000004,
+0x00000004, 0x10042004, 0x00042000, 0x10040000,
+0x10040004, 0x00040000, 0x00002004, 0x10002000,
+0x10002004, 0x00000004, 0x10040000, 0x00042000 },
+
+/* nibble 2 */
+{ 0x41000000, 0x01010040, 0x00000040, 0x41000040,
+0x40010000, 0x01000000, 0x41000040, 0x00010040,
+0x01000040, 0x00010000, 0x01010000, 0x40000000,
+0x41010040, 0x40000040, 0x40000000, 0x41010000,
+0x00000000, 0x40010000, 0x01010040, 0x00000040,
+0x40000040, 0x41010040, 0x00010000, 0x41000000,
+0x41010000, 0x01000040, 0x40010040, 0x01010000,
+0x00010040, 0x00000000, 0x01000000, 0x40010040,
+0x01010040, 0x00000040, 0x40000000, 0x00010000,
+0x40000040, 0x40010000, 0x01010000, 0x41000040,
+0x00000000, 0x01010040, 0x00010040, 0x41010000,
+0x40010000, 0x01000000, 0x41010040, 0x40000000,
+0x40010040, 0x41000000, 0x01000000, 0x41010040,
+0x00010000, 0x01000040, 0x41000040, 0x00010040,
+0x01000040, 0x00000000, 0x41010000, 0x40000040,
+0x41000000, 0x40010040, 0x00000040, 0x01010000 },
+
+/* nibble 3 */
+{ 0x00100402, 0x04000400, 0x00000002, 0x04100402,
+0x00000000, 0x04100000, 0x04000402, 0x00100002,
+0x04100400, 0x04000002, 0x04000000, 0x00000402,
+0x04000002, 0x00100402, 0x00100000, 0x04000000,
+0x04100002, 0x00100400, 0x00000400, 0x00000002,
+0x00100400, 0x04000402, 0x04100000, 0x00000400,
+0x00000402, 0x00000000, 0x00100002, 0x04100400,
+0x04000400, 0x04100002, 0x04100402, 0x00100000,
+0x04100002, 0x00000402, 0x00100000, 0x04000002,
+0x00100400, 0x04000400, 0x00000002, 0x04100000,
+0x04000402, 0x00000000, 0x00000400, 0x00100002,
+0x00000000, 0x04100002, 0x04100400, 0x00000400,
+0x04000000, 0x04100402, 0x00100402, 0x00100000,
+0x04100402, 0x00000002, 0x04000400, 0x00100402,
+0x00100002, 0x00100400, 0x04100000, 0x04000402,
+0x00000402, 0x04000000, 0x04000002, 0x04100400 },
+
+/* nibble 4 */
+{ 0x02000000, 0x00004000, 0x00000100, 0x02004108,
+0x02004008, 0x02000100, 0x00004108, 0x02004000,
+0x00004000, 0x00000008, 0x02000008, 0x00004100,
+0x02000108, 0x02004008, 0x02004100, 0x00000000,
+0x00004100, 0x02000000, 0x00004008, 0x00000108,
+0x02000100, 0x00004108, 0x00000000, 0x02000008,
+0x00000008, 0x02000108, 0x02004108, 0x00004008,
+0x02004000, 0x00000100, 0x00000108, 0x02004100,
+0x02004100, 0x02000108, 0x00004008, 0x02004000,
+0x00004000, 0x00000008, 0x02000008, 0x02000100,
+0x02000000, 0x00004100, 0x02004108, 0x00000000,
+0x00004108, 0x02000000, 0x00000100, 0x00004008,
+0x02000108, 0x00000100, 0x00000000, 0x02004108,
+0x02004008, 0x02004100, 0x00000108, 0x00004000,
+0x00004100, 0x02004008, 0x02000100, 0x00000108,
+0x00000008, 0x00004108, 0x02004000, 0x02000008 },
+
+/* nibble 5 */
+{ 0x20000010, 0x00080010, 0x00000000, 0x20080800,
+0x00080010, 0x00000800, 0x20000810, 0x00080000,
+0x00000810, 0x20080810, 0x00080800, 0x20000000,
+0x20000800, 0x20000010, 0x20080000, 0x00080810,
+0x00080000, 0x20000810, 0x20080010, 0x00000000,
+0x00000800, 0x00000010, 0x20080800, 0x20080010,
+0x20080810, 0x20080000, 0x20000000, 0x00000810,
+0x00000010, 0x00080800, 0x00080810, 0x20000800,
+0x00000810, 0x20000000, 0x20000800, 0x00080810,
+0x20080800, 0x00080010, 0x00000000, 0x20000800,
+0x20000000, 0x00000800, 0x20080010, 0x00080000,
+0x00080010, 0x20080810, 0x00080800, 0x00000010,
+0x20080810, 0x00080800, 0x00080000, 0x20000810,
+0x20000010, 0x20080000, 0x00080810, 0x00000000,
+0x00000800, 0x20000010, 0x20000810, 0x20080800,
+0x20080000, 0x00000810, 0x00000010, 0x20080010 },
+
+/* nibble 6 */
+{ 0x00001000, 0x00000080, 0x00400080, 0x00400001,
+0x00401081, 0x00001001, 0x00001080, 0x00000000,
+0x00400000, 0x00400081, 0x00000081, 0x00401000,
+0x00000001, 0x00401080, 0x00401000, 0x00000081,
+0x00400081, 0x00001000, 0x00001001, 0x00401081,
+0x00000000, 0x00400080, 0x00400001, 0x00001080,
+0x00401001, 0x00001081, 0x00401080, 0x00000001,
+0x00001081, 0x00401001, 0x00000080, 0x00400000,
+0x00001081, 0x00401000, 0x00401001, 0x00000081,
+0x00001000, 0x00000080, 0x00400000, 0x00401001,
+0x00400081, 0x00001081, 0x00001080, 0x00000000,
+0x00000080, 0x00400001, 0x00000001, 0x00400080,
+0x00000000, 0x00400081, 0x00400080, 0x00001080,
+0x00000081, 0x00001000, 0x00401081, 0x00400000,
+0x00401080, 0x00000001, 0x00001001, 0x00401081,
+0x00400001, 0x00401080, 0x00401000, 0x00001001 },
+
+/* nibble 7 */
+{ 0x08200020, 0x08208000, 0x00008020, 0x00000000,
+0x08008000, 0x00200020, 0x08200000, 0x08208020,
+0x00000020, 0x08000000, 0x00208000, 0x00008020,
+0x00208020, 0x08008020, 0x08000020, 0x08200000,
+0x00008000, 0x00208020, 0x00200020, 0x08008000,
+0x08208020, 0x08000020, 0x00000000, 0x00208000,
+0x08000000, 0x00200000, 0x08008020, 0x08200020,
+0x00200000, 0x00008000, 0x08208000, 0x00000020,
+0x00200000, 0x00008000, 0x08000020, 0x08208020,
+0x00008020, 0x08000000, 0x00000000, 0x00208000,
+0x08200020, 0x08008020, 0x08008000, 0x00200020,
+0x08208000, 0x00000020, 0x00200020, 0x08008000,
+0x08208020, 0x00200000, 0x08200000, 0x08000020,
+0x00208000, 0x00008020, 0x08008020, 0x08200000,
+0x00000020, 0x08208000, 0x00208020, 0x00000000,
+0x08000000, 0x08200020, 0x00008000, 0x00208020 }};
+
+/* Some stuff that used to be in des_locl.h.  Heavily modified. */
+       /* IP and FP
+        * The problem is more of a geometric problem that random bit fiddling.
+        0  1  2  3  4  5  6  7      62 54 46 38 30 22 14  6
+        8  9 10 11 12 13 14 15      60 52 44 36 28 20 12  4
+       16 17 18 19 20 21 22 23      58 50 42 34 26 18 10  2
+       24 25 26 27 28 29 30 31  to  56 48 40 32 24 16  8  0
+
+       32 33 34 35 36 37 38 39      63 55 47 39 31 23 15  7
+       40 41 42 43 44 45 46 47      61 53 45 37 29 21 13  5
+       48 49 50 51 52 53 54 55      59 51 43 35 27 19 11  3
+       56 57 58 59 60 61 62 63      57 49 41 33 25 17  9  1
+
+       The output has been subject to swaps of the form
+       0 1 -> 3 1 but the odd and even bits have been put into
+       2 3    2 0
+       different words.  The main trick is to remember that
+       t=((l>>size)^r)&(mask);
+       r^=t;
+       l^=(t<<size);
+       can be used to swap and move bits between words.
+
+       So l =  0  1  2  3  r = 16 17 18 19
+               4  5  6  7      20 21 22 23
+               8  9 10 11      24 25 26 27
+              12 13 14 15      28 29 30 31
+       becomes (for size == 2 and mask == 0x3333)
+          t =   2^16  3^17 -- --   l =  0  1 16 17  r =  2  3 18 19
+                6^20  7^21 -- --        4  5 20 21       6  7 22 23
+               10^24 11^25 -- --        8  9 24 25      10 11 24 25
+               14^28 15^29 -- --       12 13 28 29      14 15 28 29
+
+       Thanks for hints from Richard Outerbridge - he told me IP&FP
+       could be done in 15 xor, 10 shifts and 5 ands.
+       When I finally started to think of the problem in 2D
+       I first got ~42 operations without xors.  When I remembered
+       how to use xors :-) I got it to its final state.
+       */
+#define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
+       (b)^=(t),\
+       (a)^=((t)<<(n)))
+
+#define IP(l,r,t) \
+       PERM_OP(r,l,t, 4,0x0f0f0f0f); \
+       PERM_OP(l,r,t,16,0x0000ffff); \
+       PERM_OP(r,l,t, 2,0x33333333); \
+       PERM_OP(l,r,t, 8,0x00ff00ff); \
+       PERM_OP(r,l,t, 1,0x55555555);
+
+#define FP(l,r,t) \
+       PERM_OP(l,r,t, 1,0x55555555); \
+       PERM_OP(r,l,t, 8,0x00ff00ff); \
+       PERM_OP(l,r,t, 2,0x33333333); \
+       PERM_OP(r,l,t,16,0x0000ffff); \
+       PERM_OP(l,r,t, 4,0x0f0f0f0f);
+
+#define D_ENCRYPT(L,R,S)       \
+       u=(R^s[S  ]); \
+       t=R^s[S+1]; \
+       t=((t>>4)+(t<<28)); \
+       L^=     des_SPtrans[1][(t    )&0x3f]| \
+               des_SPtrans[3][(t>> 8)&0x3f]| \
+               des_SPtrans[5][(t>>16)&0x3f]| \
+               des_SPtrans[7][(t>>24)&0x3f]| \
+               des_SPtrans[0][(u    )&0x3f]| \
+               des_SPtrans[2][(u>> 8)&0x3f]| \
+               des_SPtrans[4][(u>>16)&0x3f]| \
+               des_SPtrans[6][(u>>24)&0x3f];
+
+/* This part is based on code that used to be in ecb_enc.c. */
+/* Copyright (C) 1993 Eric Young - see README for more details */
+
+static void des_encrypt(word32 l, word32 r, word32 *output, DESContext *ks, 
+                int encrypt)
+{
+  register word32 t,u;
+  register int i;
+  register word32 *s;
+
+  s = ks->key_schedule;
+
+  IP(l,r,t);
+  /* Things have been modified so that the initial rotate is
+   * done outside the loop.  This required the
+   * des_SPtrans values in sp.h to be rotated 1 bit to the right.
+   * One perl script later and things have a 5% speed up on a sparc2.
+   * Thanks to Richard Outerbridge <71755.204@CompuServe.COM>
+   * for pointing this out. */
+  t=(r<<1)|(r>>31);
+  r=(l<<1)|(l>>31);
+  l=t;
+  
+  /* I don't know if it is worth the effort of loop unrolling the
+   * inner loop */
+  if (encrypt)
+    {
+      for (i=0; i<32; i+=4)
+       {
+         D_ENCRYPT(l,r,i+0); /*  1 */
+         D_ENCRYPT(r,l,i+2); /*  2 */
+       }
+    }
+  else
+    {
+      for (i=30; i>0; i-=4)
+       {
+         D_ENCRYPT(l,r,i-0); /* 16 */
+         D_ENCRYPT(r,l,i-2); /* 15 */
+       }
+    }
+  l=(l>>1)|(l<<31);
+  r=(r>>1)|(r<<31);
+  
+  FP(r,l,t);
+  output[0]=l;
+  output[1]=r;
+}
+
+/* Code based on set_key.c. */
+/* Copyright (C) 1993 Eric Young - see README for more details */
+
+#define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
+       (a)=(a)^(t)^(t>>(16-(n))))
+
+static void des_set_key(unsigned char *key, DESContext *ks)
+{
+  register word32 c, d, t, s, shifts;
+  register int i;
+  register word32 *schedule;
+
+  schedule = ks->key_schedule;
+
+  c = GET_32BIT_LSB_FIRST(key);
+  d = GET_32BIT_LSB_FIRST(key + 4);
+
+  /* I now do it in 47 simple operations :-)
+   * Thanks to John Fletcher (john_fletcher@lccmail.ocf.llnl.gov)
+   * for the inspiration. :-) */
+  PERM_OP(d,c,t,4,0x0f0f0f0f);
+  HPERM_OP(c,t,-2,0xcccc0000);
+  HPERM_OP(d,t,-2,0xcccc0000);
+  PERM_OP(d,c,t,1,0x55555555);
+  PERM_OP(c,d,t,8,0x00ff00ff);
+  PERM_OP(d,c,t,1,0x55555555);
+  d = ((d & 0xff) << 16) | (d & 0xff00) |
+    ((d >> 16) & 0xff) | ((c >> 4) & 0xf000000);
+  c&=0x0fffffff;
+  
+  shifts = 0x7efc;
+  for (i=0; i < 16; i++)
+    {
+      if (shifts & 1)
+       { c=((c>>2)|(c<<26)); d=((d>>2)|(d<<26)); }
+      else
+       { c=((c>>1)|(c<<27)); d=((d>>1)|(d<<27)); }
+      shifts >>= 1;
+      c&=0x0fffffff;
+      d&=0x0fffffff;
+
+      /* could be a few less shifts but I am to lazy at this
+       * point in time to investigate */
+
+      s = des_skb[0][ (c    )&0x3f                ] |
+         des_skb[1][((c>> 6)&0x03)|((c>> 7)&0x3c)] |
+         des_skb[2][((c>>13)&0x0f)|((c>>14)&0x30)] |
+         des_skb[3][((c>>20)&0x01)|((c>>21)&0x06)|((c>>22)&0x38)];
+
+      t = des_skb[4][ (d    )&0x3f                ] |
+         des_skb[5][((d>> 7)&0x03)|((d>> 8)&0x3c)] |
+         des_skb[6][ (d>>15)&0x3f                ] |
+         des_skb[7][((d>>21)&0x0f)|((d>>22)&0x30)];
+
+      /* table contained 0213 4657 */
+      *schedule++ = ((t << 16) | (s & 0xffff));
+      s = ((s >> 16) | (t & 0xffff0000));
+      *schedule++ = (s << 4) | (s >> 28);
+    }
+}
+
+static void des_cbc_encrypt(DESContext *ks, unsigned char *iv,
+                    unsigned char *dest, const unsigned char *src,
+                    unsigned int len)
+{
+  word32 iv0, iv1, out[2];
+  unsigned int i;
+  
+  assert((len & 7) == 0);
+
+  iv0 = GET_32BIT_LSB_FIRST(iv);
+  iv1 = GET_32BIT_LSB_FIRST(iv + 4);
+  
+  for (i = 0; i < len; i += 8)
+    {
+      iv0 ^= GET_32BIT_LSB_FIRST(src + i);
+      iv1 ^= GET_32BIT_LSB_FIRST(src + i + 4);
+      des_encrypt(iv0, iv1, out, ks, 1);
+      iv0 = out[0];
+      iv1 = out[1];
+      PUT_32BIT_LSB_FIRST(dest + i, iv0);
+      PUT_32BIT_LSB_FIRST(dest + i + 4, iv1);
+    }
+  PUT_32BIT_LSB_FIRST(iv, iv0);
+  PUT_32BIT_LSB_FIRST(iv + 4, iv1);
+}
+
+static void des_cbc_decrypt(DESContext *ks, unsigned char *iv,
+                    unsigned char *dest, const unsigned char *src,
+                    unsigned int len)
+{
+  word32 iv0, iv1, d0, d1, out[2];
+  unsigned int i;
+  
+  assert((len & 7) == 0);
+
+  iv0 = GET_32BIT_LSB_FIRST(iv);
+  iv1 = GET_32BIT_LSB_FIRST(iv + 4);
+  
+  for (i = 0; i < len; i += 8)
+    {
+      d0 = GET_32BIT_LSB_FIRST(src + i);
+      d1 = GET_32BIT_LSB_FIRST(src + i + 4);
+      des_encrypt(d0, d1, out, ks, 0);
+      iv0 ^= out[0];
+      iv1 ^= out[1];
+      PUT_32BIT_LSB_FIRST(dest + i, iv0);
+      PUT_32BIT_LSB_FIRST(dest + i + 4, iv1);
+      iv0 = d0;
+      iv1 = d1;
+    }
+  PUT_32BIT_LSB_FIRST(iv, iv0);
+  PUT_32BIT_LSB_FIRST(iv + 4, iv1);
+}
+
+static void des_3cbc_encrypt(DESContext *ks1, unsigned char *iv1, 
+                     DESContext *ks2, unsigned char *iv2,
+                     DESContext *ks3, unsigned char *iv3,
+                     unsigned char *dest, const unsigned char *src,
+                     unsigned int len)
+{
+  des_cbc_encrypt(ks1, iv1, dest, src, len);
+  des_cbc_decrypt(ks2, iv2, dest, dest, len);
+  des_cbc_encrypt(ks3, iv3, dest, dest, len);
+}
+
+static void des_3cbc_decrypt(DESContext *ks1, unsigned char *iv1, 
+                     DESContext *ks2, unsigned char *iv2,
+                     DESContext *ks3, unsigned char *iv3,
+                     unsigned char *dest, const unsigned char *src,
+                     unsigned int len)
+{
+  des_cbc_decrypt(ks3, iv3, dest, src, len);
+  des_cbc_encrypt(ks2, iv2, dest, dest, len);
+  des_cbc_decrypt(ks1, iv1, dest, dest, len);
+}
+
+DESContext ekey1, ekey2, ekey3;
+unsigned char eiv1[8], eiv2[8], eiv3[8];
+
+DESContext dkey1, dkey2, dkey3;
+unsigned char div1[8], div2[8], div3[8];
+
+static void des3_sesskey(unsigned char *key) {
+    des_set_key(key, &ekey1);
+    des_set_key(key+8, &ekey2);
+    des_set_key(key+16, &ekey3);
+    memset(eiv1, 0, sizeof(eiv1));
+    memset(eiv2, 0, sizeof(eiv2));
+    memset(eiv3, 0, sizeof(eiv3));
+    des_set_key(key, &dkey1);
+    des_set_key(key+8, &dkey2);
+    des_set_key(key+16, &dkey3);
+    memset(div1, 0, sizeof(div1));
+    memset(div2, 0, sizeof(div2));
+    memset(div3, 0, sizeof(div3));
+}
+
+static void des3_encrypt_blk(unsigned char *blk, int len) {
+    des_3cbc_encrypt(&ekey1, eiv1, &ekey2, eiv2, &ekey3, eiv3, blk, blk, len);
+}
+
+static void des3_decrypt_blk(unsigned char *blk, int len) {
+    des_3cbc_decrypt(&dkey1, div1, &dkey2, div2, &dkey3, div3, blk, blk, len);
+}
+
+struct ssh_cipher ssh_3des = {
+    des3_sesskey,
+    des3_encrypt_blk,
+    des3_decrypt_blk
+};
+
+#ifdef DES_TEST
+
+void des_encrypt_buf(DESContext *ks, unsigned char *out, 
+                    const unsigned char *in, int encrypt)
+{
+  word32 in0, in1, output[0];
+
+  in0 = GET_32BIT_LSB_FIRST(in);
+  in1 = GET_32BIT_LSB_FIRST(in + 4);
+  des_encrypt(in0, in1, output, ks, encrypt);
+  PUT_32BIT_LSB_FIRST(out, output[0]);
+  PUT_32BIT_LSB_FIRST(out + 4, output[1]);
+}
+
+int main(int ac, char **av)
+{
+  FILE *f;
+  char line[1024], *cp;
+  int i, value;
+  unsigned char key[8], data[8], result[8], output[8];
+  DESContext ks;
+
+  while (fgets(line, sizeof(line), stdin))
+    {
+      for (i = 0; i < 8; i++)
+       {
+         if (sscanf(line + 2 * i, "%02x", &value) != 1)
+           {
+             fprintf(stderr, "1st col, i = %d, line: %s", i, line);
+             exit(1);
+           }
+         key[i] = value;
+       }
+      for (i = 0; i < 8; i++)
+       {
+         if (sscanf(line + 2 * i + 17, "%02x", &value) != 1)
+           {
+             fprintf(stderr, "2nd col, i = %d, line: %s", i, line);
+             exit(1);
+           }
+         data[i] = value;
+       }
+      for (i = 0; i < 8; i++)
+       {
+         if (sscanf(line + 2 * i + 2*17, "%02x", &value) != 1)
+           {
+             fprintf(stderr, "3rd col, i = %d, line: %s", i, line);
+             exit(1);
+           }
+         result[i] = value;
+       }
+      des_set_key(key, &ks);
+      des_encrypt_buf(&ks, output, data, 1);
+      if (memcmp(output, result, 8) != 0)
+       fprintf(stderr, "Encrypt failed: %s", line);
+      des_encrypt_buf(&ks, output, result, 0);
+      if (memcmp(output, data, 8) != 0)
+       fprintf(stderr, "Decrypt failed: %s", line);
+    }
+  exit(0);
+}
+#endif /* DES_TEST */
+
diff --git a/sshmd5.c b/sshmd5.c
new file mode 100644 (file)
index 0000000..c242b83
--- /dev/null
+++ b/sshmd5.c
@@ -0,0 +1,249 @@
+/* This code has been heavily hacked by Tatu Ylonen <ylo@cs.hut.fi> to
+   make it compile on machines like Cray that don't have a 32 bit integer
+   type. */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "ssh.h"
+
+#define GET_32BIT_LSB_FIRST(cp) \
+  (((unsigned long)(unsigned char)(cp)[0]) | \
+  ((unsigned long)(unsigned char)(cp)[1] << 8) | \
+  ((unsigned long)(unsigned char)(cp)[2] << 16) | \
+  ((unsigned long)(unsigned char)(cp)[3] << 24))
+
+#define PUT_32BIT_LSB_FIRST(cp, value) do { \
+  (cp)[0] = (value); \
+  (cp)[1] = (value) >> 8; \
+  (cp)[2] = (value) >> 16; \
+  (cp)[3] = (value) >> 24; } while (0)
+
+void MD5Transform(uint32 buf[4], const unsigned char in[64]);
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = (t + ((uint32)len << 3)) & 0xffffffff) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy(p, buf, len);
+           return;
+       }
+       memcpy(p, buf, t);
+       MD5Transform(ctx->buf, ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy(ctx->in, buf, 64);
+       MD5Transform(ctx->buf, ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       MD5Transform(ctx->buf, ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+
+    /* Append length in bits and transform */
+    PUT_32BIT_LSB_FIRST(ctx->in + 56, ctx->bits[0]);
+    PUT_32BIT_LSB_FIRST(ctx->in + 60, ctx->bits[1]);
+
+    MD5Transform(ctx->buf, ctx->in);
+    PUT_32BIT_LSB_FIRST(digest, ctx->buf[0]);
+    PUT_32BIT_LSB_FIRST(digest + 4, ctx->buf[1]);
+    PUT_32BIT_LSB_FIRST(digest + 8, ctx->buf[2]);
+    PUT_32BIT_LSB_FIRST(digest + 12, ctx->buf[3]);
+    memset(ctx, 0, sizeof(ctx));       /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32 buf[4], const unsigned char inext[64])
+{
+    register word32 a, b, c, d, i;
+    word32 in[16];
+    
+    for (i = 0; i < 16; i++)
+      in[i] = GET_32BIT_LSB_FIRST(inext + 4 * i);
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
diff --git a/sshrand.c b/sshrand.c
new file mode 100644 (file)
index 0000000..17ef6e3
--- /dev/null
+++ b/sshrand.c
@@ -0,0 +1,169 @@
+/*
+ * cryptographic random number generator for PuTTY's ssh client
+ */
+
+#include "ssh.h"
+
+void noise_get_heavy(void (*func) (void *, int));
+void noise_get_light(void (*func) (void *, int));
+
+/*
+ * `pool' itself is a pool of random data which we actually use: we
+ * return bytes from `pool', at position `poolpos', until `poolpos'
+ * reaches the end of the pool. At this point we generate more
+ * random data, by adding noise, stirring well, and resetting
+ * `poolpos' to point to just past the beginning of the pool (not
+ * _the_ beginning, since otherwise we'd give away the whole
+ * contents of our pool, and attackers would just have to guess the
+ * next lot of noise).
+ *
+ * `incomingb' buffers acquired noise data, until it gets full, at
+ * which point the acquired noise is SHA'ed into `incoming' and
+ * `incomingb' is cleared. The noise in `incoming' is used as part
+ * of the noise for each stirring of the pool, in addition to local
+ * time, process listings, and other such stuff.
+ */
+
+#define HASHINPUT 64                  /* 64 bytes SHA input */
+#define HASHSIZE 20                   /* 160 bits SHA output */
+#define POOLSIZE 1200                 /* size of random pool */
+
+struct RandPool {
+    unsigned char pool[POOLSIZE];
+    int poolpos;
+
+    unsigned char incoming[HASHSIZE];
+
+    unsigned char incomingb[HASHINPUT];
+    int incomingpos;
+};
+
+static struct RandPool pool;
+
+void random_add_noise(void *noise, int length) {
+    unsigned char *p = noise;
+
+    while (length >= (HASHINPUT - pool.incomingpos)) {
+       memcpy(pool.incomingb + pool.incomingpos, p,
+              HASHINPUT - pool.incomingpos);
+       p += HASHINPUT - pool.incomingpos;
+       length -= HASHINPUT - pool.incomingpos;
+       SHATransform((word32 *)pool.incoming, (word32 *)pool.incomingb);
+       pool.incomingpos = 0;
+    }
+
+    memcpy(pool.incomingb, p, length);
+    pool.incomingpos = length;
+}
+
+void random_stir(void) {
+    word32 block[HASHINPUT/sizeof(word32)];
+    word32 digest[HASHSIZE/sizeof(word32)];
+    int i, j, k;
+
+    noise_get_light(random_add_noise);
+
+    SHATransform((word32 *)pool.incoming, (word32 *)pool.incomingb);
+    pool.incomingpos = 0;
+
+    /*
+     * Chunks of this code are blatantly endianness-dependent, but
+     * as it's all random bits anyway, WHO CARES?
+     */
+    memcpy(digest, pool.incoming, sizeof(digest));
+
+    /*
+     * Make two passes over the pool.
+     */
+    for (i = 0; i < 2; i++) {
+
+       /*
+        * We operate SHA in CFB mode, repeatedly adding the same
+        * block of data to the digest. But we're also fiddling
+        * with the digest-so-far, so this shouldn't be Bad or
+        * anything.
+        */
+       memcpy(block, pool.pool, sizeof(block));
+
+       /*
+        * Each pass processes the pool backwards in blocks of
+        * HASHSIZE, just so that in general we get the output of
+        * SHA before the corresponding input, in the hope that
+        * things will be that much less predictable that way
+        * round, when we subsequently return bytes ...
+        */
+       for (j = POOLSIZE; (j -= HASHSIZE) >= 0 ;) {
+           /*
+            * XOR the bit of the pool we're processing into the
+            * digest.
+            */
+
+           for (k = 0; k < sizeof(digest)/sizeof(*digest); k++)
+               digest[k] ^= ((word32 *)(pool.pool+j))[k];
+
+           /*
+            * Munge our unrevealed first block of the pool into
+            * it.
+            */
+           SHATransform(digest, block);
+
+           /*
+            * Stick the result back into the pool.
+            */
+
+           for (k = 0; k < sizeof(digest)/sizeof(*digest); k++)
+               ((word32 *)(pool.pool+j))[k] = digest[k];
+       }
+    }
+
+    /*
+     * Might as well save this value back into `incoming', just so
+     * there'll be some extra bizarreness there.
+     */
+    SHATransform(digest, block);
+    memcpy(digest, pool.incoming, sizeof(digest));
+
+    pool.poolpos = sizeof(pool.incoming);
+}
+
+static void random_add_heavynoise(void *noise, int length) {
+    unsigned char *p = noise;
+
+    while (length >= (POOLSIZE - pool.poolpos)) {
+       memcpy(pool.pool + pool.poolpos, p, POOLSIZE - pool.poolpos);
+       p += POOLSIZE - pool.poolpos;
+       length -= POOLSIZE - pool.poolpos;
+       random_stir();
+       pool.poolpos = 0;
+    }
+
+    memcpy(pool.pool, p, length);
+    pool.poolpos = length;
+}
+
+void random_init(void) {
+    memset(&pool, 0, sizeof(pool));    /* just to start with */
+
+    /*
+     * For noise_get_heavy, we temporarily use `poolpos' as the
+     * pointer for addition of noise, rather than extraction of
+     * random numbers.
+     */
+    pool.poolpos = 0;
+    noise_get_heavy(random_add_heavynoise);
+
+    random_stir();
+}
+
+int random_byte(void) {
+    if (pool.poolpos >= POOLSIZE)
+       random_stir();
+
+    return pool.pool[pool.poolpos++];
+}
+
+void random_get_savedata(void **data, int *len) {
+    random_stir();
+    *data = pool.pool+pool.poolpos;
+    *len = POOLSIZE/2;
+}
diff --git a/sshrsa.c b/sshrsa.c
new file mode 100644 (file)
index 0000000..70cf9bb
--- /dev/null
+++ b/sshrsa.c
@@ -0,0 +1,412 @@
+/*
+ * RSA implementation just sufficient for ssh client-side
+ * initialisation step
+ */
+
+/*#include <windows.h>
+#define RSADEBUG
+#define DLVL 2
+#include "stel.h"*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ssh.h"
+
+typedef unsigned short *Bignum;
+
+static unsigned short Zero[1] = { 0 };
+
+#if defined TESTMODE || defined RSADEBUG
+#ifndef DLVL
+#define DLVL 10000
+#endif
+#define debug(x) bndebug(#x,x)
+static int level = 0;
+static void bndebug(char *name, Bignum b) {
+    int i;
+    int w = 50-level-strlen(name)-5*b[0];
+    if (level >= DLVL)
+       return;
+    if (w < 0) w = 0;
+    dprintf("%*s%s%*s", level, "", name, w, "");
+    for (i=b[0]; i>0; i--)
+       dprintf(" %04x", b[i]);
+    dprintf("\n");
+}
+#define dmsg(x) do {if(level<DLVL){dprintf("%*s",level,"");printf x;}} while(0)
+#define enter(x) do { dmsg(x); level += 4; } while(0)
+#define leave(x) do { level -= 4; dmsg(x); } while(0)
+#else
+#define debug(x)
+#define dmsg(x)
+#define enter(x)
+#define leave(x)
+#endif
+
+static Bignum newbn(int length) {
+    Bignum b = malloc((length+1)*sizeof(unsigned short));
+    if (!b)
+       abort();                       /* FIXME */
+    b[0] = length;
+    return b;
+}
+
+static void freebn(Bignum b) {
+    free(b);
+}
+
+static int msb(Bignum r) {
+    int i;
+    int j;
+    unsigned short n;
+
+    for (i=r[0]; i>0; i--)
+       if (r[i])
+           break;
+
+    j = (i-1)*16;
+    n = r[i];
+    if (n & 0xFF00) j += 8, n >>= 8;
+    if (n & 0x00F0) j += 4, n >>= 4;
+    if (n & 0x000C) j += 2, n >>= 2;
+    if (n & 0x0002) j += 1, n >>= 1;
+
+    return j;
+}
+
+static void add(Bignum r1, Bignum r2, Bignum result) {
+    int i;
+    long stuff = 0;
+
+    enter((">add\n"));
+    debug(r1);
+    debug(r2);
+
+    for (i = 1 ;; i++) {
+       if (i <= r1[0])
+           stuff += r1[i];
+       if (i <= r2[0])
+           stuff += r2[i];
+       if (i <= result[0])
+           result[i] = stuff & 0xFFFFU;
+       if (i > r1[0] && i > r2[0] && i >= result[0])
+           break;
+       stuff >>= 16;
+    }
+
+    debug(result);
+    leave(("<add\n"));
+}
+
+static void sub(Bignum r1, Bignum r2, Bignum result) {
+    int i;
+    long stuff = 0;
+
+    enter((">sub\n"));
+    debug(r1);
+    debug(r2);
+
+    for (i = 1 ;; i++) {
+       if (i <= r1[0])
+           stuff += r1[i];
+       if (i <= r2[0])
+           stuff -= r2[i];
+       if (i <= result[0])
+           result[i] = stuff & 0xFFFFU;
+       if (i > r1[0] && i > r2[0] && i >= result[0])
+           break;
+       stuff = stuff<0 ? -1 : 0;
+    }
+
+    debug(result);
+    leave(("<sub\n"));
+}
+
+static int ge(Bignum r1, Bignum r2) {
+    int i;
+
+    enter((">ge\n"));
+    debug(r1);
+    debug(r2);
+
+    if (r1[0] < r2[0])
+       i = r2[0];
+    else
+       i = r1[0];
+
+    while (i > 0) {
+       unsigned short n1 = (i > r1[0] ? 0 : r1[i]);
+       unsigned short n2 = (i > r2[0] ? 0 : r2[i]);
+
+       if (n1 > n2) {
+           dmsg(("greater\n"));
+           leave(("<ge\n"));
+           return 1;                  /* r1 > r2 */
+       } else if (n1 < n2) {
+           dmsg(("less\n"));
+           leave(("<ge\n"));
+           return 0;                  /* r1 < r2 */
+       }
+
+       i--;
+    }
+
+    dmsg(("equal\n"));
+    leave(("<ge\n"));
+    return 1;                         /* r1 = r2 */
+}
+
+static void modmult(Bignum r1, Bignum r2, Bignum modulus, Bignum result) {
+    Bignum temp = newbn(modulus[0]+1);
+    Bignum tmp2 = newbn(modulus[0]+1);
+    int i;
+    int bit, bits, digit, smallbit;
+
+    enter((">modmult\n"));
+    debug(r1);
+    debug(r2);
+    debug(modulus);
+
+    for (i=1; i<=result[0]; i++)
+       result[i] = 0;                 /* result := 0 */
+    for (i=1; i<=temp[0]; i++)
+       temp[i] = (i > r2[0] ? 0 : r2[i]);   /* temp := r2 */
+
+    bits = 1+msb(r1);
+
+    for (bit = 0; bit < bits; bit++) {
+       digit = 1 + bit / 16;
+       smallbit = bit % 16;
+
+       debug(temp);
+       if (digit <= r1[0] && (r1[digit] & (1<<smallbit))) {
+           dmsg(("bit %d\n", bit));
+           add(temp, result, tmp2);
+           if (ge(tmp2, modulus))
+               sub(tmp2, modulus, result);
+           else
+               add(tmp2, Zero, result);
+           debug(result);
+       }
+
+       add(temp, temp, tmp2);
+       if (ge(tmp2, modulus))
+           sub(tmp2, modulus, temp);
+       else
+           add(tmp2, Zero, temp);
+    }
+
+    freebn(temp);
+    freebn(tmp2);
+
+    debug(result);
+    leave(("<modmult\n"));
+}
+
+static void modpow(Bignum r1, Bignum r2, Bignum modulus, Bignum result) {
+    Bignum temp = newbn(modulus[0]+1);
+    Bignum tmp2 = newbn(modulus[0]+1);
+    int i;
+    int bit, bits, digit, smallbit;
+
+    enter((">modpow\n"));
+    debug(r1);
+    debug(r2);
+    debug(modulus);
+
+    for (i=1; i<=result[0]; i++)
+       result[i] = (i==1);            /* result := 1 */
+    for (i=1; i<=temp[0]; i++)
+       temp[i] = (i > r1[0] ? 0 : r1[i]);   /* temp := r1 */
+
+    bits = 1+msb(r2);
+
+    for (bit = 0; bit < bits; bit++) {
+       digit = 1 + bit / 16;
+       smallbit = bit % 16;
+
+       debug(temp);
+       if (digit <= r2[0] && (r2[digit] & (1<<smallbit))) {
+           dmsg(("bit %d\n", bit));
+           modmult(temp, result, modulus, tmp2);
+           add(tmp2, Zero, result);
+           debug(result);
+       }
+
+       modmult(temp, temp, modulus, tmp2);
+       add(tmp2, Zero, temp);
+    }
+
+    freebn(temp);
+    freebn(tmp2);
+
+    debug(result);
+    leave(("<modpow\n"));
+}
+
+int makekey(unsigned char *data, struct RSAKey *result,
+           unsigned char **keystr) {
+    unsigned char *p = data;
+    Bignum bn[2];
+    int i, j;
+    int w, b;
+
+    result->bits = 0;
+    for (i=0; i<4; i++)
+       result->bits = (result->bits << 8) + *p++;
+
+    for (j=0; j<2; j++) {
+
+       w = 0;
+       for (i=0; i<2; i++)
+           w = (w << 8) + *p++;
+
+       result->bytes = b = (w+7)/8;   /* bits -> bytes */
+       w = (w+15)/16;                 /* bits -> words */
+
+       bn[j] = newbn(w);
+
+       if (keystr) *keystr = p;       /* point at key string, second time */
+
+       for (i=1; i<=w; i++)
+           bn[j][i] = 0;
+       for (i=0; i<b; i++) {
+           unsigned char byte = *p++;
+           if ((b-i) & 1)
+               bn[j][w-i/2] |= byte;
+           else
+               bn[j][w-i/2] |= byte<<8;
+       }
+
+       debug(bn[j]);
+
+    }
+
+    result->exponent = bn[0];
+    result->modulus = bn[1];
+
+    return p - data;
+}
+
+void rsaencrypt(unsigned char *data, int length, struct RSAKey *key) {
+    Bignum b1, b2;
+    int w, i;
+    unsigned char *p;
+
+    debug(key->exponent);
+
+    memmove(data+key->bytes-length, data, length);
+    data[0] = 0;
+    data[1] = 2;
+
+    for (i = 2; i < key->bytes-length-1; i++) {
+       do {
+           data[i] = random_byte();
+       } while (data[i] == 0);
+    }
+    data[key->bytes-length-1] = 0;
+
+    w = (key->bytes+1)/2;
+
+    b1 = newbn(w);
+    b2 = newbn(w);
+
+    p = data;
+    for (i=1; i<=w; i++)
+       b1[i] = 0;
+    for (i=0; i<key->bytes; i++) {
+       unsigned char byte = *p++;
+       if ((key->bytes-i) & 1)
+           b1[w-i/2] |= byte;
+       else
+           b1[w-i/2] |= byte<<8;
+    }
+
+    debug(b1);
+
+    modpow(b1, key->exponent, key->modulus, b2);
+
+    debug(b2);
+
+    p = data;
+    for (i=0; i<key->bytes; i++) {
+       unsigned char b;
+       if (i & 1)
+           b = b2[w-i/2] & 0xFF;
+       else
+           b = b2[w-i/2] >> 8;
+       *p++ = b;
+    }
+
+    freebn(b1);
+    freebn(b2);
+}
+
+int rsastr_len(struct RSAKey *key) {
+    Bignum md, ex;
+
+    md = key->modulus;
+    ex = key->exponent;
+    return 4 * (ex[0]+md[0]) + 10;
+}
+
+void rsastr_fmt(char *str, struct RSAKey *key) {
+    Bignum md, ex;
+    int len = 0, i;
+
+    md = key->modulus;
+    ex = key->exponent;
+
+    for (i=1; i<=ex[0]; i++) {
+       sprintf(str+len, "%04x", ex[i]);
+       len += strlen(str+len);
+    }
+    str[len++] = '/';
+    for (i=1; i<=md[0]; i++) {
+       sprintf(str+len, "%04x", md[i]);
+       len += strlen(str+len);
+    }
+    str[len] = '\0';
+}
+
+#ifdef TESTMODE
+
+#ifndef NODDY
+#define p1 10007
+#define p2 10069
+#define p3 10177
+#else
+#define p1 3
+#define p2 7
+#define p3 13
+#endif
+
+unsigned short P1[2] = { 1, p1 };
+unsigned short P2[2] = { 1, p2 };
+unsigned short P3[2] = { 1, p3 };
+unsigned short bigmod[5] = { 4, 0, 0, 0, 32768U };
+unsigned short mod[5] = { 4, 0, 0, 0, 0 };
+unsigned short a[5] = { 4, 0, 0, 0, 0 };
+unsigned short b[5] = { 4, 0, 0, 0, 0 };
+unsigned short c[5] = { 4, 0, 0, 0, 0 };
+unsigned short One[2] = { 1, 1 };
+unsigned short Two[2] = { 1, 2 };
+
+int main(void) {
+    modmult(P1, P2, bigmod, a);   debug(a);
+    modmult(a, P3, bigmod, mod);  debug(mod);
+
+    sub(P1, One, a);              debug(a);
+    sub(P2, One, b);              debug(b);
+    modmult(a, b, bigmod, c);     debug(c);
+    sub(P3, One, a);              debug(a);
+    modmult(a, c, bigmod, b);     debug(b);
+
+    modpow(Two, b, mod, a);       debug(a);
+
+    return 0;
+}
+
+#endif
diff --git a/sshsha.c b/sshsha.c
new file mode 100644 (file)
index 0000000..a63bf8d
--- /dev/null
+++ b/sshsha.c
@@ -0,0 +1,141 @@
+/*
+ * The following code was taken directly from drivers/char/random.c
+ * in the Linux kernel.
+ */
+
+#include "ssh.h"
+
+/*
+ * SHA transform algorithm, taken from code written by Peter Gutman,
+ * and apparently in the public domain.
+ */
+
+/* The SHA f()-functions.  */
+
+#define f1(x,y,z)   ( z ^ ( x & ( y ^ z ) ) )           /* Rounds  0-19 */
+#define f2(x,y,z)   ( x ^ y ^ z )                       /* Rounds 20-39 */
+#define f3(x,y,z)   ( ( x & y ) | ( z & ( x | y ) ) )   /* Rounds 40-59 */
+#define f4(x,y,z)   ( x ^ y ^ z )                       /* Rounds 60-79 */
+
+/* The SHA Mysterious Constants */
+
+#define K1  0x5A827999L                                 /* Rounds  0-19 */
+#define K2  0x6ED9EBA1L                                 /* Rounds 20-39 */
+#define K3  0x8F1BBCDCL                                 /* Rounds 40-59 */
+#define K4  0xCA62C1D6L                                 /* Rounds 60-79 */
+
+#define ROTL(n,X)  ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) )
+
+#define expand(W,i) ( W[ i & 15 ] = \
+                    ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \
+                               W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) )
+
+#define subRound(a, b, c, d, e, f, k, data) \
+    ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )
+
+
+void SHATransform(word32 *digest, word32 *data)
+{
+    word32 A, B, C, D, E;     /* Local vars */
+    word32 eData[ 16 ];       /* Expanded data */
+
+    /* Set up first buffer and local data buffer */
+    A = digest[ 0 ];
+    B = digest[ 1 ];
+    C = digest[ 2 ];
+    D = digest[ 3 ];
+    E = digest[ 4 ];
+    memcpy( eData, data, 16*sizeof(word32));
+
+    /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */
+    subRound( A, B, C, D, E, f1, K1, eData[  0 ] );
+    subRound( E, A, B, C, D, f1, K1, eData[  1 ] );
+    subRound( D, E, A, B, C, f1, K1, eData[  2 ] );
+    subRound( C, D, E, A, B, f1, K1, eData[  3 ] );
+    subRound( B, C, D, E, A, f1, K1, eData[  4 ] );
+    subRound( A, B, C, D, E, f1, K1, eData[  5 ] );
+    subRound( E, A, B, C, D, f1, K1, eData[  6 ] );
+    subRound( D, E, A, B, C, f1, K1, eData[  7 ] );
+    subRound( C, D, E, A, B, f1, K1, eData[  8 ] );
+    subRound( B, C, D, E, A, f1, K1, eData[  9 ] );
+    subRound( A, B, C, D, E, f1, K1, eData[ 10 ] );
+    subRound( E, A, B, C, D, f1, K1, eData[ 11 ] );
+    subRound( D, E, A, B, C, f1, K1, eData[ 12 ] );
+    subRound( C, D, E, A, B, f1, K1, eData[ 13 ] );
+    subRound( B, C, D, E, A, f1, K1, eData[ 14 ] );
+    subRound( A, B, C, D, E, f1, K1, eData[ 15 ] );
+    subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) );
+    subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) );
+    subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) );
+    subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) );
+
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) );
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) );
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) );
+    subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) );
+    subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) );
+    subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) );
+    subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) );
+    subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) );
+
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) );
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) );
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) );
+    subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) );
+    subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) );
+    subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) );
+    subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) );
+    subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) );
+
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) );
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) );
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) );
+    subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) );
+    subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) );
+    subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) );
+    subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) );
+    subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) );
+
+    /* Build message digest */
+    digest[ 0 ] += A;
+    digest[ 1 ] += B;
+    digest[ 2 ] += C;
+    digest[ 3 ] += D;
+    digest[ 4 ] += E;
+}
diff --git a/telnet.c b/telnet.c
new file mode 100644 (file)
index 0000000..14881f9
--- /dev/null
+++ b/telnet.c
@@ -0,0 +1,720 @@
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <winsock.h>
+
+#include "putty.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+static SOCKET s = INVALID_SOCKET;
+
+#define        IAC     255             /* interpret as command: */
+#define        DONT    254             /* you are not to use option */
+#define        DO      253             /* please, you use option */
+#define        WONT    252             /* I won't use option */
+#define        WILL    251             /* I will use option */
+#define        SB      250             /* interpret as subnegotiation */
+#define        SE      240             /* end sub negotiation */
+
+#define GA      249             /* you may reverse the line */
+#define EL      248             /* erase the current line */
+#define EC      247             /* erase the current character */
+#define        AYT     246             /* are you there */
+#define        AO      245             /* abort output--but let prog finish */
+#define        IP      244             /* interrupt process--permanently */
+#define        BREAK   243             /* break */
+#define DM      242             /* data mark--for connect. cleaning */
+#define NOP     241             /* nop */
+#define EOR     239             /* end of record (transparent mode) */
+#define ABORT   238             /* Abort process */
+#define SUSP    237             /* Suspend process */
+#define xEOF    236             /* End of file: EOF is already used... */
+
+#define TELOPT_BINARY  0       /* 8-bit data path */
+#define TELOPT_ECHO    1       /* echo */
+#define        TELOPT_RCP      2       /* prepare to reconnect */
+#define        TELOPT_SGA      3       /* suppress go ahead */
+#define        TELOPT_NAMS     4       /* approximate message size */
+#define        TELOPT_STATUS   5       /* give status */
+#define        TELOPT_TM       6       /* timing mark */
+#define        TELOPT_RCTE     7       /* remote controlled transmission and echo */
+#define TELOPT_NAOL    8       /* negotiate about output line width */
+#define TELOPT_NAOP    9       /* negotiate about output page size */
+#define TELOPT_NAOCRD  10      /* negotiate about CR disposition */
+#define TELOPT_NAOHTS  11      /* negotiate about horizontal tabstops */
+#define TELOPT_NAOHTD  12      /* negotiate about horizontal tab disposition */
+#define TELOPT_NAOFFD  13      /* negotiate about formfeed disposition */
+#define TELOPT_NAOVTS  14      /* negotiate about vertical tab stops */
+#define TELOPT_NAOVTD  15      /* negotiate about vertical tab disposition */
+#define TELOPT_NAOLFD  16      /* negotiate about output LF disposition */
+#define TELOPT_XASCII  17      /* extended ascic character set */
+#define        TELOPT_LOGOUT   18      /* force logout */
+#define        TELOPT_BM       19      /* byte macro */
+#define        TELOPT_DET      20      /* data entry terminal */
+#define        TELOPT_SUPDUP   21      /* supdup protocol */
+#define        TELOPT_SUPDUPOUTPUT 22  /* supdup output */
+#define        TELOPT_SNDLOC   23      /* send location */
+#define        TELOPT_TTYPE    24      /* terminal type */
+#define        TELOPT_EOR      25      /* end or record */
+#define        TELOPT_TUID     26      /* TACACS user identification */
+#define        TELOPT_OUTMRK   27      /* output marking */
+#define        TELOPT_TTYLOC   28      /* terminal location number */
+#define        TELOPT_3270REGIME 29    /* 3270 regime */
+#define        TELOPT_X3PAD    30      /* X.3 PAD */
+#define        TELOPT_NAWS     31      /* window size */
+#define        TELOPT_TSPEED   32      /* terminal speed */
+#define        TELOPT_LFLOW    33      /* remote flow control */
+#define TELOPT_LINEMODE        34      /* Linemode option */
+#define TELOPT_XDISPLOC        35      /* X Display Location */
+#define TELOPT_OLD_ENVIRON 36  /* Old - Environment variables */
+#define        TELOPT_AUTHENTICATION 37/* Authenticate */
+#define        TELOPT_ENCRYPT  38      /* Encryption option */
+#define TELOPT_NEW_ENVIRON 39  /* New - Environment variables */
+#define        TELOPT_EXOPL    255     /* extended-options-list */
+
+#define        TELQUAL_IS      0       /* option is... */
+#define        TELQUAL_SEND    1       /* send option */
+#define        TELQUAL_INFO    2       /* ENVIRON: informational version of IS */
+#define BSD_VAR 1
+#define BSD_VALUE 0
+#define RFC_VAR 0
+#define RFC_VALUE 1
+
+#define CR 13
+#define LF 10
+#define NUL 0
+
+#define iswritable(x) ( (x) != IAC && (x) != CR )
+
+static char *telopt(int opt) {
+#define i(x) if (opt == TELOPT_ ## x) return #x;
+    i(BINARY); i(ECHO); i(RCP); i(SGA); i(NAMS); i(STATUS); i(TM); i(RCTE);
+    i(NAOL); i(NAOP); i(NAOCRD); i(NAOHTS); i(NAOHTD); i(NAOFFD); i(NAOVTS);
+    i(NAOVTD); i(NAOLFD); i(XASCII); i(LOGOUT); i(BM); i(DET); i(SUPDUP);
+    i(SUPDUPOUTPUT); i(SNDLOC); i(TTYPE); i(EOR); i(TUID); i(OUTMRK);
+    i(TTYLOC); i(X3PAD); i(NAWS); i(TSPEED); i(LFLOW); i(LINEMODE);
+    i(XDISPLOC); i(OLD_ENVIRON); i(AUTHENTICATION); i(ENCRYPT);
+    i(NEW_ENVIRON); i(EXOPL);
+#undef i
+    return "<unknown>";
+}
+
+static void telnet_size(void);
+
+struct Opt {
+    int send;                         /* what we initially send */
+    int nsend;                        /* -ve send if requested to stop it */
+    int ack, nak;                     /* +ve and -ve acknowledgements */
+    int option;                               /* the option code */
+    enum {
+       REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE
+    } state;
+};
+
+static struct Opt o_naws = {WILL, WONT, DO, DONT, TELOPT_NAWS, REQUESTED};
+static struct Opt o_tspeed = {WILL, WONT, DO, DONT, TELOPT_TSPEED, REQUESTED};
+static struct Opt o_ttype = {WILL, WONT, DO, DONT, TELOPT_TTYPE, REQUESTED};
+static struct Opt o_oenv = {WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON,
+    INACTIVE};
+static struct Opt o_nenv = {WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON,
+    REQUESTED};
+static struct Opt o_echo = {DO, DONT, WILL, WONT, TELOPT_ECHO, REQUESTED};
+static struct Opt o_we_sga = {WILL, WONT, DO, DONT, TELOPT_SGA, REQUESTED};
+static struct Opt o_they_sga = {DO, DONT, WILL, WONT, TELOPT_SGA, REQUESTED};
+
+static struct Opt *opts[] = {
+    &o_naws, &o_tspeed, &o_ttype, &o_oenv, &o_nenv, &o_echo,
+    &o_we_sga, &o_they_sga, NULL
+};
+
+#if 0
+static int in_synch;
+#endif
+
+static int sb_opt, sb_len;
+static char *sb_buf = NULL;
+static int sb_size = 0;
+#define SB_DELTA 1024
+
+static void try_write (void) {
+    while (outbuf_head != outbuf_reap) {
+       int end = (outbuf_reap < outbuf_head ? outbuf_head : OUTBUF_SIZE);
+       int len = end - outbuf_reap;
+       int ret;
+
+       ret = send (s, outbuf+outbuf_reap, len, 0);
+       if (ret > 0)
+           outbuf_reap = (outbuf_reap + ret) & OUTBUF_MASK;
+       if (ret < len)
+           return;
+    }
+}
+
+static void s_write (void *buf, int len) {
+    unsigned char *p = buf;
+    while (len--) {
+       int new_head = (outbuf_head + 1) & OUTBUF_MASK;
+       if (new_head != outbuf_reap) {
+           outbuf[outbuf_head] = *p++;
+           outbuf_head = new_head;
+       }
+    }
+    try_write();
+}
+
+static void c_write (char *buf, int len) {
+    while (len--) {
+       int new_head = (inbuf_head + 1) & INBUF_MASK;
+       int c = (unsigned char) *buf;
+       if (new_head != inbuf_reap) {
+           inbuf[inbuf_head] = *buf++;
+           inbuf_head = new_head;
+       }
+    }
+}
+
+static void log_option (char *sender, int cmd, int option) {
+    char buf[50];
+    sprintf(buf, "%s:\t%s %s", sender,
+           (cmd == WILL ? "WILL" : cmd == WONT ? "WONT" :
+            cmd == DO ? "DO" : cmd == DONT ? "DONT" : "<??>"),
+           telopt(option));
+    lognegot(buf);
+}
+
+static void send_opt (int cmd, int option) {
+    unsigned char b[3];
+
+    b[0] = IAC; b[1] = cmd; b[2] = option;
+    s_write (b, 3);
+    log_option("client", cmd, option);
+}
+
+static void deactivate_option (struct Opt *o) {
+    if (o->state == REQUESTED || o->state == ACTIVE)
+       send_opt (o->nsend, o->option);
+    o->state = REALLY_INACTIVE;
+}
+
+static void activate_option (struct Opt *o) {
+    if (o->send == WILL && o->option == TELOPT_NAWS)
+       telnet_size();
+    if (o->send == WILL &&
+       (o->option == TELOPT_NEW_ENVIRON ||
+        o->option == TELOPT_OLD_ENVIRON)) {
+       /*
+        * We may only have one kind of ENVIRON going at a time.
+        * This is a hack, but who cares.
+        */
+       deactivate_option (o->option==TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv);
+    }
+}
+
+static void refused_option (struct Opt *o) {
+    if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON &&
+       o_oenv.state == INACTIVE) {
+       send_opt (WILL, TELOPT_OLD_ENVIRON);
+       o_oenv.state = REQUESTED;
+    }
+}
+
+static void proc_rec_opt (int cmd, int option) {
+    struct Opt **o;
+
+    log_option ("server", cmd, option);
+    for (o = opts; *o; o++) {
+       if ((*o)->option == option && (*o)->ack == cmd) {
+           switch ((*o)->state) {
+             case REQUESTED:
+               (*o)->state = ACTIVE;
+               activate_option (*o);
+               break;
+             case ACTIVE:
+               break;
+             case INACTIVE:
+               (*o)->state = ACTIVE;
+               send_opt ((*o)->send, option);
+               activate_option (*o);
+               break;
+             case REALLY_INACTIVE:
+               send_opt ((*o)->nsend, option);
+               break;
+           }
+           return;
+       } else if ((*o)->option == option && (*o)->nak == cmd) {
+           switch ((*o)->state) {
+             case REQUESTED:
+               (*o)->state = INACTIVE;
+               refused_option (*o);
+               break;
+             case ACTIVE:
+               (*o)->state = INACTIVE;
+               send_opt ((*o)->nsend, option);
+               break;
+             case INACTIVE:
+             case REALLY_INACTIVE:
+               break;
+           }
+           return;
+       }
+    }
+    /*
+     * If we reach here, the option was one we weren't prepared to
+     * cope with. So send a negative ack.
+     */
+    send_opt ((cmd == WILL ? DONT : WONT), option);
+}
+
+static void process_subneg (void) {
+    unsigned char b[2048], *p, *q;
+    int var, value, n;
+    char *e;
+
+    switch (sb_opt) {
+      case TELOPT_TSPEED:
+       if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) {
+           char logbuf[sizeof(cfg.termspeed)+80];
+           b[0] = IAC; b[1] = SB; b[2] = TELOPT_TSPEED;
+           b[3] = TELQUAL_IS;
+           strcpy(b+4, cfg.termspeed);
+           n = 4 + strlen(cfg.termspeed);
+           b[n] = IAC; b[n+1] = SE;
+           s_write (b, n+2);
+           lognegot("server:\tSB TSPEED SEND");
+           sprintf(logbuf, "client:\tSB TSPEED IS %s", cfg.termspeed);
+           lognegot (logbuf);
+       } else
+           lognegot ("server:\tSB TSPEED <something weird>");
+       break;
+      case TELOPT_TTYPE:
+       if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) {
+           char logbuf[sizeof(cfg.termtype)+80];
+           b[0] = IAC; b[1] = SB; b[2] = TELOPT_TTYPE;
+           b[3] = TELQUAL_IS;
+           for (n = 0; cfg.termtype[n]; n++)
+               b[n+4] = (cfg.termtype[n] >= 'a' && cfg.termtype[n] <= 'z' ?
+                         cfg.termtype[n] + 'A'-'a' : cfg.termtype[n]);
+           b[n+4] = IAC; b[n+5] = SE;
+           s_write (b, n+6);
+           b[n+4] = 0;
+           lognegot("server:\tSB TTYPE SEND");
+           sprintf(logbuf, "client:\tSB TTYPE IS %s", b+4);
+           lognegot(logbuf);
+       } else
+           lognegot("server:\tSB TTYPE <something weird>\r\n");
+       break;
+      case TELOPT_OLD_ENVIRON:
+      case TELOPT_NEW_ENVIRON: 
+       p = sb_buf;
+       q = p + sb_len;
+       if (p < q && *p == TELQUAL_SEND) {
+           char logbuf[50];
+           p++;
+           sprintf (logbuf, "server:\tSB %s SEND", telopt(sb_opt));
+           lognegot (logbuf);
+           if (sb_opt == TELOPT_OLD_ENVIRON) {
+               if (cfg.rfc_environ) {
+                   value = RFC_VALUE;
+                   var = RFC_VAR;
+               } else {
+                   value = BSD_VALUE;
+                   var = BSD_VAR;
+               }
+               /*
+                * Try to guess the sense of VAR and VALUE.
+                */
+               while (p < q) {
+                   if (*p == RFC_VAR) {
+                       value = RFC_VALUE;
+                       var = RFC_VAR;
+                   } else if (*p == BSD_VAR) {
+                       value = BSD_VALUE;
+                       var = BSD_VAR;
+                   }
+                   p++;
+               }
+           } else {
+               /*
+                * With NEW_ENVIRON, the sense of VAR and VALUE
+                * isn't in doubt.
+                */
+               value = RFC_VALUE;
+               var = RFC_VAR;
+           }
+           b[0] = IAC; b[1] = SB; b[2] = sb_opt;
+           b[3] = TELQUAL_IS;
+           n = 4;
+           e = cfg.environ;
+           while (*e) {
+               b[n++] = var;
+               while (*e && *e != '\t') b[n++] = *e++;
+               if (*e == '\t') e++;
+               b[n++] = value;
+               while (*e) b[n++] = *e++;
+               e++;
+           }
+           if (*cfg.username) {
+               b[n++] = var; b[n++] = 'U'; b[n++] = 'S';
+               b[n++] = 'E'; b[n++] = 'R'; b[n++] = value;
+               e = cfg.username;
+               while (*e) b[n++] = *e++;
+           }
+           b[n++] = IAC; b[n++] = SE;
+           s_write (b, n);
+           sprintf(logbuf, "client:\tSB %s IS %s", telopt(sb_opt),
+                   n==6 ? "<nothing>" : "<stuff>");
+           lognegot (logbuf);
+       }
+       break;
+    }
+}
+
+static enum {
+    TOPLEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,
+    SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR
+} telnet_state = TOPLEVEL;
+
+static void do_telnet_read (char *buf, int len) {
+    unsigned char b[10];
+
+    while (len--) {
+       int c = (unsigned char) *buf++;
+
+       switch (telnet_state) {
+         case TOPLEVEL:
+         case SEENCR:
+           if (c == NUL && telnet_state == SEENCR)
+               telnet_state = TOPLEVEL;
+           else if (c == IAC)
+               telnet_state = SEENIAC;
+           else {
+               b[0] = c;
+#if 0
+               if (!in_synch)
+#endif
+                   c_write (b, 1);
+               if (c == CR)
+                   telnet_state = SEENCR;
+               else
+                   telnet_state = TOPLEVEL;
+           }
+           break;
+         case SEENIAC:
+           if (c == DO) telnet_state = SEENDO;
+           else if (c == DONT) telnet_state = SEENDONT;
+           else if (c == WILL) telnet_state = SEENWILL;
+           else if (c == WONT) telnet_state = SEENWONT;
+           else if (c == SB) telnet_state = SEENSB;
+           else telnet_state = TOPLEVEL;/* ignore _everything_ else! */
+           break;
+         case SEENWILL:
+           proc_rec_opt (WILL, c);
+           telnet_state = TOPLEVEL;
+           break;
+         case SEENWONT:
+           proc_rec_opt (WONT, c);
+           telnet_state = TOPLEVEL;
+           break;
+         case SEENDO:
+           proc_rec_opt (DO, c);
+           telnet_state = TOPLEVEL;
+           break;
+         case SEENDONT:
+           proc_rec_opt (DONT, c);
+           telnet_state = TOPLEVEL;
+           break;
+         case SEENSB:
+           sb_opt = c;
+           sb_len = 0;
+           telnet_state = SUBNEGOT;
+           break;
+         case SUBNEGOT:
+           if (c == IAC)
+               telnet_state = SUBNEG_IAC;
+           else {
+               subneg_addchar:
+               if (sb_len >= sb_size) {
+                   char *newbuf;
+                   sb_size += SB_DELTA;
+                   newbuf = (sb_buf ?
+                             realloc(sb_buf, sb_size) :
+                             malloc(sb_size));
+                   if (newbuf)
+                       sb_buf = newbuf;
+                   else
+                       sb_size -= SB_DELTA;
+               }
+               if (sb_len < sb_size)
+                   sb_buf[sb_len++] = c;
+               telnet_state = SUBNEGOT;/* in case we came here by goto */
+           }
+           break;
+         case SUBNEG_IAC:
+           if (c != SE)
+               goto subneg_addchar;   /* yes, it's a hack, I know, but... */
+           else {
+               process_subneg();
+               telnet_state = TOPLEVEL;
+           }
+           break;
+       }
+    }
+}
+
+/*
+ * Called to set up the Telnet connection. Will arrange for
+ * WM_NETEVENT messages to be passed to the specified window, whose
+ * window procedure should then call telnet_msg().
+ *
+ * Returns an error message, or NULL on success.
+ *
+ * Also places the canonical host name into `realhost'.
+ */
+static char *telnet_init (HWND hwnd, char *host, int port, char **realhost) {
+    SOCKADDR_IN addr;
+    struct hostent *h;
+    unsigned long a;
+
+    /*
+     * Try to find host.
+     */
+    if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) {
+       if ( (h = gethostbyname(host)) == NULL)
+           switch (WSAGetLastError()) {
+             case WSAENETDOWN: return "Network is down";
+             case WSAHOST_NOT_FOUND: case WSANO_DATA:
+               return "Host does not exist";
+             case WSATRY_AGAIN: return "Host not found";
+             default: return "gethostbyname: unknown error";
+           }
+       memcpy (&a, h->h_addr, sizeof(a));
+       *realhost = h->h_name;
+    } else
+       *realhost = host;
+    a = ntohl(a);
+
+    if (port < 0)
+       port = 23;                     /* default telnet port */
+
+    /*
+     * Open socket.
+     */
+    s = socket(AF_INET, SOCK_STREAM, 0);
+    if (s == INVALID_SOCKET)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         case WSAEAFNOSUPPORT: return "TCP/IP support not present";
+         default: return "socket(): unknown error";
+       }
+
+#if 0
+    {
+       BOOL b = TRUE;
+       setsockopt (s, SOL_SOCKET, SO_OOBINLINE, (void *)&b, sizeof(b));
+    }
+#endif
+
+    /*
+     * Bind to local address.
+     */
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = htonl(INADDR_ANY);
+    addr.sin_port = htons(0);
+    if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         default: return "bind(): unknown error";
+       }
+
+    /*
+     * Connect to remote address.
+     */
+    addr.sin_addr.s_addr = htonl(a);
+    addr.sin_port = htons((short)port);
+    if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         case WSAECONNREFUSED: return "Connection refused";
+         case WSAENETUNREACH: return "Network is unreachable";
+         case WSAEHOSTUNREACH: return "No route to host";
+         default: return "connect(): unknown error";
+       }
+
+    if (WSAAsyncSelect (s, hwnd, WM_NETEVENT, FD_READ |
+                       FD_WRITE | FD_OOB | FD_CLOSE) == SOCKET_ERROR)
+       switch (WSAGetLastError()) {
+         case WSAENETDOWN: return "Network is down";
+         default: return "WSAAsyncSelect(): unknown error";
+       }
+
+    /*
+     * Initialise option states.
+     */
+    {
+       struct Opt **o;
+
+       for (o = opts; *o; o++)
+           if ((*o)->state == REQUESTED)
+               send_opt ((*o)->send, (*o)->option);
+    }
+
+#if 0
+    /*
+     * Set up SYNCH state.
+     */
+    in_synch = FALSE;
+#endif
+
+    return NULL;
+}
+
+/*
+ * Process a WM_NETEVENT message. Will return 0 if the connection
+ * has closed, or <0 for a socket error.
+ */
+static int telnet_msg (WPARAM wParam, LPARAM lParam) {
+    int ret;
+    char buf[256];
+
+    if (s == INVALID_SOCKET)          /* how the hell did we get here?! */
+       return -5000;
+
+    if (WSAGETSELECTERROR(lParam) != 0)
+       return -WSAGETSELECTERROR(lParam);
+
+    switch (WSAGETSELECTEVENT(lParam)) {
+      case FD_READ:
+       ret = recv(s, buf, sizeof(buf), 0);
+       if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
+           return 1;
+       if (ret < 0)                   /* any _other_ error */
+           return -10000-WSAGetLastError();
+       if (ret == 0) {
+           s = INVALID_SOCKET;
+           return 0;                  /* can't happen, in theory */
+       }
+#if 0
+       if (in_synch) {
+           BOOL i;
+           if (ioctlsocket (s, SIOCATMARK, &i) < 0) {
+               return -20000-WSAGetLastError();
+           }
+           if (i)
+               in_synch = FALSE;
+       }
+#endif
+       do_telnet_read (buf, ret);
+       return 1;
+      case FD_OOB:
+       do {
+           ret = recv(s, buf, sizeof(buf), 0);
+       } while (ret > 0);
+       telnet_state = TOPLEVEL;
+       do {
+           ret = recv(s, buf, 1, MSG_OOB);
+           if (ret > 0)
+               do_telnet_read (buf, ret);
+       } while (ret > 0);
+       if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
+           return -30000-WSAGetLastError();
+       return 1;
+      case FD_WRITE:
+       if (outbuf_head != outbuf_reap)
+           try_write();
+       return 1;
+      case FD_CLOSE:
+       s = INVALID_SOCKET;
+       return 0;
+    }
+    return 1;                         /* shouldn't happen, but WTF */
+}
+
+/*
+ * Called to send data down the Telnet connection.
+ */
+static void telnet_send (char *buf, int len) {
+    char *p;
+    static unsigned char iac[2] = { IAC, IAC };
+    static unsigned char cr[2] = { CR, NUL };
+
+    if (s == INVALID_SOCKET)
+       return;
+
+    p = buf;
+    while (p < buf+len) {
+       char *q = p;
+
+       while (iswritable((unsigned char)*p) && p < buf+len) p++;
+       s_write (q, p-q);
+
+       while (p < buf+len && !iswritable((unsigned char)*p)) {
+           s_write ((unsigned char)*p == IAC ? iac : cr, 2);
+           p++;
+       }
+    }
+}
+
+/*
+ * Called to set the size of the window from Telnet's POV.
+ */
+static void telnet_size(void) {
+    unsigned char b[16];
+    char logbuf[50];
+
+    if (s == INVALID_SOCKET || o_naws.state != ACTIVE)
+       return;
+    b[0] = IAC; b[1] = SB; b[2] = TELOPT_NAWS;
+    b[3] = cols >> 8; b[4] = cols & 0xFF;
+    b[5] = rows >> 8; b[6] = rows & 0xFF;
+    b[7] = IAC; b[8] = SE;
+    s_write (b, 9);
+    sprintf(logbuf, "client:\tSB NAWS %d,%d",
+           ((unsigned char)b[3] << 8) + (unsigned char)b[4],
+           ((unsigned char)b[5] << 8) + (unsigned char)b[6]);
+    lognegot (logbuf);
+}
+
+/*
+ * Send Telnet special codes.
+ */
+static void telnet_special (Telnet_Special code) {
+    unsigned char b[2];
+
+    if (s == INVALID_SOCKET)
+       return;
+
+    b[0] = IAC;
+    switch (code) {
+      case TS_AYT: b[1] = AYT; s_write (b, 2); break;
+      case TS_BRK: b[1] = BREAK; s_write (b, 2); break;
+      case TS_EC: b[1] = EC; s_write (b, 2); break;
+      case TS_EL: b[1] = EL; s_write (b, 2); break;
+      case TS_GA: b[1] = GA; s_write (b, 2); break;
+      case TS_NOP: b[1] = NOP; s_write (b, 2); break;
+      case TS_ABORT: b[1] = ABORT; s_write (b, 2); break;
+      case TS_AO: b[1] = AO; s_write (b, 2); break;
+      case TS_IP: b[1] = IP; s_write (b, 2); break;
+      case TS_SUSP: b[1] = SUSP; s_write (b, 2); break;
+      case TS_EOR: b[1] = EOR; s_write (b, 2); break;
+      case TS_EOF: b[1] = xEOF; s_write (b, 2); break;
+      case TS_SYNCH:
+       outbuf_head = outbuf_reap = 0;
+       b[0] = DM;
+       send (s, b, 1, MSG_OOB);
+       break;
+    }
+}
+
+Backend telnet_backend = {
+    telnet_init,
+    telnet_msg,
+    telnet_send,
+    telnet_size,
+    telnet_special
+};
diff --git a/terminal.c b/terminal.c
new file mode 100644 (file)
index 0000000..1e286ce
--- /dev/null
@@ -0,0 +1,1447 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "putty.h"
+
+static unsigned long *text;           /* buffer of text on terminal screen */
+static unsigned long *scrtop;         /* top of working screen */
+static unsigned long *disptop;        /* top of displayed screen */
+static unsigned long *sbtop;          /* top of scrollback */
+static unsigned long *cpos;           /* cursor position (convenience) */
+static unsigned long *disptext;               /* buffer of text on real screen */
+static unsigned long *wanttext;               /* buffer of text we want on screen */
+static unsigned long *alttext;        /* buffer of text on alt. screen */
+
+static unsigned char *selspace;               /* buffer for building selections in */
+
+#define TSIZE (sizeof(*text))
+#define fix_cpos  do { cpos = scrtop + curs_y * (cols+1) + curs_x; } while(0)
+
+static unsigned long curr_attr, save_attr;
+
+static int curs_x, curs_y;            /* cursor */
+static int save_x, save_y;            /* saved cursor position */
+static int marg_t, marg_b;            /* scroll margins */
+static int dec_om;                    /* DEC origin mode flag */
+static int wrap, wrapnext;            /* wrap flags */
+static int insert;                    /* insert-mode flag */
+static int cset;                      /* 0 or 1: which char set */
+static int save_cset, save_csattr;     /* saved with cursor position */
+static int rvideo;                    /* global reverse video flag */
+
+static unsigned long cset_attr[2];
+
+/*
+ * Saved settings on the alternate screen.
+ */
+static int alt_x, alt_y, alt_om, alt_wrap, alt_wnext, alt_ins, alt_cset;
+static int alt_t, alt_b;
+static int alt_which;
+
+#define ARGS_MAX 32                   /* max # of esc sequence arguments */
+#define ARG_DEFAULT -1                /* if an arg isn't specified */
+#define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
+static int esc_args[ARGS_MAX];
+static int esc_nargs;
+static int esc_query;
+
+#define OSC_STR_MAX 2048
+static int osc_strlen;
+static char osc_string[OSC_STR_MAX+1];
+static int osc_w;
+
+static unsigned char *tabs;
+
+#define MAXNL 5
+static int nl_count;
+
+static int scroll_heuristic;
+
+static enum {
+    TOPLEVEL, IGNORE_NEXT,
+    SEEN_ESC, SEEN_CSI, SET_GL, SET_GR,
+    SEEN_OSC, SEEN_OSC_P, SEEN_OSC_W, OSC_STRING, OSC_MAYBE_ST,
+    SEEN_ESCHASH
+} termstate;
+
+static enum {
+    NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED
+} selstate;
+static enum {
+    SM_CHAR, SM_WORD, SM_LINE
+} selmode;
+static unsigned long *selstart, *selend, *selanchor;
+
+static short wordness[256] = {
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 01 */
+    0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2, 2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1, /* 23 */
+    1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2, /* 45 */
+    1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1, /* 67 */
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 89 */
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* AB */
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* CD */
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2, /* EF */
+};
+
+static unsigned char sel_nl[] = SEL_NL;
+
+/*
+ * Internal prototypes.
+ */
+static void do_paint (Context, int);
+static void erase_lots (int, int, int);
+static void swap_screen (int);
+static void update_sbar (void);
+static void deselect (void);
+
+/*
+ * Set up power-on settings for the terminal.
+ */
+static void power_on(void) {
+    curs_x = curs_y = alt_x = alt_y = save_x = save_y = 0;
+    alt_t = marg_t = 0;
+    if (rows != -1)
+       alt_b = marg_b = rows - 1;
+    else
+       alt_b = marg_b = 0;
+    if (cols != -1) {
+       int i;
+       for (i = 0; i < cols; i++)
+           tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
+    }
+    alt_om = dec_om = cfg.dec_om;
+    alt_wnext = wrapnext = alt_ins = insert = FALSE;
+    alt_wrap = wrap = cfg.wrap_mode;
+    alt_cset = cset = 0;
+    cset_attr[0] = cset_attr[1] = ATTR_ASCII;
+    rvideo = 0;
+    save_attr = curr_attr = ATTR_DEFAULT;
+    app_cursor_keys = cfg.app_cursor;
+    app_keypad_keys = cfg.app_keypad;
+    alt_which = 0;
+    {
+       int i;
+       for (i = 0; i < 256; i++)
+           wordness[i] = cfg.wordness[i];
+    }
+    if (text) {
+       swap_screen (1);
+       erase_lots (FALSE, TRUE, TRUE);
+       swap_screen (0);
+       erase_lots (FALSE, TRUE, TRUE);
+    }
+}
+
+/*
+ * Force a screen update.
+ */
+void term_update(void) {
+    Context ctx;
+    ctx = get_ctx();
+    if (ctx) {
+       do_paint (ctx, TRUE);
+       free_ctx (ctx);
+       nl_count = 0;
+       scroll_heuristic = 0;
+    }
+}
+
+/*
+ * Same as power_on(), but an external function.
+ */
+void term_pwron(void) {
+    power_on();
+    fix_cpos;
+    disptop = scrtop;
+    deselect();
+    term_update();
+}
+
+/*
+ * Clear the scrollback.
+ */
+void term_clrsb(void) {
+    disptop = sbtop = scrtop;
+    update_sbar();
+}
+
+/*
+ * Initialise the terminal.
+ */
+void term_init(void) {
+    text = sbtop = scrtop = disptop = cpos = NULL;
+    disptext = wanttext = NULL;
+    tabs = NULL;
+    selspace = NULL;
+    deselect();
+    rows = cols = -1;
+    nl_count = 0;
+    power_on();
+}
+
+/*
+ * Set up the terminal for a given size.
+ */
+void term_size(int newrows, int newcols, int newsavelines) {
+    unsigned long *newtext, *newdisp, *newwant, *newalt;
+    int i, j, crows, ccols;
+
+    if (newrows == rows && newcols == cols && newsavelines == savelines)
+       return;                        /* nothing to do */
+
+    alt_t = marg_t = 0;
+    alt_b = marg_b = newrows - 1;
+
+    newtext = smalloc ((newrows+newsavelines)*(newcols+1)*TSIZE);
+    disptop = newtext + newsavelines*(newcols+1);
+    for (i=0; i<(newrows+newsavelines)*(newcols+1); i++)
+       newtext[i] = ERASE_CHAR;
+    if (rows != -1) {
+       crows = rows + (scrtop - sbtop) / (cols+1);
+       if (crows > newrows+newsavelines)
+           crows = newrows+newsavelines;
+       ccols = (cols < newcols ? cols : newcols);
+       for (i=0; i<crows; i++) {
+           int oldidx = (rows + savelines - crows + i) * (cols+1);
+           int newidx = (newrows + newsavelines - crows + i) * (newcols+1);
+           for (j=0; j<ccols; j++)
+               newtext[newidx+j] = text[oldidx+j];
+           newtext[newidx+newcols] =
+               (cols == newcols ? text[oldidx+cols] : 0);
+       }
+       sbtop = disptop - (crows - newrows) * (newcols+1);
+       if (sbtop > disptop)
+           sbtop = disptop;
+    } else
+       sbtop = disptop;
+    scrtop = disptop;
+    sfree (text);
+    text = newtext;
+
+    newdisp = smalloc (newrows*(newcols+1)*TSIZE);
+    for (i=0; i<newrows*(newcols+1); i++)
+       newdisp[i] = ATTR_INVALID;
+    sfree (disptext);
+    disptext = newdisp;
+
+    newwant = smalloc (newrows*(newcols+1)*TSIZE);
+    for (i=0; i<newrows*(newcols+1); i++)
+       newwant[i] = ATTR_INVALID;
+    sfree (wanttext);
+    wanttext = newwant;
+
+    newalt = smalloc (newrows*(newcols+1)*TSIZE);
+    for (i=0; i<newrows*(newcols+1); i++)
+       newalt[i] = ERASE_CHAR;
+    sfree (alttext);
+    alttext = newalt;
+
+    sfree (selspace);
+    selspace = smalloc ( (newrows+newsavelines) * (newcols+sizeof(sel_nl)) );
+
+    tabs = srealloc (tabs, newcols*sizeof(*tabs));
+    {
+       int i;
+       for (i = (cols > 0 ? cols : 0); i < newcols; i++)
+           tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
+    }
+
+    if (rows > 0)
+       curs_y += newrows - rows;
+    if (curs_y < 0)
+       curs_y = 0;
+    if (curs_y >= newrows)
+       curs_y = newrows-1;
+    if (curs_x >= newcols)
+       curs_x = newcols-1;
+    alt_x = alt_y = 0;
+    wrapnext = alt_wnext = FALSE;
+
+    rows = newrows;
+    cols = newcols;
+    savelines = newsavelines;
+    fix_cpos;
+
+    deselect();
+    update_sbar();
+    term_update();
+}
+
+/*
+ * Swap screens.
+ */
+static void swap_screen (int which) {
+    int t;
+    unsigned long tt;
+
+    if (which == alt_which)
+       return;
+
+    alt_which = which;
+
+    for (t=0; t<rows*(cols+1); t++) {
+       tt = scrtop[t]; scrtop[t] = alttext[t]; alttext[t] = tt;
+    }
+
+    t = curs_x; curs_x = alt_x; alt_x = t;
+    t = curs_y; curs_y = alt_y; alt_y = t;
+    t = marg_t; marg_t = alt_t; alt_t = t;
+    t = marg_b; marg_b = alt_b; alt_b = t;
+    t = dec_om; dec_om = alt_om; alt_om = t;
+    t = wrap; wrap = alt_wrap; alt_wrap = t;
+    t = wrapnext; wrapnext = alt_wnext; alt_wnext = t;
+    t = insert; insert = alt_ins; alt_ins = t;
+    t = cset; cset = alt_cset; alt_cset = t;
+
+    fix_cpos;
+}
+
+/*
+ * Retrieve a character from `inbuf'.
+ */
+static int inbuf_getc(void) {
+    if (inbuf_head == inbuf_reap)
+       return -1;                     /* EOF */
+    else {
+       int n = inbuf_reap;
+       inbuf_reap = (inbuf_reap+1) & INBUF_MASK;
+       return inbuf[n];
+    }
+}
+
+/*
+ * Update the scroll bar.
+ */
+static void update_sbar(void) {
+    int min;
+
+    min = (sbtop - text) / (cols+1);
+    set_sbar ((scrtop - text) / (cols+1) + rows - min,
+             (disptop - text) / (cols+1) - min,
+             rows);
+}
+
+/*
+ * Check whether the region bounded by the two pointers intersects
+ * the scroll region, and de-select the on-screen selection if so.
+ */
+static void check_selection (unsigned long *from, unsigned long *to) {
+    if (from < selend && selstart < to)
+       deselect();
+}
+
+/*
+ * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
+ * for backward.) `sb' is TRUE if the scrolling is permitted to
+ * affect the scrollback buffer.
+ */
+static void scroll (int topline, int botline, int lines, int sb) {
+    unsigned long *scroll_top;
+    int scroll_size, size, i;
+
+    scroll_top = scrtop + topline*(cols+1);
+    size = (lines < 0 ? -lines : lines) * (cols+1);
+    scroll_size = (botline - topline + 1) * (cols+1) - size;
+
+    if (lines > 0 && topline == 0 && botline == (rows-1) && sb) {
+       /*
+        * Since we're going to scroll the whole screen upwards,
+        * let's also affect the scrollback buffer.
+        */
+       sbtop -= lines * (cols+1);
+       if (sbtop < text)
+           sbtop = text;
+       scroll_size += scroll_top - sbtop;
+       scroll_top = sbtop;
+       update_sbar();
+    }
+
+    if (scroll_size < 0) {
+       size += scroll_size;
+       scroll_size = 0;
+    }
+
+    if (lines > 0) {
+       if (scroll_size)
+           memmove (scroll_top, scroll_top + size, scroll_size*TSIZE);
+       for (i = 0; i < size; i++)
+           scroll_top[i+scroll_size] = ERASE_CHAR;
+       if (selstart > scroll_top &&
+           selstart < scroll_top + size + scroll_size) {
+           selstart -= size;
+           if (selstart < scroll_top)
+               selstart = scroll_top;
+       }
+       if (selend > scroll_top &&
+           selend < scroll_top + size + scroll_size) {
+           selend -= size;
+           if (selend < scroll_top)
+               selend = scroll_top;
+       }
+    } else {
+       if (scroll_size)
+           memmove (scroll_top + size, scroll_top, scroll_size*TSIZE);
+       for (i = 0; i < size; i++)
+           scroll_top[i] = ERASE_CHAR;
+       if (selstart > scroll_top &&
+           selstart < scroll_top + size + scroll_size) {
+           selstart += size;
+           if (selstart > scroll_top + size + scroll_size)
+               selstart = scroll_top + size + scroll_size;
+       }
+       if (selend > scroll_top &&
+           selend < scroll_top + size + scroll_size) {
+           selend += size;
+           if (selend > scroll_top + size + scroll_size)
+               selend = scroll_top + size + scroll_size;
+       }
+    }
+
+    scroll_heuristic += lines;
+}
+
+/*
+ * Move the cursor to a given position, clipping at boundaries. We
+ * may or may not want to clip at the scroll margin: marg_clip is 0
+ * not to, 1 to disallow _passing_ the margins, and 2 to disallow
+ * even _being_ outside the margins.
+ */
+static void move (int x, int y, int marg_clip) {
+    if (x < 0)
+       x = 0;
+    if (x >= cols)
+       x = cols-1;
+    if (marg_clip) {
+       if ((curs_y >= marg_t || marg_clip == 2) && y < marg_t)
+           y = marg_t;
+       if ((curs_y <= marg_b || marg_clip == 2) && y > marg_b)
+           y = marg_b;
+    }
+    if (y < 0)
+       y = 0;
+    if (y >= rows)
+       y = rows-1;
+    curs_x = x;
+    curs_y = y;
+    fix_cpos;
+    wrapnext = FALSE;
+}
+
+/*
+ * Save or restore the cursor and SGR mode.
+ */
+static void save_cursor(int save) {
+    if (save) {
+       save_x = curs_x;
+       save_y = curs_y;
+       save_attr = curr_attr;
+       save_cset = cset;
+       save_csattr = cset_attr[cset];
+    } else {
+       curs_x = save_x;
+       curs_y = save_y;
+       curr_attr = save_attr;
+       cset = save_cset;
+       cset_attr[cset] = save_csattr;
+       fix_cpos;
+    }
+}
+
+/*
+ * Erase a large portion of the screen: the whole screen, or the
+ * whole line, or parts thereof.
+ */
+static void erase_lots (int line_only, int from_begin, int to_end) {
+    unsigned long *startpos, *endpos;
+
+    if (line_only) {
+       startpos = cpos - curs_x;
+       endpos = startpos + cols+1;
+    } else {
+       startpos = scrtop;
+       endpos = startpos + rows * (cols+1);
+    }
+    if (!from_begin)
+       startpos = cpos;
+    if (!to_end)
+       endpos = cpos;
+    check_selection (startpos, endpos);
+    while (startpos < endpos)
+       *startpos++ = ERASE_CHAR;
+}
+
+/*
+ * Insert or delete characters within the current line. n is +ve if
+ * insertion is desired, and -ve for deletion.
+ */
+static void insch (int n) {
+    int dir = (n < 0 ? -1 : +1);
+    int m;
+
+    n = (n < 0 ? -n : n);
+    if (n > cols - curs_x)
+       n = cols - curs_x;
+    m = cols - curs_x - n;
+    check_selection (cpos, cpos+n);
+    if (dir < 0) {
+       memmove (cpos, cpos+n, m*TSIZE);
+       while (n--)
+           cpos[m++] = ERASE_CHAR;
+    } else {
+       memmove (cpos+n, cpos, m*TSIZE);
+       while (n--)
+           cpos[n] = ERASE_CHAR;
+    }
+}
+
+/*
+ * Toggle terminal mode `mode' to state `state'. (`query' indicates
+ * whether the mode is a DEC private one or a normal one.)
+ */
+static void toggle_mode (int mode, int query, int state) {
+    if (query) switch (mode) {
+      case 1:                         /* application cursor keys */
+       app_cursor_keys = state;
+       break;
+      case 3:                         /* 80/132 columns */
+       deselect();
+       request_resize (state ? 132 : 80, rows);
+       break;
+      case 5:                         /* reverse video */
+       rvideo = state;
+       disptop = scrtop;
+       break;
+      case 6:                         /* DEC origin mode */
+       dec_om = state;
+       break;
+      case 7:                         /* auto wrap */
+       wrap = state;
+       break;
+      case 47:                        /* alternate screen */
+       deselect();
+       swap_screen (state);
+       disptop = scrtop;
+       break;
+    } else switch (mode) {
+      case 4:                         /* set insert mode */
+       insert = state;
+       break;
+    }
+}
+
+/*
+ * Process an OSC sequence: set window title or icon name.
+ */
+static void do_osc(void) {
+    if (osc_w) {
+       while (osc_strlen--)
+           wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
+    } else {
+       osc_string[osc_strlen] = '\0';
+       switch (esc_args[0]) {
+         case 0:
+         case 1:
+           set_icon (osc_string);
+           if (esc_args[0] == 1)
+               break;
+           /* fall through: parameter 0 means set both */
+         case 2:
+         case 21:
+           set_title (osc_string);
+           break;
+       }
+    }
+}
+
+/*
+ * Remove everything currently in `inbuf' and stick it up on the
+ * in-memory display. There's a big state machine in here to
+ * process escape sequences...
+ */
+void term_out(void) {
+    int c;
+    int must_update = FALSE;
+
+    while ( (c = inbuf_getc()) != -1) {
+#ifdef LOG
+       {
+           static FILE *fp = NULL;
+           if (!fp) fp = fopen("putty.log", "wb");
+           if (fp) fputc (c, fp);
+       }
+#endif
+       switch (termstate) {
+         case TOPLEVEL:
+           do_toplevel:
+           switch (c) {
+             case '\005':             /* terminal type query */
+               back->send ("\033[?1;2c", 7);
+               break;
+             case '\007':
+               beep();
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case '\b':
+               if (curs_x == 0 && curs_y > 0)
+                   curs_x = cols-1, curs_y--;
+               else if (wrapnext)
+                   wrapnext = FALSE;
+               else
+                   curs_x--;
+               fix_cpos;
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case '\016':
+               cset = 1;
+               break;
+             case '\017':
+               cset = 0;
+               break;
+             case '\033':
+               termstate = SEEN_ESC;
+               break;
+             case 0233:
+               termstate = SEEN_CSI;
+               esc_nargs = 1;
+               esc_args[0] = ARG_DEFAULT;
+               esc_query = FALSE;
+               break;
+             case 0235:
+               termstate = SEEN_OSC;
+               esc_args[0] = 0;
+               break;
+             case '\r':
+               curs_x = 0;
+               wrapnext = FALSE;
+               fix_cpos;
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case '\013':
+             case '\014':
+             case '\n':
+               if (curs_y == marg_b)
+                   scroll (marg_t, marg_b, 1, TRUE);
+               else if (curs_y < rows-1)
+                   curs_y++;
+               fix_cpos;
+               wrapnext = FALSE;
+               disptop = scrtop;
+               nl_count++;
+               break;
+             case '\t':
+               do {
+                   curs_x++;
+               } while (curs_x < cols-1 && !tabs[curs_x]);
+               if (curs_x >= cols)
+                   curs_x = cols-1;
+               {
+                   unsigned long *old_cpos = cpos;
+                   fix_cpos;
+                   check_selection (old_cpos, cpos);
+               }
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             default:
+               if (c >= ' ' && c != 0234) {
+                   if (wrapnext) {
+                       cpos[1] = ATTR_WRAPPED;
+                       if (curs_y == marg_b)
+                           scroll (marg_t, marg_b, 1, TRUE);
+                       else if (curs_y < rows-1)
+                           curs_y++;
+                       curs_x = 0;
+                       fix_cpos;
+                       wrapnext = FALSE;
+                       nl_count++;
+                   }
+                   if (insert)
+                       insch (1);
+                   check_selection (cpos, cpos+1);
+                   *cpos++ = c | curr_attr | 
+                       (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
+                   curs_x++;
+                   if (curs_x == cols) {
+                       cpos--;
+                       curs_x--;
+                       wrapnext = wrap;
+                   }
+                   disptop = scrtop;
+               }
+           }
+           break;
+         case IGNORE_NEXT:
+           termstate = TOPLEVEL;
+           break;
+         case OSC_MAYBE_ST:
+           /*
+            * This state is virtually identical to SEEN_ESC, with the
+            * exception that we have an OSC sequence in the pipeline,
+            * and _if_ we see a backslash, we process it.
+            */
+           if (c == '\\') {
+               do_osc();
+               termstate = TOPLEVEL;
+               break;
+           }
+           /* else fall through */
+         case SEEN_ESC:
+           termstate = TOPLEVEL;
+           switch (c) {
+             case '\005': case '\007': case '\b': case '\016': case '\017':
+             case '\033': case 0233: case 0234: case 0235: case '\r':
+             case '\013': case '\014': case '\n': case '\t':
+               termstate = TOPLEVEL;
+               goto do_toplevel;      /* hack... */
+             case ' ':                /* some weird sequence? */
+               termstate = IGNORE_NEXT;
+               break;
+             case '[':                /* enter CSI mode */
+               termstate = SEEN_CSI;
+               esc_nargs = 1;
+               esc_args[0] = ARG_DEFAULT;
+               esc_query = FALSE;
+               break;
+             case ']':                /* xterm escape sequences */
+               termstate = SEEN_OSC;
+               esc_args[0] = 0;
+               break;
+             case '(':                /* should set GL */
+               termstate = SET_GL;
+               break;
+             case ')':                /* should set GR */
+               termstate = SET_GR;
+               break;
+             case '7':                /* save cursor */
+               save_cursor (TRUE);
+               break;
+             case '8':                /* restore cursor */
+               save_cursor (FALSE);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case '=':
+               app_keypad_keys = TRUE;
+               break;
+             case '>':
+               app_keypad_keys = FALSE;
+               break;
+             case 'D':                /* exactly equivalent to LF */
+               if (curs_y == marg_b)
+                   scroll (marg_t, marg_b, 1, TRUE);
+               else if (curs_y < rows-1)
+                   curs_y++;
+               fix_cpos;
+               wrapnext = FALSE;
+               disptop = scrtop;
+               nl_count++;
+               break;
+             case 'E':                /* exactly equivalent to CR-LF */
+               curs_x = 0;
+               wrapnext = FALSE;
+               if (curs_y == marg_b)
+                   scroll (marg_t, marg_b, 1, TRUE);
+               else if (curs_y < rows-1)
+                   curs_y++;
+               fix_cpos;
+               wrapnext = FALSE;
+               nl_count++;
+               disptop = scrtop;
+               break;
+             case 'M':                /* reverse index - backwards LF */
+               if (curs_y == marg_t)
+                   scroll (marg_t, marg_b, -1, TRUE);
+               else if (curs_y > 0)
+                   curs_y--;
+               fix_cpos;
+               wrapnext = FALSE;
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'Z':                /* terminal type query */
+               back->send ("\033[?6c", 5);
+               break;
+             case 'c':                /* restore power-on settings */
+               power_on();
+               fix_cpos;
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case '#':                /* ESC # 8 fills screen with Es :-) */
+               termstate = SEEN_ESCHASH;
+               break;
+             case 'H':                /* set a tab */
+               tabs[curs_x] = TRUE;
+               break;
+           }
+           break;
+         case SEEN_CSI:
+           termstate = TOPLEVEL;      /* default */
+           switch (c) {
+             case '\005': case '\007': case '\b': case '\016': case '\017':
+             case '\033': case 0233: case 0234: case 0235: case '\r':
+             case '\013': case '\014': case '\n': case '\t':
+               termstate = TOPLEVEL;
+               goto do_toplevel;      /* hack... */
+             case '0': case '1': case '2': case '3': case '4':
+             case '5': case '6': case '7': case '8': case '9':
+               if (esc_nargs <= ARGS_MAX) {
+                   if (esc_args[esc_nargs-1] == ARG_DEFAULT)
+                       esc_args[esc_nargs-1] = 0;
+                   esc_args[esc_nargs-1] =
+                       10 * esc_args[esc_nargs-1] + c - '0';
+               }
+               termstate = SEEN_CSI;
+               break;
+             case ';':
+               if (++esc_nargs <= ARGS_MAX)
+                   esc_args[esc_nargs-1] = ARG_DEFAULT;
+               termstate = SEEN_CSI;
+               break;
+             case '?':
+               esc_query = TRUE;
+               termstate = SEEN_CSI;
+               break;
+             case 'A':                /* move up N lines */
+               move (curs_x, curs_y - def(esc_args[0], 1), 1);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'B': case 'e':      /* move down N lines */
+               move (curs_x, curs_y + def(esc_args[0], 1), 1);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'C': case 'a':      /* move right N cols */
+               move (curs_x + def(esc_args[0], 1), curs_y, 1);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'D':                /* move left N cols */
+               move (curs_x - def(esc_args[0], 1), curs_y, 1);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'E':                /* move down N lines and CR */
+               move (0, curs_y + def(esc_args[0], 1), 1);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'F':                /* move up N lines and CR */
+               move (0, curs_y - def(esc_args[0], 1), 1);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'G': case '`':      /* set horizontal posn */
+               move (def(esc_args[0], 1) - 1, curs_y, 0);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'd':                /* set vertical posn */
+               move (curs_x, (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
+                     (dec_om ? 2 : 0));
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'H': case 'f':      /* set horz and vert posns at once */
+               if (esc_nargs < 2)
+                   esc_args[1] = ARG_DEFAULT;
+               move (def(esc_args[1], 1) - 1,
+                     (dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
+                     (dec_om ? 2 : 0));
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'J':                /* erase screen or parts of it */
+               {
+                   unsigned int i = def(esc_args[0], 0) + 1;
+                   if (i > 3)
+                       i = 0;
+                   erase_lots(FALSE, !!(i & 2), !!(i & 1));
+               }
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'K':                /* erase line or parts of it */
+               {
+                   unsigned int i = def(esc_args[0], 0) + 1;
+                   if (i > 3)
+                       i = 0;
+                   erase_lots(TRUE, !!(i & 2), !!(i & 1));
+               }
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'L':                /* insert lines */
+               if (curs_y <= marg_b)
+                   scroll (curs_y, marg_b, -def(esc_args[0], 1), FALSE);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'M':                /* delete lines */
+               if (curs_y <= marg_b)
+                   scroll (curs_y, marg_b, def(esc_args[0], 1), FALSE);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case '@':                /* insert chars */
+               insch (def(esc_args[0], 1));
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'P':                /* delete chars */
+               insch (-def(esc_args[0], 1));
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 'c':                /* terminal type query */
+               back->send ("\033[?6c", 5);
+               break;
+             case 'n':                /* cursor position query */
+               if (esc_args[0] == 6) {
+                   char buf[32];
+                   sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
+                   back->send (buf, strlen(buf));
+               }
+               break;
+             case 'h':                /* toggle a mode to high */
+               toggle_mode (esc_args[0], esc_query, TRUE);
+               break;
+             case 'l':                /* toggle a mode to low */
+               toggle_mode (esc_args[0], esc_query, FALSE);
+               break;
+             case 'g':                /* clear tabs */
+               if (esc_nargs == 1) {
+                   if (esc_args[0] == 0) {
+                       tabs[curs_x] = FALSE;
+                   } else if (esc_args[0] == 3) {
+                       int i;
+                       for (i = 0; i < cols; i++)
+                           tabs[i] = FALSE;
+                   }
+               }
+               break;
+             case 'r':                /* set scroll margins */
+               if (esc_nargs <= 2) {
+                   int top, bot;
+                   top = def(esc_args[0], 1) - 1;
+                   if (top < 0)
+                       top = 0;
+                   bot = (esc_nargs == 1 ? rows :
+                          def(esc_args[1], rows)) - 1;
+                   if (bot >= rows)
+                       bot = rows-1;
+                   if (top <= bot) {
+                       marg_t = top;
+                       marg_b = bot;
+                       curs_x = 0;
+                       /*
+                        * I used to think the cursor should be
+                        * placed at the top of the newly marginned
+                        * area. Apparently not: VMS TPU falls over
+                        * if so.
+                        */
+                       curs_y = 0;
+                       fix_cpos;
+                       disptop = scrtop;
+                       must_update = TRUE;
+                   }
+               }
+               break;
+             case 'm':                /* set graphics rendition */
+               {
+                   int i;
+                   for (i=0; i<esc_nargs; i++) {
+                       switch (def(esc_args[i], 0)) {
+                         case 0:      /* restore defaults */
+                           curr_attr = ATTR_DEFAULT; break;
+                         case 1:      /* enable bold */
+                           curr_attr |= ATTR_BOLD; break;
+                         case 4:      /* enable underline */
+                         case 21:     /* (enable double underline) */
+                           curr_attr |= ATTR_UNDER; break;
+                         case 7:      /* enable reverse video */
+                           curr_attr |= ATTR_REVERSE; break;
+                         case 22:     /* disable bold */
+                           curr_attr &= ~ATTR_BOLD; break;
+                         case 24:     /* disable underline */
+                           curr_attr &= ~ATTR_UNDER; break;
+                         case 27:     /* disable reverse video */
+                           curr_attr &= ~ATTR_REVERSE; break;
+                         case 30: case 31: case 32: case 33:
+                         case 34: case 35: case 36: case 37:
+                           /* foreground */
+                           curr_attr &= ~ATTR_FGMASK;
+                           curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
+                           break;
+                         case 39:     /* default-foreground */
+                           curr_attr &= ~ATTR_FGMASK;
+                           curr_attr |= ATTR_DEFFG;
+                           break;
+                         case 40: case 41: case 42: case 43:
+                         case 44: case 45: case 46: case 47:
+                           /* background */
+                           curr_attr &= ~ATTR_BGMASK;
+                           curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
+                           break;
+                         case 49:     /* default-background */
+                           curr_attr &= ~ATTR_BGMASK;
+                           curr_attr |= ATTR_DEFBG;
+                           break;
+                       }
+                   }
+               }
+               break;
+             case 's':                /* save cursor */
+               save_cursor (TRUE);
+               break;
+             case 'u':                /* restore cursor */
+               save_cursor (FALSE);
+               disptop = scrtop;
+               must_update = TRUE;
+               break;
+             case 't':                /* set page size - ie window height */
+               request_resize (cols, def(esc_args[0], 24));
+               deselect();
+               break;
+             case 'X':                /* write N spaces w/o moving cursor */
+               {
+                   int n = def(esc_args[0], 1);
+                   unsigned long *p = cpos;
+                   if (n > cols - curs_x)
+                       n = cols - curs_x;
+                   check_selection (cpos, cpos+n);
+                   while (n--)
+                       *p++ = ERASE_CHAR;
+                   disptop = scrtop;
+                   must_update = TRUE;
+               }
+               break;
+             case 'x':                /* report terminal characteristics */
+               {
+                   char buf[32];
+                   int i = def(esc_args[0], 0);
+                   if (i == 0 || i == 1) {
+                       strcpy (buf, "\033[2;1;1;112;112;1;0x");
+                       buf[2] += i;
+                       back->send (buf, 20);
+                   }
+               }
+               break;
+           }
+           break;
+         case SET_GL:
+         case SET_GR:
+           switch (c) {
+             case 'A':
+               cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
+               break;
+             case '0':
+               cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
+               break;
+             default:                 /* specifically, 'B' */
+               cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
+               break;
+           }
+           termstate = TOPLEVEL;
+           break;
+         case SEEN_OSC:
+           osc_w = FALSE;
+           switch (c) {
+             case '\005': case '\007': case '\b': case '\016': case '\017':
+             case '\033': case 0233: case 0234: case 0235: case '\r':
+             case '\013': case '\014': case '\n': case '\t':
+               termstate = TOPLEVEL;
+               goto do_toplevel;      /* hack... */
+             case 'P':                /* Linux palette sequence */
+               termstate = SEEN_OSC_P;
+               osc_strlen = 0;
+               break;
+             case 'R':                /* Linux palette reset */
+               palette_reset();
+               term_invalidate();
+               termstate = TOPLEVEL;
+               break;
+             case 'W':                /* word-set */
+               termstate = SEEN_OSC_W;
+               osc_w = TRUE;
+               break;
+             case '0': case '1': case '2': case '3': case '4':
+             case '5': case '6': case '7': case '8': case '9':
+               esc_args[0] = 10 * esc_args[0] + c - '0';
+               break;
+             case 'L':
+               /*
+                * Grotty hack to support xterm and DECterm title
+                * sequences concurrently.
+                */
+               if (esc_args[0] == 2) {
+                   esc_args[0] = 1;
+                   break;
+               }
+               /* else fall through */
+             default:
+               termstate = OSC_STRING;
+               osc_strlen = 0;
+           }
+           break;
+         case OSC_STRING:
+           if (c == 0234 || c == '\007') {
+               /*
+                * These characters terminate the string; ST and BEL
+                * terminate the sequence and trigger instant
+                * processing of it, whereas ESC goes back to SEEN_ESC
+                * mode unless it is followed by \, in which case it is
+                * synonymous with ST in the first place.
+                */
+               do_osc();
+               termstate = TOPLEVEL;
+           } else if (c == '\033')
+                   termstate = OSC_MAYBE_ST;
+           else if (osc_strlen < OSC_STR_MAX)
+               osc_string[osc_strlen++] = c;
+           break;
+         case SEEN_OSC_P:
+           {
+               int max = (osc_strlen == 0 ? 21 : 16);
+               int val;
+               if (c >= '0' && c <= '9')
+                   val = c - '0';
+               else if (c >= 'A' && c <= 'A'+max-10)
+                   val = c - 'A' + 10;
+               else if (c >= 'a' && c <= 'a'+max-10)
+                   val = c - 'a' + 10;
+               else
+                   termstate = TOPLEVEL;
+               osc_string[osc_strlen++] = val;
+               if (osc_strlen >= 7) {
+                   palette_set (osc_string[0],
+                                osc_string[1] * 16 + osc_string[2],
+                                osc_string[3] * 16 + osc_string[4],
+                                osc_string[5] * 16 + osc_string[6]);
+                   term_invalidate();
+                   termstate = TOPLEVEL;
+               }
+           }
+           break;
+         case SEEN_OSC_W:
+           switch (c) {
+             case '\005': case '\007': case '\b': case '\016': case '\017':
+             case '\033': case 0233: case 0234: case 0235: case '\r':
+             case '\013': case '\014': case '\n': case '\t':
+               termstate = TOPLEVEL;
+               goto do_toplevel;      /* hack... */
+             case '0': case '1': case '2': case '3': case '4':
+             case '5': case '6': case '7': case '8': case '9':
+               esc_args[0] = 10 * esc_args[0] + c - '0';
+               break;
+             default:
+               termstate = OSC_STRING;
+               osc_strlen = 0;
+           }
+           break;
+         case SEEN_ESCHASH:
+           if (c == '8') {
+               unsigned long *p = scrtop;
+               int n = rows * (cols+1);
+               while (n--)
+                   *p++ = ATTR_DEFAULT | 'E';
+               disptop = scrtop;
+               must_update = TRUE;
+               check_selection (scrtop, scrtop + rows * (cols+1));
+           }
+           termstate = TOPLEVEL;
+           break;
+       }
+       check_selection (cpos, cpos+1);
+    }
+       
+    if (must_update || nl_count > MAXNL)
+       term_update();
+}
+
+/*
+ * Compare two lines to determine whether they are sufficiently
+ * alike to scroll-optimise one to the other. Return the degree of
+ * similarity.
+ */
+static int linecmp (unsigned long *a, unsigned long *b) {
+    int i, n;
+
+    for (i=n=0; i < cols; i++)
+       n += (*a++ == *b++);
+    return n;
+}
+
+/*
+ * Given a context, update the window. Out of paranoia, we don't
+ * allow WM_PAINT responses to do scrolling optimisations.
+ */
+static void do_paint (Context ctx, int may_optimise){ 
+    int i, j, start, our_curs_y;
+    unsigned long attr, rv, cursor;
+    char ch[1024];
+
+    cursor = (has_focus ? ATTR_ACTCURS : ATTR_PASCURS);
+    rv = (rvideo ? ATTR_REVERSE : 0);
+    our_curs_y = curs_y + (scrtop - disptop) / (cols+1);
+
+    for (i=0; i<rows; i++) {
+       int idx = i*(cols+1);
+       for (j=0; j<=cols; j++,idx++) {
+           unsigned long *d = disptop+idx;
+           wanttext[idx] = ((*d ^ rv
+                             ^ (selstart <= d && d < selend ?
+                                ATTR_REVERSE : 0)) |
+                            (i==our_curs_y && j==curs_x ? cursor : 0));
+       }
+    }
+
+    /*
+     * We would perform scrolling optimisations in here, if they
+     * didn't have a nasty tendency to cause the whole sodding
+     * program to hang for a second at speed-critical moments.
+     * We'll leave it well alone...
+     */
+
+    for (i=0; i<rows; i++) {
+       int idx = i*(cols+1);
+       start = -1;
+       for (j=0; j<=cols; j++,idx++) {
+           unsigned long t = wanttext[idx];
+           int needs_update = (j < cols && t != disptext[idx]);
+           int keep_going = (start != -1 && needs_update &&
+                             (t & ATTR_MASK) == attr &&
+                             j-start < sizeof(ch));
+           if (start != -1 && !keep_going) {
+               do_text (ctx, start, i, ch, j-start, attr);
+               start = -1;
+           }
+           if (needs_update) {
+               if (start == -1) {
+                   start = j;
+                   attr = t & ATTR_MASK;
+               }
+               ch[j-start] = (char) (t & CHAR_MASK);
+           }
+           disptext[idx] = t;
+       }
+    }
+}
+
+/*
+ * Invalidate the whole screen so it will be repainted in full.
+ */
+void term_invalidate(void) {
+    int i;
+
+    for (i=0; i<rows*(cols+1); i++)
+       disptext[i] = ATTR_INVALID;
+}
+
+/*
+ * Paint the window in response to a WM_PAINT message.
+ */
+void term_paint (Context ctx, int l, int t, int r, int b) {
+    int i, j, left, top, right, bottom;
+
+    left = l / font_width;
+    right = (r - 1) / font_width;
+    top = t / font_height;
+    bottom = (b - 1) / font_height;
+    for (i = top; i <= bottom; i++)
+       for (j = left; j <= right; j++)
+           disptext[i*(cols+1)+j] = ATTR_INVALID;
+
+    do_paint (ctx, FALSE);
+}
+
+/*
+ * Attempt to scroll the scrollback. The second parameter gives the
+ * position we want to scroll to; the first is +1 to denote that
+ * this position is relative to the beginning of the scrollback, -1
+ * to denote it is relative to the end, and 0 to denote that it is
+ * relative to the current position.
+ */
+void term_scroll (int rel, int where) {
+    int n = where * (cols+1);
+
+    disptop = (rel < 0 ? scrtop :
+              rel > 0 ? sbtop : disptop) + n;
+    if (disptop < sbtop)
+       disptop = sbtop;
+    if (disptop > scrtop)
+       disptop = scrtop;
+    update_sbar();
+    term_update();
+}
+
+/*
+ * Spread the selection outwards according to the selection mode.
+ */
+static unsigned long *sel_spread_half (unsigned long *p, int dir) {
+    unsigned long *linestart, *lineend;
+    int x;
+    short wvalue;
+
+    x = (p - text) % (cols+1);
+    linestart = p - x;
+    lineend = linestart + cols;
+
+    switch (selmode) {
+      case SM_CHAR:
+       /*
+        * In this mode, every character is a separate unit, except
+        * for runs of spaces at the end of a non-wrapping line.
+        */
+       if (!(linestart[cols] & ATTR_WRAPPED)) {
+           unsigned long *q = lineend;
+           while (q > linestart && (q[-1] & CHAR_MASK) == 0x20)
+               q--;
+           if (q == lineend)
+               q--;
+           if (p >= q)
+               p = (dir == -1 ? q : lineend - 1);
+       }
+       break;
+      case SM_WORD:
+       /*
+        * In this mode, the units are maximal runs of characters
+        * whose `wordness' has the same value.
+        */
+       wvalue = wordness[*p & CHAR_MASK];
+       if (dir == +1) {
+           while (p < lineend && wordness[p[1] & CHAR_MASK] == wvalue)
+               p++;
+       } else {
+           while (p > linestart && wordness[p[-1] & CHAR_MASK] == wvalue)
+               p--;
+       }
+       break;
+      case SM_LINE:
+       /*
+        * In this mode, every line is a unit.
+        */
+       p = (dir == -1 ? linestart : lineend - 1);
+       break;
+    }
+    return p;
+}
+
+static void sel_spread (void) {
+    selstart = sel_spread_half (selstart, -1);
+    selend = sel_spread_half (selend - 1, +1) + 1;
+}
+
+void term_mouse (Mouse_Button b, Mouse_Action a, int x, int y) {
+    unsigned long *selpoint = disptop + y * (cols+1) + x;
+
+    if (b == MB_SELECT && a == MA_CLICK) {
+       deselect();
+       selstate = ABOUT_TO;
+       selanchor = selpoint;
+       selmode = SM_CHAR;
+    } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
+       deselect();
+       selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
+       selstate = DRAGGING;
+       selstart = selanchor = selpoint;
+       selend = selstart + 1;
+       sel_spread();
+    } else if ((b == MB_SELECT && a == MA_DRAG) ||
+              (b == MB_EXTEND && a != MA_RELEASE)) {
+       if (selstate == ABOUT_TO && selanchor == selpoint)
+           return;
+       if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) {
+           if (selpoint-selstart < (selend-selstart)/2)
+               selanchor = selend - 1;
+           else
+               selanchor = selstart;
+           selstate = DRAGGING;
+       }
+       if (selstate != ABOUT_TO && selstate != DRAGGING)
+           selanchor = selpoint;
+       selstate = DRAGGING;
+       if (selpoint < selanchor) {
+           selstart = selpoint;
+           selend = selanchor + 1;
+       } else {
+           selstart = selanchor;
+           selend = selpoint + 1;
+       }
+       sel_spread();
+    } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE) {
+       if (selstate == DRAGGING) {
+           /*
+            * We've completed a selection. We now transfer the
+            * data to the clipboard.
+            */
+           unsigned char *p = selspace;
+           unsigned long *q = selstart;
+
+           while (q < selend) {
+               int nl = FALSE;
+               unsigned long *lineend = q - (q-text) % (cols+1) + cols;
+               unsigned long *nlpos = lineend;
+
+               if (!(*nlpos & ATTR_WRAPPED)) {
+                   while ((nlpos[-1] & CHAR_MASK) == 0x20 && nlpos > q)
+                       nlpos--;
+                   if (nlpos < selend)
+                       nl = TRUE;
+               }
+               while (q < nlpos && q < selend)
+                   *p++ = (unsigned char) (*q++ & CHAR_MASK);
+               if (nl) {
+                   int i;
+                   for (i=0; i<sizeof(sel_nl); i++)
+                       *p++ = sel_nl[i];
+               }
+               q = lineend + 1;       /* start of next line */
+           }
+           write_clip (selspace, p - selspace);
+           selstate = SELECTED;
+       } else
+           selstate = NO_SELECTION;
+    } else if (b == MB_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK)) {
+       char *data;
+       int len;
+
+       get_clip((void **) &data, &len);
+       if (data) {
+           char *p, *q;
+           p = q = data;
+           while (p < data+len) {
+               while (p < data+len &&
+                      !(p <= data+len-sizeof(sel_nl) &&
+                        !memcmp(p, sel_nl, sizeof(sel_nl))))
+                   p++;
+               back->send (q, p-q);
+               if (p <= data+len-sizeof(sel_nl) &&
+                   !memcmp(p, sel_nl, sizeof(sel_nl))) {
+                   back->send ("\r", 1);
+                   p += sizeof(sel_nl);
+               }
+               q = p;
+           }
+       }
+       get_clip(NULL, NULL);
+    }
+
+    term_update();
+}
+
+static void deselect (void) {
+    selstate = NO_SELECTION;
+    selstart = selend = scrtop;
+}
+
+void term_deselect (void) {
+    deselect();
+    term_update();
+}
diff --git a/win_res.h b/win_res.h
new file mode 100644 (file)
index 0000000..56c7a0c
--- /dev/null
+++ b/win_res.h
@@ -0,0 +1,116 @@
+#ifndef PUTTY_WIN_RES_H
+#define PUTTY_WIN_RES_H
+
+#define IDI_MAINICON     200
+
+#define IDD_MAINBOX      102
+#define IDD_PANEL0       103
+#define IDD_PANEL1       104
+#define IDD_PANEL2       105
+#define IDD_PANEL3       106
+#define IDD_PANEL35      107
+#define IDD_PANEL4       108
+#define IDD_PANEL5       109
+#define IDD_LOGBOX       110
+#define IDD_ABOUTBOX     111
+#define IDD_RECONF       112
+#define IDD_LICENCEBOX   113
+
+#define IDN_LIST        1001
+
+#define IDA_ICON        1001
+#define IDA_TEXT        1002
+#define IDA_LICENCE     1003
+
+#define IDC_TAB         1001
+#define IDC_ABOUT       1002
+
+#define IDC0_HOSTSTATIC 1001
+#define IDC0_HOST       1002
+#define IDC0_PORTSTATIC 1003
+#define IDC0_PORT       1004
+#define IDC0_PROTSTATIC 1005
+#define IDC0_PROTTELNET 1006
+#define IDC0_PROTSSH    1007
+#define IDC0_SESSSTATIC 1008
+#define IDC0_SESSEDIT   1009
+#define IDC0_SESSLIST   1010
+#define IDC0_SESSLOAD   1011
+#define IDC0_SESSSAVE   1012
+#define IDC0_SESSDEL    1013
+#define IDC0_CLOSEEXIT  1014
+
+#define IDC1_DELSTATIC  1001
+#define IDC1_DEL008     1002
+#define IDC1_DEL127     1003
+#define IDC1_HOMESTATIC 1004
+#define IDC1_HOMETILDE  1005
+#define IDC1_HOMERXVT   1006
+#define IDC1_FUNCSTATIC 1007
+#define IDC1_FUNCTILDE  1008
+#define IDC1_FUNCLINUX  1009
+#define IDC1_KPSTATIC   1010
+#define IDC1_KPNORMAL   1011
+#define IDC1_KPAPPLIC   1012
+#define IDC1_CURSTATIC  1013
+#define IDC1_CURNORMAL  1014
+#define IDC1_CURAPPLIC  1015
+
+#define IDC2_WRAPMODE   1001
+#define IDC2_DECOM      1002
+#define IDC2_WINNAME    1003
+#define IDC2_DIMSTATIC  1004
+#define IDC2_ROWSSTATIC 1005
+#define IDC2_ROWSEDIT   1006
+#define IDC2_COLSSTATIC 1007
+#define IDC2_COLSEDIT   1008
+#define IDC2_SAVESTATIC 1009
+#define IDC2_SAVEEDIT   1010
+#define IDC2_FONTSTATIC 1011
+#define IDC2_CHOOSEFONT 1012
+#define IDC2_VTSTATIC   1013
+#define IDC2_VTXWINDOWS 1014
+#define IDC2_VTOEMANSI  1015
+#define IDC2_VTOEMONLY  1016
+#define IDC2_VTPOORMAN  1017
+
+#define IDC3_TTSTATIC   1001
+#define IDC3_TTEDIT     1002
+#define IDC3_TSSTATIC   1003
+#define IDC3_TSEDIT     1004
+#define IDC3_LOGSTATIC  1005
+#define IDC3_LOGEDIT    1006
+#define IDC3_ENVSTATIC  1007
+#define IDC3_VARSTATIC  1008
+#define IDC3_VAREDIT    1009
+#define IDC3_VALSTATIC  1010
+#define IDC3_VALEDIT    1011
+#define IDC3_ENVLIST    1012
+#define IDC3_ENVADD     1013
+#define IDC3_ENVREMOVE  1014
+#define IDC3_EMSTATIC   1015
+#define IDC3_EMBSD      1016
+#define IDC3_EMRFC      1017
+
+#define IDC4_MBSTATIC   1001
+#define IDC4_MBWINDOWS  1002
+#define IDC4_MBXTERM    1003
+#define IDC4_CCSTATIC   1004
+#define IDC4_CCLIST     1005
+#define IDC4_CCSET      1006
+#define IDC4_CCSTATIC2  1007
+#define IDC4_CCEDIT     1008
+
+#define IDC5_BOLDCOLOUR 1001
+#define IDC5_PALETTE    1002
+#define IDC5_STATIC     1003
+#define IDC5_LIST       1004
+#define IDC5_RSTATIC    1005
+#define IDC5_GSTATIC    1006
+#define IDC5_BSTATIC    1007
+#define IDC5_RVALUE     1008
+#define IDC5_GVALUE     1009
+#define IDC5_BVALUE     1010
+#define IDC5_CHANGE     1011
+
+#endif
diff --git a/win_res.rc b/win_res.rc
new file mode 100644 (file)
index 0000000..de4216a
--- /dev/null
@@ -0,0 +1,235 @@
+#include <winresrc.h>
+
+#define TCS_MULTILINE 0x0200
+
+#include "win_res.h"
+
+IDI_MAINICON ICON "putty.ico"
+
+IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 136, 58
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About PuTTY"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON "&Close", IDOK, 82, 40, 48, 14
+    PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 40, 70, 14
+    ICON IDI_MAINICON, IDA_ICON, 10, 10, 0, 0
+    LTEXT "PuTTY Beta 0.43\n\251 1997-8 Simon Tatham\nAll rights reserved.",
+          IDA_TEXT, 40, 6, 96, 24
+END
+
+IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 180, 216
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "PuTTY Configuration"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON "&Open", IDOK, 86, 199, 44, 14
+    PUSHBUTTON "&Cancel", IDCANCEL, 133, 199, 44, 14
+    PUSHBUTTON "&About", IDC_ABOUT, 3, 199, 44, 14, NOT WS_TABSTOP
+    CONTROL "", IDC_TAB, L"SysTabControl32", TCS_MULTILINE | WS_TABSTOP,
+       3, 3, 174, 193
+END
+
+IDD_RECONF DIALOG DISCARDABLE 0, 0, 180, 216
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "PuTTY Reconfiguration"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON "&Apply", IDOK, 86, 199, 44, 14
+    PUSHBUTTON "&Cancel", IDCANCEL, 133, 199, 44, 14
+    CONTROL "", IDC_TAB, L"SysTabControl32", TCS_MULTILINE | WS_TABSTOP,
+       3, 3, 174, 193
+END
+
+IDD_PANEL0 DIALOG DISCARDABLE 6, 30, 168, 163
+STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT "Host Name", IDC0_HOSTSTATIC, 3, 3, 122, 8
+    EDITTEXT IDC0_HOST, 3, 11, 122, 12, ES_AUTOHSCROLL
+    LTEXT "Port", IDC0_PORTSTATIC, 131, 3, 34, 8
+    EDITTEXT IDC0_PORT, 131, 11, 34, 12
+#ifdef FWHACK
+    RTEXT "Protocol:", IDC0_PROTSTATIC, 3, 29, 77, 8
+    AUTORADIOBUTTON "Telnet", IDC0_PROTTELNET, 86, 29, 33, 10, WS_GROUP
+    AUTORADIOBUTTON "SSH/hack", IDC0_PROTSSH, 122, 29, 43, 10
+#else
+    RTEXT "Protocol:", IDC0_PROTSTATIC, 3, 29, 87, 8
+    AUTORADIOBUTTON "Telnet", IDC0_PROTTELNET, 96, 29, 33, 10, WS_GROUP
+    AUTORADIOBUTTON "SSH", IDC0_PROTSSH, 132, 29, 33, 10
+#endif
+    LTEXT "Stored Sessions", IDC0_SESSSTATIC, 3, 40, 122, 8
+    EDITTEXT IDC0_SESSEDIT, 3, 48, 122, 12, ES_AUTOHSCROLL
+    LISTBOX IDC0_SESSLIST, 3, 63, 122, 81, LBS_HASSTRINGS | WS_VSCROLL
+    PUSHBUTTON "Load", IDC0_SESSLOAD, 131, 63, 34, 14
+    PUSHBUTTON "Save", IDC0_SESSSAVE, 131, 80, 34, 14
+    PUSHBUTTON "Delete", IDC0_SESSDEL, 131, 97, 34, 14
+    AUTOCHECKBOX "Close Window on Exit", IDC0_CLOSEEXIT, 3, 147, 162, 10
+END
+
+IDD_PANEL1 DIALOG DISCARDABLE 6, 30, 168, 163
+STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT "Action of Backspace:", IDC1_DELSTATIC, 3, 3, 162, 8
+    AUTORADIOBUTTON "Control-H (ASCII 8)", IDC1_DEL008, 3, 12, 162, 10, WS_GROUP
+    AUTORADIOBUTTON "Control-? (ASCII 127)", IDC1_DEL127, 3, 22, 162, 10
+    LTEXT "Action of Home and End:", IDC1_HOMESTATIC, 3, 35, 162, 8
+    AUTORADIOBUTTON "Standard (ESC [ 1 ~ and ESC [ 4 ~)",
+       IDC1_HOMETILDE, 3, 44, 162, 10, WS_GROUP
+    AUTORADIOBUTTON "rxvt (ESC [ H and ESC O w)",
+       IDC1_HOMERXVT, 3, 54, 162, 10
+    LTEXT "Action of F1 through F5:", IDC1_FUNCSTATIC, 3, 67, 162, 8
+    AUTORADIOBUTTON "Standard (ESC [ 11 ~ through ESC [ 15 ~)",
+       IDC1_FUNCTILDE, 3, 76, 162, 10, WS_GROUP
+    AUTORADIOBUTTON "Linux (ESC [ [ A through ESC [ [ E)",
+       IDC1_FUNCLINUX, 3, 86, 162, 10
+    LTEXT "Initial state of numeric keypad:", IDC1_KPSTATIC, 3, 99, 162, 8
+    AUTORADIOBUTTON "Normal (depends on NumLock)",
+       IDC1_KPNORMAL, 3, 108, 162, 10, WS_GROUP
+    AUTORADIOBUTTON "Application (ESC O P etc)",
+       IDC1_KPAPPLIC, 3, 118, 162, 10
+    LTEXT "Initial state of cursor keys:", IDC1_CURSTATIC, 3, 131, 162, 8
+    AUTORADIOBUTTON "Normal (ESC [ A through ESC [ D)",
+       IDC1_CURNORMAL, 3, 140, 162, 10, WS_GROUP
+    AUTORADIOBUTTON "Application (ESC O A through ESC O D)",
+       IDC1_CURAPPLIC, 3, 150, 162, 10
+END
+
+IDD_PANEL2 DIALOG DISCARDABLE 6, 30, 168, 163
+STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER
+FONT 8, "MS Sans Serif"
+BEGIN
+    AUTOCHECKBOX "Auto wrap mode initially on", IDC2_WRAPMODE, 3, 3, 162, 10
+    AUTOCHECKBOX "DEC Origin Mode initially on", IDC2_DECOM, 3, 13, 162, 10
+    AUTOCHECKBOX "Avoid ever using icon title", IDC2_WINNAME, 3, 23, 162, 10
+    LTEXT "Terminal screen dimensions:", IDC2_DIMSTATIC, 3, 33, 162, 8
+    RTEXT "Rows", IDC2_ROWSSTATIC, 20, 44, 90, 8
+    EDITTEXT IDC2_ROWSEDIT, 118, 42, 30, 12
+    RTEXT "Columns", IDC2_COLSSTATIC, 20, 59, 90, 8
+    EDITTEXT IDC2_COLSEDIT, 118, 57, 30, 12
+    RTEXT "Saved lines of scrollback", IDC2_SAVESTATIC, 20, 74, 90, 8
+    EDITTEXT IDC2_SAVEEDIT, 118, 72, 30, 12
+    LTEXT "Font:", IDC2_FONTSTATIC, 3, 93, 99, 8
+    PUSHBUTTON "Change...", IDC2_CHOOSEFONT, 105, 90, 60, 14
+    LTEXT "Handling of VT100 line drawing characters:",IDC2_VTSTATIC, 3, 111, 162, 8
+    AUTORADIOBUTTON "Font has XWindows encoding",
+       IDC2_VTXWINDOWS, 3, 120, 162, 10, WS_GROUP
+    AUTORADIOBUTTON "Use font in both ANSI and OEM modes",
+       IDC2_VTOEMANSI, 3, 130, 162, 10
+    AUTORADIOBUTTON "Use font in OEM mode only",
+       IDC2_VTOEMONLY, 3, 140, 162, 10
+    AUTORADIOBUTTON "Poor man's line drawing (""+"", ""-"" and ""|"")",
+       IDC2_VTPOORMAN, 3, 150, 162, 10
+END
+
+IDD_PANEL3 DIALOG DISCARDABLE 6, 30, 168, 163
+STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT "Terminal-type string", IDC3_TTSTATIC, 3, 5, 90, 8
+    EDITTEXT IDC3_TTEDIT, 96, 3, 69, 12, ES_AUTOHSCROLL
+    LTEXT "Terminal-speed string", IDC3_TSSTATIC, 3, 20, 90, 8
+    EDITTEXT IDC3_TSEDIT, 96, 18, 69, 12, ES_AUTOHSCROLL
+    LTEXT "Auto-login username", IDC3_LOGSTATIC, 3, 35, 90, 8
+    EDITTEXT IDC3_LOGEDIT, 96, 33, 69, 12, ES_AUTOHSCROLL
+    LTEXT "Environment variables:", IDC3_ENVSTATIC, 3, 53, 162, 8
+    LTEXT "Variable", IDC3_VARSTATIC, 3, 70, 29, 8
+    EDITTEXT IDC3_VAREDIT, 35, 68, 35, 12, ES_AUTOHSCROLL
+    LTEXT "Value", IDC3_VALSTATIC, 76, 70, 19, 8
+    EDITTEXT IDC3_VALEDIT, 98, 68, 67, 12, ES_AUTOHSCROLL
+    LISTBOX IDC3_ENVLIST, 3, 85, 122, 55,
+       LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL
+    PUSHBUTTON "Add", IDC3_ENVADD, 131, 85, 34, 14
+    PUSHBUTTON "Remove", IDC3_ENVREMOVE, 131, 102, 34, 14
+    LTEXT "ENVIRON interpretation:", IDC3_EMSTATIC, 3, 147, 90, 8
+    AUTORADIOBUTTON "BSD", IDC3_EMBSD, 96, 147, 33, 10, WS_GROUP
+    AUTORADIOBUTTON "RFC", IDC3_EMRFC, 132, 147, 33, 10
+END
+
+IDD_PANEL35 DIALOG DISCARDABLE 6, 30, 168, 163
+STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT "Terminal-type string", IDC3_TTSTATIC, 3, 5, 90, 8
+    EDITTEXT IDC3_TTEDIT, 96, 3, 69, 12, ES_AUTOHSCROLL
+    LTEXT "Auto-login username", IDC3_LOGSTATIC, 3, 35, 90, 8
+    EDITTEXT IDC3_LOGEDIT, 96, 33, 69, 12, ES_AUTOHSCROLL
+END
+
+IDD_PANEL4 DIALOG DISCARDABLE 6, 30, 168, 163
+STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER
+FONT 8, "MS Sans Serif"
+BEGIN
+    LTEXT "Action of mouse buttons:", IDC4_MBSTATIC, 3, 3, 162, 8
+    AUTORADIOBUTTON "Windows (Right pastes, Middle extends)",
+       IDC4_MBWINDOWS, 3, 12, 162, 10, WS_GROUP
+    AUTORADIOBUTTON "xterm (Right extends, Middle pastes)",
+       IDC4_MBXTERM, 3, 22, 162, 10
+    LTEXT "Character classes:", IDC4_CCSTATIC, 3, 35, 162, 8
+    LISTBOX IDC4_CCLIST, 3, 45, 162, 96,
+       LBS_HASSTRINGS | WS_VSCROLL | LBS_USETABSTOPS | LBS_MULTIPLESEL
+    PUSHBUTTON "Set", IDC4_CCSET, 33, 145, 34, 14
+    LTEXT "to class", IDC4_CCSTATIC2, 73, 148, 26, 8
+    EDITTEXT IDC4_CCEDIT, 105, 146, 36, 12
+END
+
+IDD_PANEL5 DIALOG DISCARDABLE 6, 30, 168, 163
+STYLE WS_CHILD | WS_VISIBLE | NOT WS_BORDER
+FONT 8, "MS Sans Serif"
+BEGIN
+    AUTOCHECKBOX "Bolded text is a different colour", IDC5_BOLDCOLOUR,3, 10, 162, 10
+    AUTOCHECKBOX "Attempt to use logical palettes", IDC5_PALETTE, 3, 25, 162, 10
+    LTEXT "Colours:", IDC5_STATIC, 3, 40, 162, 8
+    LISTBOX IDC5_LIST, 3, 50, 100, 110, LBS_HASSTRINGS | WS_VSCROLL
+    LTEXT "Red:", IDC5_RSTATIC, 109, 50, 27, 8
+    RTEXT "", IDC5_RVALUE, 138, 50, 27, 8
+    LTEXT "Green:", IDC5_GSTATIC, 109, 58, 27, 8
+    RTEXT "", IDC5_GVALUE, 138, 58, 27, 8
+    LTEXT "Blue:", IDC5_BSTATIC, 109, 66, 27, 8
+    RTEXT "", IDC5_BVALUE, 138, 66, 27, 8
+    PUSHBUTTON "Change...", IDC5_CHANGE, 109, 140, 56, 14
+END
+
+IDD_LOGBOX DIALOG DISCARDABLE 100, 20, 160, 119
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "PuTTY Negotiation Log"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON "&Close", IDOK, 58, 102, 44, 14
+    LISTBOX IDN_LIST, 3, 3, 154, 95, LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL
+END
+
+IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 231
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "PuTTY Licence"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON "OK", IDOK, 98, 211, 44, 14
+
+    LTEXT "Copyright \251 1998 Simon Tatham", 1000, 10, 10, 206, 8
+    LTEXT "Portions copyright Gary S. Brown and Eric Young", 1100, 10, 18, 206, 8
+
+    LTEXT "Permission is hereby granted, free of charge, to any person", 1002, 10, 34, 206, 8
+    LTEXT "obtaining a copy of this software and associated documentation", 1003, 10, 42, 206, 8
+    LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1004, 10, 50, 206, 8
+    LTEXT "including without limitation the rights to use, copy, modify, merge,", 1005, 10, 58, 206, 8
+    LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1006, 10, 66, 206, 8
+    LTEXT "and to permit persons to whom the Software is furnished to do so,", 1007, 10, 74, 206, 8
+    LTEXT "subject to the following conditions:", 1008, 10, 82, 206, 8
+
+    LTEXT "The above copyright notice and this permission notice shall be", 1010, 10, 98, 206, 8
+    LTEXT "included in all copies or substantial portions of the Software.", 1011, 10, 106, 206, 8
+
+    LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1013, 10, 122, 206, 8
+    LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1014, 10, 130, 206, 8
+    LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1015, 10, 138, 206, 8
+    LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1016, 10, 146, 206, 8
+    LTEXT "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL", 1017, 10, 154, 206, 8
+    LTEXT "SIMON TATHAM BE LIABLE FOR ANY CLAIM, DAMAGES OR", 1018, 10, 162, 206, 8
+    LTEXT "OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1019, 10, 170, 206, 8
+    LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1020, 10, 178, 206, 8
+    LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1021, 10, 186, 206, 8
+    LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1022, 10, 194, 206, 8
+
+END
diff --git a/windlg.c b/windlg.c
new file mode 100644 (file)
index 0000000..ecd3e5f
--- /dev/null
+++ b/windlg.c
@@ -0,0 +1,1336 @@
+#include <windows.h>
+#include <commctrl.h>
+#include <commdlg.h>
+#include <winsock.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "win_res.h"
+
+#define NPANELS 7
+#define MAIN_NPANELS 7
+#define RECONF_NPANELS 4
+
+static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
+
+static void get_sesslist(int allocate);
+
+static char **negots = NULL;
+static int nnegots = 0, negsize = 0;
+static HWND logbox = NULL, abtbox = NULL;
+
+static char hex[16] = "0123456789ABCDEF";
+
+static void mungestr(char *in, char *out) {
+    int candot = 0;
+
+    while (*in) {
+       if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
+           *in == '%' || *in < ' ' || *in > '~' || (*in == '.' && !candot)) {
+           *out++ = '%';
+           *out++ = hex[((unsigned char)*in) >> 4];
+           *out++ = hex[((unsigned char)*in) & 15];
+       } else
+           *out++ = *in;
+       in++;
+       candot = 1;
+    }
+    *out = '\0';
+    return;
+}
+
+static void unmungestr(char *in, char *out) {
+    int candot = 0;
+
+    while (*in) {
+       if (*in == '%' && in[1] && in[2]) {
+           int i, j;
+
+           i = in[1] - '0'; i -= (i > 9 ? 7 : 0);
+           j = in[2] - '0'; j -= (j > 9 ? 7 : 0);
+
+           *out++ = (i<<4) + j;
+           in += 3;
+       } else
+           *out++ = *in++;
+    }
+    *out = '\0';
+    return;
+}
+
+static void wpps(HKEY key, LPCTSTR name, LPCTSTR value) {
+    RegSetValueEx(key, name, 0, REG_SZ, value, 1+strlen(value));
+}
+
+static void wppi(HKEY key, LPCTSTR name, int value) {
+    RegSetValueEx(key, name, 0, REG_DWORD,
+                 (CONST BYTE *)&value, sizeof(value));
+}
+
+static void gpps(HKEY key, LPCTSTR name, LPCTSTR def,
+                LPTSTR val, int len) {
+    DWORD type, size;
+    size = len;
+
+    if (key == NULL ||
+       RegQueryValueEx(key, name, 0, &type, val, &size) != ERROR_SUCCESS ||
+       type != REG_SZ) {
+       strncpy(val, def, len);
+       val[len-1] = '\0';
+    }
+}
+
+static void gppi(HKEY key, LPCTSTR name, int def, int *i) {
+    DWORD type, val, size;
+    size = sizeof(val);
+
+    if (key == NULL ||
+       RegQueryValueEx(key, name, 0, &type,
+                       (BYTE *)&val, &size) != ERROR_SUCCESS ||
+       size != sizeof(val) || type != REG_DWORD)
+       *i = def;
+    else
+       *i = val;
+}
+
+typedef struct {
+    void *posn;
+    void *temp;
+    char dataspace[2048];
+} DTemplate;
+
+static HINSTANCE hinst;
+
+static char **sessions;
+static int nsessions;
+
+static void save_settings (char *section, int do_host) {
+    int i;
+    HKEY subkey1, sesskey;
+    char *p;
+
+    p = malloc(3*strlen(section)+1);
+    mungestr(section, p);
+    
+    if (RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1)!=ERROR_SUCCESS ||
+       RegCreateKey(subkey1, p, &sesskey) != ERROR_SUCCESS) {
+       free(p);
+       sesskey = NULL;
+    }
+
+    free(p);
+    RegCloseKey(subkey1);
+
+    wppi (sesskey, "Present", 1);
+    if (do_host) {
+       wpps (sesskey, "HostName", cfg.host);
+       wppi (sesskey, "PortNumber", cfg.port);
+       wpps (sesskey, "Protocol",
+             cfg.protocol == PROT_SSH ? "ssh" : "telnet");
+    }
+    wppi (sesskey, "CloseOnExit", !!cfg.close_on_exit);
+    wpps (sesskey, "TerminalType", cfg.termtype);
+    wpps (sesskey, "TerminalSpeed", cfg.termspeed);
+    {
+       char buf[2*sizeof(cfg.environ)], *p, *q;
+       p = buf;
+       q = cfg.environ;
+       while (*q) {
+           while (*q) {
+               int c = *q++;
+               if (c == '=' || c == ',' || c == '\\')
+                   *p++ = '\\';
+               if (c == '\t')
+                   c = '=';
+               *p++ = c;
+           }
+           *p++ = ',';
+           q++;
+       }
+       *p = '\0';
+       wpps (sesskey, "Environment", buf);
+    }
+    wpps (sesskey, "UserName", cfg.username);
+    wppi (sesskey, "RFCEnviron", cfg.rfc_environ);
+    wppi (sesskey, "BackspaceIsDelete", cfg.bksp_is_delete);
+    wppi (sesskey, "RXVTHomeEnd", cfg.rxvt_homeend);
+    wppi (sesskey, "LinuxFunctionKeys", cfg.linux_funkeys);
+    wppi (sesskey, "ApplicationCursorKeys", cfg.app_cursor);
+    wppi (sesskey, "ApplicationKeypad", cfg.app_keypad);
+    wppi (sesskey, "ScrollbackLines", cfg.savelines);
+    wppi (sesskey, "DECOriginMode", cfg.dec_om);
+    wppi (sesskey, "AutoWrapMode", cfg.wrap_mode);
+    wppi (sesskey, "WinNameAlways", cfg.win_name_always);
+    wppi (sesskey, "TermWidth", cfg.width);
+    wppi (sesskey, "TermHeight", cfg.height);
+    wpps (sesskey, "Font", cfg.font);
+    wppi (sesskey, "FontIsBold", cfg.fontisbold);
+    wppi (sesskey, "FontHeight", cfg.fontheight);
+    wppi (sesskey, "FontVTMode", cfg.vtmode);
+    wppi (sesskey, "TryPalette", cfg.try_palette);
+    wppi (sesskey, "BoldAsColour", cfg.bold_colour);
+    for (i=0; i<22; i++) {
+       char buf[20], buf2[30];
+       sprintf(buf, "Colour%d", i);
+       sprintf(buf2, "%d,%d,%d", cfg.colours[i][0],
+               cfg.colours[i][1], cfg.colours[i][2]);
+       wpps (sesskey, buf, buf2);
+    }
+    wppi (sesskey, "MouseIsXterm", cfg.mouse_is_xterm);
+    for (i=0; i<256; i+=32) {
+       char buf[20], buf2[256];
+       int j;
+       sprintf(buf, "Wordness%d", i);
+       *buf2 = '\0';
+       for (j=i; j<i+32; j++) {
+           sprintf(buf2+strlen(buf2), "%s%d",
+                   (*buf2 ? "," : ""), cfg.wordness[j]);
+       }
+       wpps (sesskey, buf, buf2);
+    }
+
+    RegCloseKey(sesskey);
+}
+
+static void del_session (char *section) {
+    HKEY subkey1;
+    char *p;
+
+    if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)
+       return;
+
+    p = malloc(3*strlen(section)+1);
+    mungestr(section, p);
+    RegDeleteKey(subkey1, p);
+    free(p);
+
+    RegCloseKey(subkey1);
+}
+
+static void load_settings (char *section, int do_host) {
+    int i;
+    HKEY subkey1, sesskey;
+    char *p;
+
+    p = malloc(3*strlen(section)+1);
+    mungestr(section, p);
+    
+    if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS ||
+       RegOpenKey(subkey1, p, &sesskey) != ERROR_SUCCESS) {
+       free(p);
+       sesskey = NULL;
+    }
+
+    free(p);
+    RegCloseKey(subkey1);
+
+    if (do_host) {
+       char prot[10];
+       gpps (sesskey, "HostName", "", cfg.host, sizeof(cfg.host));
+       gppi (sesskey, "PortNumber", 23, &cfg.port);
+       gpps (sesskey, "Protocol", "telnet", prot, 10);
+       if (!strcmp(prot, "ssh"))
+           cfg.protocol = PROT_SSH;
+       else
+           cfg.protocol = PROT_TELNET;
+    } else {
+       cfg.port = 23;
+       *cfg.host = '\0';
+    }
+    gppi (sesskey, "CloseOnExit", 1, &cfg.close_on_exit);
+    gpps (sesskey, "TerminalType", "xterm", cfg.termtype,
+         sizeof(cfg.termtype));
+    gpps (sesskey, "TerminalSpeed", "38400,38400", cfg.termspeed,
+         sizeof(cfg.termspeed));
+    {
+       char buf[2*sizeof(cfg.environ)], *p, *q;
+       gpps (sesskey, "Environment", "", buf, sizeof(buf));
+       p = buf;
+       q = cfg.environ;
+       while (*p) {
+           while (*p && *p != ',') {
+               int c = *p++;
+               if (c == '=')
+                   c = '\t';
+               if (c == '\\')
+                   c = *p++;
+               *p++ = c;
+           }
+           if (*p == ',') p++;
+           *q++ = '\0';
+       }
+       *q = '\0';
+    }
+    gpps (sesskey, "UserName", "", cfg.username, sizeof(cfg.username));
+    gppi (sesskey, "RFCEnviron", 0, &cfg.rfc_environ);
+    gppi (sesskey, "BackspaceIsDelete", 1, &cfg.bksp_is_delete);
+    gppi (sesskey, "RXVTHomeEnd", 0, &cfg.rxvt_homeend);
+    gppi (sesskey, "LinuxFunctionKeys", 0, &cfg.linux_funkeys);
+    gppi (sesskey, "ApplicationCursorKeys", 0, &cfg.app_cursor);
+    gppi (sesskey, "ApplicationKeypad", 0, &cfg.app_keypad);
+    gppi (sesskey, "ScrollbackLines", 200, &cfg.savelines);
+    gppi (sesskey, "DECOriginMode", 0, &cfg.dec_om);
+    gppi (sesskey, "AutoWrapMode", 1, &cfg.wrap_mode);
+    gppi (sesskey, "WinNameAlways", 0, &cfg.win_name_always);
+    gppi (sesskey, "TermWidth", 80, &cfg.width);
+    gppi (sesskey, "TermHeight", 24, &cfg.height);
+    gpps (sesskey, "Font", "Courier", cfg.font, sizeof(cfg.font));
+    gppi (sesskey, "FontIsBold", 0, &cfg.fontisbold);
+    gppi (sesskey, "FontHeight", 10, &cfg.fontheight);
+    gppi (sesskey, "FontVTMode", VT_POORMAN, &cfg.vtmode);
+    gppi (sesskey, "TryPalette", 0, &cfg.try_palette);
+    gppi (sesskey, "BoldAsColour", 1, &cfg.bold_colour);
+    for (i=0; i<22; i++) {
+       static char *defaults[] = {
+           "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0",
+           "0,255,0", "0,0,0", "85,85,85", "187,0,0", "255,85,85",
+           "0,187,0", "85,255,85", "187,187,0", "255,255,85", "0,0,187",
+           "85,85,255", "187,0,187", "255,85,255", "0,187,187",
+           "85,255,255", "187,187,187", "255,255,255"
+       };
+       char buf[20], buf2[30];
+       sprintf(buf, "Colour%d", i);
+       gpps (sesskey, buf, defaults[i], buf2, sizeof(buf2));
+       sscanf(buf2, "%d,%d,%d", &cfg.colours[i][0],
+              &cfg.colours[i][1], &cfg.colours[i][2]);
+    }
+    gppi (sesskey, "MouseIsXterm", 0, &cfg.mouse_is_xterm);
+    for (i=0; i<256; i+=32) {
+       static char *defaults[] = {
+           "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0",
+           "0,1,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1",
+           "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2",
+           "1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1",
+           "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
+           "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1",
+           "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2",
+           "2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2"
+       };
+       char buf[20], buf2[256], *p;
+       int j;
+       sprintf(buf, "Wordness%d", i);
+       gpps (sesskey, buf, defaults[i/32], buf2, sizeof(buf2));
+       p = buf2;
+       for (j=i; j<i+32; j++) {
+           char *q = p;
+           while (*p && *p != ',') p++;
+           if (*p == ',') *p++ = '\0';
+           cfg.wordness[j] = atoi(q);
+       }
+    }
+    RegCloseKey(sesskey);
+}
+
+static void MyGetDlgItemInt (HWND hwnd, int id, int *result) {
+    BOOL ok;
+    int n;
+    n = GetDlgItemInt (hwnd, id, &ok, FALSE);
+    if (ok)
+       *result = n;
+}
+
+static int CALLBACK LogProc (HWND hwnd, UINT msg,
+                            WPARAM wParam, LPARAM lParam) {
+    int i;
+
+    switch (msg) {
+      case WM_INITDIALOG:
+       for (i=0; i<nnegots; i++)
+           SendDlgItemMessage (hwnd, IDN_LIST, LB_ADDSTRING,
+                               0, (LPARAM)negots[i]);
+       return 1;
+/*      case WM_CTLCOLORDLG: */
+/*     return (int) GetStockObject (LTGRAY_BRUSH); */
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDOK:
+           logbox = NULL;
+           DestroyWindow (hwnd);
+           return 0;
+       }
+       return 0;
+      case WM_CLOSE:
+       logbox = NULL;
+       DestroyWindow (hwnd);
+       return 0;
+    }
+    return 0;
+}
+
+static int CALLBACK AboutProc (HWND hwnd, UINT msg,
+                              WPARAM wParam, LPARAM lParam) {
+    switch (msg) {
+      case WM_INITDIALOG:
+       return 1;
+/*      case WM_CTLCOLORDLG: */
+/*     return (int) GetStockObject (LTGRAY_BRUSH); */
+/*      case WM_CTLCOLORSTATIC: */
+/*     SetBkColor ((HDC)wParam, RGB(192,192,192)); */
+/*     return (int) GetStockObject (LTGRAY_BRUSH); */
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDOK:
+           abtbox = NULL;
+           DestroyWindow (hwnd);
+           return 0;
+         case IDA_LICENCE:
+           EnableWindow(hwnd, 0);
+           DialogBox (hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
+                      NULL, AboutProc);
+           EnableWindow(hwnd, 1);
+           return 0;
+       }
+       return 0;
+      case WM_CLOSE:
+       abtbox = NULL;
+       DestroyWindow (hwnd);
+       return 0;
+    }
+    return 0;
+}
+
+static int GeneralPanelProc (HWND hwnd, UINT msg,
+                            WPARAM wParam, LPARAM lParam) {
+    switch (msg) {
+      case WM_INITDIALOG:
+       SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+       return 1;
+/*      case WM_CTLCOLORDLG: */
+/*     return (int) GetStockObject (LTGRAY_BRUSH); */
+/*      case WM_CTLCOLORSTATIC: */
+/*      case WM_CTLCOLORBTN: */
+/*     SetBkColor ((HDC)wParam, RGB(192,192,192)); */
+/*     return (int) GetStockObject (LTGRAY_BRUSH); */
+      case WM_CLOSE:
+       DestroyWindow (hwnd);
+       return 1;
+    }
+    return 0;
+}
+
+static int CALLBACK ConnectionProc (HWND hwnd, UINT msg,
+                                   WPARAM wParam, LPARAM lParam) {
+    int i;
+
+    switch (msg) {
+      case WM_INITDIALOG:
+       SetDlgItemText (hwnd, IDC0_HOST, cfg.host);
+       SetDlgItemInt (hwnd, IDC0_PORT, cfg.port, FALSE);
+       for (i = 0; i < nsessions; i++)
+           SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_ADDSTRING,
+                               0, (LPARAM) (sessions[i]));
+       CheckRadioButton (hwnd, IDC0_PROTTELNET, IDC0_PROTSSH,
+                         cfg.protocol==PROT_SSH ? IDC0_PROTSSH : IDC0_PROTTELNET);
+       CheckDlgButton (hwnd, IDC0_CLOSEEXIT, cfg.close_on_exit);
+       break;
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDC0_PROTTELNET:
+         case IDC0_PROTSSH:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               int i = IsDlgButtonChecked (hwnd, IDC0_PROTSSH);
+               cfg.protocol = i ? PROT_SSH : PROT_TELNET;
+               if ((cfg.protocol == PROT_SSH && cfg.port == 23) ||
+                   (cfg.protocol == PROT_TELNET && cfg.port == 22)) {
+                   cfg.port = i ? 22 : 23;
+                   SetDlgItemInt (hwnd, IDC0_PORT, cfg.port, FALSE);
+               }
+           }
+           break;
+         case IDC0_HOST:
+           if (HIWORD(wParam) == EN_CHANGE)
+               GetDlgItemText (hwnd, IDC0_HOST, cfg.host,
+                               sizeof(cfg.host)-1);
+           break;
+         case IDC0_PORT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               MyGetDlgItemInt (hwnd, IDC0_PORT, &cfg.port);
+           break;
+         case IDC0_CLOSEEXIT:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED)
+               cfg.close_on_exit = IsDlgButtonChecked (hwnd, IDC0_CLOSEEXIT);
+           break;
+         case IDC0_SESSEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
+                                   (WPARAM) -1, 0);
+           break;
+         case IDC0_SESSSAVE:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               /*
+                * Save a session
+                */
+               char str[2048];
+               GetDlgItemText (hwnd, IDC0_SESSEDIT, str, sizeof(str)-1);
+               if (!*str) {
+                   int n = SendDlgItemMessage (hwnd, IDC0_SESSLIST,
+                                               LB_GETCURSEL, 0, 0);
+                   if (n == LB_ERR) {
+                       MessageBeep(0);
+                       break;
+                   }
+                   strcpy (str, sessions[n]);
+               }
+               save_settings (str, !!strcmp(str, "Default Settings"));
+               get_sesslist (FALSE);
+               get_sesslist (TRUE);
+               SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_RESETCONTENT,
+                                   0, 0);
+               for (i = 0; i < nsessions; i++)
+                   SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_ADDSTRING,
+                                       0, (LPARAM) (sessions[i]));
+               SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
+                                   (WPARAM) -1, 0);
+           }
+           break;
+         case IDC0_SESSLIST:
+         case IDC0_SESSLOAD:
+           if (LOWORD(wParam) == IDC0_SESSLOAD &&
+               HIWORD(wParam) != BN_CLICKED &&
+               HIWORD(wParam) != BN_DOUBLECLICKED)
+               break;
+           if (LOWORD(wParam) == IDC0_SESSLIST &&
+               HIWORD(wParam) != LBN_DBLCLK)
+               break;
+           {
+               int n = SendDlgItemMessage (hwnd, IDC0_SESSLIST,
+                                           LB_GETCURSEL, 0, 0);
+               if (n == LB_ERR) {
+                   MessageBeep(0);
+                   break;
+               }
+               load_settings (sessions[n],
+                              !!strcmp(sessions[n], "Default Settings"));
+               SetDlgItemText (hwnd, IDC0_HOST, cfg.host);
+               SetDlgItemInt (hwnd, IDC0_PORT, cfg.port, FALSE);
+               CheckRadioButton (hwnd, IDC0_PROTTELNET, IDC0_PROTSSH,
+                                 (cfg.protocol==PROT_SSH ? IDC0_PROTSSH :
+                                  IDC0_PROTTELNET));
+               CheckDlgButton (hwnd, IDC0_CLOSEEXIT, cfg.close_on_exit);
+               SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
+                                   (WPARAM) -1, 0);
+           }
+           if (LOWORD(wParam) == IDC0_SESSLIST) {
+               /*
+                * A double-click on a saved session should
+                * actually start the session, not just load it.
+                * Unless it's Default Settings or some other
+                * host-less set of saved settings.
+                */
+               if (*cfg.host)
+                   SendMessage (GetParent(hwnd), WM_COMMAND, IDOK, 0);
+           }
+           break;
+         case IDC0_SESSDEL:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               int n = SendDlgItemMessage (hwnd, IDC0_SESSLIST,
+                                           LB_GETCURSEL, 0, 0);
+               if (n == LB_ERR || n == 0) {
+                   MessageBeep(0);
+                   break;
+               }
+               del_session(sessions[n]);
+               get_sesslist (FALSE);
+               get_sesslist (TRUE);
+               SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_RESETCONTENT,
+                                   0, 0);
+               for (i = 0; i < nsessions; i++)
+                   SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_ADDSTRING,
+                                       0, (LPARAM) (sessions[i]));
+               SendDlgItemMessage (hwnd, IDC0_SESSLIST, LB_SETCURSEL,
+                                   (WPARAM) -1, 0);
+           }
+       }
+    }
+    return GeneralPanelProc (hwnd, msg, wParam, lParam);
+}
+
+static int CALLBACK KeyboardProc (HWND hwnd, UINT msg,
+                                   WPARAM wParam, LPARAM lParam) {
+    switch (msg) {
+      case WM_INITDIALOG:
+       CheckRadioButton (hwnd, IDC1_DEL008, IDC1_DEL127,
+                         cfg.bksp_is_delete ? IDC1_DEL127 : IDC1_DEL008);
+       CheckRadioButton (hwnd, IDC1_HOMETILDE, IDC1_HOMERXVT,
+                         cfg.rxvt_homeend ? IDC1_HOMERXVT : IDC1_HOMETILDE);
+       CheckRadioButton (hwnd, IDC1_FUNCTILDE, IDC1_FUNCLINUX,
+                         cfg.linux_funkeys ? IDC1_FUNCLINUX : IDC1_FUNCTILDE);
+       CheckRadioButton (hwnd, IDC1_CURNORMAL, IDC1_CURAPPLIC,
+                         cfg.app_cursor ? IDC1_CURAPPLIC : IDC1_CURNORMAL);
+       CheckRadioButton (hwnd, IDC1_KPNORMAL, IDC1_KPAPPLIC,
+                         cfg.app_keypad ? IDC1_KPAPPLIC : IDC1_KPNORMAL);
+       break;
+      case WM_COMMAND:
+       if (HIWORD(wParam) == BN_CLICKED ||
+           HIWORD(wParam) == BN_DOUBLECLICKED)
+           switch (LOWORD(wParam)) {
+             case IDC1_DEL008:
+             case IDC1_DEL127:
+               cfg.bksp_is_delete = IsDlgButtonChecked (hwnd, IDC1_DEL127);
+               break;
+             case IDC1_HOMETILDE:
+             case IDC1_HOMERXVT:
+               cfg.rxvt_homeend = IsDlgButtonChecked (hwnd, IDC1_HOMERXVT);
+               break;
+             case IDC1_FUNCTILDE:
+             case IDC1_FUNCLINUX:
+               cfg.linux_funkeys = IsDlgButtonChecked (hwnd, IDC1_FUNCLINUX);
+               break;
+             case IDC1_KPNORMAL:
+             case IDC1_KPAPPLIC:
+               cfg.app_keypad = IsDlgButtonChecked (hwnd, IDC1_KPAPPLIC);
+               break;
+             case IDC1_CURNORMAL:
+             case IDC1_CURAPPLIC:
+               cfg.app_cursor = IsDlgButtonChecked (hwnd, IDC1_CURAPPLIC);
+               break;
+           }
+    }
+    return GeneralPanelProc (hwnd, msg, wParam, lParam);
+}
+
+static void fmtfont (char *buf) {
+    sprintf (buf, "Font: %s, ", cfg.font);
+    if (cfg.fontisbold)
+       strcat(buf, "bold, ");
+    if (cfg.fontheight == 0)
+       strcat (buf, "default height");
+    else
+       sprintf (buf+strlen(buf), "%d-%s",
+                (cfg.fontheight < 0 ? -cfg.fontheight : cfg.fontheight),
+                (cfg.fontheight < 0 ? "pixel" : "point"));
+}
+
+static int CALLBACK TerminalProc (HWND hwnd, UINT msg,
+                                   WPARAM wParam, LPARAM lParam) {
+    CHOOSEFONT cf;
+    LOGFONT lf;
+    char fontstatic[256];
+
+    switch (msg) {
+      case WM_INITDIALOG:
+       CheckDlgButton (hwnd, IDC2_WRAPMODE, cfg.wrap_mode);
+       CheckDlgButton (hwnd, IDC2_WINNAME, cfg.win_name_always);
+       CheckDlgButton (hwnd, IDC2_DECOM, cfg.dec_om);
+       SetDlgItemInt (hwnd, IDC2_ROWSEDIT, cfg.height, FALSE);
+       SetDlgItemInt (hwnd, IDC2_COLSEDIT, cfg.width, FALSE);
+       SetDlgItemInt (hwnd, IDC2_SAVEEDIT, cfg.savelines, FALSE);
+       fmtfont (fontstatic);
+       SetDlgItemText (hwnd, IDC2_FONTSTATIC, fontstatic);
+       CheckRadioButton (hwnd, IDC2_VTXWINDOWS, IDC2_VTPOORMAN,
+                         cfg.vtmode == VT_XWINDOWS ? IDC2_VTXWINDOWS :
+                         cfg.vtmode == VT_OEMANSI ? IDC2_VTOEMANSI :
+                         cfg.vtmode == VT_OEMONLY ? IDC2_VTOEMONLY :
+                         IDC2_VTPOORMAN);
+       break;
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDC2_WRAPMODE:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED)
+               cfg.wrap_mode = IsDlgButtonChecked (hwnd, IDC2_WRAPMODE);
+           break;
+         case IDC2_WINNAME:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED)
+               cfg.win_name_always = IsDlgButtonChecked (hwnd, IDC2_WINNAME);
+           break;
+         case IDC2_DECOM:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED)
+               cfg.dec_om = IsDlgButtonChecked (hwnd, IDC2_DECOM);
+           break;
+         case IDC2_ROWSEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               MyGetDlgItemInt (hwnd, IDC2_ROWSEDIT, &cfg.height);
+           break;
+         case IDC2_COLSEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               MyGetDlgItemInt (hwnd, IDC2_COLSEDIT, &cfg.width);
+           break;
+         case IDC2_SAVEEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               MyGetDlgItemInt (hwnd, IDC2_SAVEEDIT, &cfg.savelines);
+           break;
+         case IDC2_CHOOSEFONT:
+           lf.lfHeight = cfg.fontheight;
+           lf.lfWidth = lf.lfEscapement = lf.lfOrientation = 0;
+           lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = 0;
+           lf.lfWeight = (cfg.fontisbold ? FW_BOLD : 0);
+           lf.lfCharSet = ANSI_CHARSET;
+           lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
+           lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+           lf.lfQuality = DEFAULT_QUALITY;
+           lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
+           strncpy (lf.lfFaceName, cfg.font, sizeof(lf.lfFaceName)-1);
+           lf.lfFaceName[sizeof(lf.lfFaceName)-1] = '\0';
+
+           cf.lStructSize = sizeof(cf);
+           cf.hwndOwner = hwnd;
+           cf.lpLogFont = &lf;
+           cf.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST |
+               CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS;
+
+           if (ChooseFont (&cf)) {
+               strncpy (cfg.font, lf.lfFaceName, sizeof(cfg.font)-1);
+               cfg.font[sizeof(cfg.font)-1] = '\0';
+               cfg.fontisbold = (lf.lfWeight == FW_BOLD);
+               cfg.fontheight = lf.lfHeight;
+               fmtfont (fontstatic);
+               SetDlgItemText (hwnd, IDC2_FONTSTATIC, fontstatic);
+           }
+           break;
+         case IDC2_VTXWINDOWS:
+         case IDC2_VTOEMANSI:
+         case IDC2_VTOEMONLY:
+         case IDC2_VTPOORMAN:
+           cfg.vtmode =
+               (IsDlgButtonChecked (hwnd, IDC2_VTXWINDOWS) ? VT_XWINDOWS :
+                IsDlgButtonChecked (hwnd, IDC2_VTOEMANSI) ? VT_OEMANSI :
+                IsDlgButtonChecked (hwnd, IDC2_VTOEMONLY) ? VT_OEMONLY :
+                VT_POORMAN);
+           break;
+       }
+       break;
+    }
+    return GeneralPanelProc (hwnd, msg, wParam, lParam);
+}
+
+static int CALLBACK TelnetProc (HWND hwnd, UINT msg,
+                                   WPARAM wParam, LPARAM lParam) {
+    int i;
+
+    switch (msg) {
+      case WM_INITDIALOG:
+       SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype);
+       SetDlgItemText (hwnd, IDC3_TSEDIT, cfg.termspeed);
+       SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username);
+       {
+           char *p = cfg.environ;
+           while (*p) {
+               SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_ADDSTRING, 0,
+                                   (LPARAM) p);
+               p += strlen(p)+1;
+           }
+       }
+       CheckRadioButton (hwnd, IDC3_EMBSD, IDC3_EMRFC,
+                         cfg.rfc_environ ? IDC3_EMRFC : IDC3_EMBSD);
+       break;
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDC3_TTEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+           GetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype,
+                           sizeof(cfg.termtype)-1);
+           break;
+         case IDC3_TSEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               GetDlgItemText (hwnd, IDC3_TSEDIT, cfg.termspeed,
+                               sizeof(cfg.termspeed)-1);
+           break;
+         case IDC3_LOGEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               GetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username,
+                               sizeof(cfg.username)-1);
+           break;
+         case IDC3_EMBSD:
+         case IDC3_EMRFC:
+           cfg.rfc_environ = IsDlgButtonChecked (hwnd, IDC3_EMRFC);
+           break;
+         case IDC3_ENVADD:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               char str[sizeof(cfg.environ)];
+               char *p;
+               GetDlgItemText (hwnd, IDC3_VAREDIT, str, sizeof(str)-1);
+               if (!*str) {
+                   MessageBeep(0);
+                   break;
+               }
+               p = str + strlen(str);
+               *p++ = '\t';
+               GetDlgItemText (hwnd, IDC3_VALEDIT, p, sizeof(str)-1-(p-str));
+               if (!*p) {
+                   MessageBeep(0);
+                   break;
+               }
+               p = cfg.environ;
+               while (*p) {
+                   while (*p) p++;
+                   p++;
+               }
+               if ((p-cfg.environ) + strlen(str) + 2 < sizeof(cfg.environ)) {
+                   strcpy (p, str);
+                   p[strlen(str)+1] = '\0';
+                   SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_ADDSTRING,
+                                       0, (LPARAM)str);
+                   SetDlgItemText (hwnd, IDC3_VAREDIT, "");
+                   SetDlgItemText (hwnd, IDC3_VALEDIT, "");
+               } else {
+                   MessageBox(hwnd, "Environment too big", "PuTTY Error",
+                              MB_OK | MB_ICONERROR);
+               }
+           }
+           break;
+         case IDC3_ENVREMOVE:
+           if (HIWORD(wParam) != BN_CLICKED &&
+               HIWORD(wParam) != BN_DOUBLECLICKED)
+               break;
+           i = SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_GETCURSEL, 0, 0);
+           if (i == LB_ERR)
+               MessageBeep (0);
+           else {
+               char *p, *q;
+
+               SendDlgItemMessage (hwnd, IDC3_ENVLIST, LB_DELETESTRING,
+                                   i, 0);
+               p = cfg.environ;
+               while (i > 0) {
+                   if (!*p)
+                       goto disaster;
+                   while (*p) p++;
+                   p++;
+                   i--;
+               }
+               q = p;
+               if (!*p)
+                   goto disaster;
+               while (*p) p++;
+               p++;
+               while (*p) {
+                   while (*p)
+                       *q++ = *p++;
+                   *q++ = *p++;
+               }
+               *q = '\0';
+               disaster:;
+           }
+           break;
+       }
+       break;
+    }
+    return GeneralPanelProc (hwnd, msg, wParam, lParam);
+}
+
+static int CALLBACK SshProc (HWND hwnd, UINT msg,
+                            WPARAM wParam, LPARAM lParam) {
+    switch (msg) {
+      case WM_INITDIALOG:
+       SetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype);
+       SetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username);
+       break;
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDC3_TTEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+           GetDlgItemText (hwnd, IDC3_TTEDIT, cfg.termtype,
+                           sizeof(cfg.termtype)-1);
+           break;
+         case IDC3_LOGEDIT:
+           if (HIWORD(wParam) == EN_CHANGE)
+               GetDlgItemText (hwnd, IDC3_LOGEDIT, cfg.username,
+                               sizeof(cfg.username)-1);
+           break;
+       }
+       break;
+    }
+    return GeneralPanelProc (hwnd, msg, wParam, lParam);
+}
+
+static int CALLBACK SelectionProc (HWND hwnd, UINT msg,
+                                   WPARAM wParam, LPARAM lParam) {
+    int i;
+
+    switch (msg) {
+      case WM_INITDIALOG:
+       CheckRadioButton (hwnd, IDC4_MBWINDOWS, IDC4_MBXTERM,
+                         cfg.mouse_is_xterm ? IDC4_MBXTERM : IDC4_MBWINDOWS);
+       {
+           static int tabs[4] = {25, 61, 96, 128};
+           SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_SETTABSTOPS, 4,
+                               (LPARAM) tabs);
+       }
+       for (i=0; i<256; i++) {
+           char str[100];
+           sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
+                   (i>=0x21 && i != 0x7F) ? i : ' ',
+                   cfg.wordness[i]);
+           SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_ADDSTRING, 0,
+                               (LPARAM) str);
+       }
+       break;
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDC4_MBWINDOWS:
+         case IDC4_MBXTERM:
+           cfg.mouse_is_xterm = IsDlgButtonChecked (hwnd, IDC4_MBXTERM);
+           break;
+         case IDC4_CCSET:
+           {
+               BOOL ok;
+               int i;
+               int n = GetDlgItemInt (hwnd, IDC4_CCEDIT, &ok, FALSE);
+
+               if (!ok)
+                   MessageBeep (0);
+               else {
+                   for (i=0; i<256; i++)
+                       if (SendDlgItemMessage (hwnd, IDC4_CCLIST, LB_GETSEL,
+                                               i, 0)) {
+                           char str[100];
+                           cfg.wordness[i] = n;
+                           SendDlgItemMessage (hwnd, IDC4_CCLIST,
+                                               LB_DELETESTRING, i, 0);
+                           sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
+                                   (i>=0x21 && i != 0x7F) ? i : ' ',
+                                   cfg.wordness[i]);
+                           SendDlgItemMessage (hwnd, IDC4_CCLIST,
+                                               LB_INSERTSTRING, i,
+                                               (LPARAM)str);
+                       }
+               }
+           }
+           break;
+       }
+       break;
+    }
+    return GeneralPanelProc (hwnd, msg, wParam, lParam);
+}
+
+static int CALLBACK ColourProc (HWND hwnd, UINT msg,
+                                   WPARAM wParam, LPARAM lParam) {
+    static const char *const colours[] = {
+       "Default Foreground", "Default Bold Foreground",
+       "Default Background", "Default Bold Background",
+       "Cursor Text", "Cursor Colour",
+       "ANSI Black", "ANSI Black Bold",
+       "ANSI Red", "ANSI Red Bold",
+       "ANSI Green", "ANSI Green Bold",
+       "ANSI Yellow", "ANSI Yellow Bold",
+       "ANSI Blue", "ANSI Blue Bold",
+       "ANSI Magenta", "ANSI Magenta Bold",
+       "ANSI Cyan", "ANSI Cyan Bold",
+       "ANSI White", "ANSI White Bold"
+    };
+    static const int permanent[] = {
+       TRUE, FALSE, TRUE, FALSE, TRUE, TRUE,
+       TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE,
+       TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE
+    };
+    switch (msg) {
+      case WM_INITDIALOG:
+       CheckDlgButton (hwnd, IDC5_BOLDCOLOUR, cfg.bold_colour);
+       CheckDlgButton (hwnd, IDC5_PALETTE, cfg.try_palette);
+       {
+           int i;
+           for (i=0; i<22; i++)
+               if (cfg.bold_colour || permanent[i])
+                   SendDlgItemMessage (hwnd, IDC5_LIST, LB_ADDSTRING, 0,
+                                       (LPARAM) colours[i]);
+       }
+       SendDlgItemMessage (hwnd, IDC5_LIST, LB_SETCURSEL, 0, 0);
+       SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[0][0], FALSE);
+       SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[0][1], FALSE);
+       SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[0][2], FALSE);
+       break;
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDC5_BOLDCOLOUR:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               int n, i;
+               cfg.bold_colour = IsDlgButtonChecked (hwnd, IDC5_BOLDCOLOUR);
+               n = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCOUNT, 0, 0);
+               if (cfg.bold_colour && n!=22) {
+                   for (i=0; i<22; i++)
+                       if (!permanent[i])
+                           SendDlgItemMessage (hwnd, IDC5_LIST,
+                                               LB_INSERTSTRING, i,
+                                               (LPARAM) colours[i]);
+               } else if (!cfg.bold_colour && n!=12) {
+                   for (i=22; i-- ;)
+                       if (!permanent[i])
+                           SendDlgItemMessage (hwnd, IDC5_LIST,
+                                               LB_DELETESTRING, i, 0);
+               }
+           }
+           break;
+         case IDC5_PALETTE:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED)
+               cfg.try_palette = IsDlgButtonChecked (hwnd, IDC5_PALETTE);
+           break;
+         case IDC5_LIST:
+           if (HIWORD(wParam) == LBN_DBLCLK ||
+               HIWORD(wParam) == LBN_SELCHANGE) {
+               int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL,
+                                           0, 0);
+               if (!cfg.bold_colour)
+                   i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
+               SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0], FALSE);
+               SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1], FALSE);
+               SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2], FALSE);
+           }
+           break;
+         case IDC5_CHANGE:
+           if (HIWORD(wParam) == BN_CLICKED ||
+               HIWORD(wParam) == BN_DOUBLECLICKED) {
+               static CHOOSECOLOR cc;
+               static DWORD custom[16] = {0};   /* zero initialisers */
+               int i = SendDlgItemMessage (hwnd, IDC5_LIST, LB_GETCURSEL,
+                                           0, 0);
+               if (!cfg.bold_colour)
+                   i = (i < 3 ? i*2 : i == 3 ? 5 : i*2-2);
+               cc.lStructSize = sizeof(cc);
+               cc.hwndOwner = hwnd;
+               cc.hInstance = hinst;
+               cc.lpCustColors = custom;
+               cc.rgbResult = RGB (cfg.colours[i][0], cfg.colours[i][1],
+                                   cfg.colours[i][2]);
+               cc.Flags = CC_FULLOPEN | CC_RGBINIT;
+               if (ChooseColor(&cc)) {
+                   cfg.colours[i][0] =
+                       (unsigned char) (cc.rgbResult & 0xFF);
+                   cfg.colours[i][1] =
+                       (unsigned char) (cc.rgbResult >> 8) & 0xFF;
+                   cfg.colours[i][2] =
+                       (unsigned char) (cc.rgbResult >> 16) & 0xFF;
+                   SetDlgItemInt (hwnd, IDC5_RVALUE, cfg.colours[i][0],
+                                  FALSE);
+                   SetDlgItemInt (hwnd, IDC5_GVALUE, cfg.colours[i][1],
+                                  FALSE);
+                   SetDlgItemInt (hwnd, IDC5_BVALUE, cfg.colours[i][2],
+                                  FALSE);
+               }
+           }
+           break;
+       }
+       break;
+    }
+    return GeneralPanelProc (hwnd, msg, wParam, lParam);
+}
+
+static DTemplate negot, main, reconf, panels[NPANELS];
+static DLGPROC panelproc[NPANELS] = {
+    ConnectionProc, KeyboardProc, TerminalProc,
+    TelnetProc, SshProc, SelectionProc, ColourProc
+};
+static char *panelids[NPANELS] = {
+    MAKEINTRESOURCE(IDD_PANEL0),
+    MAKEINTRESOURCE(IDD_PANEL1),
+    MAKEINTRESOURCE(IDD_PANEL2),
+    MAKEINTRESOURCE(IDD_PANEL3),
+    MAKEINTRESOURCE(IDD_PANEL35),
+    MAKEINTRESOURCE(IDD_PANEL4),
+    MAKEINTRESOURCE(IDD_PANEL5)
+};
+static char *names[NPANELS] = {
+    "Connection", "Keyboard", "Terminal", "Telnet", "SSH", "Selection", "Colours"
+};
+
+static int mainp[MAIN_NPANELS] = { 0, 1, 2, 3, 4, 5, 6 };
+static int reconfp[RECONF_NPANELS] = { 1, 2, 5, 6 };
+
+static int GenericMainDlgProc (HWND hwnd, UINT msg,
+                              WPARAM wParam, LPARAM lParam,
+                              int npanels, int *panelnums, HWND *page) {
+    HWND hw;
+
+    switch (msg) {
+      case WM_INITDIALOG:
+       {                              /* centre the window */
+           RECT rs, rd;
+
+           hw = GetDesktopWindow();
+           if (GetWindowRect (hw, &rs) && GetWindowRect (hwnd, &rd))
+               MoveWindow (hwnd, (rs.right + rs.left + rd.left - rd.right)/2,
+                           (rs.bottom + rs.top + rd.top - rd.bottom)/2,
+                           rd.right-rd.left, rd.bottom-rd.top, TRUE);
+       }
+       *page = NULL;
+       {                              /* initialise the tab control */
+           TC_ITEMHEADER tab;
+           int i;
+
+           hw = GetDlgItem (hwnd, IDC_TAB);
+           for (i=0; i<npanels; i++) {
+               tab.mask = TCIF_TEXT;
+               tab.pszText = names[panelnums[i]];
+               TabCtrl_InsertItem (hw, i, &tab);
+           }
+/*         *page = CreateDialogIndirect (hinst, panels[panelnums[0]].temp,
+                                         hwnd, panelproc[panelnums[0]]);*/
+           *page = CreateDialog (hinst, panelids[panelnums[0]],
+                                 hwnd, panelproc[panelnums[0]]);
+           SetWindowLong (*page, GWL_EXSTYLE,
+                          GetWindowLong (*page, GWL_EXSTYLE) |
+                          WS_EX_CONTROLPARENT);
+       }
+       SetFocus (*page);
+       return 0;
+      case WM_NOTIFY:
+       if (LOWORD(wParam) == IDC_TAB &&
+           ((LPNMHDR)lParam)->code == TCN_SELCHANGE) {
+           int i = TabCtrl_GetCurSel(((LPNMHDR)lParam)->hwndFrom);
+           if (*page)
+               DestroyWindow (*page);
+/*         *page = CreateDialogIndirect (hinst, panels[panelnums[i]].temp,     
+                                         hwnd, panelproc[panelnums[i]]);*/
+           *page = CreateDialog (hinst, panelids[panelnums[i]],
+                                 hwnd, panelproc[panelnums[i]]);
+           SetWindowLong (*page, GWL_EXSTYLE,
+                          GetWindowLong (*page, GWL_EXSTYLE) |
+                          WS_EX_CONTROLPARENT);
+           SetFocus (((LPNMHDR)lParam)->hwndFrom);   /* ensure focus stays */
+           return 0;
+       }
+       break;
+/*      case WM_CTLCOLORDLG: */
+/*     return (int) GetStockObject (LTGRAY_BRUSH); */
+      case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+         case IDOK:
+           if (*cfg.host)
+               EndDialog (hwnd, 1);
+           else
+               MessageBeep (0);
+           return 0;
+         case IDCANCEL:
+           EndDialog (hwnd, 0);
+           return 0;
+       }
+       return 0;
+      case WM_CLOSE:
+       EndDialog (hwnd, 0);
+       return 0;
+    }
+    return 0;
+}
+
+static int CALLBACK MainDlgProc (HWND hwnd, UINT msg,
+                                WPARAM wParam, LPARAM lParam) {
+#if 0
+    HWND hw;
+    int i;
+#endif
+    static HWND page = NULL;
+
+    if (msg == WM_COMMAND && LOWORD(wParam) == IDOK) {
+#if 0
+       /*
+        * If the Connection panel is active and the Session List
+        * box is selected, we treat a press of Open to have an
+        * implicit press of Load preceding it.
+        */
+       hw = GetDlgItem (hwnd, IDC_TAB);
+       i = TabCtrl_GetCurSel(hw);
+       if (panelproc[mainp[i]] == ConnectionProc &&
+           page && implicit_load_ok) {
+           SendMessage (page, WM_COMMAND,
+                        MAKELONG(IDC0_SESSLOAD, BN_CLICKED), 0);
+       }
+#endif
+    }
+    if (msg == WM_COMMAND && LOWORD(wParam) == IDC_ABOUT) {
+       EnableWindow(hwnd, 0);
+       DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
+                 GetParent(hwnd), AboutProc);
+       EnableWindow(hwnd, 1);
+    }
+    return GenericMainDlgProc (hwnd, msg, wParam, lParam,
+                              MAIN_NPANELS, mainp, &page);
+}
+
+static int CALLBACK ReconfDlgProc (HWND hwnd, UINT msg,
+                                  WPARAM wParam, LPARAM lParam) {
+    static HWND page;
+    return GenericMainDlgProc (hwnd, msg, wParam, lParam,
+                              RECONF_NPANELS, reconfp, &page);
+}
+
+static void get_sesslist(int allocate) {
+    static char *buffer;
+    int buflen, bufsize, i, ret;
+    char otherbuf[2048];
+    char *p;
+    HKEY subkey1;
+
+    if (allocate) {
+       if (RegCreateKey(HKEY_CURRENT_USER,
+                        puttystr, &subkey1) != ERROR_SUCCESS)
+           return;
+
+       buflen = bufsize = 0;
+       buffer = NULL;
+       i = 0;
+       do {
+           ret = RegEnumKey(subkey1, i++, otherbuf, sizeof(otherbuf));
+           if (ret == ERROR_SUCCESS) {
+               bufsize = buflen + 2048;
+               buffer = srealloc(buffer, bufsize);
+               unmungestr(otherbuf, buffer+buflen);
+               buflen += strlen(buffer+buflen)+1;
+           }
+       } while (ret == ERROR_SUCCESS);
+       buffer = srealloc(buffer, buflen+1);
+       buffer[buflen] = '\0';
+
+       p = buffer;
+       nsessions = 1;                 /* "Default Settings" counts as one */
+       while (*p) {
+           if (strcmp(p, "Default Settings"))
+               nsessions++;
+           while (*p) p++;
+           p++;
+       }
+
+       sessions = smalloc(nsessions * sizeof(char *));
+       sessions[0] = "Default Settings";
+       p = buffer;
+       i = 1;
+       while (*p) {
+           if (strcmp(p, "Default Settings"))
+               sessions[i++] = p;
+           while (*p) p++;
+           p++;
+       }
+    } else {
+       sfree (buffer);
+       sfree (sessions);
+    }
+}
+
+int do_config (void) {
+    int ret;
+
+    get_sesslist(TRUE);
+    ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL, MainDlgProc);
+    get_sesslist(FALSE);
+
+    return ret;
+}
+
+int do_reconfig (HWND hwnd) {
+    Config backup_cfg;
+    int ret;
+
+    backup_cfg = cfg;                 /* structure copy */
+    ret = DialogBox (hinst, MAKEINTRESOURCE(IDD_RECONF), hwnd, ReconfDlgProc);
+    if (!ret)
+       cfg = backup_cfg;              /* structure copy */
+    return ret;
+}
+
+void do_defaults (char *session) {
+    if (session)
+       load_settings (session, TRUE);
+    else
+       load_settings ("Default Settings", FALSE);
+}
+
+void lognegot (char *string) {
+    if (nnegots >= negsize) {
+       negsize += 64;
+       negots = srealloc (negots, negsize * sizeof(*negots));
+    }
+    negots[nnegots] = smalloc(1+strlen(string));
+    strcpy (negots[nnegots], string);
+    nnegots++;
+    if (logbox)
+       SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING,
+                           0, (LPARAM)string);
+}
+
+void shownegot (HWND hwnd) {
+    if (!logbox) {
+       logbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_LOGBOX),
+                              hwnd, LogProc);
+       ShowWindow (logbox, SW_SHOWNORMAL);
+    }
+}
+
+void showabout (HWND hwnd) {
+    if (!abtbox) {
+       abtbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
+                              hwnd, AboutProc);
+       ShowWindow (abtbox, SW_SHOWNORMAL);
+    }
+}
+
+void verify_ssh_host_key(char *host, struct RSAKey *key) {
+    char *keystr, *otherstr, *mungedhost;
+    int len;
+    HKEY rkey;
+
+    /*
+     * Format the key into a string.
+     */
+    len = rsastr_len(key);
+    keystr = malloc(len);
+    if (!keystr)
+       fatalbox("Out of memory");
+    rsastr_fmt(keystr, key);
+
+    /*
+     * Now read a saved key in from the registry and see what it
+     * says.
+     */
+    otherstr = malloc(len);
+    mungedhost = malloc(3*strlen(host)+1);
+    if (!otherstr || !mungedhost)
+       fatalbox("Out of memory");
+
+    mungestr(host, mungedhost);
+
+    if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
+                    &rkey) != ERROR_SUCCESS) {
+       if (MessageBox(NULL, "PuTTY was unable to open the host key cache\n"
+                      "in the registry. There is thus no way to tell\n"
+                      "if the remote host is what you think it is.\n"
+                      "Connect anyway?", "PuTTY Problem",
+                      MB_ICONWARNING | MB_YESNO) == IDNO)
+           exit(0);
+    } else {
+       DWORD readlen = len;
+       DWORD type;
+       int ret;
+
+       ret = RegQueryValueEx(rkey, mungedhost, NULL,
+                             &type, otherstr, &readlen);
+
+       if (ret == ERROR_MORE_DATA ||
+           (ret == ERROR_SUCCESS && type == REG_SZ &&
+            strcmp(otherstr, keystr))) {
+           if (MessageBox(NULL,
+                          "This host's host key is different from the\n"
+                          "one cached in the registry! Someone may be\n"
+                          "impersonating this host for malicious reasons;\n"
+                          "alternatively, the host key may have changed\n"
+                          "due to sloppy system administration.\n"
+                          "Replace key in registry and connect?",
+                          "PuTTY: Security Warning",
+                          MB_ICONWARNING | MB_YESNO) == IDNO)
+               exit(0);
+           RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr,
+                         strlen(keystr)+1);
+       } else if (ret != ERROR_SUCCESS || type != REG_SZ) {
+           if (MessageBox(NULL,
+                          "This host's host key is not cached in the\n"
+                          "registry. Do you want to add it to the cache\n"
+                          "and carry on connecting?",
+                          "PuTTY: New Host",
+                          MB_ICONWARNING | MB_YESNO) == IDNO)
+               exit(0);
+           RegSetValueEx(rkey, mungedhost, 0, REG_SZ, keystr,
+                         strlen(keystr)+1);
+       }
+
+       RegCloseKey(rkey);
+    }
+}
diff --git a/window.c b/window.c
new file mode 100644 (file)
index 0000000..151441f
--- /dev/null
+++ b/window.c
@@ -0,0 +1,1499 @@
+#include <windows.h>
+#include <commctrl.h>
+#include <winsock.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define PUTTY_DO_GLOBALS                      /* actually _define_ globals */
+#include "putty.h"
+#include "win_res.h"
+
+#define IDM_SHOWLOG   501
+#define IDM_NEWSESS   502
+#define IDM_DUPSESS   503
+#define IDM_RECONF    504
+#define IDM_CLRSB     505
+#define IDM_RESET     506
+#define IDM_TEL_AYT   507
+#define IDM_TEL_BRK   508
+#define IDM_TEL_SYNCH 509
+#define IDM_TEL_EC    510
+#define IDM_TEL_EL    511
+#define IDM_TEL_GA    512
+#define IDM_TEL_NOP   513
+#define IDM_TEL_ABORT 514
+#define IDM_TEL_AO    515
+#define IDM_TEL_IP    516
+#define IDM_TEL_SUSP  517
+#define IDM_TEL_EOR   518
+#define IDM_TEL_EOF   519
+#define IDM_ABOUT     520
+
+#define WM_IGNORE_SIZE (WM_USER + 2)
+#define WM_IGNORE_CLIP (WM_USER + 3)
+
+static int WINAPI WndProc (HWND, UINT, WPARAM, LPARAM);
+static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output);
+static void cfgtopalette(void);
+static void init_palette(void);
+static void init_fonts(void);
+
+static int extra_width, extra_height;
+
+#define FONT_NORMAL 0
+#define FONT_BOLD 1
+#define FONT_UNDERLINE 2
+#define FONT_BOLDUND 3
+#define FONT_OEM 4
+#define FONT_OEMBOLD 5
+#define FONT_OEMBOLDUND 6
+#define FONT_OEMUND 7
+static HFONT fonts[8];
+static enum {
+    BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
+} bold_mode;
+static enum {
+    UND_LINE, UND_FONT
+} und_mode;
+static int descent;
+
+#define NCOLOURS 24
+static COLORREF colours[NCOLOURS];
+static HPALETTE pal;
+static LPLOGPALETTE logpal;
+static RGBTRIPLE defpal[NCOLOURS];
+
+static HWND hwnd;
+
+static int dbltime, lasttime, lastact;
+static Mouse_Button lastbtn;
+
+static char *window_name, *icon_name;
+
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
+    static char appname[] = "PuTTY";
+    WORD winsock_ver;
+    WSADATA wsadata;
+    WNDCLASS wndclass;
+    MSG msg;
+    int guess_width, guess_height;
+
+    winsock_ver = MAKEWORD(1, 1);
+    if (WSAStartup(winsock_ver, &wsadata)) {
+       MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
+                  MB_OK | MB_ICONEXCLAMATION);
+       return 1;
+    }
+    if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
+       MessageBox(NULL, "WinSock version is incompatible with 1.1",
+                  "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
+       WSACleanup();
+       return 1;
+    }
+    /* WISHLIST: maybe allow config tweaking even if winsock not present? */
+
+    InitCommonControls();
+
+    /*
+     * Process the command line.
+     */
+    {
+       char *p;
+
+       do_defaults(NULL);
+
+       p = cmdline;
+       while (*p && isspace(*p)) p++;
+
+       /*
+        * An initial @ means to activate a saved session.
+        */
+       if (*p == '@') {
+           do_defaults (p+1);
+           if (!*cfg.host && !do_config()) {
+               WSACleanup();
+               return 0;
+           }
+       } else if (*p == '&') {
+           /*
+            * An initial & means we've been given a command line
+            * containing the hex value of a HANDLE for a file
+            * mapping object, which we must then extract as a
+            * config.
+            */
+           HANDLE filemap;
+           Config *cp;
+           if (sscanf(p+1, "%x", &filemap) == 1 &&
+               (cp = MapViewOfFile(filemap, FILE_MAP_READ,
+                                   0, 0, sizeof(Config))) != NULL) {
+               cfg = *cp;
+               UnmapViewOfFile(cp);
+               CloseHandle(filemap);
+           } else if (!do_config()) {
+               WSACleanup();
+               return 0;
+           }
+       } else if (*p) {
+           char *q = p;
+           while (*p && !isspace(*p)) p++;
+           if (*p)
+               *p++ = '\0';
+           strncpy (cfg.host, q, sizeof(cfg.host)-1);
+           cfg.host[sizeof(cfg.host)-1] = '\0';
+           while (*p && isspace(*p)) p++;
+           if (*p)
+               cfg.port = atoi(p);
+           else
+               cfg.port = -1;
+       } else {
+           if (!do_config()) {
+               WSACleanup();
+               return 0;
+           }
+       }
+    }
+
+    back = (cfg.protocol == PROT_SSH ? &ssh_backend : &telnet_backend);
+
+    if (!prev) {
+       wndclass.style         = 0;
+       wndclass.lpfnWndProc   = WndProc;
+       wndclass.cbClsExtra    = 0;
+       wndclass.cbWndExtra    = 0;
+       wndclass.hInstance     = inst;
+       wndclass.hIcon         = LoadIcon (inst,
+                                          MAKEINTRESOURCE(IDI_MAINICON));
+       wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
+       wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
+       wndclass.lpszMenuName  = NULL;
+       wndclass.lpszClassName = appname;
+
+       RegisterClass (&wndclass);
+    }
+
+    hwnd = NULL;
+
+    savelines = cfg.savelines;
+    term_init();
+
+    cfgtopalette();
+
+    /*
+     * Guess some defaults for the window size. This all gets
+     * updated later, so we don't really care too much. However, we
+     * do want the font width/height guesses to correspond to a
+     * large font rather than a small one...
+     */
+    
+    font_width = 10;
+    font_height = 20;
+    extra_width = 25;
+    extra_height = 28;
+    term_size (cfg.height, cfg.width, cfg.savelines);
+    guess_width = extra_width + font_width * cols;
+    guess_height = extra_height + font_height * rows;
+    {
+       RECT r;
+       HWND w = GetDesktopWindow();
+       GetWindowRect (w, &r);
+       if (guess_width > r.right - r.left)
+           guess_width = r.right - r.left;
+       if (guess_height > r.bottom - r.top)
+           guess_height = r.bottom - r.top;
+    }
+
+    hwnd = CreateWindow (appname, appname,
+                        WS_OVERLAPPEDWINDOW | WS_VSCROLL,
+                        CW_USEDEFAULT, CW_USEDEFAULT,
+                        guess_width, guess_height,
+                        NULL, NULL, inst, NULL);
+
+    /*
+     * Initialise the fonts, simultaneously correcting the guesses
+     * for font_{width,height}.
+     */
+    bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
+    und_mode = UND_FONT;
+    init_fonts();
+
+    /*
+     * Correct the guesses for extra_{width,height}.
+     */
+    {
+       RECT cr, wr;
+       GetWindowRect (hwnd, &wr);
+       GetClientRect (hwnd, &cr);
+       extra_width = wr.right - wr.left - cr.right + cr.left;
+       extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
+    }
+
+    /*
+     * Resize the window, now we know what size we _really_ want it
+     * to be.
+     */
+    guess_width = extra_width + font_width * cols;
+    guess_height = extra_height + font_height * rows;
+    SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
+    SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
+                 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
+
+    /*
+     * Initialise the scroll bar.
+     */
+    {
+       SCROLLINFO si;
+
+       si.cbSize = sizeof(si);
+       si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
+       si.nMin = 0;
+       si.nMax = rows-1;
+       si.nPage = rows;
+       si.nPos = 0;
+       SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
+    }
+
+    /*
+     * Start up the telnet connection.
+     */
+    {
+       char *error;
+       char msg[1024];
+       char *realhost;
+
+       error = back->init (hwnd, cfg.host, cfg.port, &realhost);
+       if (error) {
+           sprintf(msg, "Unable to open connection:\n%s", error);
+           MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
+           return 0;
+       }
+       window_name = icon_name = NULL;
+       sprintf(msg, "PuTTY: %s", realhost);
+       set_title (msg);
+       set_icon (msg);
+    }
+
+    /*
+     * Set up the input and output buffers.
+     */
+    inbuf_reap = inbuf_head = 0;
+    outbuf_reap = outbuf_head = 0;
+
+    /*
+     * Prepare the mouse handler.
+     */
+    lastact = MA_NOTHING;
+    lastbtn = MB_NOTHING;
+    dbltime = GetDoubleClickTime();
+
+    /*
+     * Set up the session-control options on the system menu.
+     */
+    {
+       HMENU m = GetSystemMenu (hwnd, FALSE);
+       HMENU p;
+
+       AppendMenu (m, MF_SEPARATOR, 0, 0);
+       if (cfg.protocol == PROT_TELNET) {
+           p = CreateMenu();
+           AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
+           AppendMenu (p, MF_SEPARATOR, 0, 0);
+           AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
+           AppendMenu (p, MF_SEPARATOR, 0, 0);
+           AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
+           AppendMenu (p, MF_SEPARATOR, 0, 0);
+           AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
+           AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
+           AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
+           AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Show Negotiation");
+           AppendMenu (m, MF_SEPARATOR, 0, 0);
+       }
+       AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "New Session");
+       AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "Duplicate Session");
+       AppendMenu (m, MF_ENABLED, IDM_RECONF, "Change Settings");
+       AppendMenu (m, MF_SEPARATOR, 0, 0);
+       AppendMenu (m, MF_ENABLED, IDM_CLRSB, "Clear Scrollback");
+       AppendMenu (m, MF_ENABLED, IDM_RESET, "Reset Terminal");
+       AppendMenu (m, MF_SEPARATOR, 0, 0);
+       AppendMenu (m, MF_ENABLED, IDM_ABOUT, "About PuTTY");
+    }
+
+    /*
+     * Finally show the window!
+     */
+    ShowWindow (hwnd, show);
+
+    /*
+     * Set the palette up.
+     */
+    pal = NULL;
+    logpal = NULL;
+    init_palette();
+
+    has_focus = (GetForegroundWindow() == hwnd);
+    UpdateWindow (hwnd);
+
+    while (GetMessage (&msg, NULL, 0, 0)) {
+       DispatchMessage (&msg);
+       if (inbuf_reap != inbuf_head)
+           term_out();
+       /* In idle moments, do a full screen update */
+       if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
+           term_update();
+    }
+
+    /*
+     * Clean up.
+     */
+    {
+       int i;
+       for (i=0; i<8; i++)
+           if (fonts[i])
+               DeleteObject(fonts[i]);
+    }
+    sfree(logpal);
+    if (pal)
+       DeleteObject(pal);
+    WSACleanup();
+
+    if (cfg.protocol == PROT_SSH)
+       random_save_seed();
+
+    return msg.wParam;
+}
+
+/*
+ * Copy the colour palette from the configuration data into defpal.
+ * This is non-trivial because the colour indices are different.
+ */
+static void cfgtopalette(void) {
+    int i;
+    static const int ww[] = {
+       6, 7, 8, 9, 10, 11, 12, 13,
+       14, 15, 16, 17, 18, 19, 20, 21,
+       0, 1, 2, 3, 4, 4, 5, 5
+    };
+
+    for (i=0; i<24; i++) {
+       int w = ww[i];
+       defpal[i].rgbtRed = cfg.colours[w][0];
+       defpal[i].rgbtGreen = cfg.colours[w][1];
+       defpal[i].rgbtBlue = cfg.colours[w][2];
+    }
+}
+
+/*
+ * Set up the colour palette.
+ */
+static void init_palette(void) {
+    int i;
+    HDC hdc = GetDC (hwnd);
+    if (hdc) {
+       if (cfg.try_palette &&
+           GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
+           logpal = smalloc(sizeof(*logpal)
+                            - sizeof(logpal->palPalEntry)
+                            + NCOLOURS * sizeof(PALETTEENTRY));
+           logpal->palVersion = 0x300;
+           logpal->palNumEntries = NCOLOURS;
+           for (i = 0; i < NCOLOURS; i++) {
+               logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
+               logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
+               logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
+               logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
+           }
+           pal = CreatePalette (logpal);
+           if (pal) {
+               SelectPalette (hdc, pal, FALSE);
+               RealizePalette (hdc);
+               SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
+                              FALSE);
+           }
+       }
+       ReleaseDC (hwnd, hdc);
+    }
+    if (pal)
+       for (i=0; i<NCOLOURS; i++)
+           colours[i] = PALETTERGB(defpal[i].rgbtRed,
+                                   defpal[i].rgbtGreen,
+                                   defpal[i].rgbtBlue);
+    else
+       for(i=0; i<NCOLOURS; i++)
+           colours[i] = RGB(defpal[i].rgbtRed,
+                            defpal[i].rgbtGreen,
+                            defpal[i].rgbtBlue);
+}
+
+/*
+ * Initialise all the fonts we will need. There may be as many as
+ * eight or as few as one. We also:
+ *
+ * - check the font width and height, correcting our guesses if
+ *   necessary.
+ *
+ * - verify that the bold font is the same width as the ordinary
+ *   one, and engage shadow bolding if not.
+ * 
+ * - verify that the underlined font is the same width as the
+ *   ordinary one (manual underlining by means of line drawing can
+ *   be done in a pinch).
+ *
+ * - verify, in OEM/ANSI combined mode, that the OEM and ANSI base
+ *   fonts are the same size, and shift to OEM-only mode if not.
+ */
+static void init_fonts(void) {
+    TEXTMETRIC tm;
+    int i, j;
+    int widths[5];
+    HDC hdc;
+    int fw_dontcare, fw_bold;
+
+    for (i=0; i<8; i++)
+       fonts[i] = NULL;
+
+    if (cfg.fontisbold) {
+       fw_dontcare = FW_BOLD;
+       fw_bold = FW_BLACK;
+   } else {
+       fw_dontcare = FW_DONTCARE;
+       fw_bold = FW_BOLD;
+    }
+
+#define f(i,c,w,u) \
+    fonts[i] = CreateFont (cfg.fontheight, 0, 0, 0, w, FALSE, u, FALSE, \
+                          c, OUT_DEFAULT_PRECIS, \
+                          CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
+                          FIXED_PITCH | FF_DONTCARE, cfg.font)
+    if (cfg.vtmode != VT_OEMONLY) {
+       f(FONT_NORMAL, ANSI_CHARSET, fw_dontcare, FALSE);
+       f(FONT_UNDERLINE, ANSI_CHARSET, fw_dontcare, TRUE);
+    }
+    if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) {
+       f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
+       f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
+    }
+    if (bold_mode == BOLD_FONT) {
+       if (cfg.vtmode != VT_OEMONLY) {
+           f(FONT_BOLD, ANSI_CHARSET, fw_bold, FALSE);
+           f(FONT_BOLDUND, ANSI_CHARSET, fw_bold, TRUE);
+       }
+       if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_OEMONLY) {
+           f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
+           f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
+       }
+    } else {
+       fonts[FONT_BOLD] = fonts[FONT_BOLDUND] = NULL;
+       fonts[FONT_OEMBOLD] = fonts[FONT_OEMBOLDUND] = NULL;
+    }
+#undef f
+
+    hdc = GetDC(hwnd);
+
+    if (cfg.vtmode == VT_OEMONLY)
+       j = 4;
+    else
+       j = 0;
+
+    for (i=0; i<(cfg.vtmode == VT_OEMANSI ? 5 : 4); i++) {
+       if (fonts[i+j]) {
+           SelectObject (hdc, fonts[i+j]);
+           GetTextMetrics(hdc, &tm);
+           if (i == 0 || i == 4) {
+               font_height = tm.tmHeight;
+               font_width = tm.tmAveCharWidth;
+               descent = tm.tmAscent + 1;
+               if (descent >= font_height)
+                   descent = font_height - 1;
+           }
+           widths[i] = tm.tmAveCharWidth;
+       }
+    }
+
+    ReleaseDC (hwnd, hdc);
+
+    if (widths[FONT_UNDERLINE] != widths[FONT_NORMAL] ||
+       (bold_mode == BOLD_FONT &&
+        widths[FONT_BOLDUND] != widths[FONT_BOLD])) {
+       und_mode = UND_LINE;
+       DeleteObject (fonts[FONT_UNDERLINE]);
+       if (bold_mode == BOLD_FONT)
+           DeleteObject (fonts[FONT_BOLDUND]);
+    }
+
+    if (bold_mode == BOLD_FONT &&
+       widths[FONT_BOLD] != widths[FONT_NORMAL]) {
+       bold_mode = BOLD_SHADOW;
+       DeleteObject (fonts[FONT_BOLD]);
+       if (und_mode == UND_FONT)
+           DeleteObject (fonts[FONT_BOLDUND]);
+    }
+
+    if (cfg.vtmode == VT_OEMANSI && widths[FONT_OEM] != widths[FONT_NORMAL]) {
+       MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
+                  "different sizes. Using OEM-only mode instead",
+                  "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
+       cfg.vtmode = VT_OEMONLY;
+       for (i=0; i<4; i++)
+           if (fonts[i])
+               DeleteObject (fonts[i]);
+    }
+}
+
+void request_resize (int w, int h) {
+    int width = extra_width + font_width * w;
+    int height = extra_height + font_height * h;
+
+    SetWindowPos (hwnd, NULL, 0, 0, width, height,
+                 SWP_NOACTIVATE | SWP_NOCOPYBITS |
+                 SWP_NOMOVE | SWP_NOZORDER);
+}
+
+static void click (Mouse_Button b, int x, int y) {
+    if (lastbtn == b && GetMessageTime() - lasttime < dbltime) {
+       lastact = (lastact == MA_CLICK ? MA_2CLK :
+                  lastact == MA_2CLK ? MA_3CLK :
+                  lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
+    } else {
+       lastbtn = b;
+       lastact = MA_CLICK;
+    }
+    if (lastact != MA_NOTHING)
+       term_mouse (b, lastact, x, y);
+    lasttime = GetMessageTime();
+}
+
+static int WINAPI WndProc (HWND hwnd, UINT message,
+                          WPARAM wParam, LPARAM lParam) {
+    HDC hdc;
+    static int ignore_size = FALSE;
+    static int ignore_clip = FALSE;
+    static int just_reconfigged = FALSE;
+
+    switch (message) {
+      case WM_CREATE:
+       break;
+      case WM_DESTROY:
+       PostQuitMessage (0);
+       return 0;
+      case WM_SYSCOMMAND:
+       switch (wParam) {
+         case IDM_SHOWLOG:
+           shownegot(hwnd);
+           break;
+         case IDM_NEWSESS:
+         case IDM_DUPSESS:
+           {
+               char b[2048];
+               char c[30], *cl;
+               STARTUPINFO si;
+               PROCESS_INFORMATION pi;
+               HANDLE filemap = NULL;
+
+               if (wParam == IDM_DUPSESS) {
+                   /*
+                    * Allocate a file-mapping memory chunk for the
+                    * config structure.
+                    */
+                   SECURITY_ATTRIBUTES sa;
+                   Config *p;
+
+                   sa.nLength = sizeof(sa);
+                   sa.lpSecurityDescriptor = NULL;
+                   sa.bInheritHandle = TRUE;
+                   filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
+                                               &sa,
+                                               PAGE_READWRITE,
+                                               0,
+                                               sizeof(Config),
+                                               NULL);
+                   if (filemap) {
+                       p = (Config *)MapViewOfFile(filemap,
+                                                   FILE_MAP_WRITE,
+                                                   0, 0, sizeof(Config));
+                       if (p) {
+                           *p = cfg;  /* structure copy */
+                           UnmapViewOfFile(p);
+                       }
+                   }
+                   sprintf(c, "putty &%08x", filemap);
+                   cl = c;
+               } else
+                   cl = NULL;
+
+               GetModuleFileName (NULL, b, sizeof(b)-1);
+               si.cb = sizeof(si);
+               si.lpReserved = NULL;
+               si.lpDesktop = NULL;
+               si.lpTitle = NULL;
+               si.dwFlags = 0;
+               si.cbReserved2 = 0;
+               si.lpReserved2 = NULL;
+               CreateProcess (b, cl, NULL, NULL, TRUE,
+                              NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
+
+               if (filemap)
+                   CloseHandle(filemap);
+           }
+           break;
+         case IDM_RECONF:
+           if (!do_reconfig(hwnd))
+               break;
+           just_reconfigged = TRUE;
+           {
+               int i;
+               for (i=0; i<8; i++)
+                   if (fonts[i])
+                       DeleteObject(fonts[i]);
+           }
+           bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
+           und_mode = UND_FONT;
+           init_fonts();
+           sfree(logpal);
+           if (pal)
+               DeleteObject(pal);
+           logpal = NULL;
+           pal = NULL;
+           cfgtopalette();
+           init_palette();
+           term_size(cfg.height, cfg.width, cfg.savelines);
+           InvalidateRect(hwnd, NULL, TRUE);
+           SetWindowPos (hwnd, NULL, 0, 0,
+                         extra_width + font_width * cfg.width,
+                         extra_height + font_height * cfg.height,
+                         SWP_NOACTIVATE | SWP_NOCOPYBITS |
+                         SWP_NOMOVE | SWP_NOZORDER);
+           if (IsIconic(hwnd)) {
+               SetWindowText (hwnd,
+                              cfg.win_name_always ? window_name : icon_name);
+           }
+           break;
+         case IDM_CLRSB:
+           term_clrsb();
+           break;
+         case IDM_RESET:
+           term_pwron();
+           break;
+         case IDM_TEL_AYT: back->special (TS_AYT); break;
+         case IDM_TEL_BRK: back->special (TS_BRK); break;
+         case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
+         case IDM_TEL_EC: back->special (TS_EC); break;
+         case IDM_TEL_EL: back->special (TS_EL); break;
+         case IDM_TEL_GA: back->special (TS_GA); break;
+         case IDM_TEL_NOP: back->special (TS_NOP); break;
+         case IDM_TEL_ABORT: back->special (TS_ABORT); break;
+         case IDM_TEL_AO: back->special (TS_AO); break;
+         case IDM_TEL_IP: back->special (TS_IP); break;
+         case IDM_TEL_SUSP: back->special (TS_SUSP); break;
+         case IDM_TEL_EOR: back->special (TS_EOR); break;
+         case IDM_TEL_EOF: back->special (TS_EOF); break;
+         case IDM_ABOUT:
+           showabout (hwnd);
+           break;
+       }
+       break;
+      case WM_LBUTTONDOWN:
+       click (MB_SELECT, LOWORD(lParam) / font_width,
+              HIWORD(lParam) / font_height);
+       return 0;
+      case WM_LBUTTONUP:
+       term_mouse (MB_SELECT, MA_RELEASE, LOWORD(lParam) / font_width,
+                   HIWORD(lParam) / font_height);
+       return 0;
+      case WM_MBUTTONDOWN:
+       click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
+              LOWORD(lParam) / font_width,
+              HIWORD(lParam) / font_height);
+       return 0;
+      case WM_MBUTTONUP:
+       term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
+                   MA_RELEASE, LOWORD(lParam) / font_width,
+                   HIWORD(lParam) / font_height);
+       return 0;
+      case WM_RBUTTONDOWN:
+       click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
+              LOWORD(lParam) / font_width,
+              HIWORD(lParam) / font_height);
+       return 0;
+      case WM_RBUTTONUP:
+       term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
+                   MA_RELEASE, LOWORD(lParam) / font_width,
+                   HIWORD(lParam) / font_height);
+       return 0;
+      case WM_MOUSEMOVE:
+       /*
+        * Add the mouse position and message time to the random
+        * number noise, if we're using ssh.
+        */
+       if (cfg.protocol == PROT_SSH)
+           noise_ultralight(lParam);
+
+       if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
+           Mouse_Button b;
+           if (wParam & MK_LBUTTON)
+               b = MB_SELECT;
+           else if (wParam & MK_MBUTTON)
+               b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
+           else
+               b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
+           term_mouse (b, MA_DRAG, LOWORD(lParam) / font_width,
+                       HIWORD(lParam) / font_height);
+       }
+       lastbtn = MB_NOTHING;
+       lastact = MA_NOTHING;
+       lasttime = GetMessageTime();
+       return 0;
+      case WM_IGNORE_CLIP:
+       ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
+       break;
+      case WM_DESTROYCLIPBOARD:
+       if (!ignore_clip)
+           term_deselect();
+       ignore_clip = FALSE;
+       return 0;
+      case WM_PAINT:
+       {
+           PAINTSTRUCT p;
+           hdc = BeginPaint (hwnd, &p);
+           if (pal) {
+               SelectPalette (hdc, pal, TRUE);
+               RealizePalette (hdc);
+           }
+           term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
+                       p.rcPaint.right, p.rcPaint.bottom);
+           SelectObject (hdc, GetStockObject(SYSTEM_FONT));
+           SelectObject (hdc, GetStockObject(WHITE_PEN));
+           EndPaint (hwnd, &p);
+       }
+       return 0;
+      case WM_NETEVENT:
+       {
+           int i = back->msg (wParam, lParam);
+           if (i < 0) {
+               char buf[1024];
+               switch (WSABASEERR + (-i) % 10000) {
+                 case WSAECONNRESET:
+                   sprintf(buf, "Connection reset by peer");
+                   break;
+                 default:
+                   sprintf(buf, "Unexpected network error %d", -i);
+                   break;
+               }
+               MessageBox(hwnd, buf, "PuTTY Fatal Error",
+                          MB_ICONERROR | MB_OK);
+               PostQuitMessage(1);
+           } else if (i == 0) {
+               if (cfg.close_on_exit)
+                   PostQuitMessage(0);
+               else {
+                   MessageBox(hwnd, "Connection closed by remote host",
+                              "PuTTY", MB_OK | MB_ICONINFORMATION);
+                   SetWindowText (hwnd, "PuTTY (inactive)");
+               }
+           }
+       }
+       return 0;
+      case WM_SETFOCUS:
+       has_focus = TRUE;
+       term_out();
+       term_update();
+       break;
+      case WM_KILLFOCUS:
+       has_focus = FALSE;
+       term_out();
+       term_update();
+       break;
+      case WM_IGNORE_SIZE:
+       ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
+       break;
+      case WM_SIZING:
+       {
+           int width, height, w, h, ew, eh;
+           LPRECT r = (LPRECT)lParam;
+
+           width = r->right - r->left - extra_width;
+           height = r->bottom - r->top - extra_height;
+           w = (width + font_width/2) / font_width; if (w < 1) w = 1;
+           h = (height + font_height/2) / font_height; if (h < 1) h = 1;
+           ew = width - w * font_width;
+           eh = height - h * font_height;
+           if (ew != 0) {
+               if (wParam == WMSZ_LEFT ||
+                   wParam == WMSZ_BOTTOMLEFT ||
+                   wParam == WMSZ_TOPLEFT)
+                   r->left += ew;
+               else
+                   r->right -= ew;
+           }
+           if (eh != 0) {
+               if (wParam == WMSZ_TOP ||
+                   wParam == WMSZ_TOPRIGHT ||
+                   wParam == WMSZ_TOPLEFT)
+                   r->top += eh;
+               else
+                   r->bottom -= eh;
+           }
+           if (ew || eh)
+               return 1;
+           else
+               return 0;
+       }
+       break;
+      case WM_SIZE:
+       if (wParam == SIZE_MINIMIZED) {
+           SetWindowText (hwnd,
+                          cfg.win_name_always ? window_name : icon_name);
+           break;
+       }
+       if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
+           SetWindowText (hwnd, window_name);
+       if (!ignore_size) {
+           int width, height, w, h, ew, eh;
+
+           width = LOWORD(lParam);
+           height = HIWORD(lParam);
+           w = width / font_width; if (w < 1) w = 1;
+           h = height / font_height; if (h < 1) h = 1;
+#if 0 /* we have fixed this using WM_SIZING now */
+           ew = width - w * font_width;
+           eh = height - h * font_height;
+           if (ew != 0 || eh != 0) {
+               RECT r;
+               GetWindowRect (hwnd, &r);
+               SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
+               SetWindowPos (hwnd, NULL, 0, 0,
+                             r.right - r.left - ew, r.bottom - r.top - eh,
+                             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
+           }
+#endif
+           if (w != cols || h != rows || just_reconfigged) {
+               term_invalidate();
+               term_size (h, w, cfg.savelines);
+               back->size();
+               just_reconfigged = FALSE;
+           }
+       }
+       ignore_size = FALSE;
+       return 0;
+      case WM_VSCROLL:
+       switch (LOWORD(wParam)) {
+         case SB_BOTTOM: term_scroll(-1, 0); break;
+         case SB_TOP: term_scroll(+1, 0); break;
+         case SB_LINEDOWN: term_scroll (0, +1); break;
+         case SB_LINEUP: term_scroll (0, -1); break;
+         case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
+         case SB_PAGEUP: term_scroll (0, -rows/2); break;
+         case SB_THUMBPOSITION: case SB_THUMBTRACK:
+           term_scroll (1, HIWORD(wParam)); break;
+       }
+       break; 
+     case WM_PALETTECHANGED:
+       if ((HWND) wParam != hwnd && pal != NULL) {
+           HDC hdc = get_ctx();
+           if (hdc) {
+               if (RealizePalette (hdc) > 0)
+                   UpdateColors (hdc);
+               free_ctx (hdc);
+           }
+       }
+       break;
+      case WM_QUERYNEWPALETTE:
+       if (pal != NULL) {
+           HDC hdc = get_ctx();
+           if (hdc) {
+               if (RealizePalette (hdc) > 0)
+                   UpdateColors (hdc);
+               free_ctx (hdc);
+               return TRUE;
+           }
+       }
+       return FALSE;
+      case WM_KEYDOWN:
+      case WM_SYSKEYDOWN:
+       /*
+        * Add the scan code and keypress timing to the random
+        * number noise, if we're using ssh.
+        */
+       if (cfg.protocol == PROT_SSH)
+           noise_ultralight(lParam);
+
+       /*
+        * We don't do TranslateMessage since it disassociates the
+        * resulting CHAR message from the KEYDOWN that sparked it,
+        * which we occasionally don't want. Instead, we process
+        * KEYDOWN, and call the Win32 translator functions so that
+        * we get the translations under _our_ control.
+        */
+       {
+           unsigned char buf[20];
+           int len;
+
+           len = TranslateKey (wParam, lParam, buf);
+           back->send (buf, len);
+       }
+       return 0;
+      case WM_CHAR:
+      case WM_SYSCHAR:
+       /*
+        * Nevertheless, we are prepared to deal with WM_CHAR
+        * messages, should they crop up. So if someone wants to
+        * post the things to us as part of a macro manoeuvre,
+        * we're ready to cope.
+        */
+       {
+           char c = wParam;
+           back->send (&c, 1);
+       }
+       return 0;
+    }
+
+    return DefWindowProc (hwnd, message, wParam, lParam);
+}
+
+/*
+ * Draw a line of text in the window, at given character
+ * coordinates, in given attributes.
+ *
+ * We are allowed to fiddle with the contents of `text'.
+ */
+void do_text (Context ctx, int x, int y, char *text, int len,
+             unsigned long attr) {
+    COLORREF fg, bg, t;
+    int nfg, nbg, nfont;
+    HDC hdc = ctx;
+
+    x *= font_width;
+    y *= font_height;
+
+    if (attr & ATTR_ACTCURS) {
+       attr &= (bold_mode == BOLD_COLOURS ? 0x200 : 0x300);
+       attr ^= ATTR_CUR_XOR;
+    }
+
+    nfont = 0;
+    if (cfg.vtmode == VT_OEMONLY)
+       nfont |= FONT_OEM;
+
+    /*
+     * Map high-half characters in order to approximate ISO using
+     * OEM character set. Characters missing are 0xC3 (Atilde) and
+     * 0xCC (Igrave).
+     */
+    if (nfont & FONT_OEM) {
+       int i;
+       for (i=0; i<len; i++)
+           if (text[i] >= '\xA0' && text[i] <= '\xFF') {
+               static const char oemhighhalf[] =
+                   "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
+                   "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
+                   "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
+                   "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
+                   "\xB7\xB5\xB6\x41\x8E\x8F\x92\x80" /* C0-C7 */
+                   "\xD4\x90\xD2\xD3\x49\xD6\xD7\xD8" /* C8-CF */
+                   "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
+                   "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
+                   "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
+                   "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
+                   "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
+                   "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
+                   ;
+               text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
+           }
+    }
+
+    if (attr & ATTR_GBCHR) {
+       int i;
+       /*
+        * GB mapping: map # to pound, and everything else stays
+        * normal.
+        */
+       for (i=0; i<len; i++)
+           if (text[i] == '#')
+               text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
+    } else if (attr & ATTR_LINEDRW) {
+       int i;
+       static const char poorman[] =
+           "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
+       static const char oemmap[] =
+           "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
+           "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
+
+       /*
+        * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
+        * VT100 line drawing chars; everything else stays normal.
+        */
+       switch (cfg.vtmode) {
+         case VT_XWINDOWS:
+           for (i=0; i<len; i++)
+               if (text[i] >= '\x60' && text[i] <= '\x7E')
+                   text[i] += '\x01' - '\x60';
+           break;
+         case VT_OEMANSI:
+         case VT_OEMONLY:
+           nfont |= FONT_OEM;
+           for (i=0; i<len; i++)
+               if (text[i] >= '\x60' && text[i] <= '\x7E')
+                   text[i] = oemmap[(unsigned char)text[i] - 0x60];
+           break;
+         case VT_POORMAN:
+           for (i=0; i<len; i++)
+               if (text[i] >= '\x60' && text[i] <= '\x7E')
+                   text[i] = poorman[(unsigned char)text[i] - 0x60];
+           break;
+       }
+    }
+
+    nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
+    nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
+    if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
+       nfont |= FONT_BOLD;
+    if (und_mode == UND_FONT && (attr & ATTR_UNDER))
+       nfont |= FONT_UNDERLINE;
+    if (attr & ATTR_REVERSE) {
+       t = nfg; nfg = nbg; nbg = t;
+    }
+    if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
+       nfg++;
+    fg = colours[nfg];
+    bg = colours[nbg];
+    SelectObject (hdc, fonts[nfont]);
+    SetTextColor (hdc, fg);
+    SetBkColor (hdc, bg);
+    SetBkMode (hdc, OPAQUE);
+    TextOut (hdc, x, y, text, len);
+    if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+       SetBkMode (hdc, TRANSPARENT);
+       TextOut (hdc, x-1, y, text, len);
+    }
+    if (und_mode == UND_LINE && (attr & ATTR_UNDER)) {
+       SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
+       MoveToEx (hdc, x, y+descent, NULL);
+       LineTo (hdc, x+len*font_width, y+descent);
+    }
+    if (attr & ATTR_PASCURS) {
+       POINT pts[5];
+       pts[0].x = pts[1].x = pts[4].x = x;
+       pts[2].x = pts[3].x = x+font_width-1;
+       pts[0].y = pts[3].y = pts[4].y = y;
+       pts[1].y = pts[2].y = y+font_height-1;
+       SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
+       Polyline (hdc, pts, 5);
+    }
+}
+
+/*
+ * Translate a WM_(SYS)?KEYDOWN message into a string of ASCII
+ * codes. Returns number of bytes used.
+ */
+static int TranslateKey(WPARAM wParam, LPARAM lParam, unsigned char *output) {
+    unsigned char *p = output;
+    BYTE keystate[256];
+    int ret, code;
+
+    /*
+     * Prepend ESC if ALT was pressed at the time.
+     */
+    if (lParam & 0x20000000)
+        *p++ = 0x1B;
+
+    /*
+     * Get hold of the keyboard state, because we'll need it a few
+     * times shortly.
+     */
+    ret = GetKeyboardState(keystate);
+
+    /*
+     * Shift-PgUp, Shift-PgDn, and Alt-F4 all produce window
+     * events: we'll deal with those now.
+     */
+    if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_PRIOR) {
+       SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
+       return 0;
+    }
+    if (ret && (keystate[VK_SHIFT] & 0x80) && wParam == VK_NEXT) {
+       SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
+       return 0;
+    }
+    if ((lParam & 0x20000000) && wParam == VK_F4) {
+       SendMessage (hwnd, WM_DESTROY, 0, 0);
+       return 0;
+    }
+
+    /*
+     * In general, the strategy is to see what the Windows keymap
+     * translation has to say for itself, and then process function
+     * keys and suchlike ourselves if that fails. But first we must
+     * deal with the small number of special cases which the
+     * Windows keymap translator thinks it can do but gets wrong.
+     *
+     * First special case: we might want the Backspace key to send
+     * 0x7F not 0x08.
+     */
+    if (wParam == VK_BACK) {
+       *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
+       return p - output;
+    }
+
+    /*
+     * Control-Space should send ^@ (0x00), not Space.
+     */
+    if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == VK_SPACE) {
+       *p++ = 0x00;
+       return p - output;
+    }
+
+    /*
+     * If we're in applications keypad mode, we have to process it
+     * before char-map translation, because it will pre-empt lots
+     * of stuff, even if NumLock is off.
+     */
+    if (app_keypad_keys) {
+       if (ret) {
+           /*
+            * Hack to ensure NumLock doesn't interfere with
+            * perception of Shift for Keypad Plus. I don't pretend
+            * to understand this, but it seems to work as is.
+            * Leave it alone, or die.
+            */
+           keystate[VK_NUMLOCK] = 0;
+           SetKeyboardState (keystate);
+           GetKeyboardState (keystate);
+       }
+       switch ( (lParam >> 16) & 0x1FF ) {
+         case 0x145: p += sprintf((char *)p, "\x1BOP"); return p - output;
+         case 0x135: p += sprintf((char *)p, "\x1BOQ"); return p - output;
+         case 0x037: p += sprintf((char *)p, "\x1BOR"); return p - output;
+         case 0x047: p += sprintf((char *)p, "\x1BOw"); return p - output;
+         case 0x048: p += sprintf((char *)p, "\x1BOx"); return p - output;
+         case 0x049: p += sprintf((char *)p, "\x1BOy"); return p - output;
+         case 0x04A: p += sprintf((char *)p, "\x1BOS"); return p - output;
+         case 0x04B: p += sprintf((char *)p, "\x1BOt"); return p - output;
+         case 0x04C: p += sprintf((char *)p, "\x1BOu"); return p - output;
+         case 0x04D: p += sprintf((char *)p, "\x1BOv"); return p - output;
+         case 0x04E: /* keypad + is ^[Ol, but ^[Om with Shift */
+           p += sprintf((char *)p,
+                        (ret && (keystate[VK_SHIFT] & 0x80)) ?
+                        "\x1BOm" : "\x1BOl");
+           return p - output;
+         case 0x04F: p += sprintf((char *)p, "\x1BOq"); return p - output;
+         case 0x050: p += sprintf((char *)p, "\x1BOr"); return p - output;
+         case 0x051: p += sprintf((char *)p, "\x1BOs"); return p - output;
+         case 0x052: p += sprintf((char *)p, "\x1BOp"); return p - output;
+         case 0x053: p += sprintf((char *)p, "\x1BOn"); return p - output;
+         case 0x11C: p += sprintf((char *)p, "\x1BOM"); return p - output;
+       }
+    }
+
+    /*
+     * Before doing Windows charmap translation, remove ALT from
+     * the keymap, since its sole effect should be to prepend ESC,
+     * which we've already done. Note that removal of ALT has to
+     * happen _after_ the above call to SetKeyboardState, or dire
+     * things will befall.
+     */
+    keystate[VK_MENU] = keystate[VK_LMENU] = keystate[VK_RMENU] = 0;
+
+    /*
+     * Attempt the Windows char-map translation.
+     */
+    if (ret) {
+       WORD chr;
+       int r = ToAscii (wParam, (lParam >> 16) & 0xFF,
+                        keystate, &chr, 0);
+       if (r == 1) {
+           *p++ = chr & 0xFF;
+           return p - output;
+       }
+    }
+
+    /*
+     * OK, we haven't had a key code from the keymap translation.
+     * We'll try our various special cases and function keys, and
+     * then give up. (There's nothing wrong with giving up:
+     * Scrollock, Pause/Break, and of course the various buckybit
+     * keys all produce KEYDOWN events that we really _do_ want to
+     * ignore.)
+     */
+
+    /*
+     * Control-2 should return ^@ (0x00), Control-6 should return
+     * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since
+     * the DOS keyboard handling did it, and we have nothing better
+     * to do with the key combo in question, we'll also map
+     * Control-Backquote to ^\ (0x1C).
+     */
+    if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '2') {
+       *p++ = 0x00;
+       return p - output;
+    }
+    if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == '6') {
+       *p++ = 0x1E;
+       return p - output;
+    }
+    if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xBD) {
+       *p++ = 0x1F;
+       return p - output;
+    }
+    if (ret && (keystate[VK_CONTROL] & 0x80) && wParam == 0xDF) {
+       *p++ = 0x1C;
+       return p - output;
+    }
+
+    /*
+     * First, all the keys that do tilde codes. (ESC '[' nn '~',
+     * for integer decimal nn.)
+     *
+     * We also deal with the weird ones here. Linux VCs replace F1
+     * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
+     * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
+     * respectively.
+     */
+    code = 0;
+    switch (wParam) {
+      case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
+      case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
+      case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
+      case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
+      case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
+      case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
+      case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
+      case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
+      case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
+      case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
+      case VK_F11: code = 23; break;
+      case VK_F12: code = 24; break;
+      case VK_HOME: code = 1; break;
+      case VK_INSERT: code = 2; break;
+      case VK_DELETE: code = 3; break;
+      case VK_END: code = 4; break;
+      case VK_PRIOR: code = 5; break;
+      case VK_NEXT: code = 6; break;
+    }
+    if (cfg.linux_funkeys && code >= 11 && code <= 15) {
+       p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
+       return p - output;
+    }
+    if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
+       p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
+       return p - output;
+    }
+    if (code) {
+       p += sprintf((char *)p, "\x1B[%d~", code);
+       return p - output;
+    }
+
+    /*
+     * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
+     * some reason seems to send VK_CLEAR to Windows...).
+     */
+    switch (wParam) {
+      case VK_UP:
+       p += sprintf((char *)p, app_cursor_keys ? "\x1BOA" : "\x1B[A");
+       return p - output;
+      case VK_DOWN:
+       p += sprintf((char *)p, app_cursor_keys ? "\x1BOB" : "\x1B[B");
+       return p - output;
+      case VK_RIGHT:
+       p += sprintf((char *)p, app_cursor_keys ? "\x1BOC" : "\x1B[C");
+       return p - output;
+      case VK_LEFT:
+       p += sprintf((char *)p, app_cursor_keys ? "\x1BOD" : "\x1B[D");
+       return p - output;
+      case VK_CLEAR: p += sprintf((char *)p, "\x1B[G"); return p - output;
+    }
+
+    return 0;
+}
+
+void set_title (char *title) {
+    sfree (window_name);
+    window_name = smalloc(1+strlen(title));
+    strcpy (window_name, title);
+    if (!IsIconic(hwnd))
+       SetWindowText (hwnd, title);
+}
+
+void set_icon (char *title) {
+    sfree (icon_name);
+    icon_name = smalloc(1+strlen(title));
+    strcpy (icon_name, title);
+    if (IsIconic(hwnd))
+       SetWindowText (hwnd, title);
+}
+
+void set_sbar (int total, int start, int page) {
+    SCROLLINFO si;
+    si.cbSize = sizeof(si);
+    si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_DISABLENOSCROLL;
+    si.nMin = 0;
+    si.nMax = total - 1;
+    si.nPage = page;
+    si.nPos = start;
+    SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
+}
+
+Context get_ctx() {
+    HDC hdc;
+    if (hwnd) {
+       hdc = GetDC (hwnd);
+       if (hdc && pal)
+           SelectPalette (hdc, pal, FALSE);
+       return hdc;
+    } else
+       return NULL;
+}
+
+void free_ctx (Context ctx) {
+    SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
+    ReleaseDC (hwnd, ctx);
+}
+
+static void real_palette_set (int n, int r, int g, int b) {
+    if (pal) {
+       logpal->palPalEntry[n].peRed = r;
+       logpal->palPalEntry[n].peGreen = g;
+       logpal->palPalEntry[n].peBlue = b;
+       logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
+       colours[n] = PALETTERGB(r, g, b);
+       SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
+    } else
+       colours[n] = RGB(r, g, b);
+}
+
+void palette_set (int n, int r, int g, int b) {
+    static const int first[21] = {
+       0, 2, 4, 6, 8, 10, 12, 14,
+       1, 3, 5, 7, 9, 11, 13, 15,
+       16, 17, 18, 20, 22
+    };
+    real_palette_set (first[n], r, g, b);
+    if (first[n] >= 18)
+       real_palette_set (first[n]+1, r, g, b);
+    if (pal) {
+       HDC hdc = get_ctx();
+       UnrealizeObject (pal);
+       RealizePalette (hdc);
+       free_ctx (hdc);
+    }
+}
+
+void palette_reset (void) {
+    int i;
+
+    for (i = 0; i < NCOLOURS; i++) {
+       if (pal) {
+           logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
+           logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
+           logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
+           logpal->palPalEntry[i].peFlags = 0;
+           colours[i] = PALETTERGB(defpal[i].rgbtRed,
+                                   defpal[i].rgbtGreen,
+                                   defpal[i].rgbtBlue);
+       } else
+           colours[i] = RGB(defpal[i].rgbtRed,
+                            defpal[i].rgbtGreen,
+                            defpal[i].rgbtBlue);
+    }
+
+    if (pal) {
+       HDC hdc;
+       SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
+       hdc = get_ctx();
+       RealizePalette (hdc);
+       free_ctx (hdc);
+    }
+}
+
+void write_clip (void *data, int len) {
+    HGLOBAL clipdata;
+    void *lock;
+
+    clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
+    if (!clipdata)
+       return;
+    lock = GlobalLock (clipdata);
+    if (!lock)
+       return;
+    memcpy (lock, data, len);
+    ((unsigned char *) lock) [len] = 0;
+    GlobalUnlock (clipdata);
+
+    SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
+    if (OpenClipboard (hwnd)) {
+       EmptyClipboard();
+       SetClipboardData (CF_TEXT, clipdata);
+       CloseClipboard();
+    } else
+       GlobalFree (clipdata);
+    SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
+}
+
+void get_clip (void **p, int *len) {
+    static HGLOBAL clipdata = NULL;
+
+    if (!p) {
+       if (clipdata)
+           GlobalUnlock (clipdata);
+       clipdata = NULL;
+       return;
+    } else {
+       if (OpenClipboard (NULL)) {
+           clipdata = GetClipboardData (CF_TEXT);
+           CloseClipboard();
+           if (clipdata) {
+               *p = GlobalLock (clipdata);
+               if (*p) {
+                   *len = strlen(*p);
+                   return;
+               }
+           }
+       }
+    }
+
+    *p = NULL;
+    *len = 0;
+}
+
+/*
+ * Move `lines' lines from position `from' to position `to' in the
+ * window.
+ */
+void optimised_move (int to, int from, int lines) {
+    RECT r;
+    int min, max, d;
+
+    min = (to < from ? to : from);
+    max = to + from - min;
+    d = max - min;
+
+    r.left = 0; r.right = cols * font_width;
+    r.top = min * font_height; r.bottom = (max+lines) * font_height;
+    ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
+}
+
+/*
+ * Print a message box and perform a fatal exit.
+ */
+void fatalbox(char *fmt, ...) {
+    va_list ap;
+    char stuff[200];
+
+    va_start(ap, fmt);
+    vsprintf(stuff, fmt, ap);
+    va_end(ap);
+    MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
+    exit(1);
+}
+
+/*
+ * Beep.
+ */
+void beep(void) {
+    MessageBeep(MB_OK);
+}