From aeca9fa80cbdf6e85f15e0c676b70f76c7ae6037 Mon Sep 17 00:00:00 2001
From: marha <marha@users.sourceforge.net>
Date: Thu, 27 Jan 2011 15:37:48 +0000
Subject: plink: updated to putty revision 9080

---
 tools/plink/cmdline.c  |   1 +
 tools/plink/putty.h    |  17 +++++
 tools/plink/settings.c |   7 ++
 tools/plink/ssh.c      |   8 ++-
 tools/plink/version.c  |   2 +-
 tools/plink/winhelp.h  |   1 +
 tools/plink/winpgntc.c |  91 +++++++++++++++++++++++++-
 tools/plink/winstore.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++
 tools/plink/winstuff.h |  48 +++++++++++++-
 9 files changed, 342 insertions(+), 6 deletions(-)

diff --git a/tools/plink/cmdline.c b/tools/plink/cmdline.c
index 6492132f1..aa376a053 100644
--- a/tools/plink/cmdline.c
+++ b/tools/plink/cmdline.c
@@ -172,6 +172,7 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
 	 * saved. */
 	do_defaults(value, cfg);
 	loaded_session = TRUE;
+	cmdline_session_name = dupstr(value);
 	return 2;
     }
     if (!strcmp(p, "-ssh")) {
diff --git a/tools/plink/putty.h b/tools/plink/putty.h
index ac2701133..c72d8eb76 100644
--- a/tools/plink/putty.h
+++ b/tools/plink/putty.h
@@ -470,6 +470,7 @@ struct config_tag {
     int sshprot;		       /* use v1 or v2 when both available */
     int ssh2_des_cbc;		       /* "des-cbc" unrecommended SSH-2 cipher */
     int ssh_no_userauth;	       /* bypass "ssh-userauth" (SSH-2 only) */
+    int ssh_show_banner;	       /* show USERAUTH_BANNERs (SSH-2 only) */
     int try_tis_auth;
     int try_ki_auth;
     int try_gssapi_auth;               /* attempt gssapi auth */
@@ -625,6 +626,7 @@ struct config_tag {
     FontSpec wideboldfont;
     int shadowboldoffset;
     int crhaslf;
+    char winclass[256];
 };
 
 /*
@@ -666,6 +668,10 @@ GLOBAL int default_port;
  * This is set TRUE by cmdline.c iff a session is loaded with "-load".
  */
 GLOBAL int loaded_session;
+/*
+ * This is set to the name of the loaded session.
+ */
+GLOBAL char *cmdline_session_name;
 
 struct RSAKey;			       /* be a little careful of scope */
 
@@ -1246,4 +1252,15 @@ void expire_timer_context(void *ctx);
 int run_timers(long now, long *next);
 void timer_change_notify(long next);
 
+/*
+ * Define no-op macros for the jump list functions, on platforms that
+ * don't support them. (This is a bit of a hack, and it'd be nicer to
+ * localise even the calls to those functions into the Windows front
+ * end, but it'll do for the moment.)
+ */
+#ifndef JUMPLIST_SUPPORTED
+#define add_session_to_jumplist(x) ((void)0)
+#define remove_session_from_jumplist(x) ((void)0)
+#endif
+
 #endif
diff --git a/tools/plink/settings.c b/tools/plink/settings.c
index bd6b97495..46e19f49c 100644
--- a/tools/plink/settings.c
+++ b/tools/plink/settings.c
@@ -348,6 +348,7 @@ void save_open_settings(void *sesskey, Config *cfg)
     write_setting_i(sesskey, "RekeyTime", cfg->ssh_rekey_time);
     write_setting_s(sesskey, "RekeyBytes", cfg->ssh_rekey_data);
     write_setting_i(sesskey, "SshNoAuth", cfg->ssh_no_userauth);
+    write_setting_i(sesskey, "SshBanner", cfg->ssh_show_banner);
     write_setting_i(sesskey, "AuthTIS", cfg->try_tis_auth);
     write_setting_i(sesskey, "AuthKI", cfg->try_ki_auth);
     write_setting_i(sesskey, "AuthGSSAPI", cfg->try_gssapi_auth);
@@ -498,6 +499,7 @@ void save_open_settings(void *sesskey, Config *cfg)
     write_setting_i(sesskey, "SerialStopHalfbits", cfg->serstopbits);
     write_setting_i(sesskey, "SerialParity", cfg->serparity);
     write_setting_i(sesskey, "SerialFlowControl", cfg->serflow);
+    write_setting_s(sesskey, "WindowClass", cfg->winclass);
 }
 
 void load_settings(char *section, Config * cfg)
@@ -507,6 +509,9 @@ void load_settings(char *section, Config * cfg)
     sesskey = open_settings_r(section);
     load_open_settings(sesskey, cfg);
     close_settings_r(sesskey);
+
+    if (cfg_launchable(cfg))
+        add_session_to_jumplist(section);
 }
 
 void load_open_settings(void *sesskey, Config *cfg)
@@ -642,6 +647,7 @@ void load_open_settings(void *sesskey, Config *cfg)
     gpps(sesskey, "LogHost", "", cfg->loghost, sizeof(cfg->loghost));
     gppi(sesskey, "SSH2DES", 0, &cfg->ssh2_des_cbc);
     gppi(sesskey, "SshNoAuth", 0, &cfg->ssh_no_userauth);
+    gppi(sesskey, "SshBanner", 1, &cfg->ssh_show_banner);
     gppi(sesskey, "AuthTIS", 0, &cfg->try_tis_auth);
     gppi(sesskey, "AuthKI", 1, &cfg->try_ki_auth);
     gppi(sesskey, "AuthGSSAPI", 1, &cfg->try_gssapi_auth);
@@ -853,6 +859,7 @@ void load_open_settings(void *sesskey, Config *cfg)
     gppi(sesskey, "SerialStopHalfbits", 2, &cfg->serstopbits);
     gppi(sesskey, "SerialParity", SER_PAR_NONE, &cfg->serparity);
     gppi(sesskey, "SerialFlowControl", SER_FLOW_XONXOFF, &cfg->serflow);
+    gpps(sesskey, "WindowClass", "", cfg->winclass, sizeof(cfg->winclass));
 }
 
 void do_defaults(char *session, Config * cfg)
diff --git a/tools/plink/ssh.c b/tools/plink/ssh.c
index 7c9b929de..0982f84a4 100644
--- a/tools/plink/ssh.c
+++ b/tools/plink/ssh.c
@@ -2862,6 +2862,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
 		x11_close(c->u.x11.s);
 		break;
 	      case CHAN_SOCKDATA:
+	      case CHAN_SOCKDATA_DORMANT:
 		pfd_close(c->u.pfd.s);
 		break;
 	    }
@@ -7194,12 +7195,14 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
 }
 
 /*
- * Buffer banner messages for later display at some convenient point.
+ * Buffer banner messages for later display at some convenient point,
+ * if we're going to display them.
  */
 static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
 {
     /* Arbitrary limit to prevent unbounded inflation of buffer */
-    if (bufchain_size(&ssh->banner) <= 131072) {
+    if (ssh->cfg.ssh_show_banner &&
+	bufchain_size(&ssh->banner) <= 131072) {
 	char *banner = NULL;
 	int size = 0;
 	ssh_pkt_getstring(pktin, &banner, &size);
@@ -9355,6 +9358,7 @@ static void ssh_free(void *handle)
 		    x11_close(c->u.x11.s);
 		break;
 	      case CHAN_SOCKDATA:
+	      case CHAN_SOCKDATA_DORMANT:
 		if (c->u.pfd.s != NULL)
 		    pfd_close(c->u.pfd.s);
 		break;
diff --git a/tools/plink/version.c b/tools/plink/version.c
index 483495771..bcb233272 100644
--- a/tools/plink/version.c
+++ b/tools/plink/version.c
@@ -5,7 +5,7 @@
 #define STR1(x) #x
 #define STR(x) STR1(x)
 
-#define SVN_REV 9025
+#define SVN_REV 9080
 
 #if defined SNAPSHOT
 
diff --git a/tools/plink/winhelp.h b/tools/plink/winhelp.h
index 9ee140fef..d670c0cb8 100644
--- a/tools/plink/winhelp.h
+++ b/tools/plink/winhelp.h
@@ -102,6 +102,7 @@
 #define WINHELP_CTX_ssh_kexlist "ssh.kex.order:config-ssh-kex-order"
 #define WINHELP_CTX_ssh_kex_repeat "ssh.kex.repeat:config-ssh-kex-rekey"
 #define WINHELP_CTX_ssh_auth_bypass "ssh.auth.bypass:config-ssh-noauth"
+#define WINHELP_CTX_ssh_auth_banner "ssh.auth.banner:config-ssh-banner"
 #define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey:config-ssh-privkey"
 #define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd:config-ssh-agentfwd"
 #define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser:config-ssh-changeuser"
diff --git a/tools/plink/winpgntc.c b/tools/plink/winpgntc.c
index 3bfafc972..2a5aa734f 100644
--- a/tools/plink/winpgntc.c
+++ b/tools/plink/winpgntc.c
@@ -7,6 +7,10 @@
 
 #include "putty.h"
 
+#ifndef NO_SECURITY
+#include <aclapi.h>
+#endif
+
 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
 #define AGENT_MAX_MSGLEN  8192
 
@@ -66,6 +70,33 @@ DWORD WINAPI agent_query_thread(LPVOID param)
 
 #endif
 
+/*
+ * Dynamically load advapi32.dll for SID manipulation. In its absence,
+ * we degrade gracefully.
+ */
+#ifndef NO_SECURITY
+int advapi_initialised = FALSE;
+static HMODULE advapi;
+DECL_WINDOWS_FUNCTION(static, BOOL, OpenProcessToken,
+		      (HANDLE, DWORD, PHANDLE));
+DECL_WINDOWS_FUNCTION(static, BOOL, GetTokenInformation,
+		      (HANDLE, TOKEN_INFORMATION_CLASS,
+                       LPVOID, DWORD, PDWORD));
+DECL_WINDOWS_FUNCTION(static, BOOL, InitializeSecurityDescriptor,
+		      (PSECURITY_DESCRIPTOR, DWORD));
+DECL_WINDOWS_FUNCTION(static, BOOL, SetSecurityDescriptorOwner,
+		      (PSECURITY_DESCRIPTOR, PSID, BOOL));
+static int init_advapi(void)
+{
+    advapi = load_system32_dll("advapi32.dll");
+    return advapi &&
+        GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) &&
+	GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) &&
+	GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) &&
+	GET_WINDOWS_FUNCTION(advapi, SetSecurityDescriptorOwner);
+}
+#endif
+
 int agent_query(void *in, int inlen, void **out, int *outlen,
 		void (*callback)(void *, void *, int), void *callback_ctx)
 {
@@ -75,6 +106,10 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     unsigned char *p, *ret;
     int id, retlen;
     COPYDATASTRUCT cds;
+    SECURITY_ATTRIBUTES sa, *psa;
+    PSECURITY_DESCRIPTOR psd = NULL;
+    HANDLE proc, tok;
+    TOKEN_USER *user = NULL;
 
     *out = NULL;
     *outlen = 0;
@@ -83,7 +118,57 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     if (!hwnd)
 	return 1;		       /* *out == NULL, so failure */
     mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
-    filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+
+#ifndef NO_SECURITY
+    if (advapi_initialised || init_advapi()) {
+        /*
+         * Make the file mapping we create for communication with
+         * Pageant owned by the user SID rather than the default. This
+         * should make communication between processes with slightly
+         * different contexts more reliable: in particular, command
+         * prompts launched as administrator should still be able to
+         * run PSFTPs which refer back to the owning user's
+         * unprivileged Pageant.
+         */
+
+        if ((proc = OpenProcess(MAXIMUM_ALLOWED, FALSE,
+                                GetCurrentProcessId())) != NULL) {
+            if (p_OpenProcessToken(proc, TOKEN_QUERY, &tok)) {
+                DWORD retlen;
+                p_GetTokenInformation(tok, TokenUser, NULL, 0, &retlen);
+                user = (TOKEN_USER *)LocalAlloc(LPTR, retlen);
+                if (!p_GetTokenInformation(tok, TokenUser,
+                                           user, retlen, &retlen)) {
+                    LocalFree(user);
+                    user = NULL;
+                }
+                CloseHandle(tok);
+            }
+            CloseHandle(proc);
+        }
+
+        psa = NULL;
+        if (user) {
+            psd = (PSECURITY_DESCRIPTOR)
+                LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+            if (psd) {
+                if (p_InitializeSecurityDescriptor
+                    (psd, SECURITY_DESCRIPTOR_REVISION) &&
+                    p_SetSecurityDescriptorOwner(psd, user->User.Sid, FALSE)) {
+                    sa.nLength = sizeof(sa);
+                    sa.bInheritHandle = TRUE;
+                    sa.lpSecurityDescriptor = psd;
+                    psa = &sa;
+                } else {
+                    LocalFree(psd);
+                    psd = NULL;
+                }
+            }
+        }
+    }
+#endif /* NO_SECURITY */
+
+    filemap = CreateFileMapping(INVALID_HANDLE_VALUE, psa, PAGE_READWRITE,
 				0, AGENT_MAX_MSGLEN, mapname);
     if (filemap == NULL || filemap == INVALID_HANDLE_VALUE)
 	return 1;		       /* *out == NULL, so failure */
@@ -134,5 +219,9 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     }
     UnmapViewOfFile(p);
     CloseHandle(filemap);
+    if (psd)
+        LocalFree(psd);
+    if (user)
+        LocalFree(user);
     return 1;
 }
diff --git a/tools/plink/winstore.c b/tools/plink/winstore.c
index f7dd98965..13ee184a5 100644
--- a/tools/plink/winstore.c
+++ b/tools/plink/winstore.c
@@ -17,6 +17,8 @@
 #define CSIDL_LOCAL_APPDATA 0x001c
 #endif
 
+static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist";
+static const char *const reg_jumplist_value = "Recent sessions";
 static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
 
 static const char hex[16] = "0123456789ABCDEF";
@@ -243,6 +245,8 @@ void del_settings(const char *sessionname)
     sfree(p);
 
     RegCloseKey(subkey1);
+
+    remove_session_from_jumplist(sessionname);
 }
 
 struct enumsettings {
@@ -581,6 +585,169 @@ void write_random_seed(void *data, int len)
     }
 }
 
+/*
+ * Internal function supporting the jump list registry code. All the
+ * functions to add, remove and read the list have substantially
+ * similar content, so this is a generalisation of all of them which
+ * transforms the list in the registry by prepending 'add' (if
+ * non-null), removing 'rem' from what's left (if non-null), and
+ * returning the resulting concatenated list of strings in 'out' (if
+ * non-null).
+ */
+static int transform_jumplist_registry
+    (const char *add, const char *rem, char **out)
+{
+    int ret;
+    HKEY pjumplist_key, psettings_tmp;
+    DWORD type;
+    int value_length;
+    char *old_value, *new_value;
+    char *piterator_old, *piterator_new, *piterator_tmp;
+
+    ret = RegCreateKeyEx(HKEY_CURRENT_USER, reg_jumplist_key, 0, NULL,
+                         REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL,
+                         &pjumplist_key, NULL);
+    if (ret != ERROR_SUCCESS) {
+	return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE;
+    }
+
+    /* Get current list of saved sessions in the registry. */
+    value_length = 200;
+    old_value = snewn(value_length, char);
+    ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,
+                          old_value, &value_length);
+    /* When the passed buffer is too small, ERROR_MORE_DATA is
+     * returned and the required size is returned in the length
+     * argument. */
+    if (ret == ERROR_MORE_DATA) {
+        sfree(old_value);
+        old_value = snewn(value_length, char);
+        ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type,
+                              old_value, &value_length);
+    }
+
+    if (ret == ERROR_FILE_NOT_FOUND) {
+        /* Value doesn't exist yet. Start from an empty value. */
+        *old_value = '\0';
+        *(old_value + 1) = '\0';
+    } else if (ret != ERROR_SUCCESS) {
+        /* Some non-recoverable error occurred. */
+        sfree(old_value);
+        RegCloseKey(pjumplist_key);
+        return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;
+    } else if (type != REG_MULTI_SZ) {
+        /* The value present in the registry has the wrong type: we
+         * try to delete it and start from an empty value. */
+        ret = RegDeleteValue(pjumplist_key, reg_jumplist_value);
+        if (ret != ERROR_SUCCESS) {
+            sfree(old_value);
+            RegCloseKey(pjumplist_key);
+            return JUMPLISTREG_ERROR_VALUEREAD_FAILURE;
+        }
+
+        *old_value = '\0';
+        *(old_value + 1) = '\0';
+    }
+
+    /* Check validity of registry data: REG_MULTI_SZ value must end
+     * with \0\0. */
+    piterator_tmp = old_value;
+    while (((piterator_tmp - old_value) < (value_length - 1)) &&
+           !(*piterator_tmp == '\0' && *(piterator_tmp+1) == '\0')) {
+        ++piterator_tmp;
+    }
+
+    if ((piterator_tmp - old_value) >= (value_length-1)) {
+        /* Invalid value. Start from an empty value. */
+        *old_value = '\0';
+        *(old_value + 1) = '\0';
+    }
+
+    /*
+     * Modify the list, if we're modifying.
+     */
+    if (add || rem) {
+        /* Walk through the existing list and construct the new list of
+         * saved sessions. */
+        new_value = snewn(value_length + (add ? strlen(add) + 1 : 0), char);
+        piterator_new = new_value;
+        piterator_old = old_value;
+
+        /* First add the new item to the beginning of the list. */
+        if (add) {
+            strcpy(piterator_new, add);
+            piterator_new += strlen(piterator_new) + 1;
+        }
+        /* Now add the existing list, taking care to leave out the removed
+         * item, if it was already in the existing list. */
+        while (*piterator_old != '\0') {
+            if (!rem || strcmp(piterator_old, rem) != 0) {
+                /* Check if this is a valid session, otherwise don't add. */
+                psettings_tmp = open_settings_r(piterator_old);
+                if (psettings_tmp != NULL) {
+                    close_settings_r(psettings_tmp);
+                    strcpy(piterator_new, piterator_old);
+                    piterator_new += strlen(piterator_new) + 1;
+                }
+            }
+            piterator_old += strlen(piterator_old) + 1;
+        }
+        *piterator_new = '\0';
+        ++piterator_new;
+
+        /* Save the new list to the registry. */
+        ret = RegSetValueEx(pjumplist_key, reg_jumplist_value, 0, REG_MULTI_SZ,
+                            new_value, piterator_new - new_value);
+
+        sfree(old_value);
+        old_value = new_value;
+    } else
+        ret = ERROR_SUCCESS;
+
+    /*
+     * Either return or free the result.
+     */
+    if (out)
+        *out = old_value;
+    else
+        sfree(old_value);
+
+    /* Clean up and return. */
+    RegCloseKey(pjumplist_key);
+
+    if (ret != ERROR_SUCCESS) {
+        return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE;
+    } else {
+        return JUMPLISTREG_OK;
+    }
+}
+
+/* Adds a new entry to the jumplist entries in the registry. */
+int add_to_jumplist_registry(const char *item)
+{
+    return transform_jumplist_registry(item, item, NULL);
+}
+
+/* Removes an item from the jumplist entries in the registry. */
+int remove_from_jumplist_registry(const char *item)
+{
+    return transform_jumplist_registry(NULL, item, NULL);
+}
+
+/* Returns the jumplist entries from the registry. Caller must free
+ * the returned pointer. */
+char *get_jumplist_registry_entries (void)
+{
+    char *list_value;
+
+    if (transform_jumplist_registry(NULL,NULL,&list_value) != ERROR_SUCCESS) {
+	list_value = snewn(2, char);
+        *list_value = '\0';
+        *(list_value + 1) = '\0';
+    }
+    return list_value;
+}
+
 /*
  * Recursively delete a registry key and everything under it.
  */
@@ -612,6 +779,12 @@ void cleanup_all(void)
      */
     access_random_seed(DEL);
 
+    /* ------------------------------------------------------------
+     * Ask Windows to delete any jump list information associated
+     * with this installation of PuTTY.
+     */
+    clear_jumplist();
+
     /* ------------------------------------------------------------
      * Destroy all registry information associated with PuTTY.
      */
diff --git a/tools/plink/winstuff.h b/tools/plink/winstuff.h
index eeceea17a..1cc48348e 100644
--- a/tools/plink/winstuff.h
+++ b/tools/plink/winstuff.h
@@ -96,9 +96,9 @@ struct FontSpec {
 #define STR1(x) #x
 #define STR(x) STR1(x)
 #define GET_WINDOWS_FUNCTION_PP(module, name) \
-    p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL
+    (p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL)
 #define GET_WINDOWS_FUNCTION(module, name) \
-    p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
+    (p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL)
 
 /*
  * Global variables. Most modules declare these `extern', but
@@ -126,6 +126,14 @@ typedef struct terminal_tag Terminal;
 #define PUTTY_REG_GPARENT "Software"
 #define PUTTY_REG_GPARENT_CHILD "SimonTatham"
 
+/* Result values for the jumplist registry functions. */
+#define JUMPLISTREG_OK 0
+#define JUMPLISTREG_ERROR_INVALID_PARAMETER 1
+#define JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE 2
+#define JUMPLISTREG_ERROR_VALUEREAD_FAILURE 3
+#define JUMPLISTREG_ERROR_VALUEWRITE_FAILURE 4
+#define JUMPLISTREG_ERROR_INVALID_VALUE 5
+
 #define PUTTY_HELP_FILE "putty.hlp"
 #define PUTTY_CHM_FILE "putty.chm"
 #define PUTTY_HELP_CONTENTS "putty.cnt"
@@ -308,6 +316,7 @@ struct dlgparam {
     struct { unsigned char r, g, b, ok; } coloursel_result;   /* 0-255 */
     tree234 *privdata;		       /* stores per-control private data */
     int ended, endresult;	       /* has the dialog been ended? */
+    int fixed_pitch_fonts;             /* are we constrained to fixed fonts? */
 };
 
 /*
@@ -366,6 +375,10 @@ void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
 	       char *btext, int bid,
 	       char *r1text, int r1id, char *r2text, int r2id);
 
+void dlg_auto_set_fixed_pitch_flag(void *dlg);
+int dlg_get_fixed_pitch_flag(void *dlg);
+void dlg_set_fixed_pitch_flag(void *dlg, int flag);
+
 #define MAX_SHORTCUTS_PER_CTRL 16
 
 /*
@@ -500,4 +513,35 @@ void agent_schedule_callback(void (*callback)(void *, void *, int),
  */
 extern Backend serial_backend;
 
+/*
+ * Exports from winjump.c.
+ */
+#define JUMPLIST_SUPPORTED             /* suppress #defines in putty.h */
+void add_session_to_jumplist(const char * const sessionname);
+void remove_session_from_jumplist(const char * const sessionname);
+void clear_jumplist(void);
+
+/*
+ * Extra functions in winstore.c over and above the interface in
+ * storage.h.
+ *
+ * These functions manipulate the Registry section which mirrors the
+ * current Windows 7 jump list. (Because the real jump list storage is
+ * write-only, we need to keep another copy of whatever we put in it,
+ * so that we can put in a slightly modified version the next time.)
+ */
+
+/* Adds a saved session to the registry jump list mirror. 'item' is a
+ * string naming a saved session. */
+int add_to_jumplist_registry(const char *item);
+
+/* Removes an item from the registry jump list mirror. */
+int remove_from_jumplist_registry(const char *item);
+
+/* Returns the current jump list entries from the registry. Caller
+ * must free the returned pointer, which points to a contiguous
+ * sequence of NUL-terminated strings in memory, terminated with an
+ * empty one. */
+char *get_jumplist_registry_entries(void);
+
 #endif
-- 
cgit v1.2.3