--- /dev/null
+# 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
--- /dev/null
+#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
+}
--- /dev/null
+/*
+ * 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));
+}
--- /dev/null
+#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
--- /dev/null
+//{{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
--- /dev/null
+#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
+};
--- /dev/null
+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);
--- /dev/null
+ /* ============================================================= */
+ /* 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;
+}
--- /dev/null
+#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 */
+
--- /dev/null
+/* 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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
+};
--- /dev/null
+#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();
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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);
+}