Colin's and my fixes to connection_fatal().
[u/mdw/putty] / window.c
index 410c37e..6d306f5 100644 (file)
--- a/window.c
+++ b/window.c
 #define IDM_RECONF    0x0040
 #define IDM_CLRSB     0x0050
 #define IDM_RESET     0x0060
-#define IDM_TEL_AYT   0x0070
-#define IDM_TEL_BRK   0x0080
-#define IDM_TEL_SYNCH 0x0090
-#define IDM_TEL_EC    0x00a0
-#define IDM_TEL_EL    0x00b0
-#define IDM_TEL_GA    0x00c0
-#define IDM_TEL_NOP   0x00d0
-#define IDM_TEL_ABORT 0x00e0
-#define IDM_TEL_AO    0x00f0
-#define IDM_TEL_IP    0x0100
-#define IDM_TEL_SUSP  0x0110
-#define IDM_TEL_EOR   0x0120
-#define IDM_TEL_EOF   0x0130
 #define IDM_HELP      0x0140
 #define IDM_ABOUT     0x0150
 #define IDM_SAVEDSESS 0x0160
 #define IDM_SESSLGP   0x0250          /* log type printable */
 #define IDM_SESSLGA   0x0260          /* log type all chars */
 #define IDM_SESSLGE   0x0270          /* log end */
+
+#define IDM_SPECIAL_MIN 0x0400
+#define IDM_SPECIAL_MAX 0x0800
+
 #define IDM_SAVED_MIN 0x1000
 #define IDM_SAVED_MAX 0x2000
 
 #define WM_IGNORE_CLIP (WM_XUSER + 2)
 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
+#define WM_AGENT_CALLBACK (WM_XUSER + 4)
 
 /* Needed for Chinese support and apparently not always defined. */
 #ifndef VK_PROCESSKEY
@@ -78,6 +70,7 @@
 #define WHEEL_DELTA 120
 #endif
 
+static Mouse_Button translate_button(Mouse_Button button);
 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        unsigned char *output);
@@ -123,10 +116,20 @@ static void *backhandle;
 static struct unicode_data ucsdata;
 static int session_closed;
 
+static const struct telnet_special *specials;
+static int specials_menu_position;
+
 Config cfg;                           /* exported to windlg.c */
 
 extern struct sesslist sesslist;       /* imported from windlg.c */
 
+struct agent_callback {
+    void (*callback)(void *, void *, int);
+    void *callback_ctx;
+    void *data;
+    int len;
+};
+
 #define FONT_NORMAL 0
 #define FONT_BOLD 1
 #define FONT_UNDERLINE 2
@@ -187,7 +190,6 @@ void ldisc_update(void *frontend, int echo, int edit)
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 {
-    static char appname[] = "PuTTY";
     WORD winsock_ver;
     WSADATA wsadata;
     WNDCLASS wndclass;
@@ -223,8 +225,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        ZeroMemory(&osVersion, sizeof(osVersion));
        osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
        if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
+           char *str = dupprintf("%s Fatal Error", appname);
             MessageBox(NULL, "Windows refuses to report a version",
-                       "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
+                       str, MB_OK | MB_ICONEXCLAMATION);
+           sfree(str);
            return 1;
         }
     }
@@ -355,18 +359,21 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                     * entries associated with PuTTY, and also find
                     * and delete the random seed file.
                     */
-                   if (MessageBox(NULL,
-                                  "This procedure will remove ALL Registry\n"
-                                  "entries associated with PuTTY, and will\n"
-                                  "also remove the PuTTY random seed file.\n"
+                   char *s1, *s2;
+                   s1 = dupprintf("This procedure will remove ALL Registry\n"
+                                  "entries associated with %s, and will\n"
+                                  "also remove the random seed file.\n"
                                   "\n"
                                   "THIS PROCESS WILL DESTROY YOUR SAVED\n"
                                   "SESSIONS. Are you really sure you want\n"
-                                  "to continue?",
-                                  "PuTTY Warning",
+                                  "to continue?", appname);
+                   s2 = dupprintf("%s Warning", appname);
+                   if (MessageBox(NULL, s1, s2,
                                   MB_YESNO | MB_ICONWARNING) == IDYES) {
                        cleanup_all();
                    }
+                   sfree(s1);
+                   sfree(s2);
                    exit(0);
                } else if (*p != '-') {
                    char *q = p;
@@ -486,8 +493,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
                break;
            }
        if (back == NULL) {
+           char *str = dupprintf("%s Internal Error", appname);
            MessageBox(NULL, "Unsupported protocol number found",
-                      "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
+                      str, MB_OK | MB_ICONEXCLAMATION);
+           sfree(str);
            WSACleanup();
            return 1;
        }
@@ -495,8 +504,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
 
     /* Check for invalid Port number (i.e. zero) */
     if (cfg.port == 0) {
+       char *str = dupprintf("%s Internal Error", appname);
        MessageBox(NULL, "Invalid Port Number",
-                  "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
+                  str, MB_OK | MB_ICONEXCLAMATION);
+       sfree(str);
        WSACleanup();
        return 1;
     }
@@ -599,7 +610,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     {
        char *bits;
        int size = (font_width + 15) / 16 * 2 * font_height;
-       bits = smalloc(size);
+       bits = snewn(size, char);
        memset(bits, 0, size);
        caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
        sfree(bits);
@@ -629,20 +640,22 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        char msg[1024], *title;
        char *realhost;
 
-       error = back->init((void *)term, &backhandle, &cfg,
+       error = back->init(NULL, &backhandle, &cfg,
                           cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
        back->provide_logctx(backhandle, logctx);
        if (error) {
+           char *str = dupprintf("%s Error", appname);
            sprintf(msg, "Unable to open connection to\n"
                    "%.800s\n" "%s", cfg.host, error);
-           MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
+           MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
+           sfree(str);
            return 0;
        }
        window_name = icon_name = NULL;
        if (*cfg.wintitle) {
            title = cfg.wintitle;
        } else {
-           sprintf(msg, "%s - PuTTY", realhost);
+           sprintf(msg, "%s - %s", realhost, appname);
            title = msg;
        }
        sfree(realhost);
@@ -674,32 +687,12 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
      */
     {
        HMENU m = GetSystemMenu(hwnd, FALSE);
-       HMENU p, s;
+       HMENU s;
        int i;
+       char *str;
 
        AppendMenu(m, MF_SEPARATOR, 0, 0);
-       if (cfg.protocol == PROT_TELNET) {
-           p = CreateMenu();
-           AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
-           AppendMenu(p, MF_SEPARATOR, 0, 0);
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
-           AppendMenu(p, MF_SEPARATOR, 0, 0);
-           AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
-           AppendMenu(p, MF_SEPARATOR, 0, 0);
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
-           AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
-           AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
-                      "Telnet Command");
-           AppendMenu(m, MF_SEPARATOR, 0, 0);
-       }
+       specials_menu_position = GetMenuItemCount(m);
        AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
        AppendMenu(m, MF_SEPARATOR, 0, 0);
        AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
@@ -723,9 +716,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        AppendMenu(m, MF_SEPARATOR, 0, 0);
         if (help_path)
             AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
-       AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
+       str = dupprintf("&About %s", appname);
+       AppendMenu(m, MF_ENABLED, IDM_ABOUT, str);
+       sfree(str);
     }
 
+    update_specials_menu(NULL);
+
     /*
      * Set up the initial input locale.
      */
@@ -787,9 +784,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
            if (pending_netevent) {
                enact_pending_netevent();
 
-               /* Force the cursor blink on */
-               term_blink(term, 1);
-
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                    continue;
            }
@@ -890,6 +884,37 @@ char *do_select(SOCKET skt, int startup)
 }
 
 /*
+ * Update the Special Commands submenu.
+ */
+void update_specials_menu(void *frontend)
+{
+    HMENU m = GetSystemMenu(hwnd, FALSE);
+    int menu_already_exists = (specials != NULL);
+    int i;
+
+    specials = back->get_specials(backhandle);
+    if (specials) {
+       HMENU p = CreateMenu();
+       for (i = 0; specials[i].name; i++) {
+           assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
+           if (*specials[i].name)
+               AppendMenu(p, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
+                          specials[i].name);
+           else
+               AppendMenu(p, MF_SEPARATOR, 0, 0);
+       }
+       if (menu_already_exists)
+           DeleteMenu(m, specials_menu_position, MF_BYPOSITION);
+       else
+           InsertMenu(m, specials_menu_position,
+                      MF_BYPOSITION | MF_SEPARATOR, 0, 0);
+       InsertMenu(m, specials_menu_position,
+                  MF_BYPOSITION | MF_POPUP | MF_ENABLED,
+                  (UINT) p, "Special Command");
+    }
+}
+
+/*
  * set or clear the "raw mouse message" mode
  */
 void set_raw_mouse_mode(void *frontend, int activate)
@@ -905,18 +930,22 @@ void set_raw_mouse_mode(void *frontend, int activate)
 void connection_fatal(void *frontend, char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
-    if (cfg.close_on_exit == COE_ALWAYS)
+    sprintf(morestuff, "%.70s Fatal Error", appname);
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
+    sfree(stuff);
+
+    if (cfg.close_on_exit == FORCE_ON)
        PostQuitMessage(1);
     else {
        session_closed = TRUE;
-       set_icon(NULL, "PuTTY (inactive)");
-       set_title(NULL, "PuTTY (inactive)");
+       sprintf(morestuff, "%.70s (inactive)", appname);
+       set_icon(NULL, morestuff);
+       set_title(NULL, morestuff);
     }
 }
 
@@ -926,12 +955,14 @@ void connection_fatal(void *frontend, char *fmt, ...)
 void cmdline_error(char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK);
+    sprintf(morestuff, "%.70s Command Line Error", appname);
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
+    sfree(stuff);
     exit(1);
 }
 
@@ -956,14 +987,16 @@ static void enact_pending_netevent(void)
     if (ret == 0 && !session_closed) {
        /* Abnormal exits will already have set session_closed and taken
         * appropriate action. */
-       if (cfg.close_on_exit == COE_ALWAYS ||
-           cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
+       if (cfg.close_on_exit == FORCE_ON ||
+           cfg.close_on_exit == AUTO) PostQuitMessage(0);
        else {
+           char morestuff[100];
            session_closed = TRUE;
-           set_icon(NULL, "PuTTY (inactive)");
-           set_title(NULL, "PuTTY (inactive)");
+           sprintf(morestuff, "%.70s (inactive)", appname);
+           set_icon(NULL, morestuff);
+           set_title(NULL, morestuff);
            MessageBox(hwnd, "Connection closed by remote host",
-                      "PuTTY", MB_OK | MB_ICONINFORMATION);
+                      appname, MB_OK | MB_ICONINFORMATION);
        }
     }
 }
@@ -998,6 +1031,10 @@ static void init_palette(void)
     HDC hdc = GetDC(hwnd);
     if (hdc) {
        if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
+           /*
+            * This is a genuine case where we must use smalloc
+            * because the snew macros can't cope.
+            */
            logpal = smalloc(sizeof(*logpal)
                             - sizeof(logpal->palPalEntry)
                             + NCOLOURS * sizeof(PALETTEENTRY));
@@ -1061,7 +1098,7 @@ static void init_fonts(int pick_width, int pick_height)
     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
     und_mode = UND_FONT;
 
-    if (cfg.fontisbold) {
+    if (cfg.font.isbold) {
        fw_dontcare = FW_BOLD;
        fw_bold = FW_HEAVY;
     } else {
@@ -1074,7 +1111,7 @@ static void init_fonts(int pick_width, int pick_height)
     if (pick_height)
        font_height = pick_height;
     else {
-       font_height = cfg.fontheight;
+       font_height = cfg.font.height;
        if (font_height > 0) {
            font_height =
                -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
@@ -1086,9 +1123,9 @@ static void init_fonts(int pick_width, int pick_height)
     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
                           c, OUT_DEFAULT_PRECIS, \
                           CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
-                          FIXED_PITCH | FF_DONTCARE, cfg.font)
+                          FIXED_PITCH | FF_DONTCARE, cfg.font.name)
 
-    f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
+    f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);
 
     lfont.lfHeight = font_height;
     lfont.lfWidth = font_width;
@@ -1098,12 +1135,12 @@ static void init_fonts(int pick_width, int pick_height)
     lfont.lfItalic = FALSE;
     lfont.lfUnderline = FALSE;
     lfont.lfStrikeOut = FALSE;
-    lfont.lfCharSet = cfg.fontcharset;
+    lfont.lfCharSet = cfg.font.charset;
     lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
     lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
     lfont.lfQuality = DEFAULT_QUALITY;
     lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
-    strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
+    strncpy(lfont.lfFaceName, cfg.font.name, LF_FACESIZE);
 
     SelectObject(hdc, fonts[FONT_NORMAL]);
     GetTextMetrics(hdc, &tm);
@@ -1137,7 +1174,7 @@ static void init_fonts(int pick_width, int pick_height)
        ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
     }
 
-    f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
+    f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE);
 
     /*
      * Some fonts, e.g. 9-pt Courier, draw their underlines
@@ -1188,7 +1225,7 @@ static void init_fonts(int pick_width, int pick_height)
     }
 
     if (bold_mode == BOLD_FONT) {
-       f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
+       f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE);
     }
 #undef f
 
@@ -1239,7 +1276,7 @@ static void another_font(int fontno)
     if (basefont != fontno && !fontflag[basefont])
        another_font(basefont);
 
-    if (cfg.fontisbold) {
+    if (cfg.font.isbold) {
        fw_dontcare = FW_BOLD;
        fw_bold = FW_HEAVY;
     } else {
@@ -1247,10 +1284,10 @@ static void another_font(int fontno)
        fw_bold = FW_BOLD;
     }
 
-    c = cfg.fontcharset;
+    c = cfg.font.charset;
     w = fw_dontcare;
     u = FALSE;
-    s = cfg.font;
+    s = cfg.font.name;
     x = font_width;
 
     if (fontno & FONT_WIDE)
@@ -1560,7 +1597,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
 
     if (send_raw_mouse && !(cfg.mouse_override && shift)) {
        lastbtn = MBT_NOTHING;
-       term_mouse(term, b, MA_CLICK, x, y, shift, ctrl, alt);
+       term_mouse(term, b, translate_button(b), MA_CLICK,
+                  x, y, shift, ctrl, alt);
        return;
     }
 
@@ -1573,7 +1611,8 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
        lastact = MA_CLICK;
     }
     if (lastact != MA_NOTHING)
-       term_mouse(term, b, lastact, x, y, shift, ctrl, alt);
+       term_mouse(term, b, translate_button(b), lastact,
+                  x, y, shift, ctrl, alt);
     lasttime = thistime;
 }
 
@@ -1581,7 +1620,7 @@ static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
  * into a cooked one (SELECT, EXTEND, PASTE).
  */
-Mouse_Button translate_button(void *frontend, Mouse_Button button)
+static Mouse_Button translate_button(Mouse_Button button)
 {
     if (button == MBT_LEFT)
        return MBT_SELECT;
@@ -1662,13 +1701,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
       case WM_CREATE:
        break;
       case WM_CLOSE:
-       show_mouseptr(1);
-       if (!cfg.warn_on_close || session_closed ||
-           MessageBox(hwnd,
-                      "Are you sure you want to close this session?",
-                      "PuTTY Exit Confirmation",
-                      MB_ICONWARNING | MB_OKCANCEL) == IDOK)
-           DestroyWindow(hwnd);
+       {
+           char *str;
+           show_mouseptr(1);
+           str = dupprintf("%s Exit Confirmation", appname);
+           if (!cfg.warn_on_close || session_closed ||
+               MessageBox(hwnd,
+                          "Are you sure you want to close this session?",
+                          str, MB_ICONWARNING | MB_OKCANCEL) == IDOK)
+               DestroyWindow(hwnd);
+           sfree(str);
+       }
        return 0;
       case WM_DESTROY:
        show_mouseptr(1);
@@ -1720,7 +1763,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                    if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
                        char *session =
                            sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
-                       cl = smalloc(16 + strlen(session));
+                       cl = snewn(16 + strlen(session), char);
                                       /* 8, but play safe */
                        if (!cl)
                            cl = NULL;    
@@ -1875,11 +1918,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                                  icon_name);
                }
 
-               if (strcmp(cfg.font, prev_cfg.font) != 0 ||
+               if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||
                    strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
-                   cfg.fontisbold != prev_cfg.fontisbold ||
-                   cfg.fontheight != prev_cfg.fontheight ||
-                   cfg.fontcharset != prev_cfg.fontcharset ||
+                   cfg.font.isbold != prev_cfg.font.isbold ||
+                   cfg.font.height != prev_cfg.font.height ||
+                   cfg.font.charset != prev_cfg.font.charset ||
                    cfg.vtmode != prev_cfg.vtmode ||
                    cfg.bold_colour != prev_cfg.bold_colour ||
                    cfg.resize_action == RESIZE_DISABLED ||
@@ -1902,58 +1945,6 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            term_pwron(term);
            ldisc_send(ldisc, NULL, 0, 0);
            break;
-         case IDM_TEL_AYT:
-           back->special(backhandle, TS_AYT);
-           net_pending_errors();
-           break;
-         case IDM_TEL_BRK:
-           back->special(backhandle, TS_BRK);
-           net_pending_errors();
-           break;
-         case IDM_TEL_SYNCH:
-           back->special(backhandle, TS_SYNCH);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EC:
-           back->special(backhandle, TS_EC);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EL:
-           back->special(backhandle, TS_EL);
-           net_pending_errors();
-           break;
-         case IDM_TEL_GA:
-           back->special(backhandle, TS_GA);
-           net_pending_errors();
-           break;
-         case IDM_TEL_NOP:
-           back->special(backhandle, TS_NOP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_ABORT:
-           back->special(backhandle, TS_ABORT);
-           net_pending_errors();
-           break;
-         case IDM_TEL_AO:
-           back->special(backhandle, TS_AO);
-           net_pending_errors();
-           break;
-         case IDM_TEL_IP:
-           back->special(backhandle, TS_IP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_SUSP:
-           back->special(backhandle, TS_SUSP);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EOR:
-           back->special(backhandle, TS_EOR);
-           net_pending_errors();
-           break;
-         case IDM_TEL_EOF:
-           back->special(backhandle, TS_EOF);
-           net_pending_errors();
-           break;
          case IDM_ABOUT:
            showabout(hwnd);
            break;
@@ -1988,6 +1979,22 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
                SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
            }
+           if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
+               int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
+               int j;
+               /*
+                * Ensure we haven't been sent a bogus SYSCOMMAND
+                * which would cause us to reference invalid memory
+                * and crash. Perhaps I'm just too paranoid here.
+                */
+               for (j = 0; j < i; j++)
+                   if (!specials || !specials[j].name)
+                       break;
+               if (j == i) {
+                   back->special(backhandle, specials[i].code);
+                   net_pending_errors();
+               }
+           }
        }
        break;
 
@@ -2052,7 +2059,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                      is_alt_pressed());
                SetCapture(hwnd);
            } else {
-               term_mouse(term, button, MA_RELEASE,
+               term_mouse(term, button, translate_button(button), MA_RELEASE,
                           TO_CHR_X(X_POS(lParam)),
                           TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
                           wParam & MK_CONTROL, is_alt_pressed());
@@ -2077,7 +2084,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                b = MBT_MIDDLE;
            else
                b = MBT_RIGHT;
-           term_mouse(term, b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
+           term_mouse(term, b, translate_button(b), MA_DRAG,
+                      TO_CHR_X(X_POS(lParam)),
                       TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
                       wParam & MK_CONTROL, is_alt_pressed());
        }
@@ -2527,7 +2535,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
 
            if (n > 0) {
                int i;
-               buff = (char*) smalloc(n);
+               buff = snewn(n, char);
                ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
                /*
                 * Jaeyoun Chung reports that Korean character
@@ -2578,6 +2586,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            return TRUE;
        }
+       break;
+      case WM_AGENT_CALLBACK:
+       {
+           struct agent_callback *c = (struct agent_callback *)lParam;
+           c->callback(c->callback_ctx, c->data, c->len);
+           sfree(c);
+       }
+       return 0;
       default:
        if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
            int shift_pressed=0, control_pressed=0;
@@ -2612,12 +2628,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                if (send_raw_mouse &&
                    !(cfg.mouse_override && shift_pressed)) {
                    /* send a mouse-down followed by a mouse up */
-                   term_mouse(term, b,
+                   term_mouse(term, b, translate_button(b),
                               MA_CLICK,
                               TO_CHR_X(X_POS(lParam)),
                               TO_CHR_Y(Y_POS(lParam)), shift_pressed,
                               control_pressed, is_alt_pressed());
-                   term_mouse(term, b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
+                   term_mouse(term, b, translate_button(b),
+                              MA_RELEASE, TO_CHR_X(X_POS(lParam)),
                               TO_CHR_Y(Y_POS(lParam)), shift_pressed,
                               control_pressed, is_alt_pressed());
                } else {
@@ -2714,7 +2731,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        int i;
        if (len > IpDxLEN) {
            sfree(IpDx);
-           IpDx = smalloc((len + 16) * sizeof(int));
+           IpDx = snewn(len + 16, int);
            IpDxLEN = (len + 16);
        }
        for (i = 0; i < IpDxLEN; i++)
@@ -2793,8 +2810,10 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     if ((attr & CSET_MASK) == ATTR_OEMCP)
        nfont |= FONT_OEM;
 
-    nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
-    nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
+    nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
+    nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
+    nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
+    nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
        nfont |= FONT_BOLD;
     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
@@ -2816,9 +2835,9 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        nbg = t;
     }
     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
-       nfg++;
+       nfg |= 1;
     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
-       nbg++;
+       nbg |= 1;
     fg = colours[nfg];
     bg = colours[nbg];
     SelectObject(hdc, fonts[nfont]);
@@ -2843,7 +2862,8 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        int nlen, mptr;
        if (len > uni_len) {
            sfree(uni_buf);
-           uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
+           uni_len = len;
+           uni_buf = snewn(uni_len, wchar_t);
        }
 
        for(nlen = mptr = 0; mptr<len; mptr++) {
@@ -2903,7 +2923,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        if (wlen < len) {
            sfree(wbuf);
            wlen = len;
-           wbuf = smalloc(wlen * sizeof(WCHAR));
+           wbuf = snewn(wlen, WCHAR);
        }
        for (i = 0; i < len; i++)
            wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
@@ -3913,7 +3933,7 @@ void request_paste(void *frontend)
 void set_title(void *frontend, char *title)
 {
     sfree(window_name);
-    window_name = smalloc(1 + strlen(title));
+    window_name = snewn(1 + strlen(title), char);
     strcpy(window_name, title);
     if (cfg.win_name_always || !IsIconic(hwnd))
        SetWindowText(hwnd, title);
@@ -3922,7 +3942,7 @@ void set_title(void *frontend, char *title)
 void set_icon(void *frontend, char *title)
 {
     sfree(icon_name);
-    icon_name = smalloc(1 + strlen(title));
+    icon_name = snewn(1 + strlen(title), char);
     strcpy(icon_name, title);
     if (!cfg.win_name_always && IsIconic(hwnd))
        SetWindowText(hwnd, title);
@@ -4092,10 +4112,10 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
 
        get_unitab(CP_ACP, unitab, 0);
 
-       rtfsize = 100 + strlen(cfg.font);
-       rtf = smalloc(rtfsize);
+       rtfsize = 100 + strlen(cfg.font.name);
+       rtf = snewn(rtfsize, char);
        sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
-               GetACP(), cfg.font);
+               GetACP(), cfg.font.name);
        rtflen = strlen(rtf);
 
        /*
@@ -4158,7 +4178,7 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
 
            if (rtfsize < rtflen + totallen + 3) {
                rtfsize = rtflen + totallen + 512;
-               rtf = srealloc(rtf, rtfsize);
+               rtf = sresize(rtf, rtfsize, char);
            }
 
            strcpy(rtf + rtflen, before); rtflen += blen;
@@ -4246,7 +4266,7 @@ void get_clip(void *frontend, wchar_t ** p, int *len)
            CloseClipboard();
            s = GlobalLock(clipdata);
            i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
-           *p = converted = smalloc(i * sizeof(wchar_t));
+           *p = converted = snewn(i, wchar_t);
            MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
            *len = i - 1;
            return;
@@ -4285,12 +4305,14 @@ void optimised_move(void *frontend, int to, int from, int lines)
 void fatalbox(char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
+    sprintf(morestuff, "%.70s Fatal Error", appname);
+    MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
+    sfree(stuff);
     cleanup_exit(1);
 }
 
@@ -4300,13 +4322,15 @@ void fatalbox(char *fmt, ...)
 void modalfatalbox(char *fmt, ...)
 {
     va_list ap;
-    char stuff[200];
+    char *stuff, morestuff[100];
 
     va_start(ap, fmt);
-    vsprintf(stuff, fmt, ap);
+    stuff = dupvprintf(fmt, ap);
     va_end(ap);
-    MessageBox(hwnd, stuff, "PuTTY Fatal Error",
+    sprintf(morestuff, "%.70s Fatal Error", appname);
+    MessageBox(hwnd, stuff, morestuff,
               MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
+    sfree(stuff);
     cleanup_exit(1);
 }
 
@@ -4371,11 +4395,14 @@ void beep(void *frontend, int mode)
         */
        lastbeep = GetTickCount();
     } else if (mode == BELL_WAVEFILE) {
-       if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
+       if (!PlaySound(cfg.bell_wavefile.path, NULL,
+                      SND_ASYNC | SND_FILENAME)) {
            char buf[sizeof(cfg.bell_wavefile) + 80];
+           char otherbuf[100];
            sprintf(buf, "Unable to play sound file\n%s\n"
                    "Using default sound instead", cfg.bell_wavefile);
-           MessageBox(hwnd, buf, "PuTTY Sound Error",
+           sprintf(otherbuf, "%.70s Sound Error", appname);
+           MessageBox(hwnd, buf, otherbuf,
                       MB_OK | MB_ICONEXCLAMATION);
            cfg.beep = BELL_DEFAULT;
        }
@@ -4615,3 +4642,19 @@ void frontend_keypress(void *handle)
      */
     return;
 }
+
+int from_backend(void *frontend, int is_stderr, const char *data, int len)
+{
+    return term_data(term, is_stderr, data, len);
+}
+
+void agent_schedule_callback(void (*callback)(void *, void *, int),
+                            void *callback_ctx, void *data, int len)
+{
+    struct agent_callback *c = snew(struct agent_callback);
+    c->callback = callback;
+    c->callback_ctx = callback_ctx;
+    c->data = data;
+    c->len = len;
+    PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);
+}