OSDN Git Service

add TortoisePlink
authorFrank Li <lznuaa@gmail.com>
Wed, 7 Jan 2009 13:30:02 +0000 (21:30 +0800)
committerFrank Li <lznuaa@gmail.com>
Wed, 7 Jan 2009 13:30:02 +0000 (21:30 +0800)
71 files changed:
src/TortoisePlink/BE_ALL_S.C [new file with mode: 0644]
src/TortoisePlink/CHARSET/CHARSET.H [new file with mode: 0644]
src/TortoisePlink/CMDLINE.C [new file with mode: 0644]
src/TortoisePlink/CPROXY.C [new file with mode: 0644]
src/TortoisePlink/INT64.H [new file with mode: 0644]
src/TortoisePlink/LDISC.C [new file with mode: 0644]
src/TortoisePlink/LDISC.H [new file with mode: 0644]
src/TortoisePlink/LICENCE [new file with mode: 0644]
src/TortoisePlink/LOGGING.C [new file with mode: 0644]
src/TortoisePlink/LoginDialog.cpp [new file with mode: 0644]
src/TortoisePlink/LoginDialog.h [new file with mode: 0644]
src/TortoisePlink/MISC.C [new file with mode: 0644]
src/TortoisePlink/MISC.H [new file with mode: 0644]
src/TortoisePlink/NETWORK.H [new file with mode: 0644]
src/TortoisePlink/PINGER.C [new file with mode: 0644]
src/TortoisePlink/PORTFWD.C [new file with mode: 0644]
src/TortoisePlink/PROXY.C [new file with mode: 0644]
src/TortoisePlink/PROXY.H [new file with mode: 0644]
src/TortoisePlink/PUTTY.H [new file with mode: 0644]
src/TortoisePlink/PUTTYMEM.H [new file with mode: 0644]
src/TortoisePlink/PUTTYPS.H [new file with mode: 0644]
src/TortoisePlink/RAW.C [new file with mode: 0644]
src/TortoisePlink/RLOGIN.C [new file with mode: 0644]
src/TortoisePlink/SETTINGS.C [new file with mode: 0644]
src/TortoisePlink/SSH.C [new file with mode: 0644]
src/TortoisePlink/SSH.H [new file with mode: 0644]
src/TortoisePlink/SSHAES.C [new file with mode: 0644]
src/TortoisePlink/SSHARCF.C [new file with mode: 0644]
src/TortoisePlink/SSHBLOWF.C [new file with mode: 0644]
src/TortoisePlink/SSHBN.C [new file with mode: 0644]
src/TortoisePlink/SSHCRC.C [new file with mode: 0644]
src/TortoisePlink/SSHCRCDA.C [new file with mode: 0644]
src/TortoisePlink/SSHDES.C [new file with mode: 0644]
src/TortoisePlink/SSHDH.C [new file with mode: 0644]
src/TortoisePlink/SSHDSS.C [new file with mode: 0644]
src/TortoisePlink/SSHMD5.C [new file with mode: 0644]
src/TortoisePlink/SSHPUBK.C [new file with mode: 0644]
src/TortoisePlink/SSHRAND.C [new file with mode: 0644]
src/TortoisePlink/SSHRSA.C [new file with mode: 0644]
src/TortoisePlink/SSHSH256.C [new file with mode: 0644]
src/TortoisePlink/SSHSH512.C [new file with mode: 0644]
src/TortoisePlink/SSHSHA.C [new file with mode: 0644]
src/TortoisePlink/SSHZLIB.C [new file with mode: 0644]
src/TortoisePlink/STORAGE.H [new file with mode: 0644]
src/TortoisePlink/TELNET.C [new file with mode: 0644]
src/TortoisePlink/TERMINAL.H [new file with mode: 0644]
src/TortoisePlink/TIMING.C [new file with mode: 0644]
src/TortoisePlink/TREE234.C [new file with mode: 0644]
src/TortoisePlink/TREE234.H [new file with mode: 0644]
src/TortoisePlink/VERSION.C [new file with mode: 0644]
src/TortoisePlink/WILDCARD.C [new file with mode: 0644]
src/TortoisePlink/Windows/MSVC/Plink/TortoisePlink.vcproj [new file with mode: 0644]
src/TortoisePlink/Windows/MSVC/Plink/TortoisePlink.vcproj.FSL.B20596.user [new file with mode: 0644]
src/TortoisePlink/Windows/PUTTY.ICO [new file with mode: 0644]
src/TortoisePlink/Windows/TortoisePlink.rc [new file with mode: 0644]
src/TortoisePlink/Windows/TortoisePlinkRes.h [new file with mode: 0644]
src/TortoisePlink/Windows/WINCONS.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINDEFS.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINHANDL.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINHELP.H [new file with mode: 0644]
src/TortoisePlink/Windows/WINMISC.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINNET.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINNOISE.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINPGNTC.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINPLINK.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINPROXY.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINSER.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINSTORE.C [new file with mode: 0644]
src/TortoisePlink/Windows/WINSTUFF.H [new file with mode: 0644]
src/TortoisePlink/Windows/WINTIME.C [new file with mode: 0644]
src/TortoisePlink/X11FWD.C [new file with mode: 0644]

diff --git a/src/TortoisePlink/BE_ALL_S.C b/src/TortoisePlink/BE_ALL_S.C
new file mode 100644 (file)
index 0000000..e93ad8b
--- /dev/null
@@ -0,0 +1,32 @@
+/*\r
+ * Linking module for PuTTY proper: list the available backends\r
+ * including ssh, plus the serial backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include "putty.h"\r
+\r
+/*\r
+ * This appname is not strictly in the right place, since Plink\r
+ * also uses this module. However, Plink doesn't currently use any\r
+ * of the dialog-box sorts of things that make use of appname, so\r
+ * it shouldn't do any harm here. I'm trying to avoid having to\r
+ * have tiny little source modules containing nothing but\r
+ * declarations of appname, for as long as I can...\r
+ */\r
+const char *const appname = "PuTTY";\r
+\r
+#ifdef TELNET_DEFAULT\r
+const int be_default_protocol = PROT_TELNET;\r
+#else\r
+const int be_default_protocol = PROT_SSH;\r
+#endif\r
+\r
+struct backend_list backends[] = {\r
+    {PROT_SSH, "ssh", &ssh_backend},\r
+    {PROT_TELNET, "telnet", &telnet_backend},\r
+    {PROT_RLOGIN, "rlogin", &rlogin_backend},\r
+    {PROT_RAW, "raw", &raw_backend},\r
+    {PROT_SERIAL, "serial", &serial_backend},\r
+    {0, NULL}\r
+};\r
diff --git a/src/TortoisePlink/CHARSET/CHARSET.H b/src/TortoisePlink/CHARSET/CHARSET.H
new file mode 100644 (file)
index 0000000..3f7eb34
--- /dev/null
@@ -0,0 +1,154 @@
+/*\r
+ * charset.h - header file for general character set conversion\r
+ * routines.\r
+ */\r
+\r
+#ifndef charset_charset_h\r
+#define charset_charset_h\r
+\r
+#include <stddef.h>\r
+\r
+/*\r
+ * Enumeration that lists all the multibyte or single-byte\r
+ * character sets known to this library.\r
+ */\r
+typedef enum {\r
+    CS_NONE,                          /* used for reporting errors, etc */\r
+    CS_ISO8859_1,\r
+    CS_ISO8859_1_X11,                 /* X font encoding with VT100 glyphs */\r
+    CS_ISO8859_2,\r
+    CS_ISO8859_3,\r
+    CS_ISO8859_4,\r
+    CS_ISO8859_5,\r
+    CS_ISO8859_6,\r
+    CS_ISO8859_7,\r
+    CS_ISO8859_8,\r
+    CS_ISO8859_9,\r
+    CS_ISO8859_10,\r
+    CS_ISO8859_11,\r
+    CS_ISO8859_13,\r
+    CS_ISO8859_14,\r
+    CS_ISO8859_15,\r
+    CS_ISO8859_16,\r
+    CS_CP437,\r
+    CS_CP850,\r
+    CS_CP866,\r
+    CS_CP1250,\r
+    CS_CP1251,\r
+    CS_CP1252,\r
+    CS_CP1253,\r
+    CS_CP1254,\r
+    CS_CP1255,\r
+    CS_CP1256,\r
+    CS_CP1257,\r
+    CS_CP1258,\r
+    CS_KOI8_R,\r
+    CS_KOI8_U,\r
+    CS_MAC_ROMAN,\r
+    CS_MAC_TURKISH,\r
+    CS_MAC_CROATIAN,\r
+    CS_MAC_ICELAND,\r
+    CS_MAC_ROMANIAN,\r
+    CS_MAC_GREEK,\r
+    CS_MAC_CYRILLIC,\r
+    CS_MAC_THAI,\r
+    CS_MAC_CENTEURO,\r
+    CS_MAC_SYMBOL,\r
+    CS_MAC_DINGBATS,\r
+    CS_MAC_ROMAN_OLD,\r
+    CS_MAC_CROATIAN_OLD,\r
+    CS_MAC_ICELAND_OLD,\r
+    CS_MAC_ROMANIAN_OLD,\r
+    CS_MAC_GREEK_OLD,\r
+    CS_MAC_CYRILLIC_OLD,\r
+    CS_MAC_UKRAINE,\r
+    CS_MAC_VT100,\r
+    CS_MAC_VT100_OLD,\r
+    CS_VISCII,\r
+    CS_HP_ROMAN8,\r
+    CS_DEC_MCS,\r
+    CS_UTF8\r
+} charset_t;\r
+\r
+typedef struct {\r
+    unsigned long s0;\r
+} charset_state;\r
+\r
+/*\r
+ * Routine to convert a MB/SB character set to Unicode.\r
+ * \r
+ * This routine accepts some number of bytes, updates a state\r
+ * variable, and outputs some number of Unicode characters. There\r
+ * are no guarantees. You can't even guarantee that at most one\r
+ * Unicode character will be output per byte you feed in; for\r
+ * example, suppose you're reading UTF-8, you've seen E1 80, and\r
+ * then you suddenly see FE. Now you need to output _two_ error\r
+ * characters - one for the incomplete sequence E1 80, and one for\r
+ * the completely invalid UTF-8 byte FE.\r
+ * \r
+ * Returns the number of wide characters output; will never output\r
+ * more than the size of the buffer (as specified on input).\r
+ * Advances the `input' pointer and decrements `inlen', to indicate\r
+ * how far along the input string it got.\r
+ * \r
+ * The sequence of `errlen' wide characters pointed to by `errstr'\r
+ * will be used to indicate a conversion error. If `errstr' is\r
+ * NULL, `errlen' will be ignored, and the library will choose\r
+ * something sensible to do on its own. For Unicode, this will be\r
+ * U+FFFD (REPLACEMENT CHARACTER).\r
+ */\r
+\r
+int charset_to_unicode(char **input, int *inlen, wchar_t *output, int outlen,\r
+                      int charset, charset_state *state,\r
+                      const wchar_t *errstr, int errlen);\r
+\r
+/*\r
+ * Routine to convert Unicode to an MB/SB character set.\r
+ * \r
+ * This routine accepts some number of Unicode characters, updates\r
+ * a state variable, and outputs some number of bytes.\r
+ * \r
+ * Returns the number of bytes characters output; will never output\r
+ * more than the size of the buffer (as specified on input), and\r
+ * will never output a partial MB character. Advances the `input'\r
+ * pointer and decrements `inlen', to indicate how far along the\r
+ * input string it got.\r
+ * \r
+ * The sequence of `errlen' characters pointed to by `errstr' will\r
+ * be used to indicate a conversion error. If `errstr' is NULL,\r
+ * `errlen' will be ignored, and the library will choose something\r
+ * sensible to do on its own (which will vary depending on the\r
+ * output charset).\r
+ */\r
+\r
+int charset_from_unicode(wchar_t **input, int *inlen, char *output, int outlen,\r
+                        int charset, charset_state *state,\r
+                        const char *errstr, int errlen);\r
+\r
+/*\r
+ * Convert X11 encoding names to and from our charset identifiers.\r
+ */\r
+const char *charset_to_xenc(int charset);\r
+int charset_from_xenc(const char *name);\r
+\r
+/*\r
+ * Convert MIME encoding names to and from our charset identifiers.\r
+ */\r
+const char *charset_to_mimeenc(int charset);\r
+int charset_from_mimeenc(const char *name);\r
+\r
+/*\r
+ * Convert our own encoding names to and from our charset\r
+ * identifiers.\r
+ */\r
+const char *charset_to_localenc(int charset);\r
+int charset_from_localenc(const char *name);\r
+int charset_localenc_nth(int n);\r
+\r
+/*\r
+ * Convert Mac OS script/region/font to our charset identifiers.\r
+ */\r
+int charset_from_macenc(int script, int region, int sysvers,\r
+                       const char *fontname);\r
+\r
+#endif /* charset_charset_h */\r
diff --git a/src/TortoisePlink/CMDLINE.C b/src/TortoisePlink/CMDLINE.C
new file mode 100644 (file)
index 0000000..19a25be
--- /dev/null
@@ -0,0 +1,442 @@
+/*\r
+ * cmdline.c - command-line parsing shared between many of the\r
+ * PuTTY applications\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include "putty.h"\r
+\r
+/*\r
+ * Some command-line parameters need to be saved up until after\r
+ * we've loaded the saved session which will form the basis of our\r
+ * eventual running configuration. For this we use the macro\r
+ * SAVEABLE, which notices if the `need_save' parameter is set and\r
+ * saves the parameter and value on a list.\r
+ * \r
+ * We also assign priorities to saved parameters, just to slightly\r
+ * ameliorate silly ordering problems. For example, if you specify\r
+ * a saved session to load, it will be loaded _before_ all your\r
+ * local modifications such as -L are evaluated; and if you specify\r
+ * a protocol and a port, the protocol is set up first so that the\r
+ * port can override its choice of port number.\r
+ * \r
+ * (In fact -load is not saved at all, since in at least Plink the\r
+ * processing of further command-line options depends on whether or\r
+ * not the loaded session contained a hostname. So it must be\r
+ * executed immediately.)\r
+ */\r
+\r
+#define NPRIORITIES 2\r
+\r
+struct cmdline_saved_param {\r
+    char *p, *value;\r
+};\r
+struct cmdline_saved_param_set {\r
+    struct cmdline_saved_param *params;\r
+    int nsaved, savesize;\r
+};\r
+\r
+/*\r
+ * C guarantees this structure will be initialised to all zero at\r
+ * program start, which is exactly what we want.\r
+ */\r
+static struct cmdline_saved_param_set saves[NPRIORITIES];\r
+\r
+static void cmdline_save_param(char *p, char *value, int pri)\r
+{\r
+    if (saves[pri].nsaved >= saves[pri].savesize) {\r
+       saves[pri].savesize = saves[pri].nsaved + 32;\r
+       saves[pri].params = sresize(saves[pri].params, saves[pri].savesize,\r
+                                   struct cmdline_saved_param);\r
+    }\r
+    saves[pri].params[saves[pri].nsaved].p = p;\r
+    saves[pri].params[saves[pri].nsaved].value = value;\r
+    saves[pri].nsaved++;\r
+}\r
+\r
+void cmdline_cleanup(void)\r
+{\r
+    int pri;\r
+\r
+    for (pri = 0; pri < NPRIORITIES; pri++)\r
+       sfree(saves[pri].params);\r
+}\r
+\r
+#define SAVEABLE(pri) do { \\r
+    if (need_save) { cmdline_save_param(p, value, pri); return ret; } \\r
+} while (0)\r
+\r
+static char *cmdline_password = NULL;\r
+\r
+/*\r
+ * Similar interface to get_userpass_input(), except that here a -1\r
+ * return means that we aren't capable of processing the prompt and\r
+ * someone else should do it.\r
+ */\r
+int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen) {\r
+\r
+    static int tried_once = 0;\r
+\r
+    /*\r
+     * We only handle prompts which don't echo (which we assume to be\r
+     * passwords), and (currently) we only cope with a password prompt\r
+     * that comes in a prompt-set on its own.\r
+     */\r
+    if (!cmdline_password || in || p->n_prompts != 1 || p->prompts[0]->echo) {\r
+       return -1;\r
+    }\r
+\r
+    /*\r
+     * If we've tried once, return utter failure (no more passwords left\r
+     * to try).\r
+     */\r
+    if (tried_once)\r
+       return 0;\r
+\r
+    strncpy(p->prompts[0]->result, cmdline_password,\r
+           p->prompts[0]->result_len);\r
+    p->prompts[0]->result[p->prompts[0]->result_len-1] = '\0';\r
+    memset(cmdline_password, 0, strlen(cmdline_password));\r
+    tried_once = 1;\r
+    return 1;\r
+\r
+}\r
+\r
+/*\r
+ * Here we have a flags word which describes the capabilities of\r
+ * the particular tool on whose behalf we're running. We will\r
+ * refuse certain command-line options if a particular tool\r
+ * inherently can't do anything sensible. For example, the file\r
+ * transfer tools (psftp, pscp) can't do a great deal with protocol\r
+ * selections (ever tried running scp over telnet?) or with port\r
+ * forwarding (even if it wasn't a hideously bad idea, they don't\r
+ * have the select() infrastructure to make them work).\r
+ */\r
+int cmdline_tooltype = 0;\r
+\r
+static int cmdline_check_unavailable(int flag, char *p)\r
+{\r
+    if (cmdline_tooltype & flag) {\r
+       cmdline_error("option \"%s\" not available in this tool", p);\r
+       return 1;\r
+    }\r
+    return 0;\r
+}\r
+\r
+#define UNAVAILABLE_IN(flag) do { \\r
+    if (cmdline_check_unavailable(flag, p)) return ret; \\r
+} while (0)\r
+\r
+/*\r
+ * Process a standard command-line parameter. `p' is the parameter\r
+ * in question; `value' is the subsequent element of argv, which\r
+ * may or may not be required as an operand to the parameter.\r
+ * If `need_save' is 1, arguments which need to be saved as\r
+ * described at this top of this file are, for later execution;\r
+ * if 0, they are processed normally. (-1 is a special value used\r
+ * by pterm to count arguments for a preliminary pass through the\r
+ * argument list; it causes immediate return with an appropriate\r
+ * value with no action taken.)\r
+ * Return value is 2 if both arguments were used; 1 if only p was\r
+ * used; 0 if the parameter wasn't one we recognised; -2 if it\r
+ * should have been 2 but value was NULL.\r
+ */\r
+\r
+#define RETURN(x) do { \\r
+    if ((x) == 2 && !value) return -2; \\r
+    ret = x; \\r
+    if (need_save < 0) return x; \\r
+} while (0)\r
+\r
+int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)\r
+{\r
+    int ret = 0;\r
+\r
+    if (!strcmp(p, "-load")) {\r
+       RETURN(2);\r
+       /* This parameter must be processed immediately rather than being\r
+        * saved. */\r
+       do_defaults(value, cfg);\r
+       loaded_session = TRUE;\r
+       return 2;\r
+    }\r
+    if (!strcmp(p, "-ssh")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_SSH;\r
+       default_port = cfg->port = 22;\r
+       return 1;\r
+    }\r
+    if (!strcmp(p, "-telnet")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_TELNET;\r
+       default_port = cfg->port = 23;\r
+       return 1;\r
+    }\r
+    if (!strcmp(p, "-rlogin")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_RLOGIN;\r
+       default_port = cfg->port = 513;\r
+       return 1;\r
+    }\r
+    if (!strcmp(p, "-raw")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       default_protocol = cfg->protocol = PROT_RAW;\r
+    }\r
+    if (!strcmp(p, "-v")) {\r
+       RETURN(1);\r
+       flags |= FLAG_VERBOSE;\r
+    }\r
+    if (!strcmp(p, "-l")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       strncpy(cfg->username, value, sizeof(cfg->username));\r
+       cfg->username[sizeof(cfg->username) - 1] = '\0';\r
+    }\r
+    if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) {\r
+       char *fwd, *ptr, *q, *qq;\r
+       int dynamic, i=0;\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       dynamic = !strcmp(p, "-D");\r
+       fwd = value;\r
+       ptr = cfg->portfwd;\r
+       /* if existing forwards, find end of list */\r
+       while (*ptr) {\r
+           while (*ptr)\r
+               ptr++;\r
+           ptr++;\r
+       }\r
+       i = ptr - cfg->portfwd;\r
+       ptr[0] = p[1];  /* insert a 'L', 'R' or 'D' at the start */\r
+       ptr++;\r
+       if (1 + strlen(fwd) + 2 > sizeof(cfg->portfwd) - i) {\r
+           cmdline_error("out of space for port forwardings");\r
+           return ret;\r
+       }\r
+       strncpy(ptr, fwd, sizeof(cfg->portfwd) - i - 2);\r
+       if (!dynamic) {\r
+           /*\r
+            * We expect _at least_ two colons in this string. The\r
+            * possible formats are `sourceport:desthost:destport',\r
+            * or `sourceip:sourceport:desthost:destport' if you're\r
+            * specifying a particular loopback address. We need to\r
+            * replace the one between source and dest with a \t;\r
+            * this means we must find the second-to-last colon in\r
+            * the string.\r
+            */\r
+           q = qq = strchr(ptr, ':');\r
+           while (qq) {\r
+               char *qqq = strchr(qq+1, ':');\r
+               if (qqq)\r
+                   q = qq;\r
+               qq = qqq;\r
+           }\r
+           if (q) *q = '\t';          /* replace second-last colon with \t */\r
+       }\r
+       cfg->portfwd[sizeof(cfg->portfwd) - 1] = '\0';\r
+       cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0';\r
+       ptr[strlen(ptr)+1] = '\000';    /* append 2nd '\000' */\r
+    }\r
+    if ((!strcmp(p, "-nc"))) {\r
+       char *host, *portp;\r
+\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+\r
+       host = portp = value;\r
+       while (*portp && *portp != ':')\r
+           portp++;\r
+       if (*portp) {\r
+           unsigned len = portp - host;\r
+           if (len >= sizeof(cfg->ssh_nc_host))\r
+               len = sizeof(cfg->ssh_nc_host) - 1;\r
+           strncpy(cfg->ssh_nc_host, value, len);\r
+           cfg->ssh_nc_host[sizeof(cfg->ssh_nc_host) - 1] = '\0';\r
+           cfg->ssh_nc_port = atoi(portp+1);\r
+       } else {\r
+           cmdline_error("-nc expects argument of form 'host:port'");\r
+           return ret;\r
+       }\r
+    }\r
+    if (!strcmp(p, "-m")) {\r
+       char *filename, *command;\r
+       int cmdlen, cmdsize;\r
+       FILE *fp;\r
+       int c, d;\r
+\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+\r
+       filename = value;\r
+\r
+       cmdlen = cmdsize = 0;\r
+       command = NULL;\r
+       fp = fopen(filename, "r");\r
+       if (!fp) {\r
+           cmdline_error("unable to open command "\r
+                         "file \"%s\"", filename);\r
+           return ret;\r
+       }\r
+       do {\r
+           c = fgetc(fp);\r
+           d = c;\r
+           if (c == EOF)\r
+               d = 0;\r
+           if (cmdlen >= cmdsize) {\r
+               cmdsize = cmdlen + 512;\r
+               command = sresize(command, cmdsize, char);\r
+           }\r
+           command[cmdlen++] = d;\r
+       } while (c != EOF);\r
+       cfg->remote_cmd_ptr = command;\r
+       cfg->remote_cmd_ptr2 = NULL;\r
+       cfg->nopty = TRUE;      /* command => no terminal */\r
+    }\r
+    if (!strcmp(p, "-P")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);                   /* lower priority than -ssh,-telnet */\r
+       cfg->port = atoi(value);\r
+    }\r
+    if (!strcmp(p, "-pw")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);\r
+       /* We delay evaluating this until after the protocol is decided,\r
+        * so that we can warn if it's of no use with the selected protocol */\r
+       if (cfg->protocol != PROT_SSH)\r
+           cmdline_error("the -pw option can only be used with the "\r
+                         "SSH protocol");\r
+       else {\r
+           cmdline_password = dupstr(value);\r
+           /* Assuming that `value' is directly from argv, make a good faith\r
+            * attempt to trample it, to stop it showing up in `ps' output\r
+            * on Unix-like systems. Not guaranteed, of course. */\r
+           memset(value, 0, strlen(value));\r
+       }\r
+    }\r
+\r
+    if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") ||\r
+       !strcmp(p, "-pageant")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->tryagent = TRUE;\r
+    }\r
+    if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") ||\r
+       !strcmp(p, "-nopageant")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->tryagent = FALSE;\r
+    }\r
+\r
+    if (!strcmp(p, "-A")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->agentfwd = 1;\r
+    }\r
+    if (!strcmp(p, "-a")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->agentfwd = 0;\r
+    }\r
+\r
+    if (!strcmp(p, "-X")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->x11_forward = 1;\r
+    }\r
+    if (!strcmp(p, "-x")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->x11_forward = 0;\r
+    }\r
+\r
+    if (!strcmp(p, "-t")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);    /* lower priority than -m */\r
+       cfg->nopty = 0;\r
+    }\r
+    if (!strcmp(p, "-T")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(1);\r
+       cfg->nopty = 1;\r
+    }\r
+\r
+    if (!strcmp(p, "-N")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->ssh_no_shell = 1;\r
+    }\r
+\r
+    if (!strcmp(p, "-C")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->compression = 1;\r
+    }\r
+\r
+    if (!strcmp(p, "-1")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->sshprot = 0;              /* ssh protocol 1 only */\r
+    }\r
+    if (!strcmp(p, "-2")) {\r
+       RETURN(1);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->sshprot = 3;              /* ssh protocol 2 only */\r
+    }\r
+\r
+    if (!strcmp(p, "-i")) {\r
+       RETURN(2);\r
+       UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
+       SAVEABLE(0);\r
+       cfg->keyfile = filename_from_str(value);\r
+    }\r
+\r
+    if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) {\r
+       RETURN(1);\r
+       SAVEABLE(1);\r
+       cfg->addressfamily = ADDRTYPE_IPV4;\r
+    }\r
+    if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) {\r
+       RETURN(1);\r
+       SAVEABLE(1);\r
+       cfg->addressfamily = ADDRTYPE_IPV6;\r
+    }\r
+\r
+    return ret;                               /* unrecognised */\r
+}\r
+\r
+void cmdline_run_saved(Config *cfg)\r
+{\r
+    int pri, i;\r
+    for (pri = 0; pri < NPRIORITIES; pri++)\r
+       for (i = 0; i < saves[pri].nsaved; i++)\r
+           cmdline_process_param(saves[pri].params[i].p,\r
+                                 saves[pri].params[i].value, 0, cfg);\r
+}\r
diff --git a/src/TortoisePlink/CPROXY.C b/src/TortoisePlink/CPROXY.C
new file mode 100644 (file)
index 0000000..5537fca
--- /dev/null
@@ -0,0 +1,190 @@
+/*\r
+ * Routines to do cryptographic interaction with proxies in PuTTY.\r
+ * This is in a separate module from proxy.c, so that it can be\r
+ * conveniently removed in PuTTYtel by replacing this module with\r
+ * the stub version nocproxy.c.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <ctype.h>\r
+#include <string.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "putty.h"\r
+#include "ssh.h" /* For MD5 support */\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+static void hmacmd5_chap(const unsigned char *challenge, int challen,\r
+                        const char *passwd, unsigned char *response)\r
+{\r
+    void *hmacmd5_ctx;\r
+    int pwlen;\r
+\r
+    hmacmd5_ctx = hmacmd5_make_context();\r
+\r
+    pwlen = strlen(passwd);\r
+    if (pwlen>64) {\r
+       unsigned char md5buf[16];\r
+       MD5Simple(passwd, pwlen, md5buf);\r
+       hmacmd5_key(hmacmd5_ctx, md5buf, 16);\r
+    } else {\r
+       hmacmd5_key(hmacmd5_ctx, passwd, pwlen);\r
+    }\r
+\r
+    hmacmd5_do_hmac(hmacmd5_ctx, challenge, challen, response);\r
+    hmacmd5_free_context(hmacmd5_ctx);\r
+}\r
+\r
+void proxy_socks5_offerencryptedauth(char *command, int *len)\r
+{\r
+    command[*len] = 0x03; /* CHAP */\r
+    (*len)++;\r
+}\r
+\r
+int proxy_socks5_handlechap (Proxy_Socket p)\r
+{\r
+\r
+    /* CHAP authentication reply format:\r
+     *  version number (1 bytes) = 1\r
+     *  number of commands (1 byte)\r
+     *\r
+     * For each command:\r
+     *  command identifier (1 byte)\r
+     *  data length (1 byte)\r
+     */\r
+    unsigned char data[260];\r
+    unsigned char outbuf[20];\r
+\r
+    while(p->chap_num_attributes == 0 ||\r
+         p->chap_num_attributes_processed < p->chap_num_attributes) {\r
+       if (p->chap_num_attributes == 0 ||\r
+           p->chap_current_attribute == -1) {\r
+           /* CHAP normally reads in two bytes, either at the\r
+            * beginning or for each attribute/value pair.  But if\r
+            * we're waiting for the value's data, we might not want\r
+            * to read 2 bytes.\r
+            */\r
\r
+           if (bufchain_size(&p->pending_input_data) < 2)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 2);\r
+           bufchain_consume(&p->pending_input_data, 2);\r
+       }\r
+\r
+       if (p->chap_num_attributes == 0) {\r
+           /* If there are no attributes, this is our first msg\r
+            * with the server, where we negotiate version and \r
+            * number of attributes\r
+            */\r
+           if (data[0] != 0x01) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy wants"\r
+                            " a different CHAP version",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           if (data[1] == 0x00) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy won't"\r
+                            " negotiate CHAP with us",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           p->chap_num_attributes = data[1];\r
+       } else {\r
+           if (p->chap_current_attribute == -1) {\r
+               /* We have to read in each attribute/value pair -\r
+                * those we don't understand can be ignored, but\r
+                * there are a few we'll need to handle.\r
+                */\r
+               p->chap_current_attribute = data[0];\r
+               p->chap_current_datalen = data[1];\r
+           }\r
+           if (bufchain_size(&p->pending_input_data) <\r
+               p->chap_current_datalen)\r
+               return 1;              /* not got everything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data,\r
+                          p->chap_current_datalen);\r
+\r
+           bufchain_consume(&p->pending_input_data,\r
+                            p->chap_current_datalen);\r
+\r
+           switch (p->chap_current_attribute) {\r
+             case 0x00:\r
+               /* Successful authentication */\r
+               if (data[0] == 0x00)\r
+                   p->state = 2;\r
+               else {\r
+                   plug_closing(p->plug, "Proxy error: SOCKS proxy"\r
+                                " refused CHAP authentication",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   return 1;\r
+               }\r
+             break;\r
+             case 0x03:\r
+               outbuf[0] = 0x01; /* Version */\r
+               outbuf[1] = 0x01; /* One attribute */\r
+               outbuf[2] = 0x04; /* Response */\r
+               outbuf[3] = 0x10; /* Length */\r
+               hmacmd5_chap(data, p->chap_current_datalen,\r
+                            p->cfg.proxy_password, &outbuf[4]);\r
+               sk_write(p->sub_socket, (char *)outbuf, 20);\r
+             break;\r
+             case 0x11:\r
+               /* Chose a protocol */\r
+               if (data[0] != 0x85) {\r
+                   plug_closing(p->plug, "Proxy error: Server chose "\r
+                                "CHAP of other than HMAC-MD5 but we "\r
+                                "didn't offer it!",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   return 1;\r
+               }\r
+             break;\r
+           }\r
+           p->chap_current_attribute = -1;\r
+           p->chap_num_attributes_processed++;\r
+       }\r
+       if (p->state == 8 &&\r
+           p->chap_num_attributes_processed >= p->chap_num_attributes) {\r
+           p->chap_num_attributes = 0;\r
+           p->chap_num_attributes_processed = 0;\r
+           p->chap_current_datalen = 0;\r
+       }\r
+    }\r
+    return 0;\r
+}\r
+\r
+int proxy_socks5_selectchap(Proxy_Socket p)\r
+{\r
+    if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+       char chapbuf[514];\r
+       int ulen;\r
+       chapbuf[0] = '\x01'; /* Version */\r
+       chapbuf[1] = '\x02'; /* Number of attributes sent */\r
+       chapbuf[2] = '\x11'; /* First attribute - algorithms list */\r
+       chapbuf[3] = '\x01'; /* Only one CHAP algorithm */\r
+       chapbuf[4] = '\x85'; /* ...and it's HMAC-MD5, the core one */\r
+       chapbuf[5] = '\x02'; /* Second attribute - username */\r
+\r
+       ulen = strlen(p->cfg.proxy_username);\r
+       if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;\r
+\r
+       chapbuf[6] = ulen;\r
+       memcpy(chapbuf+7, p->cfg.proxy_username, ulen);\r
+\r
+       sk_write(p->sub_socket, chapbuf, ulen + 7);\r
+       p->chap_num_attributes = 0;\r
+       p->chap_num_attributes_processed = 0;\r
+       p->chap_current_attribute = -1;\r
+       p->chap_current_datalen = 0;\r
+\r
+       p->state = 8;\r
+    } else \r
+       plug_closing(p->plug, "Proxy error: Server chose "\r
+                    "CHAP authentication but we didn't offer it!",\r
+                PROXY_ERROR_GENERAL, 0);\r
+    return 1;\r
+}\r
diff --git a/src/TortoisePlink/INT64.H b/src/TortoisePlink/INT64.H
new file mode 100644 (file)
index 0000000..63df3a9
--- /dev/null
@@ -0,0 +1,24 @@
+/*\r
+ * Header for int64.c.\r
+ */\r
+\r
+#ifndef PUTTY_INT64_H\r
+#define PUTTY_INT64_H\r
+\r
+typedef struct {\r
+    unsigned long hi, lo;\r
+} uint64;\r
+\r
+uint64 uint64_div10(uint64 x, int *remainder);\r
+void uint64_decimal(uint64 x, char *buffer);\r
+uint64 uint64_make(unsigned long hi, unsigned long lo);\r
+uint64 uint64_add(uint64 x, uint64 y);\r
+uint64 uint64_add32(uint64 x, unsigned long y);\r
+int uint64_compare(uint64 x, uint64 y);\r
+uint64 uint64_subtract(uint64 x, uint64 y);\r
+double uint64_to_double(uint64 x);\r
+uint64 uint64_shift_right(uint64 x, int shift);\r
+uint64 uint64_shift_left(uint64 x, int shift);\r
+uint64 uint64_from_decimal(char *str);\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/LDISC.C b/src/TortoisePlink/LDISC.C
new file mode 100644 (file)
index 0000000..20fa3c5
--- /dev/null
@@ -0,0 +1,336 @@
+/*\r
+ * ldisc.c: PuTTY line discipline. Sits between the input coming\r
+ * from keypresses in the window, and the output channel leading to\r
+ * the back end. Implements echo and/or local line editing,\r
+ * depending on what's currently configured.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+#include "terminal.h"\r
+#include "ldisc.h"\r
+\r
+#define ECHOING (ldisc->cfg->localecho == FORCE_ON || \\r
+                 (ldisc->cfg->localecho == AUTO && \\r
+                      (ldisc->back->ldisc(ldisc->backhandle, LD_ECHO) || \\r
+                          term_ldisc(ldisc->term, LD_ECHO))))\r
+#define EDITING (ldisc->cfg->localedit == FORCE_ON || \\r
+                 (ldisc->cfg->localedit == AUTO && \\r
+                      (ldisc->back->ldisc(ldisc->backhandle, LD_EDIT) || \\r
+                          term_ldisc(ldisc->term, LD_EDIT))))\r
+\r
+static void c_write(Ldisc ldisc, char *buf, int len)\r
+{\r
+    from_backend(ldisc->frontend, 0, buf, len);\r
+}\r
+\r
+static int plen(Ldisc ldisc, unsigned char c)\r
+{\r
+    if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term)))\r
+       return 1;\r
+    else if (c < 128)\r
+       return 2;                      /* ^x for some x */\r
+    else if (in_utf(ldisc->term) && c >= 0xC0)\r
+       return 1;                      /* UTF-8 introducer character\r
+                                       * (FIXME: combining / wide chars) */\r
+    else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0)\r
+       return 0;                      /* UTF-8 followup character */\r
+    else\r
+       return 4;                      /* <XY> hex representation */\r
+}\r
+\r
+static void pwrite(Ldisc ldisc, unsigned char c)\r
+{\r
+    if ((c >= 32 && c <= 126) ||\r
+       (!in_utf(ldisc->term) && c >= 0xA0) ||\r
+       (in_utf(ldisc->term) && c >= 0x80)) {\r
+       c_write(ldisc, (char *)&c, 1);\r
+    } else if (c < 128) {\r
+       char cc[2];\r
+       cc[1] = (c == 127 ? '?' : c + 0x40);\r
+       cc[0] = '^';\r
+       c_write(ldisc, cc, 2);\r
+    } else {\r
+       char cc[5];\r
+       sprintf(cc, "<%02X>", c);\r
+       c_write(ldisc, cc, 4);\r
+    }\r
+}\r
+\r
+static int char_start(Ldisc ldisc, unsigned char c)\r
+{\r
+    if (in_utf(ldisc->term))\r
+       return (c < 0x80 || c >= 0xC0);\r
+    else\r
+       return 1;\r
+}\r
+\r
+static void bsb(Ldisc ldisc, int n)\r
+{\r
+    while (n--)\r
+       c_write(ldisc, "\010 \010", 3);\r
+}\r
+\r
+#define CTRL(x) (x^'@')\r
+#define KCTRL(x) ((x^'@') | 0x100)\r
+\r
+void *ldisc_create(Config *mycfg, Terminal *term,\r
+                  Backend *back, void *backhandle,\r
+                  void *frontend)\r
+{\r
+    Ldisc ldisc = snew(struct ldisc_tag);\r
+\r
+    ldisc->buf = NULL;\r
+    ldisc->buflen = 0;\r
+    ldisc->bufsiz = 0;\r
+    ldisc->quotenext = 0;\r
+\r
+    ldisc->cfg = mycfg;\r
+    ldisc->back = back;\r
+    ldisc->backhandle = backhandle;\r
+    ldisc->term = term;\r
+    ldisc->frontend = frontend;\r
+\r
+    /* Link ourselves into the backend and the terminal */\r
+    if (term)\r
+       term->ldisc = ldisc;\r
+    if (back)\r
+       back->provide_ldisc(backhandle, ldisc);\r
+\r
+    return ldisc;\r
+}\r
+\r
+void ldisc_free(void *handle)\r
+{\r
+    Ldisc ldisc = (Ldisc) handle;\r
+\r
+    if (ldisc->term)\r
+       ldisc->term->ldisc = NULL;\r
+    if (ldisc->back)\r
+       ldisc->back->provide_ldisc(ldisc->backhandle, NULL);\r
+    if (ldisc->buf)\r
+       sfree(ldisc->buf);\r
+    sfree(ldisc);\r
+}\r
+\r
+void ldisc_send(void *handle, char *buf, int len, int interactive)\r
+{\r
+    Ldisc ldisc = (Ldisc) handle;\r
+    int keyflag = 0;\r
+    /*\r
+     * Called with len=0 when the options change. We must inform\r
+     * the front end in case it needs to know.\r
+     */\r
+    if (len == 0) {\r
+       ldisc_update(ldisc->frontend, ECHOING, EDITING);\r
+       return;\r
+    }\r
+    /*\r
+     * Notify the front end that something was pressed, in case\r
+     * it's depending on finding out (e.g. keypress termination for\r
+     * Close On Exit). \r
+     */\r
+    frontend_keypress(ldisc->frontend);\r
+\r
+    /*\r
+     * Less than zero means null terminated special string.\r
+     */\r
+    if (len < 0) {\r
+       len = strlen(buf);\r
+       keyflag = KCTRL('@');\r
+    }\r
+    /*\r
+     * Either perform local editing, or just send characters.\r
+     */\r
+    if (EDITING) {\r
+       while (len--) {\r
+           int c;\r
+           c = *buf++ + keyflag;\r
+           if (!interactive && c == '\r')\r
+               c += KCTRL('@');\r
+           switch (ldisc->quotenext ? ' ' : c) {\r
+               /*\r
+                * ^h/^?: delete, and output BSBs, to return to\r
+                * last character boundary (in UTF-8 mode this may\r
+                * be more than one byte)\r
+                * ^w: delete, and output BSBs, to return to last\r
+                * space/nonspace boundary\r
+                * ^u: delete, and output BSBs, to return to BOL\r
+                * ^c: Do a ^u then send a telnet IP\r
+                * ^z: Do a ^u then send a telnet SUSP\r
+                * ^\: Do a ^u then send a telnet ABORT\r
+                * ^r: echo "^R\n" and redraw line\r
+                * ^v: quote next char\r
+                * ^d: if at BOL, end of file and close connection,\r
+                * else send line and reset to BOL\r
+                * ^m: send line-plus-\r\n and reset to BOL\r
+                */\r
+             case KCTRL('H'):\r
+             case KCTRL('?'):         /* backspace/delete */\r
+               if (ldisc->buflen > 0) {\r
+                   do {\r
+                       if (ECHOING)\r
+                           bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                       ldisc->buflen--;\r
+                   } while (!char_start(ldisc, ldisc->buf[ldisc->buflen]));\r
+               }\r
+               break;\r
+             case CTRL('W'):          /* delete word */\r
+               while (ldisc->buflen > 0) {\r
+                   if (ECHOING)\r
+                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                   ldisc->buflen--;\r
+                   if (ldisc->buflen > 0 &&\r
+                       isspace((unsigned char)ldisc->buf[ldisc->buflen-1]) &&\r
+                       !isspace((unsigned char)ldisc->buf[ldisc->buflen]))\r
+                       break;\r
+               }\r
+               break;\r
+             case CTRL('U'):          /* delete line */\r
+             case CTRL('C'):          /* Send IP */\r
+             case CTRL('\\'):         /* Quit */\r
+             case CTRL('Z'):          /* Suspend */\r
+               while (ldisc->buflen > 0) {\r
+                   if (ECHOING)\r
+                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                   ldisc->buflen--;\r
+               }\r
+               ldisc->back->special(ldisc->backhandle, TS_EL);\r
+                /*\r
+                 * We don't send IP, SUSP or ABORT if the user has\r
+                 * configured telnet specials off! This breaks\r
+                 * talkers otherwise.\r
+                 */\r
+                if (!ldisc->cfg->telnet_keyboard)\r
+                    goto default_case;\r
+               if (c == CTRL('C'))\r
+                   ldisc->back->special(ldisc->backhandle, TS_IP);\r
+               if (c == CTRL('Z'))\r
+                   ldisc->back->special(ldisc->backhandle, TS_SUSP);\r
+               if (c == CTRL('\\'))\r
+                   ldisc->back->special(ldisc->backhandle, TS_ABORT);\r
+               break;\r
+             case CTRL('R'):          /* redraw line */\r
+               if (ECHOING) {\r
+                   int i;\r
+                   c_write(ldisc, "^R\r\n", 4);\r
+                   for (i = 0; i < ldisc->buflen; i++)\r
+                       pwrite(ldisc, ldisc->buf[i]);\r
+               }\r
+               break;\r
+             case CTRL('V'):          /* quote next char */\r
+               ldisc->quotenext = TRUE;\r
+               break;\r
+             case CTRL('D'):          /* logout or send */\r
+               if (ldisc->buflen == 0) {\r
+                   ldisc->back->special(ldisc->backhandle, TS_EOF);\r
+               } else {\r
+                   ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);\r
+                   ldisc->buflen = 0;\r
+               }\r
+               break;\r
+               /*\r
+                * This particularly hideous bit of code from RDB\r
+                * allows ordinary ^M^J to do the same thing as\r
+                * magic-^M when in Raw protocol. The line `case\r
+                * KCTRL('M'):' is _inside_ the if block. Thus:\r
+                * \r
+                *  - receiving regular ^M goes straight to the\r
+                *    default clause and inserts as a literal ^M.\r
+                *  - receiving regular ^J _not_ directly after a\r
+                *    literal ^M (or not in Raw protocol) fails the\r
+                *    if condition, leaps to the bottom of the if,\r
+                *    and falls through into the default clause\r
+                *    again.\r
+                *  - receiving regular ^J just after a literal ^M\r
+                *    in Raw protocol passes the if condition,\r
+                *    deletes the literal ^M, and falls through\r
+                *    into the magic-^M code\r
+                *  - receiving a magic-^M empties the line buffer,\r
+                *    signals end-of-line in one of the various\r
+                *    entertaining ways, and _doesn't_ fall out of\r
+                *    the bottom of the if and through to the\r
+                *    default clause because of the break.\r
+                */\r
+             case CTRL('J'):\r
+               if (ldisc->cfg->protocol == PROT_RAW &&\r
+                   ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {\r
+                   if (ECHOING)\r
+                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+                   ldisc->buflen--;\r
+                   /* FALLTHROUGH */\r
+             case KCTRL('M'):         /* send with newline */\r
+                   if (ldisc->buflen > 0)\r
+                       ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);\r
+                   if (ldisc->cfg->protocol == PROT_RAW)\r
+                       ldisc->back->send(ldisc->backhandle, "\r\n", 2);\r
+                   else if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)\r
+                       ldisc->back->special(ldisc->backhandle, TS_EOL);\r
+                   else\r
+                       ldisc->back->send(ldisc->backhandle, "\r", 1);\r
+                   if (ECHOING)\r
+                       c_write(ldisc, "\r\n", 2);\r
+                   ldisc->buflen = 0;\r
+                   break;\r
+               }\r
+               /* FALLTHROUGH */\r
+             default:                 /* get to this label from ^V handler */\r
+                default_case:\r
+               if (ldisc->buflen >= ldisc->bufsiz) {\r
+                   ldisc->bufsiz = ldisc->buflen + 256;\r
+                   ldisc->buf = sresize(ldisc->buf, ldisc->bufsiz, char);\r
+               }\r
+               ldisc->buf[ldisc->buflen++] = c;\r
+               if (ECHOING)\r
+                   pwrite(ldisc, (unsigned char) c);\r
+               ldisc->quotenext = FALSE;\r
+               break;\r
+           }\r
+       }\r
+    } else {\r
+       if (ldisc->buflen != 0) {\r
+           ldisc->back->send(ldisc->backhandle, ldisc->buf, ldisc->buflen);\r
+           while (ldisc->buflen > 0) {\r
+               bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
+               ldisc->buflen--;\r
+           }\r
+       }\r
+       if (len > 0) {\r
+           if (ECHOING)\r
+               c_write(ldisc, buf, len);\r
+           if (keyflag && ldisc->cfg->protocol == PROT_TELNET && len == 1) {\r
+               switch (buf[0]) {\r
+                 case CTRL('M'):\r
+                   if (ldisc->cfg->protocol == PROT_TELNET && ldisc->cfg->telnet_newline)\r
+                       ldisc->back->special(ldisc->backhandle, TS_EOL);\r
+                   else\r
+                       ldisc->back->send(ldisc->backhandle, "\r", 1);\r
+                   break;\r
+                 case CTRL('?'):\r
+                 case CTRL('H'):\r
+                   if (ldisc->cfg->telnet_keyboard) {\r
+                       ldisc->back->special(ldisc->backhandle, TS_EC);\r
+                       break;\r
+                   }\r
+                 case CTRL('C'):\r
+                   if (ldisc->cfg->telnet_keyboard) {\r
+                       ldisc->back->special(ldisc->backhandle, TS_IP);\r
+                       break;\r
+                   }\r
+                 case CTRL('Z'):\r
+                   if (ldisc->cfg->telnet_keyboard) {\r
+                       ldisc->back->special(ldisc->backhandle, TS_SUSP);\r
+                       break;\r
+                   }\r
+\r
+                 default:\r
+                   ldisc->back->send(ldisc->backhandle, buf, len);\r
+                   break;\r
+               }\r
+           } else\r
+               ldisc->back->send(ldisc->backhandle, buf, len);\r
+       }\r
+    }\r
+}\r
diff --git a/src/TortoisePlink/LDISC.H b/src/TortoisePlink/LDISC.H
new file mode 100644 (file)
index 0000000..ef84f6d
--- /dev/null
@@ -0,0 +1,22 @@
+/*\r
+ * ldisc.h: defines the Ldisc data structure used by ldisc.c and\r
+ * ldiscucs.c. (Unfortunately it was necessary to split the ldisc\r
+ * module in two, to avoid unnecessarily linking in the Unicode\r
+ * stuff in tools that don't require it.)\r
+ */\r
+\r
+#ifndef PUTTY_LDISC_H\r
+#define PUTTY_LDISC_H\r
+\r
+typedef struct ldisc_tag {\r
+    Terminal *term;\r
+    Backend *back;\r
+    Config *cfg;\r
+    void *backhandle;\r
+    void *frontend;\r
+\r
+    char *buf;\r
+    int buflen, bufsiz, quotenext;\r
+} *Ldisc;\r
+\r
+#endif /* PUTTY_LDISC_H */\r
diff --git a/src/TortoisePlink/LICENCE b/src/TortoisePlink/LICENCE
new file mode 100644 (file)
index 0000000..956ff6b
--- /dev/null
@@ -0,0 +1,25 @@
+PuTTY is copyright 1997-2007 Simon Tatham.\r
+\r
+Portions copyright Robert de Bath, Joris van Rantwijk, Delian\r
+Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,\r
+Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus\r
+Kuhn, and CORE SDI S.A.\r
+\r
+Permission is hereby granted, free of charge, to any person\r
+obtaining a copy of this software and associated documentation files\r
+(the "Software"), to deal in the Software without restriction,\r
+including without limitation the rights to use, copy, modify, merge,\r
+publish, distribute, sublicense, and/or sell copies of the Software,\r
+and to permit persons to whom the Software is furnished to do so,\r
+subject to the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be\r
+included in all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+NONINFRINGEMENT.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE\r
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
diff --git a/src/TortoisePlink/LOGGING.C b/src/TortoisePlink/LOGGING.C
new file mode 100644 (file)
index 0000000..3a43663
--- /dev/null
@@ -0,0 +1,415 @@
+/*\r
+ * Session logging.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+#include <time.h>\r
+#include <assert.h>\r
+\r
+#include "putty.h"\r
+\r
+/* log session to file stuff ... */\r
+struct LogContext {\r
+    FILE *lgfp;\r
+    enum { L_CLOSED, L_OPENING, L_OPEN, L_ERROR } state;\r
+    bufchain queue;\r
+    Filename currlogfilename;\r
+    void *frontend;\r
+    Config cfg;\r
+};\r
+\r
+static void xlatlognam(Filename *d, Filename s, char *hostname, struct tm *tm);\r
+\r
+/*\r
+ * Internal wrapper function which must be called for _all_ output\r
+ * to the log file. It takes care of opening the log file if it\r
+ * isn't open, buffering data if it's in the process of being\r
+ * opened asynchronously, etc.\r
+ */\r
+static void logwrite(struct LogContext *ctx, void *data, int len)\r
+{\r
+    /*\r
+     * In state L_CLOSED, we call logfopen, which will set the state\r
+     * to one of L_OPENING, L_OPEN or L_ERROR. Hence we process all of\r
+     * those three _after_ processing L_CLOSED.\r
+     */\r
+    if (ctx->state == L_CLOSED)\r
+       logfopen(ctx);\r
+\r
+    if (ctx->state == L_OPENING) {\r
+       bufchain_add(&ctx->queue, data, len);\r
+    } else if (ctx->state == L_OPEN) {\r
+       assert(ctx->lgfp);\r
+       fwrite(data, 1, len, ctx->lgfp);\r
+    }                                 /* else L_ERROR, so ignore the write */\r
+}\r
+\r
+/*\r
+ * Convenience wrapper on logwrite() which printf-formats the\r
+ * string.\r
+ */\r
+static void logprintf(struct LogContext *ctx, const char *fmt, ...)\r
+{\r
+    va_list ap;\r
+    char *data;\r
+\r
+    va_start(ap, fmt);\r
+    data = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+\r
+    logwrite(ctx, data, strlen(data));\r
+    sfree(data);\r
+}\r
+\r
+/*\r
+ * Flush any open log file.\r
+ */\r
+void logflush(void *handle) {\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if (ctx->cfg.logtype > 0)\r
+       if (ctx->state == L_OPEN)\r
+           fflush(ctx->lgfp);\r
+}\r
+\r
+static void logfopen_callback(void *handle, int mode)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    char buf[256], *event;\r
+    struct tm tm;\r
+    const char *fmode;\r
+\r
+    if (mode == 0) {\r
+       ctx->state = L_ERROR;          /* disable logging */\r
+    } else {\r
+       fmode = (mode == 1 ? "ab" : "wb");\r
+       ctx->lgfp = f_open(ctx->currlogfilename, fmode, TRUE);\r
+       if (ctx->lgfp)\r
+           ctx->state = L_OPEN;\r
+       else\r
+           ctx->state = L_ERROR;\r
+    }\r
+\r
+    if (ctx->state == L_OPEN) {\r
+       /* Write header line into log file. */\r
+       tm = ltime();\r
+       strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm);\r
+       logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s"\r
+                 " =~=~=~=~=~=~=~=~=~=~=~=\r\n", buf);\r
+    }\r
+\r
+    event = dupprintf("%s session log (%s mode) to file: %s",\r
+                     (mode == 0 ? "Disabled writing" :\r
+                       mode == 1 ? "Appending" : "Writing new"),\r
+                     (ctx->cfg.logtype == LGTYP_ASCII ? "ASCII" :\r
+                      ctx->cfg.logtype == LGTYP_DEBUG ? "raw" :\r
+                      ctx->cfg.logtype == LGTYP_PACKETS ? "SSH packets" :\r
+                      ctx->cfg.logtype == LGTYP_SSHRAW ? "SSH raw data" :\r
+                      "unknown"),\r
+                     filename_to_str(&ctx->currlogfilename));\r
+    logevent(ctx->frontend, event);\r
+    sfree(event);\r
+\r
+    /*\r
+     * Having either succeeded or failed in opening the log file,\r
+     * we should write any queued data out.\r
+     */\r
+    assert(ctx->state != L_OPENING);   /* make _sure_ it won't be requeued */\r
+    while (bufchain_size(&ctx->queue)) {\r
+       void *data;\r
+       int len;\r
+       bufchain_prefix(&ctx->queue, &data, &len);\r
+       logwrite(ctx, data, len);\r
+       bufchain_consume(&ctx->queue, len);\r
+    }\r
+}\r
+\r
+/*\r
+ * Open the log file. Takes care of detecting an already-existing\r
+ * file and asking the user whether they want to append, overwrite\r
+ * or cancel logging.\r
+ */\r
+void logfopen(void *handle)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    struct tm tm;\r
+    int mode;\r
+\r
+    /* Prevent repeat calls */\r
+    if (ctx->state != L_CLOSED)\r
+       return;\r
+\r
+    if (!ctx->cfg.logtype)\r
+       return;\r
+\r
+    tm = ltime();\r
+\r
+    /* substitute special codes in file name */\r
+    xlatlognam(&ctx->currlogfilename, ctx->cfg.logfilename,ctx->cfg.host, &tm);\r
+\r
+    ctx->lgfp = f_open(ctx->currlogfilename, "r", FALSE);  /* file already present? */\r
+    if (ctx->lgfp) {\r
+       fclose(ctx->lgfp);\r
+       if (ctx->cfg.logxfovr != LGXF_ASK) {\r
+           mode = ((ctx->cfg.logxfovr == LGXF_OVR) ? 2 : 1);\r
+       } else\r
+           mode = askappend(ctx->frontend, ctx->currlogfilename,\r
+                            logfopen_callback, ctx);\r
+    } else\r
+       mode = 2;                      /* create == overwrite */\r
+\r
+    if (mode < 0)\r
+       ctx->state = L_OPENING;\r
+    else\r
+       logfopen_callback(ctx, mode);  /* open the file */\r
+}\r
+\r
+void logfclose(void *handle)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if (ctx->lgfp) {\r
+       fclose(ctx->lgfp);\r
+       ctx->lgfp = NULL;\r
+    }\r
+    ctx->state = L_CLOSED;\r
+}\r
+\r
+/*\r
+ * Log session traffic.\r
+ */\r
+void logtraffic(void *handle, unsigned char c, int logmode)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if (ctx->cfg.logtype > 0) {\r
+       if (ctx->cfg.logtype == logmode)\r
+           logwrite(ctx, &c, 1);\r
+    }\r
+}\r
+\r
+/*\r
+ * Log an Event Log entry. Used in SSH packet logging mode; this is\r
+ * also as convenient a place as any to put the output of Event Log\r
+ * entries to stderr when a command-line tool is in verbose mode.\r
+ * (In particular, this is a better place to put it than in the\r
+ * front ends, because it only has to be done once for all\r
+ * platforms. Platforms which don't have a meaningful stderr can\r
+ * just avoid defining FLAG_STDERR.\r
+ */\r
+void log_eventlog(void *handle, const char *event)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) {\r
+       fprintf(stderr, "%s\n", event);\r
+       fflush(stderr);\r
+    }\r
+    /* If we don't have a context yet (eg winnet.c init) then skip entirely */\r
+    if (!ctx)\r
+       return;\r
+    if (ctx->cfg.logtype != LGTYP_PACKETS &&\r
+       ctx->cfg.logtype != LGTYP_SSHRAW)\r
+       return;\r
+    logprintf(ctx, "Event Log: %s\r\n", event);\r
+    logflush(ctx);\r
+}\r
+\r
+/*\r
+ * Log an SSH packet.\r
+ * If n_blanks != 0, blank or omit some parts.\r
+ * Set of blanking areas must be in increasing order.\r
+ */\r
+void log_packet(void *handle, int direction, int type,\r
+               char *texttype, void *data, int len,\r
+               int n_blanks, const struct logblank_t *blanks)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    char dumpdata[80], smalldata[5];\r
+    int p = 0, b = 0, omitted = 0;\r
+    int output_pos = 0; /* NZ if pending output in dumpdata */\r
+\r
+    if (!(ctx->cfg.logtype == LGTYP_SSHRAW ||\r
+          (ctx->cfg.logtype == LGTYP_PACKETS && texttype)))\r
+       return;\r
+\r
+    /* Packet header. */\r
+    if (texttype)\r
+        logprintf(ctx, "%s packet type %d / 0x%02x (%s)\r\n",\r
+                  direction == PKT_INCOMING ? "Incoming" : "Outgoing",\r
+                  type, type, texttype);\r
+    else\r
+        logprintf(ctx, "%s raw data\r\n",\r
+                  direction == PKT_INCOMING ? "Incoming" : "Outgoing");\r
+\r
+    /*\r
+     * Output a hex/ASCII dump of the packet body, blanking/omitting\r
+     * parts as specified.\r
+     */\r
+    while (p < len) {\r
+       int blktype;\r
+\r
+       /* Move to a current entry in the blanking array. */\r
+       while ((b < n_blanks) &&\r
+              (p >= blanks[b].offset + blanks[b].len))\r
+           b++;\r
+       /* Work out what type of blanking to apply to\r
+        * this byte. */\r
+       blktype = PKTLOG_EMIT; /* default */\r
+       if ((b < n_blanks) &&\r
+           (p >= blanks[b].offset) &&\r
+           (p < blanks[b].offset + blanks[b].len))\r
+           blktype = blanks[b].type;\r
+\r
+       /* If we're about to stop omitting, it's time to say how\r
+        * much we omitted. */\r
+       if ((blktype != PKTLOG_OMIT) && omitted) {\r
+           logprintf(ctx, "  (%d byte%s omitted)\r\n",\r
+                     omitted, (omitted==1?"":"s"));\r
+           omitted = 0;\r
+       }\r
+\r
+       /* (Re-)initialise dumpdata as necessary\r
+        * (start of row, or if we've just stopped omitting) */\r
+       if (!output_pos && !omitted)\r
+           sprintf(dumpdata, "  %08x%*s\r\n", p-(p%16), 1+3*16+2+16, "");\r
+\r
+       /* Deal with the current byte. */\r
+       if (blktype == PKTLOG_OMIT) {\r
+           omitted++;\r
+       } else {\r
+           int c;\r
+           if (blktype == PKTLOG_BLANK) {\r
+               c = 'X';\r
+               sprintf(smalldata, "XX");\r
+           } else {  /* PKTLOG_EMIT */\r
+               c = ((unsigned char *)data)[p];\r
+               sprintf(smalldata, "%02x", c);\r
+           }\r
+           dumpdata[10+2+3*(p%16)] = smalldata[0];\r
+           dumpdata[10+2+3*(p%16)+1] = smalldata[1];\r
+           dumpdata[10+1+3*16+2+(p%16)] = (isprint(c) ? c : '.');\r
+           output_pos = (p%16) + 1;\r
+       }\r
+\r
+       p++;\r
+\r
+       /* Flush row if necessary */\r
+       if (((p % 16) == 0) || (p == len) || omitted) {\r
+           if (output_pos) {\r
+               strcpy(dumpdata + 10+1+3*16+2+output_pos, "\r\n");\r
+               logwrite(ctx, dumpdata, strlen(dumpdata));\r
+               output_pos = 0;\r
+           }\r
+       }\r
+\r
+    }\r
+\r
+    /* Tidy up */\r
+    if (omitted)\r
+       logprintf(ctx, "  (%d byte%s omitted)\r\n",\r
+                 omitted, (omitted==1?"":"s"));\r
+    logflush(ctx);\r
+}\r
+\r
+void *log_init(void *frontend, Config *cfg)\r
+{\r
+    struct LogContext *ctx = snew(struct LogContext);\r
+    ctx->lgfp = NULL;\r
+    ctx->state = L_CLOSED;\r
+    ctx->frontend = frontend;\r
+    ctx->cfg = *cfg;                  /* STRUCTURE COPY */\r
+    bufchain_init(&ctx->queue);\r
+    return ctx;\r
+}\r
+\r
+void log_free(void *handle)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+\r
+    logfclose(ctx);\r
+    bufchain_clear(&ctx->queue);\r
+    sfree(ctx);\r
+}\r
+\r
+void log_reconfig(void *handle, Config *cfg)\r
+{\r
+    struct LogContext *ctx = (struct LogContext *)handle;\r
+    int reset_logging;\r
+\r
+    if (!filename_equal(ctx->cfg.logfilename, cfg->logfilename) ||\r
+       ctx->cfg.logtype != cfg->logtype)\r
+       reset_logging = TRUE;\r
+    else\r
+       reset_logging = FALSE;\r
+\r
+    if (reset_logging)\r
+       logfclose(ctx);\r
+\r
+    ctx->cfg = *cfg;                  /* STRUCTURE COPY */\r
+\r
+    if (reset_logging)\r
+       logfopen(ctx);\r
+}\r
+\r
+/*\r
+ * translate format codes into time/date strings\r
+ * and insert them into log file name\r
+ *\r
+ * "&Y":YYYY   "&m":MM   "&d":DD   "&T":hhmmss   "&h":<hostname>   "&&":&\r
+ */\r
+static void xlatlognam(Filename *dest, Filename src,\r
+                      char *hostname, struct tm *tm) {\r
+    char buf[10], *bufp;\r
+    int size;\r
+    char buffer[FILENAME_MAX];\r
+    int len = sizeof(buffer)-1;\r
+    char *d;\r
+    const char *s;\r
+\r
+    d = buffer;\r
+    s = filename_to_str(&src);\r
+\r
+    while (*s) {\r
+       /* Let (bufp, len) be the string to append. */\r
+       bufp = buf;                    /* don't usually override this */\r
+       if (*s == '&') {\r
+           char c;\r
+           s++;\r
+           size = 0;\r
+           if (*s) switch (c = *s++, tolower(c)) {\r
+             case 'y':\r
+               size = strftime(buf, sizeof(buf), "%Y", tm);\r
+               break;\r
+             case 'm':\r
+               size = strftime(buf, sizeof(buf), "%m", tm);\r
+               break;\r
+             case 'd':\r
+               size = strftime(buf, sizeof(buf), "%d", tm);\r
+               break;\r
+             case 't':\r
+               size = strftime(buf, sizeof(buf), "%H%M%S", tm);\r
+               break;\r
+             case 'h':\r
+               bufp = hostname;\r
+               size = strlen(bufp);\r
+               break;\r
+             default:\r
+               buf[0] = '&';\r
+               size = 1;\r
+               if (c != '&')\r
+                   buf[size++] = c;\r
+           }\r
+       } else {\r
+           buf[0] = *s++;\r
+           size = 1;\r
+       }\r
+       if (size > len)\r
+           size = len;\r
+       memcpy(d, bufp, size);\r
+       d += size;\r
+       len -= size;\r
+    }\r
+    *d = '\0';\r
+\r
+    *dest = filename_from_str(buffer);\r
+}\r
diff --git a/src/TortoisePlink/LoginDialog.cpp b/src/TortoisePlink/LoginDialog.cpp
new file mode 100644 (file)
index 0000000..f03a3c3
--- /dev/null
@@ -0,0 +1,163 @@
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003 - Tim Kemp and Stefan Kueng\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+\r
+#include "LoginDialog.h"\r
+#include "TortoisePlinkRes.h"\r
+#include <string>\r
+\r
+HINSTANCE g_hmodThisDll;\r
+HWND g_hwndMain;\r
+\r
+class LoginDialog\r
+{\r
+public:\r
+   LoginDialog(const std::string& prompt);\r
+   \r
+   static bool DoLoginDialog(std::string& password, const std::string& prompt);\r
+\r
+private:\r
+   bool myOK;\r
+   HWND _hdlg;\r
+\r
+   std::string  myPassword;\r
+   std::string  myPrompt;\r
+   \r
+   void CreateModule(void);\r
+   void RetrieveValues();\r
+   \r
+   std::string GetPassword();\r
+\r
+   friend BOOL CALLBACK LoginDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);\r
+};\r
+\r
+\r
+BOOL DoLoginDialog(char* password, int maxlen, const char* prompt)\r
+{\r
+   g_hmodThisDll = GetModuleHandle(0);\r
+   g_hwndMain = GetParentHwnd();\r
+   std::string passwordstr;\r
+   BOOL res = LoginDialog::DoLoginDialog(passwordstr, prompt);\r
+   if (res)\r
+      strncpy(password, passwordstr.c_str(), maxlen);\r
+   return res;\r
+}\r
+\r
+\r
+BOOL CALLBACK LoginDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+{\r
+   if (uMsg == WM_INITDIALOG)\r
+   {\r
+      LoginDialog* pDlg = (LoginDialog*) lParam;\r
+      pDlg->_hdlg = hwndDlg;\r
+      SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);\r
+      // Set prompt text\r
+      SendDlgItemMessage(hwndDlg, IDC_LOGIN_PROMPT, WM_SETTEXT,\r
+                         pDlg->myPrompt.length(), (LPARAM) pDlg->myPrompt.c_str());\r
+      // Make sure edit control has the focus\r
+      //SendDlgItemMessage(hwndDlg, IDC_LOGIN_PASSWORD, WM_SETFOCUS, 0, 0);\r
+      if (GetDlgCtrlID((HWND) wParam) != IDC_LOGIN_PASSWORD)\r
+      { \r
+         SetFocus(GetDlgItem(hwndDlg, IDC_LOGIN_PASSWORD));\r
+         return FALSE; \r
+      } \r
+      return TRUE; \r
+   }\r
+   else if (uMsg == WM_COMMAND && LOWORD(wParam) == IDCANCEL && HIWORD(wParam) == BN_CLICKED)\r
+   {\r
+      LoginDialog* pDlg = (LoginDialog*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);\r
+      pDlg->myOK = false;\r
+      EndDialog(hwndDlg, IDCANCEL);\r
+      return 1;\r
+   }\r
+   else if (uMsg == WM_COMMAND && LOWORD(wParam) == IDOK && HIWORD(wParam) == BN_CLICKED)\r
+   {\r
+      LoginDialog* pDlg = (LoginDialog*) GetWindowLongPtr(hwndDlg, GWLP_USERDATA);\r
+      pDlg->myOK = true;\r
+      pDlg->RetrieveValues();\r
+      EndDialog(hwndDlg, IDOK);\r
+      return 1;\r
+   }\r
+   return 0;\r
+}\r
+\r
+LoginDialog::LoginDialog(const std::string& prompt)\r
+{\r
+   myPrompt = prompt;\r
+}\r
+\r
+void LoginDialog::CreateModule(void)\r
+{\r
+   DialogBoxParam(g_hmodThisDll, MAKEINTRESOURCE(IDD_LOGIN), g_hwndMain,\r
+                  (DLGPROC)(LoginDialogProc), (long)this);\r
+}\r
+\r
+\r
+bool LoginDialog::DoLoginDialog(std::string& password, const std::string& prompt)\r
+{\r
+   LoginDialog *pDlg = new LoginDialog(prompt);\r
+\r
+   pDlg->CreateModule();\r
+\r
+   bool ret = pDlg->myOK;\r
+   password = pDlg->myPassword;\r
+   \r
+   delete pDlg;\r
+\r
+   return ret;\r
+}\r
+\r
+\r
+std::string LoginDialog::GetPassword()\r
+{\r
+   char szTxt[256];\r
+   SendDlgItemMessage(_hdlg, IDC_LOGIN_PASSWORD, WM_GETTEXT, sizeof(szTxt), (LPARAM)szTxt);\r
+   std::string strText = szTxt;\r
+   return strText;\r
+}\r
+\r
+void LoginDialog::RetrieveValues()\r
+{\r
+   myPassword = GetPassword();\r
+}\r
+\r
+\r
+BOOL IsWinNT()\r
+{\r
+   OSVERSIONINFO vi;\r
+   vi.dwOSVersionInfoSize = sizeof(vi);\r
+   if (GetVersionEx(&vi))\r
+   {\r
+      if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT)\r
+      {\r
+         return TRUE;\r
+      }\r
+   }\r
+   return FALSE;\r
+}\r
+\r
+HWND GetParentHwnd()\r
+{\r
+   if (IsWinNT())\r
+   {\r
+      return GetDesktopWindow();\r
+   }\r
+   else\r
+   {\r
+      return GetForegroundWindow();\r
+   }\r
+}\r
diff --git a/src/TortoisePlink/LoginDialog.h b/src/TortoisePlink/LoginDialog.h
new file mode 100644 (file)
index 0000000..c6cd0c5
--- /dev/null
@@ -0,0 +1,36 @@
+// TortoiseCVS - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2000 - Francis Irving\r
+// <francis@flourish.org> - May 2000\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
+\r
+#ifndef LOGIN_DIALOG_H\r
+#define LOGIN_DIALOG_H\r
+\r
+#include <windows.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+BOOL DoLoginDialog(char* password, int maxlen, const char* prompt);\r
+\r
+HWND GetParentHwnd();\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/MISC.C b/src/TortoisePlink/MISC.C
new file mode 100644 (file)
index 0000000..68576b8
--- /dev/null
@@ -0,0 +1,655 @@
+/*\r
+ * Platform-independent routines shared between all PuTTY programs.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <limits.h>\r
+#include <ctype.h>\r
+#include <assert.h>\r
+#include "putty.h"\r
+\r
+/*\r
+ * Parse a string block size specification. This is approximately a\r
+ * subset of the block size specs supported by GNU fileutils:\r
+ *  "nk" = n kilobytes\r
+ *  "nM" = n megabytes\r
+ *  "nG" = n gigabytes\r
+ * All numbers are decimal, and suffixes refer to powers of two.\r
+ * Case-insensitive.\r
+ */\r
+unsigned long parse_blocksize(const char *bs)\r
+{\r
+    char *suf;\r
+    unsigned long r = strtoul(bs, &suf, 10);\r
+    if (*suf != '\0') {\r
+       while (*suf && isspace((unsigned char)*suf)) suf++;\r
+       switch (*suf) {\r
+         case 'k': case 'K':\r
+           r *= 1024ul;\r
+           break;\r
+         case 'm': case 'M':\r
+           r *= 1024ul * 1024ul;\r
+           break;\r
+         case 'g': case 'G':\r
+           r *= 1024ul * 1024ul * 1024ul;\r
+           break;\r
+         case '\0':\r
+         default:\r
+           break;\r
+       }\r
+    }\r
+    return r;\r
+}\r
+\r
+/*\r
+ * Parse a ^C style character specification.\r
+ * Returns NULL in `next' if we didn't recognise it as a control character,\r
+ * in which case `c' should be ignored.\r
+ * The precise current parsing is an oddity inherited from the terminal\r
+ * answerback-string parsing code. All sequences start with ^; all except\r
+ * ^<123> are two characters. The ones that are worth keeping are probably:\r
+ *   ^?                    127\r
+ *   ^@A-Z[\]^_            0-31\r
+ *   a-z           1-26\r
+ *   <num>         specified by number (decimal, 0octal, 0xHEX)\r
+ *   ~             ^ escape\r
+ */\r
+char ctrlparse(char *s, char **next)\r
+{\r
+    char c = 0;\r
+    if (*s != '^') {\r
+       *next = NULL;\r
+    } else {\r
+       s++;\r
+       if (*s == '\0') {\r
+           *next = NULL;\r
+       } else if (*s == '<') {\r
+           s++;\r
+           c = (char)strtol(s, next, 0);\r
+           if ((*next == s) || (**next != '>')) {\r
+               c = 0;\r
+               *next = NULL;\r
+           } else\r
+               (*next)++;\r
+       } else if (*s >= 'a' && *s <= 'z') {\r
+           c = (*s - ('a' - 1));\r
+           *next = s+1;\r
+       } else if ((*s >= '@' && *s <= '_') || *s == '?' || (*s & 0x80)) {\r
+           c = ('@' ^ *s);\r
+           *next = s+1;\r
+       } else if (*s == '~') {\r
+           c = '^';\r
+           *next = s+1;\r
+       }\r
+    }\r
+    return c;\r
+}\r
+\r
+prompts_t *new_prompts(void *frontend)\r
+{\r
+    prompts_t *p = snew(prompts_t);\r
+    p->prompts = NULL;\r
+    p->n_prompts = 0;\r
+    p->frontend = frontend;\r
+    p->data = NULL;\r
+    p->to_server = TRUE; /* to be on the safe side */\r
+    p->name = p->instruction = NULL;\r
+    p->name_reqd = p->instr_reqd = FALSE;\r
+    return p;\r
+}\r
+void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len)\r
+{\r
+    prompt_t *pr = snew(prompt_t);\r
+    char *result = snewn(len, char);\r
+    pr->prompt = promptstr;\r
+    pr->echo = echo;\r
+    pr->result = result;\r
+    pr->result_len = len;\r
+    p->n_prompts++;\r
+    p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *);\r
+    p->prompts[p->n_prompts-1] = pr;\r
+}\r
+void free_prompts(prompts_t *p)\r
+{\r
+    size_t i;\r
+    for (i=0; i < p->n_prompts; i++) {\r
+       prompt_t *pr = p->prompts[i];\r
+       memset(pr->result, 0, pr->result_len); /* burn the evidence */\r
+       sfree(pr->result);\r
+       sfree(pr->prompt);\r
+       sfree(pr);\r
+    }\r
+    sfree(p->prompts);\r
+    sfree(p->name);\r
+    sfree(p->instruction);\r
+    sfree(p);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * String handling routines.\r
+ */\r
+\r
+char *dupstr(const char *s)\r
+{\r
+    char *p = NULL;\r
+    if (s) {\r
+        int len = strlen(s);\r
+        p = snewn(len + 1, char);\r
+        strcpy(p, s);\r
+    }\r
+    return p;\r
+}\r
+\r
+/* Allocate the concatenation of N strings. Terminate arg list with NULL. */\r
+char *dupcat(const char *s1, ...)\r
+{\r
+    int len;\r
+    char *p, *q, *sn;\r
+    va_list ap;\r
+\r
+    len = strlen(s1);\r
+    va_start(ap, s1);\r
+    while (1) {\r
+       sn = va_arg(ap, char *);\r
+       if (!sn)\r
+           break;\r
+       len += strlen(sn);\r
+    }\r
+    va_end(ap);\r
+\r
+    p = snewn(len + 1, char);\r
+    strcpy(p, s1);\r
+    q = p + strlen(p);\r
+\r
+    va_start(ap, s1);\r
+    while (1) {\r
+       sn = va_arg(ap, char *);\r
+       if (!sn)\r
+           break;\r
+       strcpy(q, sn);\r
+       q += strlen(q);\r
+    }\r
+    va_end(ap);\r
+\r
+    return p;\r
+}\r
+\r
+/*\r
+ * Do an sprintf(), but into a custom-allocated buffer.\r
+ * \r
+ * Currently I'm doing this via vsnprintf. This has worked so far,\r
+ * but it's not good, because vsnprintf is not available on all\r
+ * platforms. There's an ifdef to use `_vsnprintf', which seems\r
+ * to be the local name for it on Windows. Other platforms may\r
+ * lack it completely, in which case it'll be time to rewrite\r
+ * this function in a totally different way.\r
+ * \r
+ * The only `properly' portable solution I can think of is to\r
+ * implement my own format string scanner, which figures out an\r
+ * upper bound for the length of each formatting directive,\r
+ * allocates the buffer as it goes along, and calls sprintf() to\r
+ * actually process each directive. If I ever need to actually do\r
+ * this, some caveats:\r
+ * \r
+ *  - It's very hard to find a reliable upper bound for\r
+ *    floating-point values. %f, in particular, when supplied with\r
+ *    a number near to the upper or lower limit of representable\r
+ *    numbers, could easily take several hundred characters. It's\r
+ *    probably feasible to predict this statically using the\r
+ *    constants in <float.h>, or even to predict it dynamically by\r
+ *    looking at the exponent of the specific float provided, but\r
+ *    it won't be fun.\r
+ * \r
+ *  - Don't forget to _check_, after calling sprintf, that it's\r
+ *    used at most the amount of space we had available.\r
+ * \r
+ *  - Fault any formatting directive we don't fully understand. The\r
+ *    aim here is to _guarantee_ that we never overflow the buffer,\r
+ *    because this is a security-critical function. If we see a\r
+ *    directive we don't know about, we should panic and die rather\r
+ *    than run any risk.\r
+ */\r
+char *dupprintf(const char *fmt, ...)\r
+{\r
+    char *ret;\r
+    va_list ap;\r
+    va_start(ap, fmt);\r
+    ret = dupvprintf(fmt, ap);\r
+    va_end(ap);\r
+    return ret;\r
+}\r
+char *dupvprintf(const char *fmt, va_list ap)\r
+{\r
+    char *buf;\r
+    int len, size;\r
+\r
+    buf = snewn(512, char);\r
+    size = 512;\r
+\r
+    while (1) {\r
+#ifdef _WINDOWS\r
+#define vsnprintf _vsnprintf\r
+#endif\r
+#ifdef va_copy\r
+       /* Use the `va_copy' macro mandated by C99, if present.\r
+        * XXX some environments may have this as __va_copy() */\r
+       va_list aq;\r
+       va_copy(aq, ap);\r
+       len = vsnprintf(buf, size, fmt, aq);\r
+       va_end(aq);\r
+#else\r
+       /* Ugh. No va_copy macro, so do something nasty.\r
+        * Technically, you can't reuse a va_list like this: it is left\r
+        * unspecified whether advancing a va_list pointer modifies its\r
+        * value or something it points to, so on some platforms calling\r
+        * vsnprintf twice on the same va_list might fail hideously\r
+        * (indeed, it has been observed to).\r
+        * XXX the autoconf manual suggests that using memcpy() will give\r
+        *     "maximum portability". */\r
+       len = vsnprintf(buf, size, fmt, ap);\r
+#endif\r
+       if (len >= 0 && len < size) {\r
+           /* This is the C99-specified criterion for snprintf to have\r
+            * been completely successful. */\r
+           return buf;\r
+       } else if (len > 0) {\r
+           /* This is the C99 error condition: the returned length is\r
+            * the required buffer size not counting the NUL. */\r
+           size = len + 1;\r
+       } else {\r
+           /* This is the pre-C99 glibc error condition: <0 means the\r
+            * buffer wasn't big enough, so we enlarge it a bit and hope. */\r
+           size += 512;\r
+       }\r
+       buf = sresize(buf, size, char);\r
+    }\r
+}\r
+\r
+/*\r
+ * Read an entire line of text from a file. Return a buffer\r
+ * malloced to be as big as necessary (caller must free).\r
+ */\r
+char *fgetline(FILE *fp)\r
+{\r
+    char *ret = snewn(512, char);\r
+    int size = 512, len = 0;\r
+    while (fgets(ret + len, size - len, fp)) {\r
+       len += strlen(ret + len);\r
+       if (ret[len-1] == '\n')\r
+           break;                     /* got a newline, we're done */\r
+       size = len + 512;\r
+       ret = sresize(ret, size, char);\r
+    }\r
+    if (len == 0) {                   /* first fgets returned NULL */\r
+       sfree(ret);\r
+       return NULL;\r
+    }\r
+    ret[len] = '\0';\r
+    return ret;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Base64 encoding routine. This is required in public-key writing\r
+ * but also in HTTP proxy handling, so it's centralised here.\r
+ */\r
+\r
+void base64_encode_atom(unsigned char *data, int n, char *out)\r
+{\r
+    static const char base64_chars[] =\r
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
+\r
+    unsigned word;\r
+\r
+    word = data[0] << 16;\r
+    if (n > 1)\r
+       word |= data[1] << 8;\r
+    if (n > 2)\r
+       word |= data[2];\r
+    out[0] = base64_chars[(word >> 18) & 0x3F];\r
+    out[1] = base64_chars[(word >> 12) & 0x3F];\r
+    if (n > 1)\r
+       out[2] = base64_chars[(word >> 6) & 0x3F];\r
+    else\r
+       out[2] = '=';\r
+    if (n > 2)\r
+       out[3] = base64_chars[word & 0x3F];\r
+    else\r
+       out[3] = '=';\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Generic routines to deal with send buffers: a linked list of\r
+ * smallish blocks, with the operations\r
+ * \r
+ *  - add an arbitrary amount of data to the end of the list\r
+ *  - remove the first N bytes from the list\r
+ *  - return a (pointer,length) pair giving some initial data in\r
+ *    the list, suitable for passing to a send or write system\r
+ *    call\r
+ *  - retrieve a larger amount of initial data from the list\r
+ *  - return the current size of the buffer chain in bytes\r
+ */\r
+\r
+#define BUFFER_GRANULE  512\r
+\r
+struct bufchain_granule {\r
+    struct bufchain_granule *next;\r
+    int buflen, bufpos;\r
+    char buf[BUFFER_GRANULE];\r
+};\r
+\r
+void bufchain_init(bufchain *ch)\r
+{\r
+    ch->head = ch->tail = NULL;\r
+    ch->buffersize = 0;\r
+}\r
+\r
+void bufchain_clear(bufchain *ch)\r
+{\r
+    struct bufchain_granule *b;\r
+    while (ch->head) {\r
+       b = ch->head;\r
+       ch->head = ch->head->next;\r
+       sfree(b);\r
+    }\r
+    ch->tail = NULL;\r
+    ch->buffersize = 0;\r
+}\r
+\r
+int bufchain_size(bufchain *ch)\r
+{\r
+    return ch->buffersize;\r
+}\r
+\r
+void bufchain_add(bufchain *ch, const void *data, int len)\r
+{\r
+    const char *buf = (const char *)data;\r
+\r
+    if (len == 0) return;\r
+\r
+    ch->buffersize += len;\r
+\r
+    if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {\r
+       int copylen = min(len, BUFFER_GRANULE - ch->tail->buflen);\r
+       memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);\r
+       buf += copylen;\r
+       len -= copylen;\r
+       ch->tail->buflen += copylen;\r
+    }\r
+    while (len > 0) {\r
+       int grainlen = min(len, BUFFER_GRANULE);\r
+       struct bufchain_granule *newbuf;\r
+       newbuf = snew(struct bufchain_granule);\r
+       newbuf->bufpos = 0;\r
+       newbuf->buflen = grainlen;\r
+       memcpy(newbuf->buf, buf, grainlen);\r
+       buf += grainlen;\r
+       len -= grainlen;\r
+       if (ch->tail)\r
+           ch->tail->next = newbuf;\r
+       else\r
+           ch->head = ch->tail = newbuf;\r
+       newbuf->next = NULL;\r
+       ch->tail = newbuf;\r
+    }\r
+}\r
+\r
+void bufchain_consume(bufchain *ch, int len)\r
+{\r
+    struct bufchain_granule *tmp;\r
+\r
+    assert(ch->buffersize >= len);\r
+    while (len > 0) {\r
+       int remlen = len;\r
+       assert(ch->head != NULL);\r
+       if (remlen >= ch->head->buflen - ch->head->bufpos) {\r
+           remlen = ch->head->buflen - ch->head->bufpos;\r
+           tmp = ch->head;\r
+           ch->head = tmp->next;\r
+           sfree(tmp);\r
+           if (!ch->head)\r
+               ch->tail = NULL;\r
+       } else\r
+           ch->head->bufpos += remlen;\r
+       ch->buffersize -= remlen;\r
+       len -= remlen;\r
+    }\r
+}\r
+\r
+void bufchain_prefix(bufchain *ch, void **data, int *len)\r
+{\r
+    *len = ch->head->buflen - ch->head->bufpos;\r
+    *data = ch->head->buf + ch->head->bufpos;\r
+}\r
+\r
+void bufchain_fetch(bufchain *ch, void *data, int len)\r
+{\r
+    struct bufchain_granule *tmp;\r
+    char *data_c = (char *)data;\r
+\r
+    tmp = ch->head;\r
+\r
+    assert(ch->buffersize >= len);\r
+    while (len > 0) {\r
+       int remlen = len;\r
+\r
+       assert(tmp != NULL);\r
+       if (remlen >= tmp->buflen - tmp->bufpos)\r
+           remlen = tmp->buflen - tmp->bufpos;\r
+       memcpy(data_c, tmp->buf + tmp->bufpos, remlen);\r
+\r
+       tmp = tmp->next;\r
+       len -= remlen;\r
+       data_c += remlen;\r
+    }\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * My own versions of malloc, realloc and free. Because I want\r
+ * malloc and realloc to bomb out and exit the program if they run\r
+ * out of memory, realloc to reliably call malloc if passed a NULL\r
+ * pointer, and free to reliably do nothing if passed a NULL\r
+ * pointer. We can also put trace printouts in, if we need to; and\r
+ * we can also replace the allocator with an ElectricFence-like\r
+ * one.\r
+ */\r
+\r
+#ifdef MINEFIELD\r
+void *minefield_c_malloc(size_t size);\r
+void minefield_c_free(void *p);\r
+void *minefield_c_realloc(void *p, size_t size);\r
+#endif\r
+\r
+#ifdef MALLOC_LOG\r
+static FILE *fp = NULL;\r
+\r
+static char *mlog_file = NULL;\r
+static int mlog_line = 0;\r
+\r
+void mlog(char *file, int line)\r
+{\r
+    mlog_file = file;\r
+    mlog_line = line;\r
+    if (!fp) {\r
+       fp = fopen("putty_mem.log", "w");\r
+       setvbuf(fp, NULL, _IONBF, BUFSIZ);\r
+    }\r
+    if (fp)\r
+       fprintf(fp, "%s:%d: ", file, line);\r
+}\r
+#endif\r
+\r
+void *safemalloc(size_t n, size_t size)\r
+{\r
+    void *p;\r
+\r
+    if (n > INT_MAX / size) {\r
+       p = NULL;\r
+    } else {\r
+       size *= n;\r
+       if (size == 0) size = 1;\r
+#ifdef MINEFIELD\r
+       p = minefield_c_malloc(size);\r
+#else\r
+       p = malloc(size);\r
+#endif\r
+    }\r
+\r
+    if (!p) {\r
+       char str[200];\r
+#ifdef MALLOC_LOG\r
+       sprintf(str, "Out of memory! (%s:%d, size=%d)",\r
+               mlog_file, mlog_line, size);\r
+       fprintf(fp, "*** %s\n", str);\r
+       fclose(fp);\r
+#else\r
+       strcpy(str, "Out of memory!");\r
+#endif\r
+       modalfatalbox(str);\r
+    }\r
+#ifdef MALLOC_LOG\r
+    if (fp)\r
+       fprintf(fp, "malloc(%d) returns %p\n", size, p);\r
+#endif\r
+    return p;\r
+}\r
+\r
+void *saferealloc(void *ptr, size_t n, size_t size)\r
+{\r
+    void *p;\r
+\r
+    if (n > INT_MAX / size) {\r
+       p = NULL;\r
+    } else {\r
+       size *= n;\r
+       if (!ptr) {\r
+#ifdef MINEFIELD\r
+           p = minefield_c_malloc(size);\r
+#else\r
+           p = malloc(size);\r
+#endif\r
+       } else {\r
+#ifdef MINEFIELD\r
+           p = minefield_c_realloc(ptr, size);\r
+#else\r
+           p = realloc(ptr, size);\r
+#endif\r
+       }\r
+    }\r
+\r
+    if (!p) {\r
+       char str[200];\r
+#ifdef MALLOC_LOG\r
+       sprintf(str, "Out of memory! (%s:%d, size=%d)",\r
+               mlog_file, mlog_line, size);\r
+       fprintf(fp, "*** %s\n", str);\r
+       fclose(fp);\r
+#else\r
+       strcpy(str, "Out of memory!");\r
+#endif\r
+       modalfatalbox(str);\r
+    }\r
+#ifdef MALLOC_LOG\r
+    if (fp)\r
+       fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p);\r
+#endif\r
+    return p;\r
+}\r
+\r
+void safefree(void *ptr)\r
+{\r
+    if (ptr) {\r
+#ifdef MALLOC_LOG\r
+       if (fp)\r
+           fprintf(fp, "free(%p)\n", ptr);\r
+#endif\r
+#ifdef MINEFIELD\r
+       minefield_c_free(ptr);\r
+#else\r
+       free(ptr);\r
+#endif\r
+    }\r
+#ifdef MALLOC_LOG\r
+    else if (fp)\r
+       fprintf(fp, "freeing null pointer - no action taken\n");\r
+#endif\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * Debugging routines.\r
+ */\r
+\r
+#ifdef DEBUG\r
+extern void dputs(char *);             /* defined in per-platform *misc.c */\r
+\r
+void debug_printf(char *fmt, ...)\r
+{\r
+    char *buf;\r
+    va_list ap;\r
+\r
+    va_start(ap, fmt);\r
+    buf = dupvprintf(fmt, ap);\r
+    dputs(buf);\r
+    sfree(buf);\r
+    va_end(ap);\r
+}\r
+\r
+\r
+void debug_memdump(void *buf, int len, int L)\r
+{\r
+    int i;\r
+    unsigned char *p = buf;\r
+    char foo[17];\r
+    if (L) {\r
+       int delta;\r
+       debug_printf("\t%d (0x%x) bytes:\n", len, len);\r
+       delta = 15 & (int) p;\r
+       p -= delta;\r
+       len += delta;\r
+    }\r
+    for (; 0 < len; p += 16, len -= 16) {\r
+       dputs("  ");\r
+       if (L)\r
+           debug_printf("%p: ", p);\r
+       strcpy(foo, "................");        /* sixteen dots */\r
+       for (i = 0; i < 16 && i < len; ++i) {\r
+           if (&p[i] < (unsigned char *) buf) {\r
+               dputs("   ");          /* 3 spaces */\r
+               foo[i] = ' ';\r
+           } else {\r
+               debug_printf("%c%02.2x",\r
+                       &p[i] != (unsigned char *) buf\r
+                       && i % 4 ? '.' : ' ', p[i]\r
+                   );\r
+               if (p[i] >= ' ' && p[i] <= '~')\r
+                   foo[i] = (char) p[i];\r
+           }\r
+       }\r
+       foo[i] = '\0';\r
+       debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);\r
+    }\r
+}\r
+\r
+#endif                         /* def DEBUG */\r
+\r
+/*\r
+ * Determine whether or not a Config structure represents a session\r
+ * which can sensibly be launched right now.\r
+ */\r
+int cfg_launchable(const Config *cfg)\r
+{\r
+    if (cfg->protocol == PROT_SERIAL)\r
+       return cfg->serline[0] != 0;\r
+    else\r
+       return cfg->host[0] != 0;\r
+}\r
+\r
+char const *cfg_dest(const Config *cfg)\r
+{\r
+    if (cfg->protocol == PROT_SERIAL)\r
+       return cfg->serline;\r
+    else\r
+       return cfg->host;\r
+}\r
diff --git a/src/TortoisePlink/MISC.H b/src/TortoisePlink/MISC.H
new file mode 100644 (file)
index 0000000..1123314
--- /dev/null
@@ -0,0 +1,132 @@
+/*\r
+ * Header for misc.c.\r
+ */\r
+\r
+#ifndef PUTTY_MISC_H\r
+#define PUTTY_MISC_H\r
+\r
+#include "puttymem.h"\r
+\r
+#include <stdio.h>                    /* for FILE * */\r
+#include <stdarg.h>                   /* for va_list */\r
+#include <time.h>                      /* for struct tm */\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+typedef struct Filename Filename;\r
+typedef struct FontSpec FontSpec;\r
+\r
+unsigned long parse_blocksize(const char *bs);\r
+char ctrlparse(char *s, char **next);\r
+\r
+char *dupstr(const char *s);\r
+char *dupcat(const char *s1, ...);\r
+char *dupprintf(const char *fmt, ...);\r
+char *dupvprintf(const char *fmt, va_list ap);\r
+\r
+char *fgetline(FILE *fp);\r
+\r
+void base64_encode_atom(unsigned char *data, int n, char *out);\r
+\r
+struct bufchain_granule;\r
+typedef struct bufchain_tag {\r
+    struct bufchain_granule *head, *tail;\r
+    int buffersize;                   /* current amount of buffered data */\r
+} bufchain;\r
+\r
+void bufchain_init(bufchain *ch);\r
+void bufchain_clear(bufchain *ch);\r
+int bufchain_size(bufchain *ch);\r
+void bufchain_add(bufchain *ch, const void *data, int len);\r
+void bufchain_prefix(bufchain *ch, void **data, int *len);\r
+void bufchain_consume(bufchain *ch, int len);\r
+void bufchain_fetch(bufchain *ch, void *data, int len);\r
+\r
+struct tm ltime(void);\r
+\r
+/*\r
+ * Debugging functions.\r
+ *\r
+ * Output goes to debug.log\r
+ *\r
+ * debug(()) (note the double brackets) is like printf().\r
+ *\r
+ * dmemdump() and dmemdumpl() both do memory dumps.  The difference\r
+ * is that dmemdumpl() is more suited for when the memory address is\r
+ * important (say because you'll be recording pointer values later\r
+ * on).  dmemdump() is more concise.\r
+ */\r
+\r
+#ifdef DEBUG\r
+void debug_printf(char *fmt, ...);\r
+void debug_memdump(void *buf, int len, int L);\r
+#define debug(x) (debug_printf x)\r
+#define dmemdump(buf,len) debug_memdump (buf, len, 0);\r
+#define dmemdumpl(buf,len) debug_memdump (buf, len, 1);\r
+#else\r
+#define debug(x)\r
+#define dmemdump(buf,len)\r
+#define dmemdumpl(buf,len)\r
+#endif\r
+\r
+#ifndef lenof\r
+#define lenof(x) ( (sizeof((x))) / (sizeof(*(x))))\r
+#endif\r
+\r
+#ifndef min\r
+#define min(x,y) ( (x) < (y) ? (x) : (y) )\r
+#endif\r
+#ifndef max\r
+#define max(x,y) ( (x) > (y) ? (x) : (y) )\r
+#endif\r
+\r
+#define GET_32BIT_LSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0]) | \\r
+  ((unsigned long)(unsigned char)(cp)[1] << 8) | \\r
+  ((unsigned long)(unsigned char)(cp)[2] << 16) | \\r
+  ((unsigned long)(unsigned char)(cp)[3] << 24))\r
+\r
+#define PUT_32BIT_LSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)(value), \\r
+  (cp)[1] = (unsigned char)((value) >> 8), \\r
+  (cp)[2] = (unsigned char)((value) >> 16), \\r
+  (cp)[3] = (unsigned char)((value) >> 24) )\r
+\r
+#define GET_16BIT_LSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0]) | \\r
+  ((unsigned long)(unsigned char)(cp)[1] << 8))\r
+\r
+#define PUT_16BIT_LSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)(value), \\r
+  (cp)[1] = (unsigned char)((value) >> 8) )\r
+\r
+#define GET_32BIT_MSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0] << 24) | \\r
+  ((unsigned long)(unsigned char)(cp)[1] << 16) | \\r
+  ((unsigned long)(unsigned char)(cp)[2] << 8) | \\r
+  ((unsigned long)(unsigned char)(cp)[3]))\r
+\r
+#define GET_32BIT(cp) GET_32BIT_MSB_FIRST(cp)\r
+\r
+#define PUT_32BIT_MSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)((value) >> 24), \\r
+  (cp)[1] = (unsigned char)((value) >> 16), \\r
+  (cp)[2] = (unsigned char)((value) >> 8), \\r
+  (cp)[3] = (unsigned char)(value) )\r
+\r
+#define PUT_32BIT(cp, value) PUT_32BIT_MSB_FIRST(cp, value)\r
+\r
+#define GET_16BIT_MSB_FIRST(cp) \\r
+  (((unsigned long)(unsigned char)(cp)[0] << 8) | \\r
+  ((unsigned long)(unsigned char)(cp)[1]))\r
+\r
+#define PUT_16BIT_MSB_FIRST(cp, value) ( \\r
+  (cp)[0] = (unsigned char)((value) >> 8), \\r
+  (cp)[1] = (unsigned char)(value) )\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/NETWORK.H b/src/TortoisePlink/NETWORK.H
new file mode 100644 (file)
index 0000000..2d62eab
--- /dev/null
@@ -0,0 +1,235 @@
+/*\r
+ * Networking abstraction in PuTTY.\r
+ *\r
+ * The way this works is: a back end can choose to open any number\r
+ * of sockets - including zero, which might be necessary in some.\r
+ * It can register a bunch of callbacks (most notably for when \r
+ * data is received) for each socket, and it can call the networking\r
+ * abstraction to send data without having to worry about blocking.\r
+ * The stuff behind the abstraction takes care of selects and\r
+ * nonblocking writes and all that sort of painful gubbins.\r
+ */\r
+\r
+#ifndef PUTTY_NETWORK_H\r
+#define PUTTY_NETWORK_H\r
+\r
+#ifndef DONE_TYPEDEFS\r
+#define DONE_TYPEDEFS\r
+typedef struct config_tag Config;\r
+typedef struct backend_tag Backend;\r
+typedef struct terminal_tag Terminal;\r
+#endif\r
+\r
+typedef struct SockAddr_tag *SockAddr;\r
+/* pay attention to levels of indirection */\r
+typedef struct socket_function_table **Socket;\r
+typedef struct plug_function_table **Plug;\r
+\r
+#ifndef OSSOCKET_DEFINED\r
+typedef void *OSSocket;\r
+#endif\r
+\r
+struct socket_function_table {\r
+    Plug(*plug) (Socket s, Plug p);\r
+    /* use a different plug (return the old one) */\r
+    /* if p is NULL, it doesn't change the plug */\r
+    /* but it does return the one it's using */\r
+    void (*close) (Socket s);\r
+    int (*write) (Socket s, const char *data, int len);\r
+    int (*write_oob) (Socket s, const char *data, int len);\r
+    void (*flush) (Socket s);\r
+    void (*set_private_ptr) (Socket s, void *ptr);\r
+    void *(*get_private_ptr) (Socket s);\r
+    void (*set_frozen) (Socket s, int is_frozen);\r
+    /* ignored by tcp, but vital for ssl */\r
+    const char *(*socket_error) (Socket s);\r
+};\r
+\r
+struct plug_function_table {\r
+    void (*log)(Plug p, int type, SockAddr addr, int port,\r
+               const char *error_msg, int error_code);\r
+    /*\r
+     * Passes the client progress reports on the process of setting\r
+     * up the connection.\r
+     * \r
+     *         - type==0 means we are about to try to connect to address\r
+     *           `addr' (error_msg and error_code are ignored)\r
+     *         - type==1 means we have failed to connect to address `addr'\r
+     *           (error_msg and error_code are supplied). This is not a\r
+     *           fatal error - we may well have other candidate addresses\r
+     *           to fall back to. When it _is_ fatal, the closing()\r
+     *           function will be called.\r
+     */\r
+    int (*closing)\r
+     (Plug p, const char *error_msg, int error_code, int calling_back);\r
+    /* error_msg is NULL iff it is not an error (ie it closed normally) */\r
+    /* calling_back != 0 iff there is a Plug function */\r
+    /* currently running (would cure the fixme in try_send()) */\r
+    int (*receive) (Plug p, int urgent, char *data, int len);\r
+    /*\r
+     *  - urgent==0. `data' points to `len' bytes of perfectly\r
+     *    ordinary data.\r
+     * \r
+     *  - urgent==1. `data' points to `len' bytes of data,\r
+     *    which were read from before an Urgent pointer.\r
+     * \r
+     *  - urgent==2. `data' points to `len' bytes of data,\r
+     *    the first of which was the one at the Urgent mark.\r
+     */\r
+    void (*sent) (Plug p, int bufsize);\r
+    /*\r
+     * The `sent' function is called when the pending send backlog\r
+     * on a socket is cleared or partially cleared. The new backlog\r
+     * size is passed in the `bufsize' parameter.\r
+     */\r
+    int (*accepting)(Plug p, OSSocket sock);\r
+    /*\r
+     * returns 0 if the host at address addr is a valid host for connecting or error\r
+     */\r
+};\r
+\r
+/* proxy indirection layer */\r
+/* NB, control of 'addr' is passed via new_connection, which takes\r
+ * responsibility for freeing it */\r
+Socket new_connection(SockAddr addr, char *hostname,\r
+                     int port, int privport,\r
+                     int oobinline, int nodelay, int keepalive,\r
+                     Plug plug, const Config *cfg);\r
+Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,\r
+                   const Config *cfg, int addressfamily);\r
+SockAddr name_lookup(char *host, int port, char **canonicalname,\r
+                    const Config *cfg, int addressfamily);\r
+\r
+/* platform-dependent callback from new_connection() */\r
+/* (same caveat about addr as new_connection()) */\r
+Socket platform_new_connection(SockAddr addr, char *hostname,\r
+                              int port, int privport,\r
+                              int oobinline, int nodelay, int keepalive,\r
+                              Plug plug, const Config *cfg);\r
+\r
+/* socket functions */\r
+\r
+void sk_init(void);                   /* called once at program startup */\r
+void sk_cleanup(void);                /* called just before program exit */\r
+\r
+SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family);\r
+SockAddr sk_nonamelookup(const char *host);\r
+void sk_getaddr(SockAddr addr, char *buf, int buflen);\r
+int sk_hostname_is_local(char *name);\r
+int sk_address_is_local(SockAddr addr);\r
+int sk_addrtype(SockAddr addr);\r
+void sk_addrcopy(SockAddr addr, char *buf);\r
+void sk_addr_free(SockAddr addr);\r
+\r
+/* NB, control of 'addr' is passed via sk_new, which takes responsibility\r
+ * for freeing it, as for new_connection() */\r
+Socket sk_new(SockAddr addr, int port, int privport, int oobinline,\r
+             int nodelay, int keepalive, Plug p);\r
+\r
+Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int address_family);\r
+\r
+Socket sk_register(OSSocket sock, Plug plug);\r
+\r
+#define sk_plug(s,p) (((*s)->plug) (s, p))\r
+#define sk_close(s) (((*s)->close) (s))\r
+#define sk_write(s,buf,len) (((*s)->write) (s, buf, len))\r
+#define sk_write_oob(s,buf,len) (((*s)->write_oob) (s, buf, len))\r
+#define sk_flush(s) (((*s)->flush) (s))\r
+\r
+#ifdef DEFINE_PLUG_METHOD_MACROS\r
+#define plug_log(p,type,addr,port,msg,code) (((*p)->log) (p, type, addr, port, msg, code))\r
+#define plug_closing(p,msg,code,callback) (((*p)->closing) (p, msg, code, callback))\r
+#define plug_receive(p,urgent,buf,len) (((*p)->receive) (p, urgent, buf, len))\r
+#define plug_sent(p,bufsize) (((*p)->sent) (p, bufsize))\r
+#define plug_accepting(p, sock) (((*p)->accepting)(p, sock))\r
+#endif\r
+\r
+/*\r
+ * Each socket abstraction contains a `void *' private field in\r
+ * which the client can keep state.\r
+ *\r
+ * This is perhaps unnecessary now that we have the notion of a plug,\r
+ * but there is some existing code that uses it, so it stays.\r
+ */\r
+#define sk_set_private_ptr(s, ptr) (((*s)->set_private_ptr) (s, ptr))\r
+#define sk_get_private_ptr(s) (((*s)->get_private_ptr) (s))\r
+\r
+/*\r
+ * Special error values are returned from sk_namelookup and sk_new\r
+ * if there's a problem. These functions extract an error message,\r
+ * or return NULL if there's no problem.\r
+ */\r
+const char *sk_addr_error(SockAddr addr);\r
+#define sk_socket_error(s) (((*s)->socket_error) (s))\r
+\r
+/*\r
+ * Set the `frozen' flag on a socket. A frozen socket is one in\r
+ * which all READABLE notifications are ignored, so that data is\r
+ * not accepted from the peer until the socket is unfrozen. This\r
+ * exists for two purposes:\r
+ * \r
+ *  - Port forwarding: when a local listening port receives a\r
+ *    connection, we do not want to receive data from the new\r
+ *    socket until we have somewhere to send it. Hence, we freeze\r
+ *    the socket until its associated SSH channel is ready; then we\r
+ *    unfreeze it and pending data is delivered.\r
+ * \r
+ *  - Socket buffering: if an SSH channel (or the whole connection)\r
+ *    backs up or presents a zero window, we must freeze the\r
+ *    associated local socket in order to avoid unbounded buffer\r
+ *    growth.\r
+ */\r
+#define sk_set_frozen(s, is_frozen) (((*s)->set_frozen) (s, is_frozen))\r
+\r
+/*\r
+ * Call this after an operation that might have tried to send on a\r
+ * socket, to clean up any pending network errors.\r
+ */\r
+void net_pending_errors(void);\r
+\r
+/*\r
+ * Simple wrapper on getservbyname(), needed by ssh.c. Returns the\r
+ * port number, in host byte order (suitable for printf and so on).\r
+ * Returns 0 on failure. Any platform not supporting getservbyname\r
+ * can just return 0 - this function is not required to handle\r
+ * numeric port specifications.\r
+ */\r
+int net_service_lookup(char *service);\r
+\r
+/********** SSL stuff **********/\r
+\r
+/*\r
+ * This section is subject to change, but you get the general idea\r
+ * of what it will eventually look like.\r
+ */\r
+\r
+typedef struct certificate *Certificate;\r
+typedef struct our_certificate *Our_Certificate;\r
+    /* to be defined somewhere else, somehow */\r
+\r
+typedef struct ssl_client_socket_function_table **SSL_Client_Socket;\r
+typedef struct ssl_client_plug_function_table **SSL_Client_Plug;\r
+\r
+struct ssl_client_socket_function_table {\r
+    struct socket_function_table base;\r
+    void (*renegotiate) (SSL_Client_Socket s);\r
+    /* renegotiate the cipher spec */\r
+};\r
+\r
+struct ssl_client_plug_function_table {\r
+    struct plug_function_table base;\r
+    int (*refuse_cert) (SSL_Client_Plug p, Certificate cert[]);\r
+    /* do we accept this certificate chain?  If not, why not? */\r
+    /* cert[0] is the server's certificate, cert[] is NULL-terminated */\r
+    /* the last certificate may or may not be the root certificate */\r
+     Our_Certificate(*client_cert) (SSL_Client_Plug p);\r
+    /* the server wants us to identify ourselves */\r
+    /* may return NULL if we want anonymity */\r
+};\r
+\r
+SSL_Client_Socket sk_ssl_client_over(Socket s, /* pre-existing (tcp) connection */\r
+                                    SSL_Client_Plug p);\r
+\r
+#define sk_renegotiate(s) (((*s)->renegotiate) (s))\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/PINGER.C b/src/TortoisePlink/PINGER.C
new file mode 100644 (file)
index 0000000..b6fde24
--- /dev/null
@@ -0,0 +1,71 @@
+/*\r
+ * pinger.c: centralised module that deals with sending TS_PING\r
+ * keepalives, to avoid replicating this code in multiple backends.\r
+ */\r
+\r
+#include "putty.h"\r
+\r
+struct pinger_tag {\r
+    int interval;\r
+    int pending;\r
+    long next;\r
+    Backend *back;\r
+    void *backhandle;\r
+};\r
+\r
+static void pinger_schedule(Pinger pinger);\r
+\r
+static void pinger_timer(void *ctx, long now)\r
+{\r
+    Pinger pinger = (Pinger)ctx;\r
+\r
+    if (pinger->pending && now - pinger->next >= 0) {\r
+       pinger->back->special(pinger->backhandle, TS_PING);\r
+       pinger->pending = FALSE;\r
+       pinger_schedule(pinger);\r
+    }\r
+}\r
+\r
+static void pinger_schedule(Pinger pinger)\r
+{\r
+    int next;\r
+\r
+    if (!pinger->interval) {\r
+       pinger->pending = FALSE;       /* cancel any pending ping */\r
+       return;\r
+    }\r
+\r
+    next = schedule_timer(pinger->interval * TICKSPERSEC,\r
+                         pinger_timer, pinger);\r
+    if (!pinger->pending || next < pinger->next) {\r
+       pinger->next = next;\r
+       pinger->pending = TRUE;\r
+    }\r
+}\r
+\r
+Pinger pinger_new(Config *cfg, Backend *back, void *backhandle)\r
+{\r
+    Pinger pinger = snew(struct pinger_tag);\r
+\r
+    pinger->interval = cfg->ping_interval;\r
+    pinger->pending = FALSE;\r
+    pinger->back = back;\r
+    pinger->backhandle = backhandle;\r
+    pinger_schedule(pinger);\r
+\r
+    return pinger;\r
+}\r
+\r
+void pinger_reconfig(Pinger pinger, Config *oldcfg, Config *newcfg)\r
+{\r
+    if (oldcfg->ping_interval != newcfg->ping_interval) {\r
+       pinger->interval = newcfg->ping_interval;\r
+       pinger_schedule(pinger);\r
+    }\r
+}\r
+\r
+void pinger_free(Pinger pinger)\r
+{\r
+    expire_timer_context(pinger);\r
+    sfree(pinger);\r
+}\r
diff --git a/src/TortoisePlink/PORTFWD.C b/src/TortoisePlink/PORTFWD.C
new file mode 100644 (file)
index 0000000..2885295
--- /dev/null
@@ -0,0 +1,555 @@
+/*\r
+ * SSH port forwarding.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+#include "ssh.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+struct PFwdPrivate {\r
+    const struct plug_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+    void *c;                          /* (channel) data used by ssh.c */\r
+    void *backhandle;                 /* instance of SSH backend itself */\r
+    /* Note that backhandle need not be filled in if c is non-NULL */\r
+    Socket s;\r
+    int throttled, throttle_override;\r
+    int ready;\r
+    /*\r
+     * `dynamic' does double duty. It's set to 0 for an ordinary\r
+     * forwarded port, and nonzero for SOCKS-style dynamic port\r
+     * forwarding; but it also represents the state of the SOCKS\r
+     * exchange.\r
+     */\r
+    int dynamic;\r
+    /*\r
+     * `hostname' and `port' are the real hostname and port, once\r
+     * we know what we're connecting to; they're unused for this\r
+     * purpose while conducting a local SOCKS exchange, which means\r
+     * we can also use them as a buffer and pointer for reading\r
+     * data from the SOCKS client.\r
+     */\r
+    char hostname[256+8];\r
+    int port;\r
+    /*\r
+     * When doing dynamic port forwarding, we can receive\r
+     * connection data before we are actually able to send it; so\r
+     * we may have to temporarily hold some in a dynamically\r
+     * allocated buffer here.\r
+     */\r
+    void *buffer;\r
+    int buflen;\r
+};\r
+\r
+static void pfd_log(Plug plug, int type, SockAddr addr, int port,\r
+                   const char *error_msg, int error_code)\r
+{\r
+    /* we have to dump these since we have no interface to logging.c */\r
+}\r
+\r
+static int pfd_closing(Plug plug, const char *error_msg, int error_code,\r
+                      int calling_back)\r
+{\r
+    struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
+\r
+    /*\r
+     * We have no way to communicate down the forwarded connection,\r
+     * so if an error occurred on the socket, we just ignore it\r
+     * and treat it like a proper close.\r
+     */\r
+    if (pr->c)\r
+       sshfwd_close(pr->c);\r
+    pfd_close(pr->s);\r
+    return 1;\r
+}\r
+\r
+static int pfd_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
+    if (pr->dynamic) {\r
+       while (len--) {\r
+           /*\r
+            * Throughout SOCKS negotiation, "hostname" is re-used as a\r
+            * random protocol buffer with "port" storing the length.\r
+            */ \r
+           if (pr->port >= lenof(pr->hostname)) {\r
+               /* Request too long. */\r
+               if ((pr->dynamic >> 12) == 4) {\r
+                   /* Send back a SOCKS 4 error before closing. */\r
+                   char data[8];\r
+                   memset(data, 0, sizeof(data));\r
+                   data[1] = 91;      /* generic `request rejected' */\r
+                   sk_write(pr->s, data, 8);\r
+               }\r
+               pfd_close(pr->s);\r
+               return 1;\r
+           }\r
+           pr->hostname[pr->port++] = *data++;\r
+\r
+           /*\r
+            * Now check what's in the buffer to see if it's a\r
+            * valid and complete message in the SOCKS exchange.\r
+            */\r
+           if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) &&\r
+               pr->hostname[0] == 4) {\r
+               /*\r
+                * SOCKS 4.\r
+                */\r
+               if (pr->dynamic == 1)\r
+                   pr->dynamic = 0x4000;\r
+               if (pr->port < 2) continue;/* don't have command code yet */\r
+               if (pr->hostname[1] != 1) {\r
+                   /* Not CONNECT. */\r
+                   /* Send back a SOCKS 4 error before closing. */\r
+                   char data[8];\r
+                   memset(data, 0, sizeof(data));\r
+                   data[1] = 91;      /* generic `request rejected' */\r
+                   sk_write(pr->s, data, 8);\r
+                   pfd_close(pr->s);\r
+                   return 1;\r
+               }\r
+               if (pr->port <= 8) continue; /* haven't started user/hostname */\r
+               if (pr->hostname[pr->port-1] != 0)\r
+                   continue;          /* haven't _finished_ user/hostname */\r
+               /*\r
+                * Now we have a full SOCKS 4 request. Check it to\r
+                * see if it's a SOCKS 4A request.\r
+                */\r
+               if (pr->hostname[4] == 0 && pr->hostname[5] == 0 &&\r
+                   pr->hostname[6] == 0 && pr->hostname[7] != 0) {\r
+                   /*\r
+                    * It's SOCKS 4A. So if we haven't yet\r
+                    * collected the host name, we should continue\r
+                    * waiting for data in order to do so; if we\r
+                    * have, we can go ahead.\r
+                    */\r
+                   int len;\r
+                   if (pr->dynamic == 0x4000) {\r
+                       pr->dynamic = 0x4001;\r
+                       pr->port = 8;      /* reset buffer to overwrite name */\r
+                       continue;\r
+                   }\r
+                   pr->hostname[0] = 0;   /* reply version code */\r
+                   pr->hostname[1] = 90;   /* request granted */\r
+                   sk_write(pr->s, pr->hostname, 8);\r
+                   len= pr->port - 8;\r
+                   pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2);\r
+                   memmove(pr->hostname, pr->hostname + 8, len);\r
+                   goto connect;\r
+               } else {\r
+                   /*\r
+                    * It's SOCKS 4, which means we should format\r
+                    * the IP address into the hostname string and\r
+                    * then just go.\r
+                    */\r
+                   pr->hostname[0] = 0;   /* reply version code */\r
+                   pr->hostname[1] = 90;   /* request granted */\r
+                   sk_write(pr->s, pr->hostname, 8);\r
+                   pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2);\r
+                   sprintf(pr->hostname, "%d.%d.%d.%d",\r
+                           (unsigned char)pr->hostname[4],\r
+                           (unsigned char)pr->hostname[5],\r
+                           (unsigned char)pr->hostname[6],\r
+                           (unsigned char)pr->hostname[7]);\r
+                   goto connect;\r
+               }\r
+           }\r
+\r
+           if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) &&\r
+               pr->hostname[0] == 5) {\r
+               /*\r
+                * SOCKS 5.\r
+                */\r
+               if (pr->dynamic == 1)\r
+                   pr->dynamic = 0x5000;\r
+\r
+               if (pr->dynamic == 0x5000) {\r
+                   int i, method;\r
+                   char data[2];\r
+                   /*\r
+                    * We're receiving a set of method identifiers.\r
+                    */\r
+                   if (pr->port < 2) continue;/* no method count yet */\r
+                   if (pr->port < 2 + (unsigned char)pr->hostname[1])\r
+                       continue;      /* no methods yet */\r
+                   method = 0xFF;     /* invalid */\r
+                   for (i = 0; i < (unsigned char)pr->hostname[1]; i++)\r
+                       if (pr->hostname[2+i] == 0) {\r
+                           method = 0;/* no auth */\r
+                           break;\r
+                       }\r
+                   data[0] = 5;\r
+                   data[1] = method;\r
+                   sk_write(pr->s, data, 2);\r
+                   pr->dynamic = 0x5001;\r
+                   pr->port = 0;      /* re-empty the buffer */\r
+                   continue;\r
+               }\r
+\r
+               if (pr->dynamic == 0x5001) {\r
+                   /*\r
+                    * We're receiving a SOCKS request.\r
+                    */\r
+                   unsigned char reply[10]; /* SOCKS5 atyp=1 reply */\r
+                   int atype, alen = 0;\r
+\r
+                   /*\r
+                    * Pre-fill reply packet.\r
+                    * In all cases, we set BND.{HOST,ADDR} to 0.0.0.0:0\r
+                    * (atyp=1) in the reply; if we succeed, we don't know\r
+                    * the right answers, and if we fail, they should be\r
+                    * ignored.\r
+                    */\r
+                   memset(reply, 0, lenof(reply));\r
+                   reply[0] = 5; /* VER */\r
+                   reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */\r
+\r
+                   if (pr->port < 6) continue;\r
+                   atype = (unsigned char)pr->hostname[3];\r
+                   if (atype == 1)    /* IPv4 address */\r
+                       alen = 4;\r
+                   if (atype == 4)    /* IPv6 address */\r
+                       alen = 16;\r
+                   if (atype == 3)    /* domain name has leading length */\r
+                       alen = 1 + (unsigned char)pr->hostname[4];\r
+                   if (pr->port < 6 + alen) continue;\r
+                   if (pr->hostname[1] != 1 || pr->hostname[2] != 0) {\r
+                       /* Not CONNECT or reserved field nonzero - error */\r
+                       reply[1] = 1;   /* generic failure */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       pfd_close(pr->s);\r
+                       return 1;\r
+                   }\r
+                   /*\r
+                    * Now we have a viable connect request. Switch\r
+                    * on atype.\r
+                    */\r
+                   pr->port = GET_16BIT_MSB_FIRST(pr->hostname+4+alen);\r
+                   if (atype == 1) {\r
+                       /* REP=0 (success) already */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       sprintf(pr->hostname, "%d.%d.%d.%d",\r
+                               (unsigned char)pr->hostname[4],\r
+                               (unsigned char)pr->hostname[5],\r
+                               (unsigned char)pr->hostname[6],\r
+                               (unsigned char)pr->hostname[7]);\r
+                       goto connect;\r
+                   } else if (atype == 3) {\r
+                       /* REP=0 (success) already */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       memmove(pr->hostname, pr->hostname + 5, alen-1);\r
+                       pr->hostname[alen-1] = '\0';\r
+                       goto connect;\r
+                   } else {\r
+                       /*\r
+                        * Unknown address type. (FIXME: support IPv6!)\r
+                        */\r
+                       reply[1] = 8;   /* atype not supported */\r
+                       sk_write(pr->s, (char *) reply, lenof(reply));\r
+                       pfd_close(pr->s);\r
+                       return 1;\r
+                   }\r
+               }\r
+           }\r
+\r
+           /*\r
+            * If we get here without either having done `continue'\r
+            * or `goto connect', it must be because there is no\r
+            * sensible interpretation of what's in our buffer. So\r
+            * close the connection rudely.\r
+            */\r
+           pfd_close(pr->s);\r
+           return 1;\r
+       }\r
+       return 1;\r
+\r
+       /*\r
+        * We come here when we're ready to make an actual\r
+        * connection.\r
+        */\r
+       connect:\r
+\r
+       pr->c = new_sock_channel(pr->backhandle, pr->s);\r
+       if (pr->c == NULL) {\r
+           pfd_close(pr->s);\r
+           return 1;\r
+       } else {\r
+           /* asks to forward to the specified host/port for this */\r
+           ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");\r
+       }\r
+       pr->dynamic = 0;\r
+\r
+       /*\r
+        * Now freeze the socket until the SSH server confirms the\r
+        * connection.\r
+        */\r
+       sk_set_frozen(pr->s, 1);\r
+       /*\r
+        * If there's any data remaining in our current buffer,\r
+        * save it to be sent on pfd_confirm().\r
+        */\r
+       if (len > 0) {\r
+           pr->buffer = snewn(len, char);\r
+           memcpy(pr->buffer, data, len);\r
+           pr->buflen = len;\r
+       }\r
+    }\r
+    if (pr->ready) {\r
+       if (sshfwd_write(pr->c, data, len) > 0) {\r
+           pr->throttled = 1;\r
+           sk_set_frozen(pr->s, 1);\r
+       }\r
+    }\r
+    return 1;\r
+}\r
+\r
+static void pfd_sent(Plug plug, int bufsize)\r
+{\r
+    struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
+\r
+    if (pr->c)\r
+       sshfwd_unthrottle(pr->c, bufsize);\r
+}\r
+\r
+/*\r
+ * Called when receiving a PORT OPEN from the server\r
+ */\r
+const char *pfd_newconnect(Socket *s, char *hostname, int port,\r
+                          void *c, const Config *cfg, int addressfamily)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       pfd_log,\r
+       pfd_closing,\r
+       pfd_receive,\r
+       pfd_sent,\r
+       NULL\r
+    };\r
+\r
+    SockAddr addr;\r
+    const char *err;\r
+    char *dummy_realhost;\r
+    struct PFwdPrivate *pr;\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    pr = snew(struct PFwdPrivate);\r
+    pr->buffer = NULL;\r
+    pr->fn = &fn_table;\r
+    pr->throttled = pr->throttle_override = 0;\r
+    pr->ready = 1;\r
+    pr->c = c;\r
+    pr->backhandle = NULL;            /* we shouldn't need this */\r
+    pr->dynamic = 0;\r
+\r
+    pr->s = *s = new_connection(addr, dummy_realhost, port,\r
+                               0, 1, 0, 0, (Plug) pr, cfg);\r
+    if ((err = sk_socket_error(*s)) != NULL) {\r
+       sfree(pr);\r
+       return err;\r
+    }\r
+\r
+    sk_set_private_ptr(*s, pr);\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ called when someone connects to the local port\r
+ */\r
+\r
+static int pfd_accepting(Plug p, OSSocket sock)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       pfd_log,\r
+       pfd_closing,\r
+       pfd_receive,\r
+       pfd_sent,\r
+       NULL\r
+    };\r
+    struct PFwdPrivate *pr, *org;\r
+    Socket s;\r
+    const char *err;\r
+\r
+    org = (struct PFwdPrivate *)p;\r
+    pr = snew(struct PFwdPrivate);\r
+    pr->buffer = NULL;\r
+    pr->fn = &fn_table;\r
+\r
+    pr->c = NULL;\r
+    pr->backhandle = org->backhandle;\r
+\r
+    pr->s = s = sk_register(sock, (Plug) pr);\r
+    if ((err = sk_socket_error(s)) != NULL) {\r
+       sfree(pr);\r
+       return err != NULL;\r
+    }\r
+\r
+    sk_set_private_ptr(s, pr);\r
+\r
+    pr->throttled = pr->throttle_override = 0;\r
+    pr->ready = 0;\r
+\r
+    if (org->dynamic) {\r
+       pr->dynamic = 1;\r
+       pr->port = 0;                  /* "hostname" buffer is so far empty */\r
+       sk_set_frozen(s, 0);           /* we want to receive SOCKS _now_! */\r
+    } else {\r
+       pr->dynamic = 0;\r
+       strcpy(pr->hostname, org->hostname);\r
+       pr->port = org->port;   \r
+       pr->c = new_sock_channel(org->backhandle, s);\r
+\r
+       if (pr->c == NULL) {\r
+           sfree(pr);\r
+           return 1;\r
+       } else {\r
+           /* asks to forward to the specified host/port for this */\r
+           ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");\r
+       }\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+\r
+/* Add a new forwarding from port -> desthost:destport\r
+ sets up a listener on the local machine on (srcaddr:)port\r
+ */\r
+const char *pfd_addforward(char *desthost, int destport, char *srcaddr,\r
+                          int port, void *backhandle, const Config *cfg,\r
+                          void **sockdata, int address_family)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       pfd_log,\r
+       pfd_closing,\r
+       pfd_receive,                   /* should not happen... */\r
+       pfd_sent,                      /* also should not happen */\r
+       pfd_accepting\r
+    };\r
+\r
+    const char *err;\r
+    struct PFwdPrivate *pr;\r
+    Socket s;\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    pr = snew(struct PFwdPrivate);\r
+    pr->buffer = NULL;\r
+    pr->fn = &fn_table;\r
+    pr->c = NULL;\r
+    if (desthost) {\r
+       strcpy(pr->hostname, desthost);\r
+       pr->port = destport;\r
+       pr->dynamic = 0;\r
+    } else\r
+       pr->dynamic = 1;\r
+    pr->throttled = pr->throttle_override = 0;\r
+    pr->ready = 0;\r
+    pr->backhandle = backhandle;\r
+\r
+    pr->s = s = new_listener(srcaddr, port, (Plug) pr,\r
+                            !cfg->lport_acceptall, cfg, address_family);\r
+    if ((err = sk_socket_error(s)) != NULL) {\r
+       sfree(pr);\r
+       return err;\r
+    }\r
+\r
+    sk_set_private_ptr(s, pr);\r
+\r
+    *sockdata = (void *)s;\r
+\r
+    return NULL;\r
+}\r
+\r
+void pfd_close(Socket s)\r
+{\r
+    struct PFwdPrivate *pr;\r
+\r
+    if (!s)\r
+       return;\r
+\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+\r
+    sfree(pr->buffer);\r
+    sfree(pr);\r
+\r
+    sk_close(s);\r
+}\r
+\r
+/*\r
+ * Terminate a listener.\r
+ */\r
+void pfd_terminate(void *sv)\r
+{\r
+    pfd_close((Socket)sv);\r
+}\r
+\r
+void pfd_unthrottle(Socket s)\r
+{\r
+    struct PFwdPrivate *pr;\r
+    if (!s)\r
+       return;\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+\r
+    pr->throttled = 0;\r
+    sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
+}\r
+\r
+void pfd_override_throttle(Socket s, int enable)\r
+{\r
+    struct PFwdPrivate *pr;\r
+    if (!s)\r
+       return;\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+\r
+    pr->throttle_override = enable;\r
+    sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
+}\r
+\r
+/*\r
+ * Called to send data down the raw connection.\r
+ */\r
+int pfd_send(Socket s, char *data, int len)\r
+{\r
+    if (s == NULL)\r
+       return 0;\r
+    return sk_write(s, data, len);\r
+}\r
+\r
+\r
+void pfd_confirm(Socket s)\r
+{\r
+    struct PFwdPrivate *pr;\r
+\r
+    if (s == NULL)\r
+       return;\r
+\r
+    pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
+    pr->ready = 1;\r
+    sk_set_frozen(s, 0);\r
+    sk_write(s, NULL, 0);\r
+    if (pr->buffer) {\r
+       sshfwd_write(pr->c, pr->buffer, pr->buflen);\r
+       sfree(pr->buffer);\r
+       pr->buffer = NULL;\r
+    }\r
+}\r
diff --git a/src/TortoisePlink/PROXY.C b/src/TortoisePlink/PROXY.C
new file mode 100644 (file)
index 0000000..1f42999
--- /dev/null
@@ -0,0 +1,1478 @@
+/*\r
+ * Network proxy abstraction in PuTTY\r
+ *\r
+ * A proxy layer, if necessary, wedges itself between the network\r
+ * code and the higher level backend.\r
+ */\r
+\r
+#include <assert.h>\r
+#include <ctype.h>\r
+#include <string.h>\r
+\r
+#define DEFINE_PLUG_METHOD_MACROS\r
+#include "putty.h"\r
+#include "network.h"\r
+#include "proxy.h"\r
+\r
+#define do_proxy_dns(cfg) \\r
+    (cfg->proxy_dns == FORCE_ON || \\r
+        (cfg->proxy_dns == AUTO && cfg->proxy_type != PROXY_SOCKS4))\r
+\r
+/*\r
+ * Call this when proxy negotiation is complete, so that this\r
+ * socket can begin working normally.\r
+ */\r
+void proxy_activate (Proxy_Socket p)\r
+{\r
+    void *data;\r
+    int len;\r
+    long output_before, output_after;\r
+    \r
+    p->state = PROXY_STATE_ACTIVE;\r
+\r
+    /* we want to ignore new receive events until we have sent\r
+     * all of our buffered receive data.\r
+     */\r
+    sk_set_frozen(p->sub_socket, 1);\r
+\r
+    /* how many bytes of output have we buffered? */\r
+    output_before = bufchain_size(&p->pending_oob_output_data) +\r
+       bufchain_size(&p->pending_output_data);\r
+    /* and keep track of how many bytes do not get sent. */\r
+    output_after = 0;\r
+    \r
+    /* send buffered OOB writes */\r
+    while (bufchain_size(&p->pending_oob_output_data) > 0) {\r
+       bufchain_prefix(&p->pending_oob_output_data, &data, &len);\r
+       output_after += sk_write_oob(p->sub_socket, data, len);\r
+       bufchain_consume(&p->pending_oob_output_data, len);\r
+    }\r
+\r
+    /* send buffered normal writes */\r
+    while (bufchain_size(&p->pending_output_data) > 0) {\r
+       bufchain_prefix(&p->pending_output_data, &data, &len);\r
+       output_after += sk_write(p->sub_socket, data, len);\r
+       bufchain_consume(&p->pending_output_data, len);\r
+    }\r
+\r
+    /* if we managed to send any data, let the higher levels know. */\r
+    if (output_after < output_before)\r
+       plug_sent(p->plug, output_after);\r
+\r
+    /* if we were asked to flush the output during\r
+     * the proxy negotiation process, do so now.\r
+     */\r
+    if (p->pending_flush) sk_flush(p->sub_socket);\r
+\r
+    /* if the backend wanted the socket unfrozen, try to unfreeze.\r
+     * our set_frozen handler will flush buffered receive data before\r
+     * unfreezing the actual underlying socket.\r
+     */\r
+    if (!p->freeze)\r
+       sk_set_frozen((Socket)p, 0);\r
+}\r
+\r
+/* basic proxy socket functions */\r
+\r
+static Plug sk_proxy_plug (Socket s, Plug p)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    Plug ret = ps->plug;\r
+    if (p)\r
+       ps->plug = p;\r
+    return ret;\r
+}\r
+\r
+static void sk_proxy_close (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    sk_close(ps->sub_socket);\r
+    sk_addr_free(ps->remote_addr);\r
+    sfree(ps);\r
+}\r
+\r
+static int sk_proxy_write (Socket s, const char *data, int len)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       bufchain_add(&ps->pending_output_data, data, len);\r
+       return bufchain_size(&ps->pending_output_data);\r
+    }\r
+    return sk_write(ps->sub_socket, data, len);\r
+}\r
+\r
+static int sk_proxy_write_oob (Socket s, const char *data, int len)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       bufchain_clear(&ps->pending_output_data);\r
+       bufchain_clear(&ps->pending_oob_output_data);\r
+       bufchain_add(&ps->pending_oob_output_data, data, len);\r
+       return len;\r
+    }\r
+    return sk_write_oob(ps->sub_socket, data, len);\r
+}\r
+\r
+static void sk_proxy_flush (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->pending_flush = 1;\r
+       return;\r
+    }\r
+    sk_flush(ps->sub_socket);\r
+}\r
+\r
+static void sk_proxy_set_private_ptr (Socket s, void *ptr)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    sk_set_private_ptr(ps->sub_socket, ptr);\r
+}\r
+\r
+static void * sk_proxy_get_private_ptr (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    return sk_get_private_ptr(ps->sub_socket);\r
+}\r
+\r
+static void sk_proxy_set_frozen (Socket s, int is_frozen)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->freeze = is_frozen;\r
+       return;\r
+    }\r
+    \r
+    /* handle any remaining buffered recv data first */\r
+    if (bufchain_size(&ps->pending_input_data) > 0) {\r
+       ps->freeze = is_frozen;\r
+\r
+       /* loop while we still have buffered data, and while we are\r
+        * unfrozen. the plug_receive call in the loop could result \r
+        * in a call back into this function refreezing the socket, \r
+        * so we have to check each time.\r
+        */\r
+        while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) {\r
+           void *data;\r
+           char databuf[512];\r
+           int len;\r
+           bufchain_prefix(&ps->pending_input_data, &data, &len);\r
+           if (len > lenof(databuf))\r
+               len = lenof(databuf);\r
+           memcpy(databuf, data, len);\r
+           bufchain_consume(&ps->pending_input_data, len);\r
+           plug_receive(ps->plug, 0, databuf, len);\r
+       }\r
+\r
+       /* if we're still frozen, we'll have to wait for another\r
+        * call from the backend to finish unbuffering the data.\r
+        */\r
+       if (ps->freeze) return;\r
+    }\r
+    \r
+    sk_set_frozen(ps->sub_socket, is_frozen);\r
+}\r
+\r
+static const char * sk_proxy_socket_error (Socket s)\r
+{\r
+    Proxy_Socket ps = (Proxy_Socket) s;\r
+    if (ps->error != NULL || ps->sub_socket == NULL) {\r
+       return ps->error;\r
+    }\r
+    return sk_socket_error(ps->sub_socket);\r
+}\r
+\r
+/* basic proxy plug functions */\r
+\r
+static void plug_proxy_log(Plug plug, int type, SockAddr addr, int port,\r
+                          const char *error_msg, int error_code)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) plug;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    plug_log(ps->plug, type, addr, port, error_msg, error_code);\r
+}\r
+\r
+static int plug_proxy_closing (Plug p, const char *error_msg,\r
+                              int error_code, int calling_back)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->closing_error_msg = error_msg;\r
+       ps->closing_error_code = error_code;\r
+       ps->closing_calling_back = calling_back;\r
+       return ps->negotiate(ps, PROXY_CHANGE_CLOSING);\r
+    }\r
+    return plug_closing(ps->plug, error_msg,\r
+                       error_code, calling_back);\r
+}\r
+\r
+static int plug_proxy_receive (Plug p, int urgent, char *data, int len)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       /* we will lose the urgentness of this data, but since most,\r
+        * if not all, of this data will be consumed by the negotiation\r
+        * process, hopefully it won't affect the protocol above us\r
+        */\r
+       bufchain_add(&ps->pending_input_data, data, len);\r
+       ps->receive_urgent = urgent;\r
+       ps->receive_data = data;\r
+       ps->receive_len = len;\r
+       return ps->negotiate(ps, PROXY_CHANGE_RECEIVE);\r
+    }\r
+    return plug_receive(ps->plug, urgent, data, len);\r
+}\r
+\r
+static void plug_proxy_sent (Plug p, int bufsize)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->sent_bufsize = bufsize;\r
+       ps->negotiate(ps, PROXY_CHANGE_SENT);\r
+       return;\r
+    }\r
+    plug_sent(ps->plug, bufsize);\r
+}\r
+\r
+static int plug_proxy_accepting (Plug p, OSSocket sock)\r
+{\r
+    Proxy_Plug pp = (Proxy_Plug) p;\r
+    Proxy_Socket ps = pp->proxy_socket;\r
+\r
+    if (ps->state != PROXY_STATE_ACTIVE) {\r
+       ps->accepting_sock = sock;\r
+       return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);\r
+    }\r
+    return plug_accepting(ps->plug, sock);\r
+}\r
+\r
+/*\r
+ * This function can accept a NULL pointer as `addr', in which case\r
+ * it will only check the host name.\r
+ */\r
+static int proxy_for_destination (SockAddr addr, char *hostname, int port,\r
+                                 const Config *cfg)\r
+{\r
+    int s = 0, e = 0;\r
+    char hostip[64];\r
+    int hostip_len, hostname_len;\r
+    const char *exclude_list;\r
+\r
+    /*\r
+     * Check the host name and IP against the hard-coded\r
+     * representations of `localhost'.\r
+     */\r
+    if (!cfg->even_proxy_localhost &&\r
+       (sk_hostname_is_local(hostname) ||\r
+        (addr && sk_address_is_local(addr))))\r
+       return 0;                      /* do not proxy */\r
+\r
+    /* we want a string representation of the IP address for comparisons */\r
+    if (addr) {\r
+       sk_getaddr(addr, hostip, 64);\r
+       hostip_len = strlen(hostip);\r
+    } else\r
+       hostip_len = 0;                /* placate gcc; shouldn't be required */\r
+\r
+    hostname_len = strlen(hostname);\r
+\r
+    exclude_list = cfg->proxy_exclude_list;\r
+\r
+    /* now parse the exclude list, and see if either our IP\r
+     * or hostname matches anything in it.\r
+     */\r
+\r
+    while (exclude_list[s]) {\r
+       while (exclude_list[s] &&\r
+              (isspace((unsigned char)exclude_list[s]) ||\r
+               exclude_list[s] == ',')) s++;\r
+\r
+       if (!exclude_list[s]) break;\r
+\r
+       e = s;\r
+\r
+       while (exclude_list[e] &&\r
+              (isalnum((unsigned char)exclude_list[e]) ||\r
+               exclude_list[e] == '-' ||\r
+               exclude_list[e] == '.' ||\r
+               exclude_list[e] == '*')) e++;\r
+\r
+       if (exclude_list[s] == '*') {\r
+           /* wildcard at beginning of entry */\r
+\r
+           if ((addr && strnicmp(hostip + hostip_len - (e - s - 1),\r
+                                 exclude_list + s + 1, e - s - 1) == 0) ||\r
+               strnicmp(hostname + hostname_len - (e - s - 1),\r
+                        exclude_list + s + 1, e - s - 1) == 0)\r
+               return 0; /* IP/hostname range excluded. do not use proxy. */\r
+\r
+       } else if (exclude_list[e-1] == '*') {\r
+           /* wildcard at end of entry */\r
+\r
+           if ((addr && strnicmp(hostip, exclude_list + s, e - s - 1) == 0) ||\r
+               strnicmp(hostname, exclude_list + s, e - s - 1) == 0)\r
+               return 0; /* IP/hostname range excluded. do not use proxy. */\r
+\r
+       } else {\r
+           /* no wildcard at either end, so let's try an absolute\r
+            * match (ie. a specific IP)\r
+            */\r
+\r
+           if (addr && strnicmp(hostip, exclude_list + s, e - s) == 0)\r
+               return 0; /* IP/hostname excluded. do not use proxy. */\r
+           if (strnicmp(hostname, exclude_list + s, e - s) == 0)\r
+               return 0; /* IP/hostname excluded. do not use proxy. */\r
+       }\r
+\r
+       s = e;\r
+\r
+       /* Make sure we really have reached the next comma or end-of-string */\r
+       while (exclude_list[s] &&\r
+              !isspace((unsigned char)exclude_list[s]) &&\r
+              exclude_list[s] != ',') s++;\r
+    }\r
+\r
+    /* no matches in the exclude list, so use the proxy */\r
+    return 1;\r
+}\r
+\r
+SockAddr name_lookup(char *host, int port, char **canonicalname,\r
+                    const Config *cfg, int addressfamily)\r
+{\r
+    if (cfg->proxy_type != PROXY_NONE &&\r
+       do_proxy_dns(cfg) &&\r
+       proxy_for_destination(NULL, host, port, cfg)) {\r
+       *canonicalname = dupstr(host);\r
+       return sk_nonamelookup(host);\r
+    }\r
+\r
+    return sk_namelookup(host, canonicalname, addressfamily);\r
+}\r
+\r
+Socket new_connection(SockAddr addr, char *hostname,\r
+                     int port, int privport,\r
+                     int oobinline, int nodelay, int keepalive,\r
+                     Plug plug, const Config *cfg)\r
+{\r
+    static const struct socket_function_table socket_fn_table = {\r
+       sk_proxy_plug,\r
+       sk_proxy_close,\r
+       sk_proxy_write,\r
+       sk_proxy_write_oob,\r
+       sk_proxy_flush,\r
+       sk_proxy_set_private_ptr,\r
+       sk_proxy_get_private_ptr,\r
+       sk_proxy_set_frozen,\r
+       sk_proxy_socket_error\r
+    };\r
+\r
+    static const struct plug_function_table plug_fn_table = {\r
+       plug_proxy_log,\r
+       plug_proxy_closing,\r
+       plug_proxy_receive,\r
+       plug_proxy_sent,\r
+       plug_proxy_accepting\r
+    };\r
+\r
+    if (cfg->proxy_type != PROXY_NONE &&\r
+       proxy_for_destination(addr, hostname, port, cfg))\r
+    {\r
+       Proxy_Socket ret;\r
+       Proxy_Plug pplug;\r
+       SockAddr proxy_addr;\r
+       char *proxy_canonical_name;\r
+       Socket sret;\r
+\r
+       if ((sret = platform_new_connection(addr, hostname, port, privport,\r
+                                           oobinline, nodelay, keepalive,\r
+                                           plug, cfg)) !=\r
+           NULL)\r
+           return sret;\r
+\r
+       ret = snew(struct Socket_proxy_tag);\r
+       ret->fn = &socket_fn_table;\r
+       ret->cfg = *cfg;               /* STRUCTURE COPY */\r
+       ret->plug = plug;\r
+       ret->remote_addr = addr;       /* will need to be freed on close */\r
+       ret->remote_port = port;\r
+\r
+       ret->error = NULL;\r
+       ret->pending_flush = 0;\r
+       ret->freeze = 0;\r
+\r
+       bufchain_init(&ret->pending_input_data);\r
+       bufchain_init(&ret->pending_output_data);\r
+       bufchain_init(&ret->pending_oob_output_data);\r
+\r
+       ret->sub_socket = NULL;\r
+       ret->state = PROXY_STATE_NEW;\r
+       ret->negotiate = NULL;\r
+       \r
+       if (cfg->proxy_type == PROXY_HTTP) {\r
+           ret->negotiate = proxy_http_negotiate;\r
+       } else if (cfg->proxy_type == PROXY_SOCKS4) {\r
+            ret->negotiate = proxy_socks4_negotiate;\r
+       } else if (cfg->proxy_type == PROXY_SOCKS5) {\r
+            ret->negotiate = proxy_socks5_negotiate;\r
+       } else if (cfg->proxy_type == PROXY_TELNET) {\r
+           ret->negotiate = proxy_telnet_negotiate;\r
+       } else {\r
+           ret->error = "Proxy error: Unknown proxy method";\r
+           return (Socket) ret;\r
+       }\r
+\r
+       /* create the proxy plug to map calls from the actual\r
+        * socket into our proxy socket layer */\r
+       pplug = snew(struct Plug_proxy_tag);\r
+       pplug->fn = &plug_fn_table;\r
+       pplug->proxy_socket = ret;\r
+\r
+       /* look-up proxy */\r
+       proxy_addr = sk_namelookup(cfg->proxy_host,\r
+                                  &proxy_canonical_name, cfg->addressfamily);\r
+       if (sk_addr_error(proxy_addr) != NULL) {\r
+           ret->error = "Proxy error: Unable to resolve proxy host name";\r
+           return (Socket)ret;\r
+       }\r
+       sfree(proxy_canonical_name);\r
+\r
+       /* create the actual socket we will be using,\r
+        * connected to our proxy server and port.\r
+        */\r
+       ret->sub_socket = sk_new(proxy_addr, cfg->proxy_port,\r
+                                privport, oobinline,\r
+                                nodelay, keepalive, (Plug) pplug);\r
+       if (sk_socket_error(ret->sub_socket) != NULL)\r
+           return (Socket) ret;\r
+\r
+       /* start the proxy negotiation process... */\r
+       sk_set_frozen(ret->sub_socket, 0);\r
+       ret->negotiate(ret, PROXY_CHANGE_NEW);\r
+\r
+       return (Socket) ret;\r
+    }\r
+\r
+    /* no proxy, so just return the direct socket */\r
+    return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);\r
+}\r
+\r
+Socket new_listener(char *srcaddr, int port, Plug plug, int local_host_only,\r
+                   const Config *cfg, int addressfamily)\r
+{\r
+    /* TODO: SOCKS (and potentially others) support inbound\r
+     * TODO: connections via the proxy. support them.\r
+     */\r
+\r
+    return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily);\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * HTTP CONNECT proxy type.\r
+ */\r
+\r
+static int get_line_end (char * data, int len)\r
+{\r
+    int off = 0;\r
+\r
+    while (off < len)\r
+    {\r
+       if (data[off] == '\n') {\r
+           /* we have a newline */\r
+           off++;\r
+\r
+           /* is that the only thing on this line? */\r
+           if (off <= 2) return off;\r
+\r
+           /* if not, then there is the possibility that this header\r
+            * continues onto the next line, if it starts with a space\r
+            * or a tab.\r
+            */\r
+\r
+           if (off + 1 < len &&\r
+               data[off+1] != ' ' &&\r
+               data[off+1] != '\t') return off;\r
+\r
+           /* the line does continue, so we have to keep going\r
+            * until we see an the header's "real" end of line.\r
+            */\r
+           off++;\r
+       }\r
+\r
+       off++;\r
+    }\r
+\r
+    return -1;\r
+}\r
+\r
+int proxy_http_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_STATE_NEW) {\r
+       /* we are just beginning the proxy negotiate process,\r
+        * so we'll send off the initial bits of the request.\r
+        * for this proxy method, it's just a simple HTTP\r
+        * request\r
+        */\r
+       char *buf, dest[512];\r
+\r
+       sk_getaddr(p->remote_addr, dest, lenof(dest));\r
+\r
+       buf = dupprintf("CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n",\r
+                       dest, p->remote_port, dest, p->remote_port);\r
+       sk_write(p->sub_socket, buf, strlen(buf));\r
+       sfree(buf);\r
+\r
+       if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+           char buf[sizeof(p->cfg.proxy_username)+sizeof(p->cfg.proxy_password)];\r
+           char buf2[sizeof(buf)*4/3 + 100];\r
+           int i, j, len;\r
+           sprintf(buf, "%s:%s", p->cfg.proxy_username, p->cfg.proxy_password);\r
+           len = strlen(buf);\r
+           sprintf(buf2, "Proxy-Authorization: Basic ");\r
+           for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4)\r
+               base64_encode_atom((unsigned char *)(buf+i),\r
+                                  (len-i > 3 ? 3 : len-i), buf2+j);\r
+           strcpy(buf2+j, "\r\n");\r
+           sk_write(p->sub_socket, buf2, strlen(buf2));\r
+       }\r
+\r
+       sk_write(p->sub_socket, "\r\n", 2);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       char *data, *datap;\r
+       int len;\r
+       int eol;\r
+\r
+       if (p->state == 1) {\r
+\r
+           int min_ver, maj_ver, status;\r
+\r
+           /* get the status line */\r
+           len = bufchain_size(&p->pending_input_data);\r
+           assert(len > 0);           /* or we wouldn't be here */\r
+           data = snewn(len+1, char);\r
+           bufchain_fetch(&p->pending_input_data, data, len);\r
+           /*\r
+            * We must NUL-terminate this data, because Windows\r
+            * sscanf appears to require a NUL at the end of the\r
+            * string because it strlens it _first_. Sigh.\r
+            */\r
+           data[len] = '\0';\r
+\r
+           eol = get_line_end(data, len);\r
+           if (eol < 0) {\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           status = -1;\r
+           /* We can't rely on whether the %n incremented the sscanf return */\r
+           if (sscanf((char *)data, "HTTP/%i.%i %n",\r
+                      &maj_ver, &min_ver, &status) < 2 || status == -1) {\r
+               plug_closing(p->plug, "Proxy error: HTTP response was absent",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           /* remove the status line from the input buffer. */\r
+           bufchain_consume(&p->pending_input_data, eol);\r
+           if (data[status] != '2') {\r
+               /* error */\r
+               char *buf;\r
+               data[eol] = '\0';\r
+               while (eol > status &&\r
+                      (data[eol-1] == '\r' || data[eol-1] == '\n'))\r
+                   data[--eol] = '\0';\r
+               buf = dupprintf("Proxy error: %s", data+status);\r
+               plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
+               sfree(buf);\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           sfree(data);\r
+\r
+           p->state = 2;\r
+       }\r
+\r
+       if (p->state == 2) {\r
+\r
+           /* get headers. we're done when we get a\r
+            * header of length 2, (ie. just "\r\n")\r
+            */\r
+\r
+           len = bufchain_size(&p->pending_input_data);\r
+           assert(len > 0);           /* or we wouldn't be here */\r
+           data = snewn(len, char);\r
+           datap = data;\r
+           bufchain_fetch(&p->pending_input_data, data, len);\r
+\r
+           eol = get_line_end(datap, len);\r
+           if (eol < 0) {\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+           while (eol > 2)\r
+           {\r
+               bufchain_consume(&p->pending_input_data, eol);\r
+               datap += eol;\r
+               len   -= eol;\r
+               eol = get_line_end(datap, len);\r
+           }\r
+\r
+           if (eol == 2) {\r
+               /* we're done */\r
+               bufchain_consume(&p->pending_input_data, 2);\r
+               proxy_activate(p);\r
+               /* proxy activate will have dealt with\r
+                * whatever is left of the buffer */\r
+               sfree(data);\r
+               return 1;\r
+           }\r
+\r
+           sfree(data);\r
+           return 1;\r
+       }\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * SOCKS proxy type.\r
+ */\r
+\r
+/* SOCKS version 4 */\r
+int proxy_socks4_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_CHANGE_NEW) {\r
+\r
+       /* request format:\r
+        *  version number (1 byte) = 4\r
+        *  command code (1 byte)\r
+        *    1 = CONNECT\r
+        *    2 = BIND\r
+        *  dest. port (2 bytes) [network order]\r
+        *  dest. address (4 bytes)\r
+        *  user ID (variable length, null terminated string)\r
+        */\r
+\r
+       int length, type, namelen;\r
+       char *command, addr[4], hostname[512];\r
+\r
+       type = sk_addrtype(p->remote_addr);\r
+       if (type == ADDRTYPE_IPV6) {\r
+           plug_closing(p->plug, "Proxy error: SOCKS version 4 does"\r
+                        " not support IPv6", PROXY_ERROR_GENERAL, 0);\r
+           return 1;\r
+       } else if (type == ADDRTYPE_IPV4) {\r
+           namelen = 0;\r
+           sk_addrcopy(p->remote_addr, addr);\r
+       } else {                       /* type == ADDRTYPE_NAME */\r
+           assert(type == ADDRTYPE_NAME);\r
+           sk_getaddr(p->remote_addr, hostname, lenof(hostname));\r
+           namelen = strlen(hostname) + 1;   /* include the NUL */\r
+           addr[0] = addr[1] = addr[2] = 0;\r
+           addr[3] = 1;\r
+       }\r
+\r
+       length = strlen(p->cfg.proxy_username) + namelen + 9;\r
+       command = snewn(length, char);\r
+       strcpy(command + 8, p->cfg.proxy_username);\r
+\r
+       command[0] = 4; /* version 4 */\r
+       command[1] = 1; /* CONNECT command */\r
+\r
+       /* port */\r
+       command[2] = (char) (p->remote_port >> 8) & 0xff;\r
+       command[3] = (char) p->remote_port & 0xff;\r
+\r
+       /* address */\r
+       memcpy(command + 4, addr, 4);\r
+\r
+       /* hostname */\r
+       memcpy(command + 8 + strlen(p->cfg.proxy_username) + 1,\r
+              hostname, namelen);\r
+\r
+       sk_write(p->sub_socket, command, length);\r
+       sfree(command);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       if (p->state == 1) {\r
+           /* response format:\r
+            *  version number (1 byte) = 4\r
+            *  reply code (1 byte)\r
+            *    90 = request granted\r
+            *    91 = request rejected or failed\r
+            *    92 = request rejected due to lack of IDENTD on client\r
+            *    93 = request rejected due to difference in user ID \r
+            *         (what we sent vs. what IDENTD said)\r
+            *  dest. port (2 bytes)\r
+            *  dest. address (4 bytes)\r
+            */\r
+\r
+           char data[8];\r
+\r
+           if (bufchain_size(&p->pending_input_data) < 8)\r
+               return 1;              /* not got anything yet */\r
+           \r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 8);\r
+\r
+           if (data[0] != 0) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy responded with "\r
+                                     "unexpected reply code version",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] != 90) {\r
+\r
+               switch (data[1]) {\r
+                 case 92:\r
+                   plug_closing(p->plug, "Proxy error: SOCKS server wanted IDENTD on client",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   break;\r
+                 case 93:\r
+                   plug_closing(p->plug, "Proxy error: Username and IDENTD on client don't agree",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   break;\r
+                 case 91:\r
+                 default:\r
+                   plug_closing(p->plug, "Proxy error: Error while communicating with proxy",\r
+                                PROXY_ERROR_GENERAL, 0);\r
+                   break;\r
+               }\r
+\r
+               return 1;\r
+           }\r
+           bufchain_consume(&p->pending_input_data, 8);\r
+\r
+           /* we're done */\r
+           proxy_activate(p);\r
+           /* proxy activate will have dealt with\r
+            * whatever is left of the buffer */\r
+           return 1;\r
+       }\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
+\r
+/* SOCKS version 5 */\r
+int proxy_socks5_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_CHANGE_NEW) {\r
+\r
+       /* initial command:\r
+        *  version number (1 byte) = 5\r
+        *  number of available authentication methods (1 byte)\r
+        *  available authentication methods (1 byte * previous value)\r
+        *    authentication methods:\r
+        *     0x00 = no authentication\r
+        *     0x01 = GSSAPI\r
+        *     0x02 = username/password\r
+        *     0x03 = CHAP\r
+        */\r
+\r
+       char command[5];\r
+       int len;\r
+\r
+       command[0] = 5; /* version 5 */\r
+       if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+           command[2] = 0x00;         /* no authentication */\r
+           len = 3;\r
+           proxy_socks5_offerencryptedauth (command, &len);\r
+           command[len++] = 0x02;             /* username/password */\r
+           command[1] = len - 2;       /* Number of methods supported */\r
+       } else {\r
+           command[1] = 1;            /* one methods supported: */\r
+           command[2] = 0x00;         /* no authentication */\r
+           len = 3;\r
+       }\r
+\r
+       sk_write(p->sub_socket, command, len);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       if (p->state == 1) {\r
+\r
+           /* initial response:\r
+            *  version number (1 byte) = 5\r
+            *  authentication method (1 byte)\r
+            *    authentication methods:\r
+            *     0x00 = no authentication\r
+            *     0x01 = GSSAPI\r
+            *     0x02 = username/password\r
+            *     0x03 = CHAP\r
+            *     0xff = no acceptable methods\r
+            */\r
+           char data[2];\r
+\r
+           if (bufchain_size(&p->pending_input_data) < 2)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 2);\r
+\r
+           if (data[0] != 5) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] == 0x00) p->state = 2; /* no authentication needed */\r
+           else if (data[1] == 0x01) p->state = 4; /* GSSAPI authentication */\r
+           else if (data[1] == 0x02) p->state = 5; /* username/password authentication */\r
+           else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */\r
+           else {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           bufchain_consume(&p->pending_input_data, 2);\r
+       }\r
+\r
+       if (p->state == 7) {\r
+\r
+           /* password authentication reply format:\r
+            *  version number (1 bytes) = 1\r
+            *  reply code (1 byte)\r
+            *    0 = succeeded\r
+            *    >0 = failed\r
+            */\r
+           char data[2];\r
+\r
+           if (bufchain_size(&p->pending_input_data) < 2)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 2);\r
+\r
+           if (data[0] != 1) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS password "\r
+                            "subnegotiation contained wrong version number",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] != 0) {\r
+\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy refused"\r
+                            " password authentication",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           bufchain_consume(&p->pending_input_data, 2);\r
+           p->state = 2;              /* now proceed as authenticated */\r
+       }\r
+\r
+       if (p->state == 8) {\r
+           int ret;\r
+           ret = proxy_socks5_handlechap(p);\r
+           if (ret) return ret;\r
+       }\r
+\r
+       if (p->state == 2) {\r
+\r
+           /* request format:\r
+            *  version number (1 byte) = 5\r
+            *  command code (1 byte)\r
+            *    1 = CONNECT\r
+            *    2 = BIND\r
+            *    3 = UDP ASSOCIATE\r
+            *  reserved (1 byte) = 0x00\r
+            *  address type (1 byte)\r
+            *    1 = IPv4\r
+            *    3 = domainname (first byte has length, no terminating null)\r
+            *    4 = IPv6\r
+            *  dest. address (variable)\r
+            *  dest. port (2 bytes) [network order]\r
+            */\r
+\r
+           char command[512];\r
+           int len;\r
+           int type;\r
+\r
+           type = sk_addrtype(p->remote_addr);\r
+           if (type == ADDRTYPE_IPV4) {\r
+               len = 10;              /* 4 hdr + 4 addr + 2 trailer */\r
+               command[3] = 1; /* IPv4 */\r
+               sk_addrcopy(p->remote_addr, command+4);\r
+           } else if (type == ADDRTYPE_IPV6) {\r
+               len = 22;              /* 4 hdr + 16 addr + 2 trailer */\r
+               command[3] = 4; /* IPv6 */\r
+               sk_addrcopy(p->remote_addr, command+4);\r
+           } else {\r
+               assert(type == ADDRTYPE_NAME);\r
+               command[3] = 3;\r
+               sk_getaddr(p->remote_addr, command+5, 256);\r
+               command[4] = strlen(command+5);\r
+               len = 7 + command[4];  /* 4 hdr, 1 len, N addr, 2 trailer */\r
+           }\r
+\r
+           command[0] = 5; /* version 5 */\r
+           command[1] = 1; /* CONNECT command */\r
+           command[2] = 0x00;\r
+\r
+           /* port */\r
+           command[len-2] = (char) (p->remote_port >> 8) & 0xff;\r
+           command[len-1] = (char) p->remote_port & 0xff;\r
+\r
+           sk_write(p->sub_socket, command, len);\r
+\r
+           p->state = 3;\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 3) {\r
+\r
+           /* reply format:\r
+            *  version number (1 bytes) = 5\r
+            *  reply code (1 byte)\r
+            *    0 = succeeded\r
+            *    1 = general SOCKS server failure\r
+            *    2 = connection not allowed by ruleset\r
+            *    3 = network unreachable\r
+            *    4 = host unreachable\r
+            *    5 = connection refused\r
+            *    6 = TTL expired\r
+            *    7 = command not supported\r
+            *    8 = address type not supported\r
+            * reserved (1 byte) = x00\r
+            * address type (1 byte)\r
+            *    1 = IPv4\r
+            *    3 = domainname (first byte has length, no terminating null)\r
+            *    4 = IPv6\r
+            * server bound address (variable)\r
+            * server bound port (2 bytes) [network order]\r
+            */\r
+           char data[5];\r
+           int len;\r
+\r
+           /* First 5 bytes of packet are enough to tell its length. */ \r
+           if (bufchain_size(&p->pending_input_data) < 5)\r
+               return 1;              /* not got anything yet */\r
+\r
+           /* get the response */\r
+           bufchain_fetch(&p->pending_input_data, data, 5);\r
+\r
+           if (data[0] != 5) {\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+\r
+           if (data[1] != 0) {\r
+               char buf[256];\r
+\r
+               strcpy(buf, "Proxy error: ");\r
+\r
+               switch (data[1]) {\r
+                 case 1: strcat(buf, "General SOCKS server failure"); break;\r
+                 case 2: strcat(buf, "Connection not allowed by ruleset"); break;\r
+                 case 3: strcat(buf, "Network unreachable"); break;\r
+                 case 4: strcat(buf, "Host unreachable"); break;\r
+                 case 5: strcat(buf, "Connection refused"); break;\r
+                 case 6: strcat(buf, "TTL expired"); break;\r
+                 case 7: strcat(buf, "Command not supported"); break;\r
+                 case 8: strcat(buf, "Address type not supported"); break;\r
+                 default: sprintf(buf+strlen(buf),\r
+                                  "Unrecognised SOCKS error code %d",\r
+                                  data[1]);\r
+                   break;\r
+               }\r
+               plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
+\r
+               return 1;\r
+           }\r
+\r
+           /*\r
+            * Eat the rest of the reply packet.\r
+            */\r
+           len = 6;                   /* first 4 bytes, last 2 */\r
+           switch (data[3]) {\r
+             case 1: len += 4; break; /* IPv4 address */\r
+             case 4: len += 16; break;/* IPv6 address */\r
+             case 3: len += (unsigned char)data[4]; break; /* domain name */\r
+             default:\r
+               plug_closing(p->plug, "Proxy error: SOCKS proxy returned "\r
+                            "unrecognised address format",\r
+                            PROXY_ERROR_GENERAL, 0);\r
+               return 1;\r
+           }\r
+           if (bufchain_size(&p->pending_input_data) < len)\r
+               return 1;              /* not got whole reply yet */\r
+           bufchain_consume(&p->pending_input_data, len);\r
+\r
+           /* we're done */\r
+           proxy_activate(p);\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 4) {\r
+           /* TODO: Handle GSSAPI authentication */\r
+           plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication",\r
+                        PROXY_ERROR_GENERAL, 0);\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 5) {\r
+           if (p->cfg.proxy_username[0] || p->cfg.proxy_password[0]) {\r
+               char userpwbuf[514];\r
+               int ulen, plen;\r
+               ulen = strlen(p->cfg.proxy_username);\r
+               if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1;\r
+               plen = strlen(p->cfg.proxy_password);\r
+               if (plen > 255) plen = 255; if (plen < 1) plen = 1;\r
+               userpwbuf[0] = 1;      /* version number of subnegotiation */\r
+               userpwbuf[1] = ulen;\r
+               memcpy(userpwbuf+2, p->cfg.proxy_username, ulen);\r
+               userpwbuf[ulen+2] = plen;\r
+               memcpy(userpwbuf+ulen+3, p->cfg.proxy_password, plen);\r
+               sk_write(p->sub_socket, userpwbuf, ulen + plen + 3);\r
+               p->state = 7;\r
+           } else \r
+               plug_closing(p->plug, "Proxy error: Server chose "\r
+                            "username/password authentication but we "\r
+                            "didn't offer it!",\r
+                        PROXY_ERROR_GENERAL, 0);\r
+           return 1;\r
+       }\r
+\r
+       if (p->state == 6) {\r
+           int ret;\r
+           ret = proxy_socks5_selectchap(p);\r
+           if (ret) return ret;\r
+       }\r
+\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
+\r
+/* ----------------------------------------------------------------------\r
+ * `Telnet' proxy type.\r
+ *\r
+ * (This is for ad-hoc proxies where you connect to the proxy's\r
+ * telnet port and send a command such as `connect host port'. The\r
+ * command is configurable, since this proxy type is typically not\r
+ * standardised or at all well-defined.)\r
+ */\r
+\r
+char *format_telnet_command(SockAddr addr, int port, const Config *cfg)\r
+{\r
+    char *ret = NULL;\r
+    int retlen = 0, retsize = 0;\r
+    int so = 0, eo = 0;\r
+#define ENSURE(n) do { \\r
+    if (retsize < retlen + n) { \\r
+       retsize = retlen + n + 512; \\r
+       ret = sresize(ret, retsize, char); \\r
+    } \\r
+} while (0)\r
+\r
+    /* we need to escape \\, \%, \r, \n, \t, \x??, \0???, \r
+     * %%, %host, %port, %user, and %pass\r
+     */\r
+\r
+    while (cfg->proxy_telnet_command[eo] != 0) {\r
+\r
+       /* scan forward until we hit end-of-line,\r
+        * or an escape character (\ or %) */\r
+       while (cfg->proxy_telnet_command[eo] != 0 &&\r
+              cfg->proxy_telnet_command[eo] != '%' &&\r
+              cfg->proxy_telnet_command[eo] != '\\') eo++;\r
+\r
+       /* if we hit eol, break out of our escaping loop */\r
+       if (cfg->proxy_telnet_command[eo] == 0) break;\r
+\r
+       /* if there was any unescaped text before the escape\r
+        * character, send that now */\r
+       if (eo != so) {\r
+           ENSURE(eo - so);\r
+           memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);\r
+           retlen += eo - so;\r
+       }\r
+\r
+       so = eo++;\r
+\r
+       /* if the escape character was the last character of\r
+        * the line, we'll just stop and send it. */\r
+       if (cfg->proxy_telnet_command[eo] == 0) break;\r
+\r
+       if (cfg->proxy_telnet_command[so] == '\\') {\r
+\r
+           /* we recognize \\, \%, \r, \n, \t, \x??.\r
+            * anything else, we just send unescaped (including the \).\r
+            */\r
+\r
+           switch (cfg->proxy_telnet_command[eo]) {\r
+\r
+             case '\\':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\\';\r
+               eo++;\r
+               break;\r
+\r
+             case '%':\r
+               ENSURE(1);\r
+               ret[retlen++] = '%';\r
+               eo++;\r
+               break;\r
+\r
+             case 'r':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\r';\r
+               eo++;\r
+               break;\r
+\r
+             case 'n':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\n';\r
+               eo++;\r
+               break;\r
+\r
+             case 't':\r
+               ENSURE(1);\r
+               ret[retlen++] = '\t';\r
+               eo++;\r
+               break;\r
+\r
+             case 'x':\r
+             case 'X':\r
+               {\r
+                   /* escaped hexadecimal value (ie. \xff) */\r
+                   unsigned char v = 0;\r
+                   int i = 0;\r
+\r
+                   for (;;) {\r
+                       eo++;\r
+                       if (cfg->proxy_telnet_command[eo] >= '0' &&\r
+                           cfg->proxy_telnet_command[eo] <= '9')\r
+                           v += cfg->proxy_telnet_command[eo] - '0';\r
+                       else if (cfg->proxy_telnet_command[eo] >= 'a' &&\r
+                                cfg->proxy_telnet_command[eo] <= 'f')\r
+                           v += cfg->proxy_telnet_command[eo] - 'a' + 10;\r
+                       else if (cfg->proxy_telnet_command[eo] >= 'A' &&\r
+                                cfg->proxy_telnet_command[eo] <= 'F')\r
+                           v += cfg->proxy_telnet_command[eo] - 'A' + 10;\r
+                       else {\r
+                           /* non hex character, so we abort and just\r
+                            * send the whole thing unescaped (including \x)\r
+                            */\r
+                           ENSURE(1);\r
+                           ret[retlen++] = '\\';\r
+                           eo = so + 1;\r
+                           break;\r
+                       }\r
+\r
+                       /* we only extract two hex characters */\r
+                       if (i == 1) {\r
+                           ENSURE(1);\r
+                           ret[retlen++] = v;\r
+                           eo++;\r
+                           break;\r
+                       }\r
+\r
+                       i++;\r
+                       v <<= 4;\r
+                   }\r
+               }\r
+               break;\r
+\r
+             default:\r
+               ENSURE(2);\r
+               memcpy(ret+retlen, cfg->proxy_telnet_command + so, 2);\r
+               retlen += 2;\r
+               eo++;\r
+               break;\r
+           }\r
+       } else {\r
+\r
+           /* % escape. we recognize %%, %host, %port, %user, %pass.\r
+            * %proxyhost, %proxyport. Anything else we just send\r
+            * unescaped (including the %).\r
+            */\r
+\r
+           if (cfg->proxy_telnet_command[eo] == '%') {\r
+               ENSURE(1);\r
+               ret[retlen++] = '%';\r
+               eo++;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "host", 4) == 0) {\r
+               char dest[512];\r
+               int destlen;\r
+               sk_getaddr(addr, dest, lenof(dest));\r
+               destlen = strlen(dest);\r
+               ENSURE(destlen);\r
+               memcpy(ret+retlen, dest, destlen);\r
+               retlen += destlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "port", 4) == 0) {\r
+               char portstr[8], portlen;\r
+               portlen = sprintf(portstr, "%i", port);\r
+               ENSURE(portlen);\r
+               memcpy(ret + retlen, portstr, portlen);\r
+               retlen += portlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "user", 4) == 0) {\r
+               int userlen = strlen(cfg->proxy_username);\r
+               ENSURE(userlen);\r
+               memcpy(ret+retlen, cfg->proxy_username, userlen);\r
+               retlen += userlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "pass", 4) == 0) {\r
+               int passlen = strlen(cfg->proxy_password);\r
+               ENSURE(passlen);\r
+               memcpy(ret+retlen, cfg->proxy_password, passlen);\r
+               retlen += passlen;\r
+               eo += 4;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "proxyhost", 9) == 0) {\r
+               int phlen = strlen(cfg->proxy_host);\r
+               ENSURE(phlen);\r
+               memcpy(ret+retlen, cfg->proxy_host, phlen);\r
+               retlen += phlen;\r
+               eo += 9;\r
+           }\r
+           else if (strnicmp(cfg->proxy_telnet_command + eo,\r
+                             "proxyport", 9) == 0) {\r
+                char pport[50];\r
+               int pplen;\r
+                sprintf(pport, "%d", cfg->proxy_port);\r
+                pplen = strlen(pport);\r
+               ENSURE(pplen);\r
+               memcpy(ret+retlen, pport, pplen);\r
+               retlen += pplen;\r
+               eo += 9;\r
+           }\r
+           else {\r
+               /* we don't escape this, so send the % now, and\r
+                * don't advance eo, so that we'll consider the\r
+                * text immediately following the % as unescaped.\r
+                */\r
+               ENSURE(1);\r
+               ret[retlen++] = '%';\r
+           }\r
+       }\r
+\r
+       /* resume scanning for additional escapes after this one. */\r
+       so = eo;\r
+    }\r
+\r
+    /* if there is any unescaped text at the end of the line, send it */\r
+    if (eo != so) {\r
+       ENSURE(eo - so);\r
+       memcpy(ret + retlen, cfg->proxy_telnet_command + so, eo - so);\r
+       retlen += eo - so;\r
+    }\r
+\r
+    ENSURE(1);\r
+    ret[retlen] = '\0';\r
+    return ret;\r
+\r
+#undef ENSURE\r
+}\r
+\r
+int proxy_telnet_negotiate (Proxy_Socket p, int change)\r
+{\r
+    if (p->state == PROXY_CHANGE_NEW) {\r
+       char *formatted_cmd;\r
+\r
+       formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,\r
+                                             &p->cfg);\r
+\r
+       sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));\r
+       sfree(formatted_cmd);\r
+\r
+       p->state = 1;\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_CLOSING) {\r
+       /* if our proxy negotiation process involves closing and opening\r
+        * new sockets, then we would want to intercept this closing\r
+        * callback when we were expecting it. if we aren't anticipating\r
+        * a socket close, then some error must have occurred. we'll\r
+        * just pass those errors up to the backend.\r
+        */\r
+       return plug_closing(p->plug, p->closing_error_msg,\r
+                           p->closing_error_code,\r
+                           p->closing_calling_back);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_SENT) {\r
+       /* some (or all) of what we wrote to the proxy was sent.\r
+        * we don't do anything new, however, until we receive the\r
+        * proxy's response. we might want to set a timer so we can\r
+        * timeout the proxy negotiation after a while...\r
+        */\r
+       return 0;\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_ACCEPTING) {\r
+       /* we should _never_ see this, as we are using our socket to\r
+        * connect to a proxy, not accepting inbound connections.\r
+        * what should we do? close the socket with an appropriate\r
+        * error message?\r
+        */\r
+       return plug_accepting(p->plug, p->accepting_sock);\r
+    }\r
+\r
+    if (change == PROXY_CHANGE_RECEIVE) {\r
+       /* we have received data from the underlying socket, which\r
+        * we'll need to parse, process, and respond to appropriately.\r
+        */\r
+\r
+       /* we're done */\r
+       proxy_activate(p);\r
+       /* proxy activate will have dealt with\r
+        * whatever is left of the buffer */\r
+       return 1;\r
+    }\r
+\r
+    plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
+                PROXY_ERROR_UNEXPECTED, 0);\r
+    return 1;\r
+}\r
diff --git a/src/TortoisePlink/PROXY.H b/src/TortoisePlink/PROXY.H
new file mode 100644 (file)
index 0000000..683b260
--- /dev/null
@@ -0,0 +1,123 @@
+/*\r
+ * Network proxy abstraction in PuTTY\r
+ *\r
+ * A proxy layer, if necessary, wedges itself between the\r
+ * network code and the higher level backend.\r
+ *\r
+ * Supported proxies: HTTP CONNECT, generic telnet, SOCKS 4 & 5\r
+ */\r
+\r
+#ifndef PUTTY_PROXY_H\r
+#define PUTTY_PROXY_H\r
+\r
+#define PROXY_ERROR_GENERAL 8000\r
+#define PROXY_ERROR_UNEXPECTED 8001\r
+\r
+typedef struct Socket_proxy_tag * Proxy_Socket;\r
+\r
+struct Socket_proxy_tag {\r
+    const struct socket_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+\r
+    char * error;\r
+\r
+    Socket sub_socket;\r
+    Plug plug;\r
+    SockAddr remote_addr;\r
+    int remote_port;\r
+\r
+    bufchain pending_output_data;\r
+    bufchain pending_oob_output_data;\r
+    int pending_flush;\r
+    bufchain pending_input_data;\r
+\r
+#define PROXY_STATE_NEW    -1\r
+#define PROXY_STATE_ACTIVE  0\r
+\r
+    int state; /* proxy states greater than 0 are implementation\r
+               * dependent, but represent various stages/states\r
+               * of the initialization/setup/negotiation with the\r
+               * proxy server.\r
+               */\r
+    int freeze; /* should we freeze the underlying socket when\r
+                * we are done with the proxy negotiation? this\r
+                * simply caches the value of sk_set_frozen calls.\r
+                */\r
+\r
+#define PROXY_CHANGE_NEW      -1\r
+#define PROXY_CHANGE_CLOSING   0\r
+#define PROXY_CHANGE_SENT      1\r
+#define PROXY_CHANGE_RECEIVE   2\r
+#define PROXY_CHANGE_ACCEPTING 3\r
+\r
+    /* something has changed (a call from the sub socket\r
+     * layer into our Proxy Plug layer, or we were just\r
+     * created, etc), so the proxy layer needs to handle\r
+     * this change (the type of which is the second argument)\r
+     * and further the proxy negotiation process.\r
+     */\r
+\r
+    int (*negotiate) (Proxy_Socket /* this */, int /* change type */);\r
+\r
+    /* current arguments of plug handlers\r
+     * (for use by proxy's negotiate function)\r
+     */\r
+\r
+    /* closing */\r
+    const char *closing_error_msg;\r
+    int closing_error_code;\r
+    int closing_calling_back;\r
+\r
+    /* receive */\r
+    int receive_urgent;\r
+    char *receive_data;\r
+    int receive_len;\r
+\r
+    /* sent */\r
+    int sent_bufsize;\r
+\r
+    /* accepting */\r
+    OSSocket accepting_sock;\r
+\r
+    /* configuration, used to look up proxy settings */\r
+    Config cfg;\r
+\r
+    /* CHAP transient data */\r
+    int chap_num_attributes;\r
+    int chap_num_attributes_processed;\r
+    int chap_current_attribute;\r
+    int chap_current_datalen;\r
+};\r
+\r
+typedef struct Plug_proxy_tag * Proxy_Plug;\r
+\r
+struct Plug_proxy_tag {\r
+    const struct plug_function_table *fn;\r
+    /* the above variable absolutely *must* be the first in this structure */\r
+\r
+    Proxy_Socket proxy_socket;\r
+\r
+};\r
+\r
+extern void proxy_activate (Proxy_Socket);\r
+\r
+extern int proxy_http_negotiate (Proxy_Socket, int);\r
+extern int proxy_telnet_negotiate (Proxy_Socket, int);\r
+extern int proxy_socks4_negotiate (Proxy_Socket, int);\r
+extern int proxy_socks5_negotiate (Proxy_Socket, int);\r
+\r
+/*\r
+ * This may be reused by local-command proxies on individual\r
+ * platforms.\r
+ */\r
+char *format_telnet_command(SockAddr addr, int port, const Config *cfg);\r
+\r
+/*\r
+ * These are implemented in cproxy.c or nocproxy.c, depending on\r
+ * whether encrypted proxy authentication is available.\r
+ */\r
+extern void proxy_socks5_offerencryptedauth(char *command, int *len);\r
+extern int proxy_socks5_handlechap (Proxy_Socket p);\r
+extern int proxy_socks5_selectchap(Proxy_Socket p);\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/PUTTY.H b/src/TortoisePlink/PUTTY.H
new file mode 100644 (file)
index 0000000..f92d6b9
--- /dev/null
@@ -0,0 +1,1214 @@
+#ifndef PUTTY_PUTTY_H\r
+#define PUTTY_PUTTY_H\r
+\r
+#include <stddef.h>                   /* for wchar_t */\r
+\r
+/*\r
+ * Global variables. Most modules declare these `extern', but\r
+ * window.c will do `#define PUTTY_DO_GLOBALS' before including this\r
+ * module, and so will get them properly defined.\r
+ */\r
+#ifndef GLOBAL\r
+#ifdef PUTTY_DO_GLOBALS\r
+#define GLOBAL\r
+#else\r
+#define GLOBAL extern\r
+#endif\r
+#endif\r
+\r
+#ifndef DONE_TYPEDEFS\r
+#define DONE_TYPEDEFS\r
+typedef struct config_tag Config;\r
+typedef struct backend_tag Backend;\r
+typedef struct terminal_tag Terminal;\r
+#endif\r
+\r
+#include "puttyps.h"\r
+#include "network.h"\r
+#include "misc.h"\r
+\r
+/*\r
+ * Fingerprints of the PGP master keys that can be used to establish a trust\r
+ * path between an executable and other files.\r
+ */\r
+#define PGP_RSA_MASTER_KEY_FP \\r
+    "8F 15 97 DA 25 30 AB 0D  88 D1 92 54 11 CF 0C 4C"\r
+#define PGP_DSA_MASTER_KEY_FP \\r
+    "313C 3E76 4B74 C2C5 F2AE  83A8 4F5E 6DF5 6A93 B34E"\r
+\r
+/* Three attribute types: \r
+ * The ATTRs (normal attributes) are stored with the characters in\r
+ * the main display arrays\r
+ *\r
+ * The TATTRs (temporary attributes) are generated on the fly, they\r
+ * can overlap with characters but not with normal attributes.\r
+ *\r
+ * The LATTRs (line attributes) are an entirely disjoint space of\r
+ * flags.\r
+ * \r
+ * The DATTRs (display attributes) are internal to terminal.c (but\r
+ * defined here because their values have to match the others\r
+ * here); they reuse the TATTR_* space but are always masked off\r
+ * before sending to the front end.\r
+ *\r
+ * ATTR_INVALID is an illegal colour combination.\r
+ */\r
+\r
+#define TATTR_ACTCURS      0x40000000UL      /* active cursor (block) */\r
+#define TATTR_PASCURS      0x20000000UL      /* passive cursor (box) */\r
+#define TATTR_RIGHTCURS            0x10000000UL      /* cursor-on-RHS */\r
+#define TATTR_COMBINING            0x80000000UL      /* combining characters */\r
+\r
+#define DATTR_STARTRUN      0x80000000UL   /* start of redraw run */\r
+\r
+#define TDATTR_MASK         0xF0000000UL\r
+#define TATTR_MASK (TDATTR_MASK)\r
+#define DATTR_MASK (TDATTR_MASK)\r
+\r
+#define LATTR_NORM   0x00000000UL\r
+#define LATTR_WIDE   0x00000001UL\r
+#define LATTR_TOP    0x00000002UL\r
+#define LATTR_BOT    0x00000003UL\r
+#define LATTR_MODE   0x00000003UL\r
+#define LATTR_WRAPPED 0x00000010UL     /* this line wraps to next */\r
+#define LATTR_WRAPPED2 0x00000020UL    /* with WRAPPED: CJK wide character\r
+                                         wrapped to next line, so last\r
+                                         single-width cell is empty */\r
+\r
+#define ATTR_INVALID 0x03FFFFU\r
+\r
+/* Like Linux use the F000 page for direct to font. */\r
+#define CSET_OEMCP   0x0000F000UL      /* OEM Codepage DTF */\r
+#define CSET_ACP     0x0000F100UL      /* Ansi Codepage DTF */\r
+\r
+/* These are internal use overlapping with the UTF-16 surrogates */\r
+#define CSET_ASCII   0x0000D800UL      /* normal ASCII charset ESC ( B */\r
+#define CSET_LINEDRW 0x0000D900UL      /* line drawing charset ESC ( 0 */\r
+#define CSET_SCOACS  0x0000DA00UL      /* SCO Alternate charset */\r
+#define CSET_GBCHR   0x0000DB00UL      /* UK variant   charset ESC ( A */\r
+#define CSET_MASK    0xFFFFFF00UL      /* Character set mask */\r
+\r
+#define DIRECT_CHAR(c) ((c&0xFFFFFC00)==0xD800)\r
+#define DIRECT_FONT(c) ((c&0xFFFFFE00)==0xF000)\r
+\r
+#define UCSERR      (CSET_LINEDRW|'a') /* UCS Format error character. */\r
+/*\r
+ * UCSWIDE is a special value used in the terminal data to signify\r
+ * the character cell containing the right-hand half of a CJK wide\r
+ * character. We use 0xDFFF because it's part of the surrogate\r
+ * range and hence won't be used for anything else (it's impossible\r
+ * to input it via UTF-8 because our UTF-8 decoder correctly\r
+ * rejects surrogates).\r
+ */\r
+#define UCSWIDE             0xDFFF\r
+\r
+#define ATTR_NARROW  0x800000U\r
+#define ATTR_WIDE    0x400000U\r
+#define ATTR_BOLD    0x040000U\r
+#define ATTR_UNDER   0x080000U\r
+#define ATTR_REVERSE 0x100000U\r
+#define ATTR_BLINK   0x200000U\r
+#define ATTR_FGMASK  0x0001FFU\r
+#define ATTR_BGMASK  0x03FE00U\r
+#define ATTR_COLOURS 0x03FFFFU\r
+#define ATTR_FGSHIFT 0\r
+#define ATTR_BGSHIFT 9\r
+\r
+/*\r
+ * The definitive list of colour numbers stored in terminal\r
+ * attribute words is kept here. It is:\r
+ * \r
+ *  - 0-7 are ANSI colours (KRGYBMCW).\r
+ *  - 8-15 are the bold versions of those colours.\r
+ *  - 16-255 are the remains of the xterm 256-colour mode (a\r
+ *    216-colour cube with R at most significant and B at least,\r
+ *    followed by a uniform series of grey shades running between\r
+ *    black and white but not including either on grounds of\r
+ *    redundancy).\r
+ *  - 256 is default foreground\r
+ *  - 257 is default bold foreground\r
+ *  - 258 is default background\r
+ *  - 259 is default bold background\r
+ *  - 260 is cursor foreground\r
+ *  - 261 is cursor background\r
+ */\r
+\r
+#define ATTR_DEFFG   (256 << ATTR_FGSHIFT)\r
+#define ATTR_DEFBG   (258 << ATTR_BGSHIFT)\r
+#define ATTR_DEFAULT (ATTR_DEFFG | ATTR_DEFBG)\r
+\r
+struct sesslist {\r
+    int nsessions;\r
+    char **sessions;\r
+    char *buffer;                     /* so memory can be freed later */\r
+};\r
+\r
+struct unicode_data {\r
+    char **uni_tbl;\r
+    int dbcs_screenfont;\r
+    int font_codepage;\r
+    int line_codepage;\r
+    wchar_t unitab_scoacs[256];\r
+    wchar_t unitab_line[256];\r
+    wchar_t unitab_font[256];\r
+    wchar_t unitab_xterm[256];\r
+    wchar_t unitab_oemcp[256];\r
+    unsigned char unitab_ctrl[256];\r
+};\r
+\r
+#define LGXF_OVR  1                   /* existing logfile overwrite */\r
+#define LGXF_APN  0                   /* existing logfile append */\r
+#define LGXF_ASK -1                   /* existing logfile ask */\r
+#define LGTYP_NONE  0                 /* logmode: no logging */\r
+#define LGTYP_ASCII 1                 /* logmode: pure ascii */\r
+#define LGTYP_DEBUG 2                 /* logmode: all chars of traffic */\r
+#define LGTYP_PACKETS 3                       /* logmode: SSH data packets */\r
+#define LGTYP_SSHRAW 4                /* logmode: SSH raw data */\r
+\r
+typedef enum {\r
+    /* Actual special commands. Originally Telnet, but some codes have\r
+     * been re-used for similar specials in other protocols. */\r
+    TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,\r
+    TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING,\r
+    TS_EOL,\r
+    /* Special command for SSH. */\r
+    TS_REKEY,\r
+    /* POSIX-style signals. (not Telnet) */\r
+    TS_SIGABRT, TS_SIGALRM, TS_SIGFPE,  TS_SIGHUP,  TS_SIGILL,\r
+    TS_SIGINT,  TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV,\r
+    TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2,\r
+    /* Pseudo-specials used for constructing the specials menu. */\r
+    TS_SEP,        /* Separator */\r
+    TS_SUBMENU,            /* Start a new submenu with specified name */\r
+    TS_EXITMENU            /* Exit current submenu or end of specials */\r
+} Telnet_Special;\r
+\r
+struct telnet_special {\r
+    const char *name;\r
+    int code;\r
+};\r
+\r
+typedef enum {\r
+    MBT_NOTHING,\r
+    MBT_LEFT, MBT_MIDDLE, MBT_RIGHT,   /* `raw' button designations */\r
+    MBT_SELECT, MBT_EXTEND, MBT_PASTE, /* `cooked' button designations */\r
+    MBT_WHEEL_UP, MBT_WHEEL_DOWN       /* mouse wheel */\r
+} Mouse_Button;\r
+\r
+typedef enum {\r
+    MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE\r
+} Mouse_Action;\r
+\r
+/* Keyboard modifiers -- keys the user is actually holding down */\r
+\r
+#define PKM_SHIFT      0x01\r
+#define PKM_CONTROL    0x02\r
+#define PKM_META       0x04\r
+#define PKM_ALT                0x08\r
+\r
+/* Keyboard flags that aren't really modifiers */\r
+#define PKF_CAPSLOCK   0x10\r
+#define PKF_NUMLOCK    0x20\r
+#define PKF_REPEAT     0x40\r
+\r
+/* Stand-alone keysyms for function keys */\r
+\r
+typedef enum {\r
+    PK_NULL,           /* No symbol for this key */\r
+    /* Main keypad keys */\r
+    PK_ESCAPE, PK_TAB, PK_BACKSPACE, PK_RETURN, PK_COMPOSE,\r
+    /* Editing keys */\r
+    PK_HOME, PK_INSERT, PK_DELETE, PK_END, PK_PAGEUP, PK_PAGEDOWN,\r
+    /* Cursor keys */\r
+    PK_UP, PK_DOWN, PK_RIGHT, PK_LEFT, PK_REST,\r
+    /* Numeric keypad */                       /* Real one looks like: */\r
+    PK_PF1, PK_PF2, PK_PF3, PK_PF4,            /* PF1 PF2 PF3 PF4 */\r
+    PK_KPCOMMA, PK_KPMINUS, PK_KPDECIMAL,      /*  7   8   9   -  */\r
+    PK_KP0, PK_KP1, PK_KP2, PK_KP3, PK_KP4,    /*  4   5   6   ,  */\r
+    PK_KP5, PK_KP6, PK_KP7, PK_KP8, PK_KP9,    /*  1   2   3  en- */\r
+    PK_KPBIGPLUS, PK_KPENTER,                  /*    0     .  ter */\r
+    /* Top row */\r
+    PK_F1,  PK_F2,  PK_F3,  PK_F4,  PK_F5,\r
+    PK_F6,  PK_F7,  PK_F8,  PK_F9,  PK_F10,\r
+    PK_F11, PK_F12, PK_F13, PK_F14, PK_F15,\r
+    PK_F16, PK_F17, PK_F18, PK_F19, PK_F20,\r
+    PK_PAUSE\r
+} Key_Sym;\r
+\r
+#define PK_ISEDITING(k)        ((k) >= PK_HOME && (k) <= PK_PAGEDOWN)\r
+#define PK_ISCURSOR(k) ((k) >= PK_UP && (k) <= PK_REST)\r
+#define PK_ISKEYPAD(k) ((k) >= PK_PF1 && (k) <= PK_KPENTER)\r
+#define PK_ISFKEY(k)   ((k) >= PK_F1 && (k) <= PK_F20)\r
+\r
+enum {\r
+    VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN, VT_UNICODE\r
+};\r
+\r
+enum {\r
+    /*\r
+     * SSH-2 key exchange algorithms\r
+     */\r
+    KEX_WARN,\r
+    KEX_DHGROUP1,\r
+    KEX_DHGROUP14,\r
+    KEX_DHGEX,\r
+    KEX_MAX\r
+};\r
+\r
+enum {\r
+    /*\r
+     * SSH ciphers (both SSH-1 and SSH-2)\r
+     */\r
+    CIPHER_WARN,                      /* pseudo 'cipher' */\r
+    CIPHER_3DES,\r
+    CIPHER_BLOWFISH,\r
+    CIPHER_AES,                               /* (SSH-2 only) */\r
+    CIPHER_DES,\r
+    CIPHER_ARCFOUR,\r
+    CIPHER_MAX                        /* no. ciphers (inc warn) */\r
+};\r
+\r
+enum {\r
+    /*\r
+     * Several different bits of the PuTTY configuration seem to be\r
+     * three-way settings whose values are `always yes', `always\r
+     * no', and `decide by some more complex automated means'. This\r
+     * is true of line discipline options (local echo and line\r
+     * editing), proxy DNS, Close On Exit, and SSH server bug\r
+     * workarounds. Accordingly I supply a single enum here to deal\r
+     * with them all.\r
+     */\r
+    FORCE_ON, FORCE_OFF, AUTO\r
+};\r
+\r
+enum {\r
+    /*\r
+     * Proxy types.\r
+     */\r
+    PROXY_NONE, PROXY_SOCKS4, PROXY_SOCKS5,\r
+    PROXY_HTTP, PROXY_TELNET, PROXY_CMD\r
+};\r
+\r
+enum {\r
+    /*\r
+     * Line discipline options which the backend might try to control.\r
+     */\r
+    LD_EDIT,                          /* local line editing */\r
+    LD_ECHO                           /* local echo */\r
+};\r
+\r
+enum {\r
+    /* Actions on remote window title query */\r
+    TITLE_NONE, TITLE_EMPTY, TITLE_REAL\r
+};\r
+\r
+enum {\r
+    /* Protocol back ends. (cfg.protocol) */\r
+    PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH,\r
+    /* PROT_SERIAL is supported on a subset of platforms, but it doesn't\r
+     * hurt to define it globally. */\r
+    PROT_SERIAL\r
+};\r
+\r
+enum {\r
+    /* Bell settings (cfg.beep) */\r
+    BELL_DISABLED, BELL_DEFAULT, BELL_VISUAL, BELL_WAVEFILE, BELL_PCSPEAKER\r
+};\r
+\r
+enum {\r
+    /* Taskbar flashing indication on bell (cfg.beep_ind) */\r
+    B_IND_DISABLED, B_IND_FLASH, B_IND_STEADY\r
+};\r
+\r
+enum {\r
+    /* Resize actions (cfg.resize_action) */\r
+    RESIZE_TERM, RESIZE_DISABLED, RESIZE_FONT, RESIZE_EITHER\r
+};\r
+\r
+enum {\r
+    /* Function key types (cfg.funky_type) */\r
+    FUNKY_TILDE,\r
+    FUNKY_LINUX,\r
+    FUNKY_XTERM,\r
+    FUNKY_VT400,\r
+    FUNKY_VT100P,\r
+    FUNKY_SCO\r
+};\r
+\r
+enum {\r
+    FQ_DEFAULT, FQ_ANTIALIASED, FQ_NONANTIALIASED, FQ_CLEARTYPE\r
+};\r
+\r
+enum {\r
+    SER_PAR_NONE, SER_PAR_ODD, SER_PAR_EVEN, SER_PAR_MARK, SER_PAR_SPACE\r
+};\r
+\r
+enum {\r
+    SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR\r
+};\r
+\r
+extern const char *const ttymodes[];\r
+\r
+enum {\r
+    /*\r
+     * Network address types. Used for specifying choice of IPv4/v6\r
+     * in config; also used in proxy.c to indicate whether a given\r
+     * host name has already been resolved or will be resolved at\r
+     * the proxy end.\r
+     */\r
+    ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_NAME\r
+};\r
+\r
+struct backend_tag {\r
+    const char *(*init) (void *frontend_handle, void **backend_handle,\r
+                        Config *cfg,\r
+                        char *host, int port, char **realhost, int nodelay,\r
+                        int keepalive);\r
+    void (*free) (void *handle);\r
+    /* back->reconfig() passes in a replacement configuration. */\r
+    void (*reconfig) (void *handle, Config *cfg);\r
+    /* back->send() returns the current amount of buffered data. */\r
+    int (*send) (void *handle, char *buf, int len);\r
+    /* back->sendbuffer() does the same thing but without attempting a send */\r
+    int (*sendbuffer) (void *handle);\r
+    void (*size) (void *handle, int width, int height);\r
+    void (*special) (void *handle, Telnet_Special code);\r
+    const struct telnet_special *(*get_specials) (void *handle);\r
+    int (*connected) (void *handle);\r
+    int (*exitcode) (void *handle);\r
+    /* If back->sendok() returns FALSE, data sent to it from the frontend\r
+     * may be lost. */\r
+    int (*sendok) (void *handle);\r
+    int (*ldisc) (void *handle, int);\r
+    void (*provide_ldisc) (void *handle, void *ldisc);\r
+    void (*provide_logctx) (void *handle, void *logctx);\r
+    /*\r
+     * back->unthrottle() tells the back end that the front end\r
+     * buffer is clearing.\r
+     */\r
+    void (*unthrottle) (void *handle, int);\r
+    int (*cfg_info) (void *handle);\r
+    int default_port;\r
+};\r
+\r
+extern struct backend_list {\r
+    int protocol;\r
+    char *name;\r
+    Backend *backend;\r
+} backends[];\r
+\r
+/*\r
+ * Suggested default protocol provided by the backend link module.\r
+ * The application is free to ignore this.\r
+ */\r
+extern const int be_default_protocol;\r
+\r
+/*\r
+ * Name of this particular application, for use in the config box\r
+ * and other pieces of text.\r
+ */\r
+extern const char *const appname;\r
+\r
+/*\r
+ * IMPORTANT POLICY POINT: everything in this structure which wants\r
+ * to be treated like an integer must be an actual, honest-to-\r
+ * goodness `int'. No enum-typed variables. This is because parts\r
+ * of the code will want to pass around `int *' pointers to them\r
+ * and we can't run the risk of porting to some system on which the\r
+ * enum comes out as a different size from int.\r
+ */\r
+struct config_tag {\r
+    /* Basic options */\r
+    char host[512];\r
+    int port;\r
+    int protocol;\r
+    int addressfamily;\r
+    int close_on_exit;\r
+    int warn_on_close;\r
+    int ping_interval;                /* in seconds */\r
+    int tcp_nodelay;\r
+    int tcp_keepalives;\r
+    /* Proxy options */\r
+    char proxy_exclude_list[512];\r
+    int proxy_dns;\r
+    int even_proxy_localhost;\r
+    int proxy_type;\r
+    char proxy_host[512];\r
+    int proxy_port;\r
+    char proxy_username[128];\r
+    char proxy_password[128];\r
+    char proxy_telnet_command[512];\r
+    /* SSH options */\r
+    char remote_cmd[512];\r
+    char *remote_cmd_ptr;             /* might point to a larger command\r
+                                       * but never for loading/saving */\r
+    char *remote_cmd_ptr2;            /* might point to a larger command\r
+                                       * but never for loading/saving */\r
+    int nopty;\r
+    int compression;\r
+    int ssh_kexlist[KEX_MAX];\r
+    int ssh_rekey_time;                       /* in minutes */\r
+    char ssh_rekey_data[16];\r
+    int tryagent;\r
+    int agentfwd;\r
+    int change_username;              /* allow username switching in SSH-2 */\r
+    int ssh_cipherlist[CIPHER_MAX];\r
+    Filename keyfile;\r
+    int sshprot;                      /* use v1 or v2 when both available */\r
+    int ssh2_des_cbc;                 /* "des-cbc" unrecommended SSH-2 cipher */\r
+    int ssh_no_userauth;              /* bypass "ssh-userauth" (SSH-2 only) */\r
+    int try_tis_auth;\r
+    int try_ki_auth;\r
+    int ssh_subsys;                   /* run a subsystem rather than a command */\r
+    int ssh_subsys2;                  /* fallback to go with remote_cmd_ptr2 */\r
+    int ssh_no_shell;                 /* avoid running a shell */\r
+    char ssh_nc_host[512];            /* host to connect to in `nc' mode */\r
+    int ssh_nc_port;                  /* port to connect to in `nc' mode */\r
+    /* Telnet options */\r
+    char termtype[32];\r
+    char termspeed[32];\r
+    char ttymodes[768];                       /* MODE\tVvalue\0MODE\tA\0\0 */\r
+    char environmt[1024];             /* VAR\tvalue\0VAR\tvalue\0\0 */\r
+    char username[100];\r
+    char localusername[100];\r
+    int rfc_environ;\r
+    int passive_telnet;\r
+    /* Serial port options */\r
+    char serline[256];\r
+    int serspeed;\r
+    int serdatabits, serstopbits;\r
+    int serparity;\r
+    int serflow;\r
+    /* Keyboard options */\r
+    int bksp_is_delete;\r
+    int rxvt_homeend;\r
+    int funky_type;\r
+    int no_applic_c;                  /* totally disable app cursor keys */\r
+    int no_applic_k;                  /* totally disable app keypad */\r
+    int no_mouse_rep;                 /* totally disable mouse reporting */\r
+    int no_remote_resize;             /* disable remote resizing */\r
+    int no_alt_screen;                /* disable alternate screen */\r
+    int no_remote_wintitle;           /* disable remote retitling */\r
+    int no_dbackspace;                /* disable destructive backspace */\r
+    int no_remote_charset;            /* disable remote charset config */\r
+    int remote_qtitle_action;         /* remote win title query action */\r
+    int app_cursor;\r
+    int app_keypad;\r
+    int nethack_keypad;\r
+    int telnet_keyboard;\r
+    int telnet_newline;\r
+    int alt_f4;                               /* is it special? */\r
+    int alt_space;                    /* is it special? */\r
+    int alt_only;                     /* is it special? */\r
+    int localecho;\r
+    int localedit;\r
+    int alwaysontop;\r
+    int fullscreenonaltenter;\r
+    int scroll_on_key;\r
+    int scroll_on_disp;\r
+    int erase_to_scrollback;\r
+    int compose_key;\r
+    int ctrlaltkeys;\r
+    char wintitle[256];                       /* initial window title */\r
+    /* Terminal options */\r
+    int savelines;\r
+    int dec_om;\r
+    int wrap_mode;\r
+    int lfhascr;\r
+    int cursor_type;                  /* 0=block 1=underline 2=vertical */\r
+    int blink_cur;\r
+    int beep;\r
+    int beep_ind;\r
+    int bellovl;                      /* bell overload protection active? */\r
+    int bellovl_n;                    /* number of bells to cause overload */\r
+    int bellovl_t;                    /* time interval for overload (seconds) */\r
+    int bellovl_s;                    /* period of silence to re-enable bell (s) */\r
+    Filename bell_wavefile;\r
+    int scrollbar;\r
+    int scrollbar_in_fullscreen;\r
+    int resize_action;\r
+    int bce;\r
+    int blinktext;\r
+    int win_name_always;\r
+    int width, height;\r
+    FontSpec font;\r
+    int font_quality;\r
+    Filename logfilename;\r
+    int logtype;\r
+    int logxfovr;\r
+    int logflush;\r
+    int logomitpass;\r
+    int logomitdata;\r
+    int hide_mouseptr;\r
+    int sunken_edge;\r
+    int window_border;\r
+    char answerback[256];\r
+    char printer[128];\r
+    int arabicshaping;\r
+    int bidi;\r
+    /* Colour options */\r
+    int ansi_colour;\r
+    int xterm_256_colour;\r
+    int system_colour;\r
+    int try_palette;\r
+    int bold_colour;\r
+    unsigned char colours[22][3];\r
+    /* Selection options */\r
+    int mouse_is_xterm;\r
+    int rect_select;\r
+    int rawcnp;\r
+    int rtf_paste;\r
+    int mouse_override;\r
+    short wordness[256];\r
+    /* translations */\r
+    int vtmode;\r
+    char line_codepage[128];\r
+    int cjk_ambig_wide;\r
+    int utf8_override;\r
+    int xlat_capslockcyr;\r
+    /* X11 forwarding */\r
+    int x11_forward;\r
+    char x11_display[128];\r
+    int x11_auth;\r
+    /* port forwarding */\r
+    int lport_acceptall; /* accept conns from hosts other than localhost */\r
+    int rport_acceptall; /* same for remote forwarded ports (SSH-2 only) */\r
+    /*\r
+     * The port forwarding string contains a number of\r
+     * NUL-terminated substrings, terminated in turn by an empty\r
+     * string (i.e. a second NUL immediately after the previous\r
+     * one). Each string can be of one of the following forms:\r
+     * \r
+     *   [LR]localport\thost:port\r
+     *   [LR]localaddr:localport\thost:port\r
+     *   Dlocalport\r
+     *   Dlocaladdr:localport\r
+     */\r
+    char portfwd[1024];\r
+    /* SSH bug compatibility modes */\r
+    int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,\r
+       sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,\r
+       sshbug_pksessid2, sshbug_rekey2;\r
+    /* Options for pterm. Should split out into platform-dependent part. */\r
+    int stamp_utmp;\r
+    int login_shell;\r
+    int scrollbar_on_left;\r
+    int shadowbold;\r
+    FontSpec boldfont;\r
+    FontSpec widefont;\r
+    FontSpec wideboldfont;\r
+    int shadowboldoffset;\r
+};\r
+\r
+/*\r
+ * Some global flags denoting the type of application.\r
+ * \r
+ * FLAG_VERBOSE is set when the user requests verbose details.\r
+ * \r
+ * FLAG_STDERR is set in command-line applications (which have a\r
+ * functioning stderr that it makes sense to write to) and not in\r
+ * GUI applications (which don't).\r
+ * \r
+ * FLAG_INTERACTIVE is set when a full interactive shell session is\r
+ * being run, _either_ because no remote command has been provided\r
+ * _or_ because the application is GUI and can't run non-\r
+ * interactively.\r
+ * \r
+ * These flags describe the type of _application_ - they wouldn't\r
+ * vary between individual sessions - and so it's OK to have this\r
+ * variable be GLOBAL.\r
+ * \r
+ * Note that additional flags may be defined in platform-specific\r
+ * headers. It's probably best if those ones start from 0x1000, to\r
+ * avoid collision.\r
+ */\r
+#define FLAG_VERBOSE     0x0001\r
+#define FLAG_STDERR      0x0002\r
+#define FLAG_INTERACTIVE 0x0004\r
+GLOBAL int flags;\r
+\r
+/*\r
+ * Likewise, these two variables are set up when the application\r
+ * initialises, and inform all default-settings accesses after\r
+ * that.\r
+ */\r
+GLOBAL int default_protocol;\r
+GLOBAL int default_port;\r
+\r
+/*\r
+ * This is set TRUE by cmdline.c iff a session is loaded with "-load".\r
+ */\r
+GLOBAL int loaded_session;\r
+\r
+struct RSAKey;                        /* be a little careful of scope */\r
+\r
+/*\r
+ * Mechanism for getting text strings such as usernames and passwords\r
+ * from the front-end.\r
+ * The fields are mostly modelled after SSH's keyboard-interactive auth.\r
+ * FIXME We should probably mandate a character set/encoding (probably UTF-8).\r
+ *\r
+ * Since many of the pieces of text involved may be chosen by the server,\r
+ * the caller must take care to ensure that the server can't spoof locally-\r
+ * generated prompts such as key passphrase prompts. Some ground rules:\r
+ *  - If the front-end needs to truncate a string, it should lop off the\r
+ *    end.\r
+ *  - The front-end should filter out any dangerous characters and\r
+ *    generally not trust the strings. (But \n is required to behave\r
+ *    vaguely sensibly, at least in `instruction', and ideally in\r
+ *    `prompt[]' too.)\r
+ */\r
+typedef struct {\r
+    char *prompt;\r
+    int echo;\r
+    char *result;      /* allocated/freed by caller */\r
+    size_t result_len;\r
+} prompt_t;\r
+typedef struct {\r
+    /*\r
+     * Indicates whether the information entered is to be used locally\r
+     * (for instance a key passphrase prompt), or is destined for the wire.\r
+     * This is a hint only; the front-end is at liberty not to use this\r
+     * information (so the caller should ensure that the supplied text is\r
+     * sufficient).\r
+     */\r
+    int to_server;\r
+    char *name;                /* Short description, perhaps for dialog box title */\r
+    int name_reqd;     /* Display of `name' required or optional? */\r
+    char *instruction; /* Long description, maybe with embedded newlines */\r
+    int instr_reqd;    /* Display of `instruction' required or optional? */\r
+    size_t n_prompts;\r
+    prompt_t **prompts;\r
+    void *frontend;\r
+    void *data;                /* slot for housekeeping data, managed by\r
+                        * get_userpass_input(); initially NULL */\r
+} prompts_t;\r
+prompts_t *new_prompts(void *frontend);\r
+void add_prompt(prompts_t *p, char *promptstr, int echo, size_t len);\r
+/* Burn the evidence. (Assumes _all_ strings want free()ing.) */\r
+void free_prompts(prompts_t *p);\r
+\r
+/*\r
+ * Exports from the front end.\r
+ */\r
+void request_resize(void *frontend, int, int);\r
+void do_text(Context, int, int, wchar_t *, int, unsigned long, int);\r
+void do_cursor(Context, int, int, wchar_t *, int, unsigned long, int);\r
+int char_width(Context ctx, int uc);\r
+#ifdef OPTIMISE_SCROLL\r
+void do_scroll(Context, int, int, int);\r
+#endif\r
+void set_title(void *frontend, char *);\r
+void set_icon(void *frontend, char *);\r
+void set_sbar(void *frontend, int, int, int);\r
+Context get_ctx(void *frontend);\r
+void free_ctx(Context);\r
+void palette_set(void *frontend, int, int, int, int);\r
+void palette_reset(void *frontend);\r
+void write_aclip(void *frontend, char *, int, int);\r
+void write_clip(void *frontend, wchar_t *, int *, int, int);\r
+void get_clip(void *frontend, wchar_t **, int *);\r
+void optimised_move(void *frontend, int, int, int);\r
+void set_raw_mouse_mode(void *frontend, int);\r
+void connection_fatal(void *frontend, char *, ...);\r
+void fatalbox(char *, ...);\r
+void modalfatalbox(char *, ...);\r
+#ifdef macintosh\r
+#pragma noreturn(fatalbox)\r
+#pragma noreturn(modalfatalbox)\r
+#endif\r
+void do_beep(void *frontend, int);\r
+void begin_session(void *frontend);\r
+void sys_cursor(void *frontend, int x, int y);\r
+void request_paste(void *frontend);\r
+void frontend_keypress(void *frontend);\r
+void ldisc_update(void *frontend, int echo, int edit);\r
+/* It's the backend's responsibility to invoke this at the start of a\r
+ * connection, if necessary; it can also invoke it later if the set of\r
+ * special commands changes. It does not need to invoke it at session\r
+ * shutdown. */\r
+void update_specials_menu(void *frontend);\r
+int from_backend(void *frontend, int is_stderr, const char *data, int len);\r
+int from_backend_untrusted(void *frontend, const char *data, int len);\r
+void notify_remote_exit(void *frontend);\r
+/* Get a sensible value for a tty mode. NULL return = don't set.\r
+ * Otherwise, returned value should be freed by caller. */\r
+char *get_ttymode(void *frontend, const char *mode);\r
+/*\r
+ * >0 = `got all results, carry on'\r
+ * 0  = `user cancelled' (FIXME distinguish "give up entirely" and "next auth"?)\r
+ * <0 = `please call back later with more in/inlen'\r
+ */\r
+int get_userpass_input(prompts_t *p, unsigned char *in, int inlen);\r
+#define OPTIMISE_IS_SCROLL 1\r
+\r
+void set_iconic(void *frontend, int iconic);\r
+void move_window(void *frontend, int x, int y);\r
+void set_zorder(void *frontend, int top);\r
+void refresh_window(void *frontend);\r
+void set_zoomed(void *frontend, int zoomed);\r
+int is_iconic(void *frontend);\r
+void get_window_pos(void *frontend, int *x, int *y);\r
+void get_window_pixels(void *frontend, int *x, int *y);\r
+char *get_window_title(void *frontend, int icon);\r
+/* Hint from backend to frontend about time-consuming operations.\r
+ * Initial state is assumed to be BUSY_NOT. */\r
+enum {\r
+    BUSY_NOT,      /* Not busy, all user interaction OK */\r
+    BUSY_WAITING,   /* Waiting for something; local event loops still running\r
+                      so some local interaction (e.g. menus) OK, but network\r
+                      stuff is suspended */\r
+    BUSY_CPU       /* Locally busy (e.g. crypto); user interaction suspended */\r
+};\r
+void set_busy_status(void *frontend, int status);\r
+\r
+void cleanup_exit(int);\r
+\r
+/*\r
+ * Exports from noise.c.\r
+ */\r
+void noise_get_heavy(void (*func) (void *, int));\r
+void noise_get_light(void (*func) (void *, int));\r
+void noise_regular(void);\r
+void noise_ultralight(unsigned long data);\r
+void random_save_seed(void);\r
+void random_destroy_seed(void);\r
+\r
+/*\r
+ * Exports from settings.c.\r
+ */\r
+char *save_settings(char *section, Config * cfg);\r
+void save_open_settings(void *sesskey, Config *cfg);\r
+void load_settings(char *section, Config * cfg);\r
+void load_open_settings(void *sesskey, Config *cfg);\r
+void get_sesslist(struct sesslist *, int allocate);\r
+void do_defaults(char *, Config *);\r
+void registry_cleanup(void);\r
+\r
+/*\r
+ * Functions used by settings.c to provide platform-specific\r
+ * default settings.\r
+ * \r
+ * (The integer one is expected to return `def' if it has no clear\r
+ * opinion of its own. This is because there's no integer value\r
+ * which I can reliably set aside to indicate `nil'. The string\r
+ * function is perfectly all right returning NULL, of course. The\r
+ * Filename and FontSpec functions are _not allowed_ to fail to\r
+ * return, since these defaults _must_ be per-platform.)\r
+ */\r
+char *platform_default_s(const char *name);\r
+int platform_default_i(const char *name, int def);\r
+Filename platform_default_filename(const char *name);\r
+FontSpec platform_default_fontspec(const char *name);\r
+\r
+/*\r
+ * Exports from terminal.c.\r
+ */\r
+\r
+Terminal *term_init(Config *, struct unicode_data *, void *);\r
+void term_free(Terminal *);\r
+void term_size(Terminal *, int, int, int);\r
+void term_paint(Terminal *, Context, int, int, int, int, int);\r
+void term_scroll(Terminal *, int, int);\r
+void term_pwron(Terminal *, int);\r
+void term_clrsb(Terminal *);\r
+void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,\r
+               int,int,int,int,int);\r
+void term_key(Terminal *, Key_Sym, wchar_t *, size_t, unsigned int,\r
+             unsigned int);\r
+void term_deselect(Terminal *);\r
+void term_update(Terminal *);\r
+void term_invalidate(Terminal *);\r
+void term_blink(Terminal *, int set_cursor);\r
+void term_do_paste(Terminal *);\r
+int term_paste_pending(Terminal *);\r
+void term_paste(Terminal *);\r
+void term_nopaste(Terminal *);\r
+int term_ldisc(Terminal *, int option);\r
+void term_copyall(Terminal *);\r
+void term_reconfig(Terminal *, Config *);\r
+void term_seen_key_event(Terminal *); \r
+int term_data(Terminal *, int is_stderr, const char *data, int len);\r
+int term_data_untrusted(Terminal *, const char *data, int len);\r
+void term_provide_resize_fn(Terminal *term,\r
+                           void (*resize_fn)(void *, int, int),\r
+                           void *resize_ctx);\r
+void term_provide_logctx(Terminal *term, void *logctx);\r
+void term_set_focus(Terminal *term, int has_focus);\r
+char *term_get_ttymode(Terminal *term, const char *mode);\r
+int term_get_userpass_input(Terminal *term, prompts_t *p,\r
+                           unsigned char *in, int inlen);\r
+\r
+/*\r
+ * Exports from logging.c.\r
+ */\r
+void *log_init(void *frontend, Config *cfg);\r
+void log_free(void *logctx);\r
+void log_reconfig(void *logctx, Config *cfg);\r
+void logfopen(void *logctx);\r
+void logfclose(void *logctx);\r
+void logtraffic(void *logctx, unsigned char c, int logmode);\r
+void logflush(void *logctx);\r
+void log_eventlog(void *logctx, const char *string);\r
+enum { PKT_INCOMING, PKT_OUTGOING };\r
+enum { PKTLOG_EMIT, PKTLOG_BLANK, PKTLOG_OMIT };\r
+struct logblank_t {\r
+    int offset;\r
+    int len;\r
+    int type;\r
+};\r
+void log_packet(void *logctx, int direction, int type,\r
+               char *texttype, void *data, int len,\r
+               int n_blanks, const struct logblank_t *blanks);\r
+\r
+/*\r
+ * Exports from testback.c\r
+ */\r
+\r
+extern Backend null_backend;\r
+extern Backend loop_backend;\r
+\r
+/*\r
+ * Exports from raw.c.\r
+ */\r
+\r
+extern Backend raw_backend;\r
+\r
+/*\r
+ * Exports from rlogin.c.\r
+ */\r
+\r
+extern Backend rlogin_backend;\r
+\r
+/*\r
+ * Exports from telnet.c.\r
+ */\r
+\r
+extern Backend telnet_backend;\r
+\r
+/*\r
+ * Exports from ssh.c.\r
+ */\r
+extern Backend ssh_backend;\r
+\r
+/*\r
+ * Exports from ldisc.c.\r
+ */\r
+void *ldisc_create(Config *, Terminal *, Backend *, void *, void *);\r
+void ldisc_free(void *);\r
+void ldisc_send(void *handle, char *buf, int len, int interactive);\r
+\r
+/*\r
+ * Exports from ldiscucs.c.\r
+ */\r
+void lpage_send(void *, int codepage, char *buf, int len, int interactive);\r
+void luni_send(void *, wchar_t * widebuf, int len, int interactive);\r
+\r
+/*\r
+ * Exports from sshrand.c.\r
+ */\r
+\r
+void random_add_noise(void *noise, int length);\r
+int random_byte(void);\r
+void random_get_savedata(void **data, int *len);\r
+extern int random_active;\r
+/* The random number subsystem is activated if at least one other entity\r
+ * within the program expresses an interest in it. So each SSH session\r
+ * calls random_ref on startup and random_unref on shutdown. */\r
+void random_ref(void);\r
+void random_unref(void);\r
+\r
+/*\r
+ * Exports from pinger.c.\r
+ */\r
+typedef struct pinger_tag *Pinger;\r
+Pinger pinger_new(Config *cfg, Backend *back, void *backhandle);\r
+void pinger_reconfig(Pinger, Config *oldcfg, Config *newcfg);\r
+void pinger_free(Pinger);\r
+\r
+/*\r
+ * Exports from misc.c.\r
+ */\r
+\r
+#include "misc.h"\r
+int cfg_launchable(const Config *cfg);\r
+char const *cfg_dest(const Config *cfg);\r
+\r
+/*\r
+ * Exports from sercfg.c.\r
+ */\r
+void ser_setup_config_box(struct controlbox *b, int midsession,\r
+                         int parity_mask, int flow_mask);\r
+\r
+/*\r
+ * Exports from version.c.\r
+ */\r
+extern char ver[];\r
+\r
+/*\r
+ * Exports from unicode.c.\r
+ */\r
+#ifndef CP_UTF8\r
+#define CP_UTF8 65001\r
+#endif\r
+/* void init_ucs(void); -- this is now in platform-specific headers */\r
+int is_dbcs_leadbyte(int codepage, char byte);\r
+int mb_to_wc(int codepage, int flags, char *mbstr, int mblen,\r
+            wchar_t *wcstr, int wclen);\r
+int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen,\r
+            char *mbstr, int mblen, char *defchr, int *defused,\r
+            struct unicode_data *ucsdata);\r
+wchar_t xlat_uskbd2cyrllic(int ch);\r
+int check_compose(int first, int second);\r
+int decode_codepage(char *cp_name);\r
+const char *cp_enumerate (int index);\r
+const char *cp_name(int codepage);\r
+void get_unitab(int codepage, wchar_t * unitab, int ftype);\r
+\r
+/*\r
+ * Exports from wcwidth.c\r
+ */\r
+int mk_wcwidth(wchar_t ucs);\r
+int mk_wcswidth(const wchar_t *pwcs, size_t n);\r
+int mk_wcwidth_cjk(wchar_t ucs);\r
+int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n);\r
+\r
+/*\r
+ * Exports from mscrypto.c\r
+ */\r
+#ifdef MSCRYPTOAPI\r
+int crypto_startup();\r
+void crypto_wrapup();\r
+#endif\r
+\r
+/*\r
+ * Exports from pageantc.c.\r
+ * \r
+ * agent_query returns 1 for here's-a-response, and 0 for query-in-\r
+ * progress. In the latter case there will be a call to `callback'\r
+ * at some future point, passing callback_ctx as the first\r
+ * parameter and the actual reply data as the second and third.\r
+ * \r
+ * The response may be a NULL pointer (in either of the synchronous\r
+ * or asynchronous cases), which indicates failure to receive a\r
+ * response.\r
+ */\r
+int agent_query(void *in, int inlen, void **out, int *outlen,\r
+               void (*callback)(void *, void *, int), void *callback_ctx);\r
+int agent_exists(void);\r
+\r
+/*\r
+ * Exports from wildcard.c\r
+ */\r
+const char *wc_error(int value);\r
+int wc_match(const char *wildcard, const char *target);\r
+int wc_unescape(char *output, const char *wildcard);\r
+\r
+/*\r
+ * Exports from frontend (windlg.c etc)\r
+ */\r
+void logevent(void *frontend, const char *);\r
+void pgp_fingerprints(void);\r
+/*\r
+ * verify_ssh_host_key() can return one of three values:\r
+ * \r
+ *  - +1 means `key was OK' (either already known or the user just\r
+ *    approved it) `so continue with the connection'\r
+ * \r
+ *  - 0 means `key was not OK, abandon the connection'\r
+ * \r
+ *  - -1 means `I've initiated enquiries, please wait to be called\r
+ *    back via the provided function with a result that's either 0\r
+ *    or +1'.\r
+ */\r
+int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,\r
+                        char *keystr, char *fingerprint,\r
+                        void (*callback)(void *ctx, int result), void *ctx);\r
+/*\r
+ * askalg has the same set of return values as verify_ssh_host_key.\r
+ */\r
+int askalg(void *frontend, const char *algtype, const char *algname,\r
+          void (*callback)(void *ctx, int result), void *ctx);\r
+/*\r
+ * askappend can return four values:\r
+ * \r
+ *  - 2 means overwrite the log file\r
+ *  - 1 means append to the log file\r
+ *  - 0 means cancel logging for this session\r
+ *  - -1 means please wait.\r
+ */\r
+int askappend(void *frontend, Filename filename,\r
+             void (*callback)(void *ctx, int result), void *ctx);\r
+\r
+/*\r
+ * Exports from console frontends (wincons.c, uxcons.c)\r
+ * that aren't equivalents to things in windlg.c et al.\r
+ */\r
+extern int console_batch_mode;\r
+int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen);\r
+void console_provide_logctx(void *logctx);\r
+int is_interactive(void);\r
+\r
+/*\r
+ * Exports from printing.c.\r
+ */\r
+typedef struct printer_enum_tag printer_enum;\r
+typedef struct printer_job_tag printer_job;\r
+printer_enum *printer_start_enum(int *nprinters);\r
+char *printer_get_name(printer_enum *, int);\r
+void printer_finish_enum(printer_enum *);\r
+printer_job *printer_start_job(char *printer);\r
+void printer_job_data(printer_job *, void *, int);\r
+void printer_finish_job(printer_job *);\r
+\r
+/*\r
+ * Exports from cmdline.c (and also cmdline_error(), which is\r
+ * defined differently in various places and required _by_\r
+ * cmdline.c).\r
+ */\r
+int cmdline_process_param(char *, char *, int, Config *);\r
+void cmdline_run_saved(Config *);\r
+void cmdline_cleanup(void);\r
+int cmdline_get_passwd_input(prompts_t *p, unsigned char *in, int inlen);\r
+#define TOOLTYPE_FILETRANSFER 1\r
+#define TOOLTYPE_NONNETWORK 2\r
+extern int cmdline_tooltype;\r
+\r
+void cmdline_error(char *, ...);\r
+\r
+/*\r
+ * Exports from config.c.\r
+ */\r
+struct controlbox;\r
+void setup_config_box(struct controlbox *b, int midsession,\r
+                     int protocol, int protcfginfo);\r
+\r
+/*\r
+ * Exports from minibidi.c.\r
+ */\r
+typedef struct bidi_char {\r
+    wchar_t origwc, wc;\r
+    unsigned short index;\r
+} bidi_char;\r
+int do_bidi(bidi_char *line, int count);\r
+int do_shape(bidi_char *line, bidi_char *to, int count);\r
+int is_rtl(int c);\r
+\r
+/*\r
+ * X11 auth mechanisms we know about.\r
+ */\r
+enum {\r
+    X11_NO_AUTH,\r
+    X11_MIT,                           /* MIT-MAGIC-COOKIE-1 */\r
+    X11_XDM,                          /* XDM-AUTHORIZATION-1 */\r
+    X11_NAUTHS\r
+};\r
+extern const char *const x11_authnames[];  /* declared in x11fwd.c */\r
+\r
+/*\r
+ * Miscellaneous exports from the platform-specific code.\r
+ */\r
+Filename filename_from_str(const char *string);\r
+const char *filename_to_str(const Filename *fn);\r
+int filename_equal(Filename f1, Filename f2);\r
+int filename_is_null(Filename fn);\r
+char *get_username(void);             /* return value needs freeing */\r
+char *get_random_data(int bytes);      /* used in cmdgen.c */\r
+\r
+/*\r
+ * Exports and imports from timing.c.\r
+ *\r
+ * schedule_timer() asks the front end to schedule a callback to a\r
+ * timer function in a given number of ticks. The returned value is\r
+ * the time (in ticks since an arbitrary offset) at which the\r
+ * callback can be expected. This value will also be passed as the\r
+ * `now' parameter to the callback function. Hence, you can (for\r
+ * example) schedule an event at a particular time by calling\r
+ * schedule_timer() and storing the return value in your context\r
+ * structure as the time when that event is due. The first time a\r
+ * callback function gives you that value or more as `now', you do\r
+ * the thing.\r
+ * \r
+ * expire_timer_context() drops all current timers associated with\r
+ * a given value of ctx (for when you're about to free ctx).\r
+ * \r
+ * run_timers() is called from the front end when it has reason to\r
+ * think some timers have reached their moment, or when it simply\r
+ * needs to know how long to wait next. We pass it the time we\r
+ * think it is. It returns TRUE and places the time when the next\r
+ * timer needs to go off in `next', or alternatively it returns\r
+ * FALSE if there are no timers at all pending.\r
+ * \r
+ * timer_change_notify() must be supplied by the front end; it\r
+ * notifies the front end that a new timer has been added to the\r
+ * list which is sooner than any existing ones. It provides the\r
+ * time when that timer needs to go off.\r
+ * \r
+ * *** FRONT END IMPLEMENTORS NOTE:\r
+ * \r
+ * There's an important subtlety in the front-end implementation of\r
+ * the timer interface. When a front end is given a `next' value,\r
+ * either returned from run_timers() or via timer_change_notify(),\r
+ * it should ensure that it really passes _that value_ as the `now'\r
+ * parameter to its next run_timers call. It should _not_ simply\r
+ * call GETTICKCOUNT() to get the `now' parameter when invoking\r
+ * run_timers().\r
+ * \r
+ * The reason for this is that an OS's system clock might not agree\r
+ * exactly with the timing mechanisms it supplies to wait for a\r
+ * given interval. I'll illustrate this by the simple example of\r
+ * Unix Plink, which uses timeouts to select() in a way which for\r
+ * these purposes can simply be considered to be a wait() function.\r
+ * Suppose, for the sake of argument, that this wait() function\r
+ * tends to return early by 1%. Then a possible sequence of actions\r
+ * is:\r
+ * \r
+ *  - run_timers() tells the front end that the next timer firing\r
+ *    is 10000ms from now.\r
+ *  - Front end calls wait(10000ms), but according to\r
+ *    GETTICKCOUNT() it has only waited for 9900ms.\r
+ *  - Front end calls run_timers() again, passing time T-100ms as\r
+ *    `now'.\r
+ *  - run_timers() does nothing, and says the next timer firing is\r
+ *    still 100ms from now.\r
+ *  - Front end calls wait(100ms), which only waits for 99ms.\r
+ *  - Front end calls run_timers() yet again, passing time T-1ms.\r
+ *  - run_timers() says there's still 1ms to wait.\r
+ *  - Front end calls wait(1ms).\r
+ * \r
+ * If you're _lucky_ at this point, wait(1ms) will actually wait\r
+ * for 1ms and you'll only have woken the program up three times.\r
+ * If you're unlucky, wait(1ms) might do nothing at all due to\r
+ * being below some minimum threshold, and you might find your\r
+ * program spends the whole of the last millisecond tight-looping\r
+ * between wait() and run_timers().\r
+ * \r
+ * Instead, what you should do is to _save_ the precise `next'\r
+ * value provided by run_timers() or via timer_change_notify(), and\r
+ * use that precise value as the input to the next run_timers()\r
+ * call. So:\r
+ * \r
+ *  - run_timers() tells the front end that the next timer firing\r
+ *    is at time T, 10000ms from now.\r
+ *  - Front end calls wait(10000ms).\r
+ *  - Front end then immediately calls run_timers() and passes it\r
+ *    time T, without stopping to check GETTICKCOUNT() at all.\r
+ * \r
+ * This guarantees that the program wakes up only as many times as\r
+ * there are actual timer actions to be taken, and that the timing\r
+ * mechanism will never send it into a tight loop.\r
+ * \r
+ * (It does also mean that the timer action in the above example\r
+ * will occur 100ms early, but this is not generally critical. And\r
+ * the hypothetical 1% error in wait() will be partially corrected\r
+ * for anyway when, _after_ run_timers() returns, you call\r
+ * GETTICKCOUNT() and compare the result with the returned `next'\r
+ * value to find out how long you have to make your next wait().)\r
+ */\r
+typedef void (*timer_fn_t)(void *ctx, long now);\r
+long schedule_timer(int ticks, timer_fn_t fn, void *ctx);\r
+void expire_timer_context(void *ctx);\r
+int run_timers(long now, long *next);\r
+void timer_change_notify(long next);\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/PUTTYMEM.H b/src/TortoisePlink/PUTTYMEM.H
new file mode 100644 (file)
index 0000000..d478f79
--- /dev/null
@@ -0,0 +1,42 @@
+/*\r
+ * PuTTY memory-handling header.\r
+ */\r
+\r
+#ifndef PUTTY_PUTTYMEM_H\r
+#define PUTTY_PUTTYMEM_H\r
+\r
+#include <stddef.h>                   /* for size_t */\r
+#include <string.h>                   /* for memcpy() */\r
+\r
+\r
+/* #define MALLOC_LOG  do this if you suspect putty of leaking memory */\r
+#ifdef MALLOC_LOG\r
+#define smalloc(z) (mlog(__FILE__,__LINE__), safemalloc(z,1))\r
+#define snmalloc(z,s) (mlog(__FILE__,__LINE__), safemalloc(z,s))\r
+#define srealloc(y,z) (mlog(__FILE__,__LINE__), saferealloc(y,z,1))\r
+#define snrealloc(y,z,s) (mlog(__FILE__,__LINE__), saferealloc(y,z,s))\r
+#define sfree(z) (mlog(__FILE__,__LINE__), safefree(z))\r
+void mlog(char *, int);\r
+#else\r
+#define smalloc(z) safemalloc(z,1)\r
+#define snmalloc safemalloc\r
+#define srealloc(y,z) saferealloc(y,z,1)\r
+#define snrealloc saferealloc\r
+#define sfree safefree\r
+#endif\r
+\r
+void *safemalloc(size_t, size_t);\r
+void *saferealloc(void *, size_t, size_t);\r
+void safefree(void *);\r
+\r
+/*\r
+ * Direct use of smalloc within the code should be avoided where\r
+ * possible, in favour of these type-casting macros which ensure\r
+ * you don't mistakenly allocate enough space for one sort of\r
+ * structure and assign it to a different sort of pointer.\r
+ */\r
+#define snew(type) ((type *)snmalloc(1, sizeof(type)))\r
+#define snewn(n, type) ((type *)snmalloc((n), sizeof(type)))\r
+#define sresize(ptr, n, type) ((type *)snrealloc((ptr), (n), sizeof(type)))\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/PUTTYPS.H b/src/TortoisePlink/PUTTYPS.H
new file mode 100644 (file)
index 0000000..e7d2808
--- /dev/null
@@ -0,0 +1,26 @@
+/*\r
+ * Find the platform-specific header for this platform.\r
+ */\r
+\r
+#ifndef PUTTY_PUTTYPS_H\r
+#define PUTTY_PUTTYPS_H\r
+\r
+#ifdef _WINDOWS\r
+\r
+#include "winstuff.h"\r
+\r
+#elif defined(macintosh)\r
+\r
+#include "macstuff.h"\r
+\r
+#elif defined(MACOSX)\r
+\r
+#include "osx.h"\r
+\r
+#else\r
+\r
+#include "unix.h"\r
+\r
+#endif\r
+\r
+#endif\r
diff --git a/src/TortoisePlink/RAW.C b/src/TortoisePlink/RAW.C
new file mode 100644 (file)
index 0000000..cea97e1
--- /dev/null
@@ -0,0 +1,282 @@
+/*\r
+ * "Raw" backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+\r
+#include "putty.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+#define RAW_MAX_BACKLOG 4096\r
+\r
+typedef struct raw_backend_data {\r
+    const struct plug_function_table *fn;\r
+    /* the above field _must_ be first in the structure */\r
+\r
+    Socket s;\r
+    int bufsize;\r
+    void *frontend;\r
+} *Raw;\r
+\r
+static void raw_size(void *handle, int width, int height);\r
+\r
+static void c_write(Raw raw, char *buf, int len)\r
+{\r
+    int backlog = from_backend(raw->frontend, 0, buf, len);\r
+    sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);\r
+}\r
+\r
+static void raw_log(Plug plug, int type, SockAddr addr, int port,\r
+                   const char *error_msg, int error_code)\r
+{\r
+    Raw raw = (Raw) plug;\r
+    char addrbuf[256], *msg;\r
+\r
+    sk_getaddr(addr, addrbuf, lenof(addrbuf));\r
+\r
+    if (type == 0)\r
+       msg = dupprintf("Connecting to %s port %d", addrbuf, port);\r
+    else\r
+       msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);\r
+\r
+    logevent(raw->frontend, msg);\r
+}\r
+\r
+static int raw_closing(Plug plug, const char *error_msg, int error_code,\r
+                      int calling_back)\r
+{\r
+    Raw raw = (Raw) plug;\r
+\r
+    if (raw->s) {\r
+        sk_close(raw->s);\r
+        raw->s = NULL;\r
+       notify_remote_exit(raw->frontend);\r
+    }\r
+    if (error_msg) {\r
+       /* A socket error has occurred. */\r
+       logevent(raw->frontend, error_msg);\r
+       connection_fatal(raw->frontend, "%s", error_msg);\r
+    }                                 /* Otherwise, the remote side closed the connection normally. */\r
+    return 0;\r
+}\r
+\r
+static int raw_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    Raw raw = (Raw) plug;\r
+    c_write(raw, data, len);\r
+    return 1;\r
+}\r
+\r
+static void raw_sent(Plug plug, int bufsize)\r
+{\r
+    Raw raw = (Raw) plug;\r
+    raw->bufsize = bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set up the raw connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *raw_init(void *frontend_handle, void **backend_handle,\r
+                           Config *cfg,\r
+                           char *host, int port, char **realhost, int nodelay,\r
+                           int keepalive)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       raw_log,\r
+       raw_closing,\r
+       raw_receive,\r
+       raw_sent\r
+    };\r
+    SockAddr addr;\r
+    const char *err;\r
+    Raw raw;\r
+\r
+    raw = snew(struct raw_backend_data);\r
+    raw->fn = &fn_table;\r
+    raw->s = NULL;\r
+    *backend_handle = raw;\r
+\r
+    raw->frontend = frontend_handle;\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    {\r
+       char *buf;\r
+       buf = dupprintf("Looking up host \"%s\"%s", host,\r
+                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
+                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :\r
+                         "")));\r
+       logevent(raw->frontend, buf);\r
+       sfree(buf);\r
+    }\r
+    addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+\r
+    if (port < 0)\r
+       port = 23;                     /* default telnet port */\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, keepalive,\r
+                           (Plug) raw, cfg);\r
+    if ((err = sk_socket_error(raw->s)) != NULL)\r
+       return err;\r
+\r
+    return NULL;\r
+}\r
+\r
+static void raw_free(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+\r
+    if (raw->s)\r
+       sk_close(raw->s);\r
+    sfree(raw);\r
+}\r
+\r
+/*\r
+ * Stub routine (we don't have any need to reconfigure this backend).\r
+ */\r
+static void raw_reconfig(void *handle, Config *cfg)\r
+{\r
+}\r
+\r
+/*\r
+ * Called to send data down the raw connection.\r
+ */\r
+static int raw_send(void *handle, char *buf, int len)\r
+{\r
+    Raw raw = (Raw) handle;\r
+\r
+    if (raw->s == NULL)\r
+       return 0;\r
+\r
+    raw->bufsize = sk_write(raw->s, buf, len);\r
+\r
+    return raw->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to query the current socket sendability status.\r
+ */\r
+static int raw_sendbuffer(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    return raw->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void raw_size(void *handle, int width, int height)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Send raw special codes.\r
+ */\r
+static void raw_special(void *handle, Telnet_Special code)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *raw_get_specials(void *handle)\r
+{\r
+    return NULL;\r
+}\r
+\r
+static int raw_connected(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    return raw->s != NULL;\r
+}\r
+\r
+static int raw_sendok(void *handle)\r
+{\r
+    return 1;\r
+}\r
+\r
+static void raw_unthrottle(void *handle, int backlog)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);\r
+}\r
+\r
+static int raw_ldisc(void *handle, int option)\r
+{\r
+    if (option == LD_EDIT || option == LD_ECHO)\r
+       return 1;\r
+    return 0;\r
+}\r
+\r
+static void raw_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static void raw_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static int raw_exitcode(void *handle)\r
+{\r
+    Raw raw = (Raw) handle;\r
+    if (raw->s != NULL)\r
+        return -1;                     /* still connected */\r
+    else\r
+        /* Exit codes are a meaningless concept in the Raw protocol */\r
+        return 0;\r
+}\r
+\r
+/*\r
+ * cfg_info for Raw does nothing at all.\r
+ */\r
+static int raw_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+Backend raw_backend = {\r
+    raw_init,\r
+    raw_free,\r
+    raw_reconfig,\r
+    raw_send,\r
+    raw_sendbuffer,\r
+    raw_size,\r
+    raw_special,\r
+    raw_get_specials,\r
+    raw_connected,\r
+    raw_exitcode,\r
+    raw_sendok,\r
+    raw_ldisc,\r
+    raw_provide_ldisc,\r
+    raw_provide_logctx,\r
+    raw_unthrottle,\r
+    raw_cfg_info,\r
+    1\r
+};\r
diff --git a/src/TortoisePlink/RLOGIN.C b/src/TortoisePlink/RLOGIN.C
new file mode 100644 (file)
index 0000000..2b12842
--- /dev/null
@@ -0,0 +1,353 @@
+/*\r
+ * Rlogin backend.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+#include "putty.h"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+#define RLOGIN_MAX_BACKLOG 4096\r
+\r
+typedef struct rlogin_tag {\r
+    const struct plug_function_table *fn;\r
+    /* the above field _must_ be first in the structure */\r
+\r
+    Socket s;\r
+    int bufsize;\r
+    int firstbyte;\r
+    int cansize;\r
+    int term_width, term_height;\r
+    void *frontend;\r
+} *Rlogin;\r
+\r
+static void rlogin_size(void *handle, int width, int height);\r
+\r
+static void c_write(Rlogin rlogin, char *buf, int len)\r
+{\r
+    int backlog = from_backend(rlogin->frontend, 0, buf, len);\r
+    sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);\r
+}\r
+\r
+static void rlogin_log(Plug plug, int type, SockAddr addr, int port,\r
+                      const char *error_msg, int error_code)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    char addrbuf[256], *msg;\r
+\r
+    sk_getaddr(addr, addrbuf, lenof(addrbuf));\r
+\r
+    if (type == 0)\r
+       msg = dupprintf("Connecting to %s port %d", addrbuf, port);\r
+    else\r
+       msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg);\r
+\r
+    logevent(rlogin->frontend, msg);\r
+}\r
+\r
+static int rlogin_closing(Plug plug, const char *error_msg, int error_code,\r
+                         int calling_back)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    if (rlogin->s) {\r
+        sk_close(rlogin->s);\r
+        rlogin->s = NULL;\r
+       notify_remote_exit(rlogin->frontend);\r
+    }\r
+    if (error_msg) {\r
+       /* A socket error has occurred. */\r
+       logevent(rlogin->frontend, error_msg);\r
+       connection_fatal(rlogin->frontend, "%s", error_msg);\r
+    }                                 /* Otherwise, the remote side closed the connection normally. */\r
+    return 0;\r
+}\r
+\r
+static int rlogin_receive(Plug plug, int urgent, char *data, int len)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    if (urgent == 2) {\r
+       char c;\r
+\r
+       c = *data++;\r
+       len--;\r
+       if (c == '\x80') {\r
+           rlogin->cansize = 1;\r
+           rlogin_size(rlogin, rlogin->term_width, rlogin->term_height);\r
+        }\r
+       /*\r
+        * We should flush everything (aka Telnet SYNCH) if we see\r
+        * 0x02, and we should turn off and on _local_ flow control\r
+        * on 0x10 and 0x20 respectively. I'm not convinced it's\r
+        * worth it...\r
+        */\r
+    } else {\r
+       /*\r
+        * Main rlogin protocol. This is really simple: the first\r
+        * byte is expected to be NULL and is ignored, and the rest\r
+        * is printed.\r
+        */\r
+       if (rlogin->firstbyte) {\r
+           if (data[0] == '\0') {\r
+               data++;\r
+               len--;\r
+           }\r
+           rlogin->firstbyte = 0;\r
+       }\r
+       if (len > 0)\r
+            c_write(rlogin, data, len);\r
+    }\r
+    return 1;\r
+}\r
+\r
+static void rlogin_sent(Plug plug, int bufsize)\r
+{\r
+    Rlogin rlogin = (Rlogin) plug;\r
+    rlogin->bufsize = bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set up the rlogin connection.\r
+ * \r
+ * Returns an error message, or NULL on success.\r
+ *\r
+ * Also places the canonical host name into `realhost'. It must be\r
+ * freed by the caller.\r
+ */\r
+static const char *rlogin_init(void *frontend_handle, void **backend_handle,\r
+                              Config *cfg,\r
+                              char *host, int port, char **realhost,\r
+                              int nodelay, int keepalive)\r
+{\r
+    static const struct plug_function_table fn_table = {\r
+       rlogin_log,\r
+       rlogin_closing,\r
+       rlogin_receive,\r
+       rlogin_sent\r
+    };\r
+    SockAddr addr;\r
+    const char *err;\r
+    Rlogin rlogin;\r
+\r
+    rlogin = snew(struct rlogin_tag);\r
+    rlogin->fn = &fn_table;\r
+    rlogin->s = NULL;\r
+    rlogin->frontend = frontend_handle;\r
+    rlogin->term_width = cfg->width;\r
+    rlogin->term_height = cfg->height;\r
+    rlogin->firstbyte = 1;\r
+    rlogin->cansize = 0;\r
+    *backend_handle = rlogin;\r
+\r
+    /*\r
+     * Try to find host.\r
+     */\r
+    {\r
+       char *buf;\r
+       buf = dupprintf("Looking up host \"%s\"%s", host,\r
+                       (cfg->addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
+                        (cfg->addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :\r
+                         "")));\r
+       logevent(rlogin->frontend, buf);\r
+       sfree(buf);\r
+    }\r
+    addr = name_lookup(host, port, realhost, cfg, cfg->addressfamily);\r
+    if ((err = sk_addr_error(addr)) != NULL) {\r
+       sk_addr_free(addr);\r
+       return err;\r
+    }\r
+\r
+    if (port < 0)\r
+       port = 513;                    /* default rlogin port */\r
+\r
+    /*\r
+     * Open socket.\r
+     */\r
+    rlogin->s = new_connection(addr, *realhost, port, 1, 0,\r
+                              nodelay, keepalive, (Plug) rlogin, cfg);\r
+    if ((err = sk_socket_error(rlogin->s)) != NULL)\r
+       return err;\r
+\r
+    /*\r
+     * Send local username, remote username, terminal/speed\r
+     */\r
+\r
+    {\r
+       char z = 0;\r
+       char *p;\r
+       sk_write(rlogin->s, &z, 1);\r
+       sk_write(rlogin->s, cfg->localusername,\r
+                strlen(cfg->localusername));\r
+       sk_write(rlogin->s, &z, 1);\r
+       sk_write(rlogin->s, cfg->username,\r
+                strlen(cfg->username));\r
+       sk_write(rlogin->s, &z, 1);\r
+       sk_write(rlogin->s, cfg->termtype,\r
+                strlen(cfg->termtype));\r
+       sk_write(rlogin->s, "/", 1);\r
+       for (p = cfg->termspeed; isdigit((unsigned char)*p); p++) continue;\r
+       sk_write(rlogin->s, cfg->termspeed, p - cfg->termspeed);\r
+       rlogin->bufsize = sk_write(rlogin->s, &z, 1);\r
+    }\r
+\r
+    return NULL;\r
+}\r
+\r
+static void rlogin_free(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+\r
+    if (rlogin->s)\r
+       sk_close(rlogin->s);\r
+    sfree(rlogin);\r
+}\r
+\r
+/*\r
+ * Stub routine (we don't have any need to reconfigure this backend).\r
+ */\r
+static void rlogin_reconfig(void *handle, Config *cfg)\r
+{\r
+}\r
+\r
+/*\r
+ * Called to send data down the rlogin connection.\r
+ */\r
+static int rlogin_send(void *handle, char *buf, int len)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+\r
+    if (rlogin->s == NULL)\r
+       return 0;\r
+\r
+    rlogin->bufsize = sk_write(rlogin->s, buf, len);\r
+\r
+    return rlogin->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to query the current socket sendability status.\r
+ */\r
+static int rlogin_sendbuffer(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    return rlogin->bufsize;\r
+}\r
+\r
+/*\r
+ * Called to set the size of the window\r
+ */\r
+static void rlogin_size(void *handle, int width, int height)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };\r
+\r
+    rlogin->term_width = width;\r
+    rlogin->term_height = height;\r
+\r
+    if (rlogin->s == NULL || !rlogin->cansize)\r
+       return;\r
+\r
+    b[6] = rlogin->term_width >> 8;\r
+    b[7] = rlogin->term_width & 0xFF;\r
+    b[4] = rlogin->term_height >> 8;\r
+    b[5] = rlogin->term_height & 0xFF;\r
+    rlogin->bufsize = sk_write(rlogin->s, b, 12);\r
+    return;\r
+}\r
+\r
+/*\r
+ * Send rlogin special codes.\r
+ */\r
+static void rlogin_special(void *handle, Telnet_Special code)\r
+{\r
+    /* Do nothing! */\r
+    return;\r
+}\r
+\r
+/*\r
+ * Return a list of the special codes that make sense in this\r
+ * protocol.\r
+ */\r
+static const struct telnet_special *rlogin_get_specials(void *handle)\r
+{\r
+    return NULL;\r
+}\r
+\r
+static int rlogin_connected(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    return rlogin->s != NULL;\r
+}\r
+\r
+static int rlogin_sendok(void *handle)\r
+{\r
+    /* Rlogin rlogin = (Rlogin) handle; */\r
+    return 1;\r
+}\r
+\r
+static void rlogin_unthrottle(void *handle, int backlog)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);\r
+}\r
+\r
+static int rlogin_ldisc(void *handle, int option)\r
+{\r
+    /* Rlogin rlogin = (Rlogin) handle; */\r
+    return 0;\r
+}\r
+\r
+static void rlogin_provide_ldisc(void *handle, void *ldisc)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static void rlogin_provide_logctx(void *handle, void *logctx)\r
+{\r
+    /* This is a stub. */\r
+}\r
+\r
+static int rlogin_exitcode(void *handle)\r
+{\r
+    Rlogin rlogin = (Rlogin) handle;\r
+    if (rlogin->s != NULL)\r
+        return -1;                     /* still connected */\r
+    else\r
+        /* If we ever implement RSH, we'll probably need to do this properly */\r
+        return 0;\r
+}\r
+\r
+/*\r
+ * cfg_info for rlogin does nothing at all.\r
+ */\r
+static int rlogin_cfg_info(void *handle)\r
+{\r
+    return 0;\r
+}\r
+\r
+Backend rlogin_backend = {\r
+    rlogin_init,\r
+    rlogin_free,\r
+    rlogin_reconfig,\r
+    rlogin_send,\r
+    rlogin_sendbuffer,\r
+    rlogin_size,\r
+    rlogin_special,\r
+    rlogin_get_specials,\r
+    rlogin_connected,\r
+    rlogin_exitcode,\r
+    rlogin_sendok,\r
+    rlogin_ldisc,\r
+    rlogin_provide_ldisc,\r
+    rlogin_provide_logctx,\r
+    rlogin_unthrottle,\r
+    rlogin_cfg_info,\r
+    1\r
+};\r
diff --git a/src/TortoisePlink/SETTINGS.C b/src/TortoisePlink/SETTINGS.C
new file mode 100644 (file)
index 0000000..f9eda84
--- /dev/null
@@ -0,0 +1,869 @@
+/*\r
+ * settings.c: read and write saved sessions. (platform-independent)\r
+ */\r
+\r
+#include <assert.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include "putty.h"\r
+#include "storage.h"\r
+\r
+/*\r
+ * Tables of string <-> enum value mappings\r
+ */\r
+struct keyval { char *s; int v; };\r
+\r
+/* The cipher order given here is the default order. */\r
+static const struct keyval ciphernames[] = {\r
+    { "aes",       CIPHER_AES },\r
+    { "blowfish",   CIPHER_BLOWFISH },\r
+    { "3des",      CIPHER_3DES },\r
+    { "WARN",      CIPHER_WARN },\r
+    { "arcfour",    CIPHER_ARCFOUR },\r
+    { "des",       CIPHER_DES }\r
+};\r
+\r
+static const struct keyval kexnames[] = {\r
+    { "dh-gex-sha1",       KEX_DHGEX },\r
+    { "dh-group14-sha1",    KEX_DHGROUP14 },\r
+    { "dh-group1-sha1",            KEX_DHGROUP1 },\r
+    { "WARN",              KEX_WARN }\r
+};\r
+\r
+/*\r
+ * All the terminal modes that we know about for the "TerminalModes"\r
+ * setting. (Also used by config.c for the drop-down list.)\r
+ * This is currently precisely the same as the set in ssh.c, but could\r
+ * in principle differ if other backends started to support tty modes\r
+ * (e.g., the pty backend).\r
+ */\r
+const char *const ttymodes[] = {\r
+    "INTR",    "QUIT",     "ERASE",    "KILL",     "EOF",\r
+    "EOL",     "EOL2",     "START",    "STOP",     "SUSP",\r
+    "DSUSP",   "REPRINT",  "WERASE",   "LNEXT",    "FLUSH",\r
+    "SWTCH",   "STATUS",   "DISCARD",  "IGNPAR",   "PARMRK",\r
+    "INPCK",   "ISTRIP",   "INLCR",    "IGNCR",    "ICRNL",\r
+    "IUCLC",   "IXON",     "IXANY",    "IXOFF",    "IMAXBEL",\r
+    "ISIG",    "ICANON",   "XCASE",    "ECHO",     "ECHOE",\r
+    "ECHOK",   "ECHONL",   "NOFLSH",   "TOSTOP",   "IEXTEN",\r
+    "ECHOCTL", "ECHOKE",   "PENDIN",   "OPOST",    "OLCUC",\r
+    "ONLCR",   "OCRNL",    "ONOCR",    "ONLRET",   "CS7",\r
+    "CS8",     "PARENB",   "PARODD",   NULL\r
+};\r
+\r
+static void gpps(void *handle, const char *name, const char *def,\r
+                char *val, int len)\r
+{\r
+    if (!read_setting_s(handle, name, val, len)) {\r
+       char *pdef;\r
+\r
+       pdef = platform_default_s(name);\r
+       if (pdef) {\r
+           strncpy(val, pdef, len);\r
+           sfree(pdef);\r
+       } else {\r
+           strncpy(val, def, len);\r
+       }\r
+\r
+       val[len - 1] = '\0';\r
+    }\r
+}\r
+\r
+/*\r
+ * gppfont and gppfile cannot have local defaults, since the very\r
+ * format of a Filename or Font is platform-dependent. So the\r
+ * platform-dependent functions MUST return some sort of value.\r
+ */\r
+static void gppfont(void *handle, const char *name, FontSpec *result)\r
+{\r
+    if (!read_setting_fontspec(handle, name, result))\r
+       *result = platform_default_fontspec(name);\r
+}\r
+static void gppfile(void *handle, const char *name, Filename *result)\r
+{\r
+    if (!read_setting_filename(handle, name, result))\r
+       *result = platform_default_filename(name);\r
+}\r
+\r
+static void gppi(void *handle, char *name, int def, int *i)\r
+{\r
+    def = platform_default_i(name, def);\r
+    *i = read_setting_i(handle, name, def);\r
+}\r
+\r
+/*\r
+ * Read a set of name-value pairs in the format we occasionally use:\r
+ *   NAME\tVALUE\0NAME\tVALUE\0\0 in memory\r
+ *   NAME=VALUE,NAME=VALUE, in storage\r
+ * `def' is in the storage format.\r
+ */\r
+static void gppmap(void *handle, char *name, char *def, char *val, int len)\r
+{\r
+    char *buf = snewn(2*len, char), *p, *q;\r
+    gpps(handle, name, def, buf, 2*len);\r
+    p = buf;\r
+    q = val;\r
+    while (*p) {\r
+       while (*p && *p != ',') {\r
+           int c = *p++;\r
+           if (c == '=')\r
+               c = '\t';\r
+           if (c == '\\')\r
+               c = *p++;\r
+           *q++ = c;\r
+       }\r
+       if (*p == ',')\r
+           p++;\r
+       *q++ = '\0';\r
+    }\r
+    *q = '\0';\r
+    sfree(buf);\r
+}\r
+\r
+/*\r
+ * Write a set of name/value pairs in the above format.\r
+ */\r
+static void wmap(void *handle, char const *key, char const *value, int len)\r
+{\r
+    char *buf = snewn(2*len, char), *p;\r
+    const char *q;\r
+    p = buf;\r
+    q = value;\r
+    while (*q) {\r
+       while (*q) {\r
+           int c = *q++;\r
+           if (c == '=' || c == ',' || c == '\\')\r
+               *p++ = '\\';\r
+           if (c == '\t')\r
+               c = '=';\r
+           *p++ = c;\r
+       }\r
+       *p++ = ',';\r
+       q++;\r
+    }\r
+    *p = '\0';\r
+    write_setting_s(handle, key, buf);\r
+    sfree(buf);\r
+}\r
+\r
+static int key2val(const struct keyval *mapping, int nmaps, char *key)\r
+{\r
+    int i;\r
+    for (i = 0; i < nmaps; i++)\r
+       if (!strcmp(mapping[i].s, key)) return mapping[i].v;\r
+    return -1;\r
+}\r
+\r
+static const char *val2key(const struct keyval *mapping, int nmaps, int val)\r
+{\r
+    int i;\r
+    for (i = 0; i < nmaps; i++)\r
+       if (mapping[i].v == val) return mapping[i].s;\r
+    return NULL;\r
+}\r
+\r
+/*\r
+ * Helper function to parse a comma-separated list of strings into\r
+ * a preference list array of values. Any missing values are added\r
+ * to the end and duplicates are weeded.\r
+ * XXX: assumes vals in 'mapping' are small +ve integers\r
+ */\r
+static void gprefs(void *sesskey, char *name, char *def,\r
+                  const struct keyval *mapping, int nvals,\r
+                  int *array)\r
+{\r
+    char commalist[80];\r
+    char *tokarg = commalist;\r
+    int n;\r
+    unsigned long seen = 0;           /* bitmap for weeding dups etc */\r
+    gpps(sesskey, name, def, commalist, sizeof(commalist));\r
+\r
+    /* Grotty parsing of commalist. */\r
+    n = 0;\r
+    do {\r
+       int v;\r
+       char *key;\r
+       key = strtok(tokarg, ","); /* sorry */\r
+       tokarg = NULL;\r
+       if (!key) break;\r
+       if (((v = key2val(mapping, nvals, key)) != -1) &&\r
+           !(seen & 1<<v)) {\r
+           array[n] = v;\r
+           n++;\r
+           seen |= 1<<v;\r
+       }\r
+    } while (n < nvals);\r
+    /* Add any missing values (backward compatibility ect). */\r
+    {\r
+       int i;\r
+       for (i = 0; i < nvals; i++) {\r
+           assert(mapping[i].v < 32);\r
+           if (!(seen & 1<<mapping[i].v)) {\r
+               array[n] = mapping[i].v;\r
+               n++;\r
+           }\r
+       }\r
+    }\r
+}\r
+\r
+/* \r
+ * Write out a preference list.\r
+ */\r
+static void wprefs(void *sesskey, char *name,\r
+                  const struct keyval *mapping, int nvals,\r
+                  int *array)\r
+{\r
+    char buf[80] = ""; /* XXX assumed big enough */\r
+    int l = sizeof(buf)-1, i;\r
+    buf[l] = '\0';\r
+    for (i = 0; l > 0 && i < nvals; i++) {\r
+       const char *s = val2key(mapping, nvals, array[i]);\r
+       if (s) {\r
+           int sl = strlen(s);\r
+           if (i > 0) {\r
+               strncat(buf, ",", l);\r
+               l--;\r
+           }\r
+           strncat(buf, s, l);\r
+           l -= sl;\r
+       }\r
+    }\r
+    write_setting_s(sesskey, name, buf);\r
+}\r
+\r
+char *save_settings(char *section, Config * cfg)\r
+{\r
+    void *sesskey;\r
+    char *errmsg;\r
+\r
+    sesskey = open_settings_w(section, &errmsg);\r
+    if (!sesskey)\r
+       return errmsg;\r
+    save_open_settings(sesskey, cfg);\r
+    close_settings_w(sesskey);\r
+    return NULL;\r
+}\r
+\r
+void save_open_settings(void *sesskey, Config *cfg)\r
+{\r
+    int i;\r
+    char *p;\r
+\r
+    write_setting_i(sesskey, "Present", 1);\r
+    write_setting_s(sesskey, "HostName", cfg->host);\r
+    write_setting_filename(sesskey, "LogFileName", cfg->logfilename);\r
+    write_setting_i(sesskey, "LogType", cfg->logtype);\r
+    write_setting_i(sesskey, "LogFileClash", cfg->logxfovr);\r
+    write_setting_i(sesskey, "LogFlush", cfg->logflush);\r
+    write_setting_i(sesskey, "SSHLogOmitPasswords", cfg->logomitpass);\r
+    write_setting_i(sesskey, "SSHLogOmitData", cfg->logomitdata);\r
+    p = "raw";\r
+    for (i = 0; backends[i].name != NULL; i++)\r
+       if (backends[i].protocol == cfg->protocol) {\r
+           p = backends[i].name;\r
+           break;\r
+       }\r
+    write_setting_s(sesskey, "Protocol", p);\r
+    write_setting_i(sesskey, "PortNumber", cfg->port);\r
+    /* The CloseOnExit numbers are arranged in a different order from\r
+     * the standard FORCE_ON / FORCE_OFF / AUTO. */\r
+    write_setting_i(sesskey, "CloseOnExit", (cfg->close_on_exit+2)%3);\r
+    write_setting_i(sesskey, "WarnOnClose", !!cfg->warn_on_close);\r
+    write_setting_i(sesskey, "PingInterval", cfg->ping_interval / 60); /* minutes */\r
+    write_setting_i(sesskey, "PingIntervalSecs", cfg->ping_interval % 60);     /* seconds */\r
+    write_setting_i(sesskey, "TCPNoDelay", cfg->tcp_nodelay);\r
+    write_setting_i(sesskey, "TCPKeepalives", cfg->tcp_keepalives);\r
+    write_setting_s(sesskey, "TerminalType", cfg->termtype);\r
+    write_setting_s(sesskey, "TerminalSpeed", cfg->termspeed);\r
+    wmap(sesskey, "TerminalModes", cfg->ttymodes, lenof(cfg->ttymodes));\r
+\r
+    /* Address family selection */\r