mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-01 03:22:48 -05:00
Now that we have Subversion's file renaming ability, it's time at
long last to move all the Windows-specific source files down into a `windows' subdirectory. Only platform-specific files remain at the top level. With any luck this will act as a hint to anyone still contemplating sending us a Windows-centric patch... [originally from svn r4792]
This commit is contained in:
BIN
windows/pageant.ico
Normal file
BIN
windows/pageant.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
21
windows/pageant.mft
Normal file
21
windows/pageant.mft
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!-- This is present purely to make Visual Styles in XP work better.
|
||||
See 20020104174954.A12067@imsa.edu. -->
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="x86"
|
||||
name="Pageant"
|
||||
type="win32" />
|
||||
<description>SSH authentication agent for PuTTY.</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
processorArchitecture="x86"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
102
windows/pageant.rc
Normal file
102
windows/pageant.rc
Normal file
@ -0,0 +1,102 @@
|
||||
/* Some compilers, like Borland, don't have winresrc.h */
|
||||
#ifdef __LCC__
|
||||
#include <win.h>
|
||||
#else
|
||||
|
||||
#ifndef NO_WINRESRC_H
|
||||
#ifndef MSVC4
|
||||
#include <winresrc.h>
|
||||
#else
|
||||
#include <winres.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* end #ifdef __LCC__ */
|
||||
|
||||
/* Some systems don't define this, so I do it myself if necessary */
|
||||
#ifndef RT_MANIFEST
|
||||
#define RT_MANIFEST 24
|
||||
#endif
|
||||
|
||||
200 ICON "pageant.ico"
|
||||
201 ICON "pageants.ico"
|
||||
|
||||
210 DIALOG DISCARDABLE 0, 0, 140, 60
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Pageant: Enter Passphrase"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
CTEXT "Enter passphrase for key", 100, 10, 6, 120, 8
|
||||
CTEXT "", 101, 10, 16, 120, 8
|
||||
EDITTEXT 102, 10, 26, 120, 12, ES_PASSWORD | ES_AUTOHSCROLL
|
||||
DEFPUSHBUTTON "O&K", IDOK, 20, 42, 40, 14
|
||||
PUSHBUTTON "&Cancel", IDCANCEL, 80, 42, 40, 14
|
||||
END
|
||||
|
||||
211 DIALOG DISCARDABLE 0, 0, 330, 200
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "Pageant Key List"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
LISTBOX 100, 10, 10, 310, 155,
|
||||
LBS_EXTENDEDSEL | LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | WS_TABSTOP
|
||||
PUSHBUTTON "&Add Key", 101, 75, 162, 60, 14
|
||||
PUSHBUTTON "&Remove Key", 102, 195, 162, 60, 14
|
||||
PUSHBUTTON "&Help", 103, 10, 182, 50, 14
|
||||
DEFPUSHBUTTON "&Close", IDOK, 270, 182, 50, 14
|
||||
END
|
||||
|
||||
/* Accelerators used: cl */
|
||||
213 DIALOG DISCARDABLE 140, 40, 136, 70
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "About Pageant"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14
|
||||
PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14
|
||||
CTEXT "Pageant", 102, 10, 6, 120, 8
|
||||
CTEXT "", 100, 10, 16, 120, 16
|
||||
CTEXT "\251 1997-2004 Simon Tatham. All rights reserved.",
|
||||
103, 10, 34, 120, 16
|
||||
END
|
||||
|
||||
/* No accelerators used */
|
||||
214 DIALOG DISCARDABLE 50, 50, 226, 263
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "PuTTY Licence"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
|
||||
|
||||
LTEXT "Copyright \251 1997-2004 Simon Tatham", 1000, 10, 10, 206, 8
|
||||
|
||||
LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
|
||||
LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
|
||||
LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, and CORE", 1003, 10, 42, 206, 8
|
||||
LTEXT "SDI S.A.", 1004, 10, 50, 206, 8
|
||||
|
||||
LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8
|
||||
LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8
|
||||
LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8
|
||||
LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8
|
||||
LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8
|
||||
LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8
|
||||
LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8
|
||||
|
||||
LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8
|
||||
LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8
|
||||
|
||||
LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8
|
||||
LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8
|
||||
LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8
|
||||
LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8
|
||||
LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8
|
||||
LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8
|
||||
LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8
|
||||
LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8
|
||||
LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8
|
||||
LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8
|
||||
|
||||
END
|
||||
|
||||
1 RT_MANIFEST "pageant.mft"
|
BIN
windows/pageants.ico
Normal file
BIN
windows/pageants.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 318 B |
1
windows/plink.rc
Normal file
1
windows/plink.rc
Normal file
@ -0,0 +1 @@
|
||||
200 ICON "putty.ico"
|
BIN
windows/putty.ico
Normal file
BIN
windows/putty.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
62
windows/putty.iss
Normal file
62
windows/putty.iss
Normal file
@ -0,0 +1,62 @@
|
||||
; -*- no -*-
|
||||
; putty.iss
|
||||
;
|
||||
; -- Inno Setup installer script for PuTTY and its related tools.
|
||||
;
|
||||
; TODO for future releases:
|
||||
;
|
||||
; - It would be neighbourly to set up an [UninstallRun] entry that ran
|
||||
; some close cousin of `putty -cleanup', only it should prompt first
|
||||
; in case the user wants to keep stuff. And make the `leave it alone'
|
||||
; button the DEFAULT. And perhaps warn that on NT-style systems not
|
||||
; everything will be caught by this.
|
||||
;
|
||||
; - The Quick Launch bar is an interesting thought. Certainly a fair
|
||||
; number of people actually _believe_ my silly joke about how PuTTY
|
||||
; is the only thing that makes Windows usable, so perhaps they'd like
|
||||
; that. Unchecked by default, though, I think.
|
||||
; * does this need to be conditional on the Windows version?
|
||||
|
||||
[Setup]
|
||||
AppName=PuTTY
|
||||
AppVerName=PuTTY version 0.56
|
||||
DefaultDirName={pf}\PuTTY
|
||||
DefaultGroupName=PuTTY
|
||||
UninstallDisplayIcon={app}\putty.exe
|
||||
ChangesAssociations=yes
|
||||
Compression=zip/9
|
||||
|
||||
[Files]
|
||||
Source: "putty.exe"; DestDir: "{app}"
|
||||
Source: "pageant.exe"; DestDir: "{app}"
|
||||
Source: "puttygen.exe"; DestDir: "{app}"
|
||||
Source: "pscp.exe"; DestDir: "{app}"
|
||||
Source: "psftp.exe"; DestDir: "{app}"
|
||||
Source: "plink.exe"; DestDir: "{app}"
|
||||
Source: "website.url"; DestDir: "{app}"
|
||||
Source: "doc\putty.hlp"; DestDir: "{app}"
|
||||
Source: "doc\putty.cnt"; DestDir: "{app}"
|
||||
Source: "LICENCE"; DestDir: "{app}"
|
||||
Source: "README.txt"; DestDir: "{app}"; Flags: isreadme
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\PuTTY"; Filename: "{app}\putty.exe"; Tasks: startmenu
|
||||
Name: "{group}\PuTTY Manual"; Filename: "{app}\putty.hlp"; Tasks: startmenu
|
||||
Name: "{group}\PuTTY Web Site"; Filename: "{app}\website.url"; Tasks: startmenu
|
||||
Name: "{group}\PSFTP"; Filename: "{app}\psftp.exe"; Tasks: startmenu
|
||||
Name: "{group}\PuTTYgen"; Filename: "{app}\puttygen.exe"; Tasks: startmenu
|
||||
Name: "{group}\Pageant"; Filename: "{app}\pageant.exe"; Tasks: startmenu
|
||||
Name: "{userdesktop}\PuTTY"; Filename: "{app}\putty.exe"; Tasks: desktopicon
|
||||
|
||||
[Tasks]
|
||||
Name: startmenu; Description: "Create a &Start Menu group"
|
||||
Name: desktopicon; Description: "Create a &desktop icon for PuTTY"
|
||||
Name: associate; Description: "&Associate .PPK files (PuTTY Private Key) with Pageant"
|
||||
|
||||
[Registry]
|
||||
Root: HKCR; Subkey: ".ppk"; ValueType: string; ValueName: ""; ValueData: "PuTTYPrivateKey"; Flags: uninsdeletevalue; Tasks: associate
|
||||
Root: HKCR; Subkey: "PuTTYPrivateKey"; ValueType: string; ValueName: ""; ValueData: "PuTTY Private Key File"; Flags: uninsdeletekey; Tasks: associate
|
||||
Root: HKCR; Subkey: "PuTTYPrivateKey\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\pageant.exe,0"; Tasks: associate
|
||||
Root: HKCR; Subkey: "PuTTYPrivateKey\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\pageant.exe"" ""%1"""; Tasks: associate
|
||||
Root: HKCR; Subkey: "PuTTYPrivateKey\shell\edit"; ValueType: string; ValueName: ""; ValueData: "&Edit"; Tasks: associate
|
||||
Root: HKCR; Subkey: "PuTTYPrivateKey\shell\edit\command"; ValueType: string; ValueName: ""; ValueData: """{app}\puttygen.exe"" ""%1"""; Tasks: associate
|
21
windows/putty.mft
Normal file
21
windows/putty.mft
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!-- This is present purely to make Visual Styles in XP work better.
|
||||
See 20020104174954.A12067@imsa.edu. -->
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="x86"
|
||||
name="PuTTY"
|
||||
type="win32" />
|
||||
<description>A free SSH and Telnet client.</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
processorArchitecture="x86"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
BIN
windows/puttycfg.ico
Normal file
BIN
windows/puttycfg.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
windows/puttygen.ico
Normal file
BIN
windows/puttygen.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 766 B |
21
windows/puttygen.mft
Normal file
21
windows/puttygen.mft
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!-- This is present purely to make Visual Styles in XP work better.
|
||||
See 20020104174954.A12067@imsa.edu. -->
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="x86"
|
||||
name="PuTTYgen"
|
||||
type="win32" />
|
||||
<description>SSH key generator for PuTTY.</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
processorArchitecture="x86"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
95
windows/puttygen.rc
Normal file
95
windows/puttygen.rc
Normal file
@ -0,0 +1,95 @@
|
||||
/* Some compilers, like Borland, don't have winresrc.h */
|
||||
#ifdef __LCC__
|
||||
#include <win.h>
|
||||
#else
|
||||
|
||||
#ifndef NO_WINRESRC_H
|
||||
#ifndef MSVC4
|
||||
#include <winresrc.h>
|
||||
#else
|
||||
#include <winres.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* end #ifdef __LCC__ */
|
||||
|
||||
/* Some systems don't define this, so I do it myself if necessary */
|
||||
#ifndef RT_MANIFEST
|
||||
#define RT_MANIFEST 24
|
||||
#endif
|
||||
|
||||
200 ICON "puttygen.ico"
|
||||
|
||||
201 DIALOG DISCARDABLE 0, 0, 318, 270
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "PuTTY Key Generator"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
END
|
||||
|
||||
210 DIALOG DISCARDABLE 0, 0, 140, 60
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "PuTTYgen: Enter Passphrase"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
CTEXT "Enter passphrase for key", 100, 10, 6, 120, 8
|
||||
CTEXT "", 101, 10, 16, 120, 8
|
||||
EDITTEXT 102, 10, 26, 120, 12, ES_PASSWORD | ES_AUTOHSCROLL
|
||||
DEFPUSHBUTTON "O&K", IDOK, 20, 42, 40, 14
|
||||
PUSHBUTTON "&Cancel", IDCANCEL, 80, 42, 40, 14
|
||||
END
|
||||
|
||||
/* Accelerators used: cl */
|
||||
213 DIALOG DISCARDABLE 140, 40, 136, 70
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "About PuTTYgen"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14
|
||||
PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14
|
||||
CTEXT "PuTTYgen", 102, 10, 6, 120, 8
|
||||
CTEXT "", 100, 10, 16, 120, 16
|
||||
CTEXT "\251 1997-2004 Simon Tatham. All rights reserved.",
|
||||
103, 10, 34, 120, 16
|
||||
END
|
||||
|
||||
/* No accelerators used */
|
||||
214 DIALOG DISCARDABLE 50, 50, 226, 263
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "PuTTY Licence"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
|
||||
|
||||
LTEXT "Copyright \251 1997-2004 Simon Tatham", 1000, 10, 10, 206, 8
|
||||
|
||||
LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
|
||||
LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
|
||||
LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, and CORE", 1003, 10, 42, 206, 8
|
||||
LTEXT "SDI S.A.", 1004, 10, 50, 206, 8
|
||||
|
||||
LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8
|
||||
LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8
|
||||
LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8
|
||||
LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8
|
||||
LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8
|
||||
LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8
|
||||
LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8
|
||||
|
||||
LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8
|
||||
LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8
|
||||
|
||||
LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8
|
||||
LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8
|
||||
LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8
|
||||
LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8
|
||||
LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8
|
||||
LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8
|
||||
LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8
|
||||
LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8
|
||||
LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8
|
||||
LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8
|
||||
|
||||
END
|
||||
|
||||
1 RT_MANIFEST "puttygen.mft"
|
BIN
windows/scp.ico
Normal file
BIN
windows/scp.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
2
windows/scp.rc
Normal file
2
windows/scp.rc
Normal file
@ -0,0 +1,2 @@
|
||||
200 ICON "scp.ico"
|
||||
|
190
windows/sizetip.c
Normal file
190
windows/sizetip.c
Normal file
@ -0,0 +1,190 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
static ATOM tip_class = 0;
|
||||
|
||||
static HFONT tip_font;
|
||||
static COLORREF tip_bg;
|
||||
static COLORREF tip_text;
|
||||
|
||||
static LRESULT CALLBACK SizeTipWndProc(HWND hWnd, UINT nMsg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
|
||||
switch (nMsg) {
|
||||
case WM_ERASEBKGND:
|
||||
return TRUE;
|
||||
|
||||
case WM_PAINT:
|
||||
{
|
||||
HBRUSH hbr;
|
||||
HGDIOBJ holdbr;
|
||||
RECT cr;
|
||||
int wtlen;
|
||||
LPTSTR wt;
|
||||
HDC hdc;
|
||||
|
||||
PAINTSTRUCT ps;
|
||||
hdc = BeginPaint(hWnd, &ps);
|
||||
|
||||
SelectObject(hdc, tip_font);
|
||||
SelectObject(hdc, GetStockObject(BLACK_PEN));
|
||||
|
||||
hbr = CreateSolidBrush(tip_bg);
|
||||
holdbr = SelectObject(hdc, hbr);
|
||||
|
||||
GetClientRect(hWnd, &cr);
|
||||
Rectangle(hdc, cr.left, cr.top, cr.right, cr.bottom);
|
||||
|
||||
wtlen = GetWindowTextLength(hWnd);
|
||||
wt = (LPTSTR) snewn(wtlen + 1, TCHAR);
|
||||
GetWindowText(hWnd, wt, wtlen + 1);
|
||||
|
||||
SetTextColor(hdc, tip_text);
|
||||
SetBkColor(hdc, tip_bg);
|
||||
|
||||
TextOut(hdc, cr.left + 3, cr.top + 3, wt, wtlen);
|
||||
|
||||
sfree(wt);
|
||||
|
||||
SelectObject(hdc, holdbr);
|
||||
DeleteObject(hbr);
|
||||
|
||||
EndPaint(hWnd, &ps);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_NCHITTEST:
|
||||
return HTTRANSPARENT;
|
||||
|
||||
case WM_DESTROY:
|
||||
DeleteObject(tip_font);
|
||||
tip_font = NULL;
|
||||
break;
|
||||
|
||||
case WM_SETTEXT:
|
||||
{
|
||||
LPCTSTR str = (LPCTSTR) lParam;
|
||||
SIZE sz;
|
||||
HDC hdc = CreateCompatibleDC(NULL);
|
||||
|
||||
SelectObject(hdc, tip_font);
|
||||
GetTextExtentPoint32(hdc, str, _tcslen(str), &sz);
|
||||
|
||||
SetWindowPos(hWnd, NULL, 0, 0, sz.cx + 6, sz.cy + 6,
|
||||
SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
|
||||
InvalidateRect(hWnd, NULL, FALSE);
|
||||
|
||||
DeleteDC(hdc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, nMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
static HWND tip_wnd = NULL;
|
||||
static int tip_enabled = 0;
|
||||
|
||||
void UpdateSizeTip(HWND src, int cx, int cy)
|
||||
{
|
||||
TCHAR str[32];
|
||||
|
||||
if (!tip_enabled)
|
||||
return;
|
||||
|
||||
if (!tip_wnd) {
|
||||
NONCLIENTMETRICS nci;
|
||||
|
||||
/* First make sure the window class is registered */
|
||||
|
||||
if (!tip_class) {
|
||||
WNDCLASS wc;
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = SizeTipWndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = hinst;
|
||||
wc.hIcon = NULL;
|
||||
wc.hCursor = NULL;
|
||||
wc.hbrBackground = NULL;
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = "SizeTipClass";
|
||||
|
||||
tip_class = RegisterClass(&wc);
|
||||
}
|
||||
#if 0
|
||||
/* Default values based on Windows Standard color scheme */
|
||||
|
||||
tip_font = GetStockObject(SYSTEM_FONT);
|
||||
tip_bg = RGB(255, 255, 225);
|
||||
tip_text = RGB(0, 0, 0);
|
||||
#endif
|
||||
|
||||
/* Prepare other GDI objects and drawing info */
|
||||
|
||||
tip_bg = GetSysColor(COLOR_INFOBK);
|
||||
tip_text = GetSysColor(COLOR_INFOTEXT);
|
||||
|
||||
memset(&nci, 0, sizeof(NONCLIENTMETRICS));
|
||||
nci.cbSize = sizeof(NONCLIENTMETRICS);
|
||||
SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
|
||||
sizeof(NONCLIENTMETRICS), &nci, 0);
|
||||
tip_font = CreateFontIndirect(&nci.lfStatusFont);
|
||||
}
|
||||
|
||||
/* Generate the tip text */
|
||||
|
||||
sprintf(str, "%dx%d", cx, cy);
|
||||
|
||||
if (!tip_wnd) {
|
||||
HDC hdc;
|
||||
SIZE sz;
|
||||
RECT wr;
|
||||
int ix, iy;
|
||||
|
||||
/* calculate the tip's size */
|
||||
|
||||
hdc = CreateCompatibleDC(NULL);
|
||||
GetTextExtentPoint32(hdc, str, _tcslen(str), &sz);
|
||||
DeleteDC(hdc);
|
||||
|
||||
GetWindowRect(src, &wr);
|
||||
|
||||
ix = wr.left;
|
||||
if (ix < 16)
|
||||
ix = 16;
|
||||
|
||||
iy = wr.top - sz.cy;
|
||||
if (iy < 16)
|
||||
iy = 16;
|
||||
|
||||
/* Create the tip window */
|
||||
|
||||
tip_wnd =
|
||||
CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
|
||||
MAKEINTRESOURCE(tip_class), str, WS_POPUP, ix,
|
||||
iy, sz.cx, sz.cy, NULL, NULL, hinst, NULL);
|
||||
|
||||
ShowWindow(tip_wnd, SW_SHOWNOACTIVATE);
|
||||
|
||||
} else {
|
||||
|
||||
/* Tip already exists, just set the text */
|
||||
|
||||
SetWindowText(tip_wnd, str);
|
||||
}
|
||||
}
|
||||
|
||||
void EnableSizeTip(int bEnable)
|
||||
{
|
||||
if (tip_wnd && !bEnable) {
|
||||
DestroyWindow(tip_wnd);
|
||||
tip_wnd = NULL;
|
||||
}
|
||||
|
||||
tip_enabled = bEnable;
|
||||
}
|
BIN
windows/website.url
Normal file
BIN
windows/website.url
Normal file
Binary file not shown.
30
windows/win_res.h
Normal file
30
windows/win_res.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef PUTTY_WIN_RES_H
|
||||
#define PUTTY_WIN_RES_H
|
||||
|
||||
#define IDI_MAINICON 200
|
||||
#define IDI_CFGICON 201
|
||||
|
||||
#define IDD_MAINBOX 102
|
||||
#define IDD_LOGBOX 110
|
||||
#define IDD_ABOUTBOX 111
|
||||
#define IDD_RECONF 112
|
||||
#define IDD_LICENCEBOX 113
|
||||
|
||||
#define IDN_LIST 1001
|
||||
#define IDN_COPY 1002
|
||||
|
||||
#define IDA_ICON 1001
|
||||
#define IDA_TEXT1 1002
|
||||
#define IDA_VERSION 1003
|
||||
#define IDA_TEXT2 1004
|
||||
#define IDA_LICENCE 1005
|
||||
#define IDA_WEB 1006
|
||||
|
||||
#define IDC_TAB 1001
|
||||
#define IDC_TABSTATIC1 1002
|
||||
#define IDC_TABSTATIC2 1003
|
||||
#define IDC_TABLIST 1004
|
||||
#define IDC_HELPBTN 1005
|
||||
#define IDC_ABOUT 1006
|
||||
|
||||
#endif
|
110
windows/win_res.rc
Normal file
110
windows/win_res.rc
Normal file
@ -0,0 +1,110 @@
|
||||
/* Some compilers, like Borland, don't have winresrc.h */
|
||||
#ifdef __LCC__
|
||||
#include <win.h>
|
||||
#else
|
||||
|
||||
#ifndef NO_WINRESRC_H
|
||||
#ifndef MSVC4
|
||||
#include <winresrc.h>
|
||||
#else
|
||||
#include <winres.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* end #ifdef __LCC__ */
|
||||
|
||||
/* Some systems don't define this, so I do it myself if necessary */
|
||||
#ifndef TCS_MULTILINE
|
||||
#define TCS_MULTILINE 0x0200
|
||||
#endif
|
||||
|
||||
/* Likewise */
|
||||
#ifndef RT_MANIFEST
|
||||
#define RT_MANIFEST 24
|
||||
#endif
|
||||
|
||||
#ifdef MINGW32_FIX
|
||||
#define EDITTEXT EDITTEXT "",
|
||||
#endif
|
||||
|
||||
#include "win_res.h"
|
||||
|
||||
IDI_MAINICON ICON "putty.ico"
|
||||
|
||||
IDI_CFGICON ICON "puttycfg.ico"
|
||||
|
||||
/* Accelerators used: clw */
|
||||
IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 214, 70
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "About PuTTY"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "&Close", IDOK, 160, 52, 48, 14
|
||||
PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 52, 70, 14
|
||||
PUSHBUTTON "Visit &Web Site", IDA_WEB, 84, 52, 70, 14
|
||||
CTEXT "PuTTY", IDA_TEXT1, 10, 6, 194, 8
|
||||
CTEXT "", IDA_VERSION, 10, 16, 194, 16
|
||||
CTEXT "\251 1997-2004 Simon Tatham. All rights reserved.",
|
||||
IDA_TEXT2, 10, 34, 194, 16
|
||||
END
|
||||
|
||||
/* Accelerators used: aco */
|
||||
IDD_MAINBOX DIALOG DISCARDABLE 0, 0, 280, 252
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "PuTTY Configuration"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
CLASS "PuTTYConfigBox"
|
||||
BEGIN
|
||||
END
|
||||
|
||||
/* Accelerators used: co */
|
||||
IDD_LOGBOX DIALOG DISCARDABLE 100, 20, 300, 119
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "PuTTY Event Log"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "&Close", IDOK, 135, 102, 44, 14
|
||||
PUSHBUTTON "C&opy", IDN_COPY, 81, 102, 44, 14
|
||||
LISTBOX IDN_LIST, 3, 3, 294, 95, LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | LBS_EXTENDEDSEL
|
||||
END
|
||||
|
||||
/* No accelerators used */
|
||||
IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 263
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "PuTTY Licence"
|
||||
FONT 8, "MS Shell Dlg"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14
|
||||
|
||||
LTEXT "Copyright \251 1997-2004 Simon Tatham", 1000, 10, 10, 206, 8
|
||||
|
||||
LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8
|
||||
LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8
|
||||
LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, and CORE", 1003, 10, 42, 206, 8
|
||||
LTEXT "SDI S.A.", 1004, 10, 50, 206, 8
|
||||
|
||||
LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8
|
||||
LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8
|
||||
LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8
|
||||
LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8
|
||||
LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8
|
||||
LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8
|
||||
LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8
|
||||
|
||||
LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8
|
||||
LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8
|
||||
|
||||
LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8
|
||||
LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8
|
||||
LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8
|
||||
LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8
|
||||
LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8
|
||||
LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8
|
||||
LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8
|
||||
LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8
|
||||
LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8
|
||||
LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8
|
||||
|
||||
END
|
||||
|
||||
1 RT_MANIFEST "putty.mft"
|
313
windows/wincfg.c
Normal file
313
windows/wincfg.c
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* wincfg.c - the Windows-specific parts of the PuTTY configuration
|
||||
* box.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "dialog.h"
|
||||
#include "storage.h"
|
||||
|
||||
static void about_handler(union control *ctrl, void *dlg,
|
||||
void *data, int event)
|
||||
{
|
||||
HWND *hwndp = (HWND *)ctrl->generic.context.p;
|
||||
|
||||
if (event == EVENT_ACTION) {
|
||||
modal_about_box(*hwndp);
|
||||
}
|
||||
}
|
||||
|
||||
static void help_handler(union control *ctrl, void *dlg,
|
||||
void *data, int event)
|
||||
{
|
||||
HWND *hwndp = (HWND *)ctrl->generic.context.p;
|
||||
|
||||
if (event == EVENT_ACTION) {
|
||||
show_help(*hwndp);
|
||||
}
|
||||
}
|
||||
|
||||
void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
|
||||
int midsession)
|
||||
{
|
||||
struct controlset *s;
|
||||
union control *c;
|
||||
char *str;
|
||||
|
||||
if (!midsession) {
|
||||
/*
|
||||
* Add the About and Help buttons to the standard panel.
|
||||
*/
|
||||
s = ctrl_getset(b, "", "", "");
|
||||
c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help),
|
||||
about_handler, P(hwndp));
|
||||
c->generic.column = 0;
|
||||
if (has_help) {
|
||||
c = ctrl_pushbutton(s, "Help", 'h', HELPCTX(no_help),
|
||||
help_handler, P(hwndp));
|
||||
c->generic.column = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Full-screen mode is a Windows peculiarity; hence
|
||||
* scrollbar_in_fullscreen is as well.
|
||||
*/
|
||||
s = ctrl_getset(b, "Window", "scrollback",
|
||||
"Control the scrollback in the window");
|
||||
ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i',
|
||||
HELPCTX(window_scrollback),
|
||||
dlg_stdcheckbox_handler,
|
||||
I(offsetof(Config,scrollbar_in_fullscreen)));
|
||||
/*
|
||||
* Really this wants to go just after `Display scrollbar'. See
|
||||
* if we can find that control, and do some shuffling.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < s->ncontrols; i++) {
|
||||
c = s->ctrls[i];
|
||||
if (c->generic.type == CTRL_CHECKBOX &&
|
||||
c->generic.context.i == offsetof(Config,scrollbar)) {
|
||||
/*
|
||||
* Control i is the scrollbar checkbox.
|
||||
* Control s->ncontrols-1 is the scrollbar-in-FS one.
|
||||
*/
|
||||
if (i < s->ncontrols-2) {
|
||||
c = s->ctrls[s->ncontrols-1];
|
||||
memmove(s->ctrls+i+2, s->ctrls+i+1,
|
||||
(s->ncontrols-i-2)*sizeof(union control *));
|
||||
s->ctrls[i+1] = c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Windows has the AltGr key, which has various Windows-
|
||||
* specific options.
|
||||
*/
|
||||
s = ctrl_getset(b, "Terminal/Keyboard", "features",
|
||||
"Enable extra keyboard features:");
|
||||
ctrl_checkbox(s, "AltGr acts as Compose key", 't',
|
||||
HELPCTX(keyboard_compose),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,compose_key)));
|
||||
ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd',
|
||||
HELPCTX(keyboard_ctrlalt),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,ctrlaltkeys)));
|
||||
|
||||
/*
|
||||
* Windows allows an arbitrary .WAV to be played as a bell, and
|
||||
* also the use of the PC speaker. For this we must search the
|
||||
* existing controlset for the radio-button set controlling the
|
||||
* `beep' option, and add extra buttons to it.
|
||||
*
|
||||
* Note that although this _looks_ like a hideous hack, it's
|
||||
* actually all above board. The well-defined interface to the
|
||||
* per-platform dialog box code is the _data structures_ `union
|
||||
* control', `struct controlset' and so on; so code like this
|
||||
* that reaches into those data structures and changes bits of
|
||||
* them is perfectly legitimate and crosses no boundaries. All
|
||||
* the ctrl_* routines that create most of the controls are
|
||||
* convenient shortcuts provided on the cross-platform side of
|
||||
* the interface, and template creation code is under no actual
|
||||
* obligation to use them.
|
||||
*/
|
||||
s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < s->ncontrols; i++) {
|
||||
c = s->ctrls[i];
|
||||
if (c->generic.type == CTRL_RADIO &&
|
||||
c->generic.context.i == offsetof(Config, beep)) {
|
||||
assert(c->generic.handler == dlg_stdradiobutton_handler);
|
||||
c->radio.nbuttons += 2;
|
||||
c->radio.buttons =
|
||||
sresize(c->radio.buttons, c->radio.nbuttons, char *);
|
||||
c->radio.buttons[c->radio.nbuttons-1] =
|
||||
dupstr("Play a custom sound file");
|
||||
c->radio.buttons[c->radio.nbuttons-2] =
|
||||
dupstr("Beep using the PC speaker");
|
||||
c->radio.buttondata =
|
||||
sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);
|
||||
c->radio.buttondata[c->radio.nbuttons-1] = I(BELL_WAVEFILE);
|
||||
c->radio.buttondata[c->radio.nbuttons-2] = I(BELL_PCSPEAKER);
|
||||
if (c->radio.shortcuts) {
|
||||
c->radio.shortcuts =
|
||||
sresize(c->radio.shortcuts, c->radio.nbuttons, char);
|
||||
c->radio.shortcuts[c->radio.nbuttons-1] = NO_SHORTCUT;
|
||||
c->radio.shortcuts[c->radio.nbuttons-2] = NO_SHORTCUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT,
|
||||
FILTER_WAVE_FILES, FALSE, "Select bell sound file",
|
||||
HELPCTX(bell_style),
|
||||
dlg_stdfilesel_handler, I(offsetof(Config, bell_wavefile)));
|
||||
|
||||
/*
|
||||
* While we've got this box open, taskbar flashing on a bell is
|
||||
* also Windows-specific.
|
||||
*/
|
||||
ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3,
|
||||
HELPCTX(bell_taskbar),
|
||||
dlg_stdradiobutton_handler,
|
||||
I(offsetof(Config, beep_ind)),
|
||||
"Disabled", I(B_IND_DISABLED),
|
||||
"Flashing", I(B_IND_FLASH),
|
||||
"Steady", I(B_IND_STEADY), NULL);
|
||||
|
||||
/*
|
||||
* The sunken-edge border is a Windows GUI feature.
|
||||
*/
|
||||
s = ctrl_getset(b, "Window/Appearance", "border",
|
||||
"Adjust the window border");
|
||||
ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's',
|
||||
HELPCTX(appearance_border),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,sunken_edge)));
|
||||
|
||||
/*
|
||||
* Cyrillic Lock is a horrid misfeature even on Windows, and
|
||||
* the least we can do is ensure it never makes it to any other
|
||||
* platform (at least unless someone fixes it!).
|
||||
*/
|
||||
s = ctrl_getset(b, "Window/Translation", "input",
|
||||
"Enable character set translation on input data");
|
||||
ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's',
|
||||
HELPCTX(translation_cyrillic),
|
||||
dlg_stdcheckbox_handler,
|
||||
I(offsetof(Config,xlat_capslockcyr)));
|
||||
|
||||
/*
|
||||
* Windows has the weird OEM font mode, which gives us some
|
||||
* additional options when working with line-drawing
|
||||
* characters.
|
||||
*/
|
||||
str = dupprintf("Adjust how %s displays line drawing characters", appname);
|
||||
s = ctrl_getset(b, "Window/Translation", "linedraw", str);
|
||||
sfree(str);
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < s->ncontrols; i++) {
|
||||
c = s->ctrls[i];
|
||||
if (c->generic.type == CTRL_RADIO &&
|
||||
c->generic.context.i == offsetof(Config, vtmode)) {
|
||||
assert(c->generic.handler == dlg_stdradiobutton_handler);
|
||||
c->radio.nbuttons += 3;
|
||||
c->radio.buttons =
|
||||
sresize(c->radio.buttons, c->radio.nbuttons, char *);
|
||||
c->radio.buttons[c->radio.nbuttons-3] =
|
||||
dupstr("Font has XWindows encoding");
|
||||
c->radio.buttons[c->radio.nbuttons-2] =
|
||||
dupstr("Use font in both ANSI and OEM modes");
|
||||
c->radio.buttons[c->radio.nbuttons-1] =
|
||||
dupstr("Use font in OEM mode only");
|
||||
c->radio.buttondata =
|
||||
sresize(c->radio.buttondata, c->radio.nbuttons, intorptr);
|
||||
c->radio.buttondata[c->radio.nbuttons-3] = I(VT_XWINDOWS);
|
||||
c->radio.buttondata[c->radio.nbuttons-2] = I(VT_OEMANSI);
|
||||
c->radio.buttondata[c->radio.nbuttons-1] = I(VT_OEMONLY);
|
||||
if (!c->radio.shortcuts) {
|
||||
int j;
|
||||
c->radio.shortcuts = snewn(c->radio.nbuttons, char);
|
||||
for (j = 0; j < c->radio.nbuttons; j++)
|
||||
c->radio.shortcuts[j] = NO_SHORTCUT;
|
||||
} else {
|
||||
c->radio.shortcuts = sresize(c->radio.shortcuts,
|
||||
c->radio.nbuttons, char);
|
||||
}
|
||||
c->radio.shortcuts[c->radio.nbuttons-3] = 'x';
|
||||
c->radio.shortcuts[c->radio.nbuttons-2] = 'b';
|
||||
c->radio.shortcuts[c->radio.nbuttons-1] = 'e';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RTF paste is Windows-specific.
|
||||
*/
|
||||
s = ctrl_getset(b, "Window/Selection", "format",
|
||||
"Formatting of pasted characters");
|
||||
ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f',
|
||||
HELPCTX(selection_rtf),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,rtf_paste)));
|
||||
|
||||
/*
|
||||
* Windows often has no middle button, so we supply a selection
|
||||
* mode in which the more critical Paste action is available on
|
||||
* the right button instead.
|
||||
*/
|
||||
s = ctrl_getset(b, "Window/Selection", "mouse",
|
||||
"Control use of mouse");
|
||||
ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1,
|
||||
HELPCTX(selection_buttons),
|
||||
dlg_stdradiobutton_handler,
|
||||
I(offsetof(Config, mouse_is_xterm)),
|
||||
"Windows (Middle extends, Right brings up menu)", I(2),
|
||||
"Compromise (Middle extends, Right pastes)", I(0),
|
||||
"xterm (Right extends, Middle pastes)", I(1), NULL);
|
||||
/*
|
||||
* This really ought to go at the _top_ of its box, not the
|
||||
* bottom, so we'll just do some shuffling now we've set it
|
||||
* up...
|
||||
*/
|
||||
c = s->ctrls[s->ncontrols-1]; /* this should be the new control */
|
||||
memmove(s->ctrls+1, s->ctrls, (s->ncontrols-1)*sizeof(union control *));
|
||||
s->ctrls[0] = c;
|
||||
|
||||
/*
|
||||
* Logical palettes don't even make sense anywhere except Windows.
|
||||
*/
|
||||
s = ctrl_getset(b, "Window/Colours", "general",
|
||||
"General options for colour usage");
|
||||
ctrl_checkbox(s, "Attempt to use logical palettes", 'l',
|
||||
HELPCTX(colours_logpal),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,try_palette)));
|
||||
ctrl_checkbox(s, "Use system colours", 's',
|
||||
HELPCTX(colours_system),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,system_colour)));
|
||||
|
||||
|
||||
/*
|
||||
* Resize-by-changing-font is a Windows insanity.
|
||||
*/
|
||||
s = ctrl_getset(b, "Window", "size", "Set the size of the window");
|
||||
ctrl_radiobuttons(s, "When window is resized:", 'z', 1,
|
||||
HELPCTX(window_resize),
|
||||
dlg_stdradiobutton_handler,
|
||||
I(offsetof(Config, resize_action)),
|
||||
"Change the number of rows and columns", I(RESIZE_TERM),
|
||||
"Change the size of the font", I(RESIZE_FONT),
|
||||
"Change font size only when maximised", I(RESIZE_EITHER),
|
||||
"Forbid resizing completely", I(RESIZE_DISABLED), NULL);
|
||||
|
||||
/*
|
||||
* Most of the Window/Behaviour stuff is there to mimic Windows
|
||||
* conventions which PuTTY can optionally disregard. Hence,
|
||||
* most of these options are Windows-specific.
|
||||
*/
|
||||
s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
|
||||
ctrl_checkbox(s, "Window closes on ALT-F4", '4',
|
||||
HELPCTX(behaviour_altf4),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,alt_f4)));
|
||||
ctrl_checkbox(s, "System menu appears on ALT-Space", 'y',
|
||||
HELPCTX(behaviour_altspace),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,alt_space)));
|
||||
ctrl_checkbox(s, "System menu appears on ALT alone", 'l',
|
||||
HELPCTX(behaviour_altonly),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,alt_only)));
|
||||
ctrl_checkbox(s, "Ensure window is always on top", 'e',
|
||||
HELPCTX(behaviour_alwaysontop),
|
||||
dlg_stdcheckbox_handler, I(offsetof(Config,alwaysontop)));
|
||||
ctrl_checkbox(s, "Full screen on Alt-Enter", 'f',
|
||||
HELPCTX(behaviour_altenter),
|
||||
dlg_stdcheckbox_handler,
|
||||
I(offsetof(Config,fullscreenonaltenter)));
|
||||
}
|
325
windows/wincons.c
Normal file
325
windows/wincons.c
Normal file
@ -0,0 +1,325 @@
|
||||
/*
|
||||
* console.c: various interactive-prompt routines shared between
|
||||
* the Windows console PuTTY tools
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "storage.h"
|
||||
#include "ssh.h"
|
||||
|
||||
int console_batch_mode = FALSE;
|
||||
|
||||
static void *console_logctx = NULL;
|
||||
|
||||
/*
|
||||
* Clean up and exit.
|
||||
*/
|
||||
void cleanup_exit(int code)
|
||||
{
|
||||
/*
|
||||
* Clean up.
|
||||
*/
|
||||
sk_cleanup();
|
||||
|
||||
random_save_seed();
|
||||
#ifdef MSCRYPTOAPI
|
||||
crypto_wrapup();
|
||||
#endif
|
||||
|
||||
exit(code);
|
||||
}
|
||||
|
||||
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
|
||||
char *keystr, char *fingerprint)
|
||||
{
|
||||
int ret;
|
||||
HANDLE hin;
|
||||
DWORD savemode, i;
|
||||
|
||||
static const char absentmsg_batch[] =
|
||||
"The server's host key is not cached in the registry. You\n"
|
||||
"have no guarantee that the server is the computer you\n"
|
||||
"think it is.\n"
|
||||
"The server's %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"Connection abandoned.\n";
|
||||
static const char absentmsg[] =
|
||||
"The server's host key is not cached in the registry. You\n"
|
||||
"have no guarantee that the server is the computer you\n"
|
||||
"think it is.\n"
|
||||
"The server's %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"If you trust this host, enter \"y\" to add the key to\n"
|
||||
"PuTTY's cache and carry on connecting.\n"
|
||||
"If you want to carry on connecting just once, without\n"
|
||||
"adding the key to the cache, enter \"n\".\n"
|
||||
"If you do not trust this host, press Return to abandon the\n"
|
||||
"connection.\n"
|
||||
"Store key in cache? (y/n) ";
|
||||
|
||||
static const char wrongmsg_batch[] =
|
||||
"WARNING - POTENTIAL SECURITY BREACH!\n"
|
||||
"The server's host key does not match the one PuTTY has\n"
|
||||
"cached in the registry. This means that either the\n"
|
||||
"server administrator has changed the host key, or you\n"
|
||||
"have actually connected to another computer pretending\n"
|
||||
"to be the server.\n"
|
||||
"The new %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"Connection abandoned.\n";
|
||||
static const char wrongmsg[] =
|
||||
"WARNING - POTENTIAL SECURITY BREACH!\n"
|
||||
"The server's host key does not match the one PuTTY has\n"
|
||||
"cached in the registry. This means that either the\n"
|
||||
"server administrator has changed the host key, or you\n"
|
||||
"have actually connected to another computer pretending\n"
|
||||
"to be the server.\n"
|
||||
"The new %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"If you were expecting this change and trust the new key,\n"
|
||||
"enter \"y\" to update PuTTY's cache and continue connecting.\n"
|
||||
"If you want to carry on connecting but without updating\n"
|
||||
"the cache, enter \"n\".\n"
|
||||
"If you want to abandon the connection completely, press\n"
|
||||
"Return to cancel. Pressing Return is the ONLY guaranteed\n"
|
||||
"safe choice.\n"
|
||||
"Update cached key? (y/n, Return cancels connection) ";
|
||||
|
||||
static const char abandoned[] = "Connection abandoned.\n";
|
||||
|
||||
char line[32];
|
||||
|
||||
/*
|
||||
* Verify the key against the registry.
|
||||
*/
|
||||
ret = verify_host_key(host, port, keytype, keystr);
|
||||
|
||||
if (ret == 0) /* success - key matched OK */
|
||||
return;
|
||||
|
||||
if (ret == 2) { /* key was different */
|
||||
if (console_batch_mode) {
|
||||
fprintf(stderr, wrongmsg_batch, keytype, fingerprint);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
fprintf(stderr, wrongmsg, keytype, fingerprint);
|
||||
fflush(stderr);
|
||||
}
|
||||
if (ret == 1) { /* key was absent */
|
||||
if (console_batch_mode) {
|
||||
fprintf(stderr, absentmsg_batch, keytype, fingerprint);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
fprintf(stderr, absentmsg, keytype, fingerprint);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
hin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(hin, &savemode);
|
||||
SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
|
||||
ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
|
||||
ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
|
||||
SetConsoleMode(hin, savemode);
|
||||
|
||||
if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') {
|
||||
if (line[0] == 'y' || line[0] == 'Y')
|
||||
store_host_key(host, port, keytype, keystr);
|
||||
} else {
|
||||
fprintf(stderr, abandoned);
|
||||
cleanup_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void update_specials_menu(void *frontend)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask whether the selected cipher is acceptable (since it was
|
||||
* below the configured 'warn' threshold).
|
||||
* cs: 0 = both ways, 1 = client->server, 2 = server->client
|
||||
*/
|
||||
void askcipher(void *frontend, char *ciphername, int cs)
|
||||
{
|
||||
HANDLE hin;
|
||||
DWORD savemode, i;
|
||||
|
||||
static const char msg[] =
|
||||
"The first %scipher supported by the server is\n"
|
||||
"%s, which is below the configured warning threshold.\n"
|
||||
"Continue with connection? (y/n) ";
|
||||
static const char msg_batch[] =
|
||||
"The first %scipher supported by the server is\n"
|
||||
"%s, which is below the configured warning threshold.\n"
|
||||
"Connection abandoned.\n";
|
||||
static const char abandoned[] = "Connection abandoned.\n";
|
||||
|
||||
char line[32];
|
||||
|
||||
if (console_batch_mode) {
|
||||
fprintf(stderr, msg_batch,
|
||||
(cs == 0) ? "" :
|
||||
(cs == 1) ? "client-to-server " : "server-to-client ",
|
||||
ciphername);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
|
||||
fprintf(stderr, msg,
|
||||
(cs == 0) ? "" :
|
||||
(cs == 1) ? "client-to-server " : "server-to-client ",
|
||||
ciphername);
|
||||
fflush(stderr);
|
||||
|
||||
hin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(hin, &savemode);
|
||||
SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
|
||||
ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
|
||||
ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
|
||||
SetConsoleMode(hin, savemode);
|
||||
|
||||
if (line[0] == 'y' || line[0] == 'Y') {
|
||||
return;
|
||||
} else {
|
||||
fprintf(stderr, abandoned);
|
||||
cleanup_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask whether to wipe a session log file before writing to it.
|
||||
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
|
||||
*/
|
||||
int askappend(void *frontend, Filename filename)
|
||||
{
|
||||
HANDLE hin;
|
||||
DWORD savemode, i;
|
||||
|
||||
static const char msgtemplate[] =
|
||||
"The session log file \"%.*s\" already exists.\n"
|
||||
"You can overwrite it with a new session log,\n"
|
||||
"append your session log to the end of it,\n"
|
||||
"or disable session logging for this session.\n"
|
||||
"Enter \"y\" to wipe the file, \"n\" to append to it,\n"
|
||||
"or just press Return to disable logging.\n"
|
||||
"Wipe the log file? (y/n, Return cancels logging) ";
|
||||
|
||||
static const char msgtemplate_batch[] =
|
||||
"The session log file \"%.*s\" already exists.\n"
|
||||
"Logging will not be enabled.\n";
|
||||
|
||||
char line[32];
|
||||
|
||||
if (console_batch_mode) {
|
||||
fprintf(stderr, msgtemplate_batch, FILENAME_MAX, filename.path);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
fprintf(stderr, msgtemplate, FILENAME_MAX, filename.path);
|
||||
fflush(stderr);
|
||||
|
||||
hin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(hin, &savemode);
|
||||
SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT |
|
||||
ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT));
|
||||
ReadFile(hin, line, sizeof(line) - 1, &i, NULL);
|
||||
SetConsoleMode(hin, savemode);
|
||||
|
||||
if (line[0] == 'y' || line[0] == 'Y')
|
||||
return 2;
|
||||
else if (line[0] == 'n' || line[0] == 'N')
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Warn about the obsolescent key file format.
|
||||
*
|
||||
* Uniquely among these functions, this one does _not_ expect a
|
||||
* frontend handle. This means that if PuTTY is ported to a
|
||||
* platform which requires frontend handles, this function will be
|
||||
* an anomaly. Fortunately, the problem it addresses will not have
|
||||
* been present on that platform, so it can plausibly be
|
||||
* implemented as an empty function.
|
||||
*/
|
||||
void old_keyfile_warning(void)
|
||||
{
|
||||
static const char message[] =
|
||||
"You are loading an SSH 2 private key which has an\n"
|
||||
"old version of the file format. This means your key\n"
|
||||
"file is not fully tamperproof. Future versions of\n"
|
||||
"PuTTY may stop supporting this private key format,\n"
|
||||
"so we recommend you convert your key to the new\n"
|
||||
"format.\n"
|
||||
"\n"
|
||||
"Once the key is loaded into PuTTYgen, you can perform\n"
|
||||
"this conversion simply by saving it again.\n";
|
||||
|
||||
fputs(message, stderr);
|
||||
}
|
||||
|
||||
void console_provide_logctx(void *logctx)
|
||||
{
|
||||
console_logctx = logctx;
|
||||
}
|
||||
|
||||
void logevent(void *frontend, const char *string)
|
||||
{
|
||||
if (console_logctx)
|
||||
log_eventlog(console_logctx, string);
|
||||
}
|
||||
|
||||
int console_get_line(const char *prompt, char *str,
|
||||
int maxlen, int is_pw)
|
||||
{
|
||||
HANDLE hin, hout;
|
||||
DWORD savemode, newmode, i;
|
||||
|
||||
if (console_batch_mode) {
|
||||
if (maxlen > 0)
|
||||
str[0] = '\0';
|
||||
} else {
|
||||
hin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
hout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hin == INVALID_HANDLE_VALUE || hout == INVALID_HANDLE_VALUE) {
|
||||
fprintf(stderr, "Cannot get standard input/output handles\n");
|
||||
cleanup_exit(1);
|
||||
}
|
||||
|
||||
GetConsoleMode(hin, &savemode);
|
||||
newmode = savemode | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT;
|
||||
if (is_pw)
|
||||
newmode &= ~ENABLE_ECHO_INPUT;
|
||||
else
|
||||
newmode |= ENABLE_ECHO_INPUT;
|
||||
SetConsoleMode(hin, newmode);
|
||||
|
||||
WriteFile(hout, prompt, strlen(prompt), &i, NULL);
|
||||
ReadFile(hin, str, maxlen - 1, &i, NULL);
|
||||
|
||||
SetConsoleMode(hin, savemode);
|
||||
|
||||
if ((int) i > maxlen)
|
||||
i = maxlen - 1;
|
||||
else
|
||||
i = i - 2;
|
||||
str[i] = '\0';
|
||||
|
||||
if (is_pw)
|
||||
WriteFile(hout, "\r\n", 2, &i, NULL);
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void frontend_keypress(void *handle)
|
||||
{
|
||||
/*
|
||||
* This is nothing but a stub, in console code.
|
||||
*/
|
||||
return;
|
||||
}
|
2535
windows/winctrls.c
Normal file
2535
windows/winctrls.c
Normal file
File diff suppressed because it is too large
Load Diff
41
windows/windefs.c
Normal file
41
windows/windefs.c
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* windefs.c: default settings that are specific to Windows.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#include <commctrl.h>
|
||||
|
||||
FontSpec platform_default_fontspec(const char *name)
|
||||
{
|
||||
FontSpec ret;
|
||||
if (!strcmp(name, "Font")) {
|
||||
strcpy(ret.name, "Courier New");
|
||||
ret.isbold = 0;
|
||||
ret.charset = ANSI_CHARSET;
|
||||
ret.height = 10;
|
||||
} else {
|
||||
ret.name[0] = '\0';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Filename platform_default_filename(const char *name)
|
||||
{
|
||||
Filename ret;
|
||||
if (!strcmp(name, "LogFileName"))
|
||||
strcpy(ret.path, "putty.log");
|
||||
else
|
||||
*ret.path = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *platform_default_s(const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int platform_default_i(const char *name, int def)
|
||||
{
|
||||
return def;
|
||||
}
|
875
windows/windlg.c
Normal file
875
windows/windlg.c
Normal file
@ -0,0 +1,875 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "win_res.h"
|
||||
#include "storage.h"
|
||||
#include "dialog.h"
|
||||
|
||||
#include <commctrl.h>
|
||||
#include <commdlg.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#ifdef MSVC4
|
||||
#define TVINSERTSTRUCT TV_INSERTSTRUCT
|
||||
#define TVITEM TV_ITEM
|
||||
#define ICON_BIG 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These are the various bits of data required to handle the
|
||||
* portable-dialog stuff in the config box. Having them at file
|
||||
* scope in here isn't too bad a place to put them; if we were ever
|
||||
* to need more than one config box per process we could always
|
||||
* shift them to a per-config-box structure stored in GWL_USERDATA.
|
||||
*/
|
||||
static struct controlbox *ctrlbox;
|
||||
/*
|
||||
* ctrls_base holds the OK and Cancel buttons: the controls which
|
||||
* are present in all dialog panels. ctrls_panel holds the ones
|
||||
* which change from panel to panel.
|
||||
*/
|
||||
static struct winctrls ctrls_base, ctrls_panel;
|
||||
static struct dlgparam dp;
|
||||
|
||||
static char **events = NULL;
|
||||
static int nevents = 0, negsize = 0;
|
||||
|
||||
static int requested_help;
|
||||
|
||||
extern Config cfg; /* defined in window.c */
|
||||
|
||||
struct sesslist sesslist; /* exported to window.c */
|
||||
|
||||
#define PRINTER_DISABLED_STRING "None (printing disabled)"
|
||||
|
||||
void force_normal(HWND hwnd)
|
||||
{
|
||||
static int recurse = 0;
|
||||
|
||||
WINDOWPLACEMENT wp;
|
||||
|
||||
if (recurse)
|
||||
return;
|
||||
recurse = 1;
|
||||
|
||||
wp.length = sizeof(wp);
|
||||
if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) {
|
||||
wp.showCmd = SW_SHOWNORMAL;
|
||||
SetWindowPlacement(hwnd, &wp);
|
||||
}
|
||||
recurse = 0;
|
||||
}
|
||||
|
||||
static int CALLBACK LogProc(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
char *str = dupprintf("%s Event Log", appname);
|
||||
SetWindowText(hwnd, str);
|
||||
sfree(str);
|
||||
}
|
||||
{
|
||||
static int tabs[4] = { 78, 108 };
|
||||
SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2,
|
||||
(LPARAM) tabs);
|
||||
}
|
||||
for (i = 0; i < nevents; i++)
|
||||
SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING,
|
||||
0, (LPARAM) events[i]);
|
||||
return 1;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam)) {
|
||||
case IDOK:
|
||||
case IDCANCEL:
|
||||
logbox = NULL;
|
||||
SetActiveWindow(GetParent(hwnd));
|
||||
DestroyWindow(hwnd);
|
||||
return 0;
|
||||
case IDN_COPY:
|
||||
if (HIWORD(wParam) == BN_CLICKED ||
|
||||
HIWORD(wParam) == BN_DOUBLECLICKED) {
|
||||
int selcount;
|
||||
int *selitems;
|
||||
selcount = SendDlgItemMessage(hwnd, IDN_LIST,
|
||||
LB_GETSELCOUNT, 0, 0);
|
||||
if (selcount == 0) { /* don't even try to copy zero items */
|
||||
MessageBeep(0);
|
||||
break;
|
||||
}
|
||||
|
||||
selitems = snewn(selcount, int);
|
||||
if (selitems) {
|
||||
int count = SendDlgItemMessage(hwnd, IDN_LIST,
|
||||
LB_GETSELITEMS,
|
||||
selcount,
|
||||
(LPARAM) selitems);
|
||||
int i;
|
||||
int size;
|
||||
char *clipdata;
|
||||
static unsigned char sel_nl[] = SEL_NL;
|
||||
|
||||
if (count == 0) { /* can't copy zero stuff */
|
||||
MessageBeep(0);
|
||||
break;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
for (i = 0; i < count; i++)
|
||||
size +=
|
||||
strlen(events[selitems[i]]) + sizeof(sel_nl);
|
||||
|
||||
clipdata = snewn(size, char);
|
||||
if (clipdata) {
|
||||
char *p = clipdata;
|
||||
for (i = 0; i < count; i++) {
|
||||
char *q = events[selitems[i]];
|
||||
int qlen = strlen(q);
|
||||
memcpy(p, q, qlen);
|
||||
p += qlen;
|
||||
memcpy(p, sel_nl, sizeof(sel_nl));
|
||||
p += sizeof(sel_nl);
|
||||
}
|
||||
write_aclip(NULL, clipdata, size, TRUE);
|
||||
sfree(clipdata);
|
||||
}
|
||||
sfree(selitems);
|
||||
|
||||
for (i = 0; i < nevents; i++)
|
||||
SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL,
|
||||
FALSE, i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
logbox = NULL;
|
||||
SetActiveWindow(GetParent(hwnd));
|
||||
DestroyWindow(hwnd);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
char *str = dupprintf("%s Licence", appname);
|
||||
SetWindowText(hwnd, str);
|
||||
sfree(str);
|
||||
}
|
||||
return 1;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam)) {
|
||||
case IDOK:
|
||||
case IDCANCEL:
|
||||
EndDialog(hwnd, 1);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
EndDialog(hwnd, 1);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CALLBACK AboutProc(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
char *str;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
str = dupprintf("About %s", appname);
|
||||
SetWindowText(hwnd, str);
|
||||
sfree(str);
|
||||
SetDlgItemText(hwnd, IDA_TEXT1, appname);
|
||||
SetDlgItemText(hwnd, IDA_VERSION, ver);
|
||||
return 1;
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam)) {
|
||||
case IDOK:
|
||||
case IDCANCEL:
|
||||
EndDialog(hwnd, TRUE);
|
||||
return 0;
|
||||
case IDA_LICENCE:
|
||||
EnableWindow(hwnd, 0);
|
||||
DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX),
|
||||
hwnd, LicenceProc);
|
||||
EnableWindow(hwnd, 1);
|
||||
SetActiveWindow(hwnd);
|
||||
return 0;
|
||||
|
||||
case IDA_WEB:
|
||||
/* Load web browser */
|
||||
ShellExecute(hwnd, "open",
|
||||
"http://www.chiark.greenend.org.uk/~sgtatham/putty/",
|
||||
0, 0, SW_SHOWDEFAULT);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
EndDialog(hwnd, TRUE);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Null dialog procedure.
|
||||
*/
|
||||
static int CALLBACK NullDlgProc(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
IDCX_ABOUT = IDC_ABOUT,
|
||||
IDCX_TVSTATIC,
|
||||
IDCX_TREEVIEW,
|
||||
IDCX_STDBASE,
|
||||
IDCX_PANELBASE = IDCX_STDBASE + 32
|
||||
};
|
||||
|
||||
struct treeview_faff {
|
||||
HWND treeview;
|
||||
HTREEITEM lastat[4];
|
||||
};
|
||||
|
||||
static HTREEITEM treeview_insert(struct treeview_faff *faff,
|
||||
int level, char *text, char *path)
|
||||
{
|
||||
TVINSERTSTRUCT ins;
|
||||
int i;
|
||||
HTREEITEM newitem;
|
||||
ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT);
|
||||
ins.hInsertAfter = faff->lastat[level];
|
||||
#if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION
|
||||
#define INSITEM DUMMYUNIONNAME.item
|
||||
#else
|
||||
#define INSITEM item
|
||||
#endif
|
||||
ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM;
|
||||
ins.INSITEM.pszText = text;
|
||||
ins.INSITEM.cchTextMax = strlen(text)+1;
|
||||
ins.INSITEM.lParam = (LPARAM)path;
|
||||
newitem = TreeView_InsertItem(faff->treeview, &ins);
|
||||
if (level > 0)
|
||||
TreeView_Expand(faff->treeview, faff->lastat[level - 1],
|
||||
TVE_EXPAND);
|
||||
faff->lastat[level] = newitem;
|
||||
for (i = level + 1; i < 4; i++)
|
||||
faff->lastat[i] = NULL;
|
||||
return newitem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the panelfuls of controls in the configuration box.
|
||||
*/
|
||||
static void create_controls(HWND hwnd, char *path)
|
||||
{
|
||||
struct ctlpos cp;
|
||||
int index;
|
||||
int base_id;
|
||||
struct winctrls *wc;
|
||||
|
||||
if (!path[0]) {
|
||||
/*
|
||||
* Here we must create the basic standard controls.
|
||||
*/
|
||||
ctlposinit(&cp, hwnd, 3, 3, 235);
|
||||
wc = &ctrls_base;
|
||||
base_id = IDCX_STDBASE;
|
||||
} else {
|
||||
/*
|
||||
* Otherwise, we're creating the controls for a particular
|
||||
* panel.
|
||||
*/
|
||||
ctlposinit(&cp, hwnd, 80, 3, 13);
|
||||
wc = &ctrls_panel;
|
||||
base_id = IDCX_PANELBASE;
|
||||
}
|
||||
|
||||
for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) {
|
||||
struct controlset *s = ctrlbox->ctrlsets[index];
|
||||
winctrl_layout(&dp, wc, &cp, s, &base_id);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is the configuration box.
|
||||
*/
|
||||
static int CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
HWND hw, treeview;
|
||||
struct treeview_faff tvfaff;
|
||||
int ret;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
dp.hwnd = hwnd;
|
||||
create_controls(hwnd, ""); /* Open and Cancel buttons etc */
|
||||
SetWindowText(hwnd, dp.wintitle);
|
||||
SetWindowLong(hwnd, GWL_USERDATA, 0);
|
||||
if (help_path)
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE,
|
||||
GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
|
||||
else {
|
||||
HWND item = GetDlgItem(hwnd, IDC_HELPBTN);
|
||||
if (item)
|
||||
DestroyWindow(item);
|
||||
}
|
||||
requested_help = FALSE;
|
||||
SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
|
||||
(LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON)));
|
||||
/*
|
||||
* Centre the window.
|
||||
*/
|
||||
{ /* centre the window */
|
||||
RECT rs, rd;
|
||||
|
||||
hw = GetDesktopWindow();
|
||||
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
|
||||
MoveWindow(hwnd,
|
||||
(rs.right + rs.left + rd.left - rd.right) / 2,
|
||||
(rs.bottom + rs.top + rd.top - rd.bottom) / 2,
|
||||
rd.right - rd.left, rd.bottom - rd.top, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the tree view.
|
||||
*/
|
||||
{
|
||||
RECT r;
|
||||
WPARAM font;
|
||||
HWND tvstatic;
|
||||
|
||||
r.left = 3;
|
||||
r.right = r.left + 75;
|
||||
r.top = 3;
|
||||
r.bottom = r.top + 10;
|
||||
MapDialogRect(hwnd, &r);
|
||||
tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:",
|
||||
WS_CHILD | WS_VISIBLE,
|
||||
r.left, r.top,
|
||||
r.right - r.left, r.bottom - r.top,
|
||||
hwnd, (HMENU) IDCX_TVSTATIC, hinst,
|
||||
NULL);
|
||||
font = SendMessage(hwnd, WM_GETFONT, 0, 0);
|
||||
SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
|
||||
|
||||
r.left = 3;
|
||||
r.right = r.left + 75;
|
||||
r.top = 13;
|
||||
r.bottom = r.top + 219;
|
||||
MapDialogRect(hwnd, &r);
|
||||
treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "",
|
||||
WS_CHILD | WS_VISIBLE |
|
||||
WS_TABSTOP | TVS_HASLINES |
|
||||
TVS_DISABLEDRAGDROP | TVS_HASBUTTONS
|
||||
| TVS_LINESATROOT |
|
||||
TVS_SHOWSELALWAYS, r.left, r.top,
|
||||
r.right - r.left, r.bottom - r.top,
|
||||
hwnd, (HMENU) IDCX_TREEVIEW, hinst,
|
||||
NULL);
|
||||
font = SendMessage(hwnd, WM_GETFONT, 0, 0);
|
||||
SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(TRUE, 0));
|
||||
tvfaff.treeview = treeview;
|
||||
memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the tree view contents.
|
||||
*/
|
||||
{
|
||||
HTREEITEM hfirst = NULL;
|
||||
int i;
|
||||
char *path = NULL;
|
||||
|
||||
for (i = 0; i < ctrlbox->nctrlsets; i++) {
|
||||
struct controlset *s = ctrlbox->ctrlsets[i];
|
||||
HTREEITEM item;
|
||||
int j;
|
||||
char *c;
|
||||
|
||||
if (!s->pathname[0])
|
||||
continue;
|
||||
j = path ? ctrl_path_compare(s->pathname, path) : 0;
|
||||
if (j == INT_MAX)
|
||||
continue; /* same path, nothing to add to tree */
|
||||
|
||||
/*
|
||||
* We expect never to find an implicit path
|
||||
* component. For example, we expect never to see
|
||||
* A/B/C followed by A/D/E, because that would
|
||||
* _implicitly_ create A/D. All our path prefixes
|
||||
* are expected to contain actual controls and be
|
||||
* selectable in the treeview; so we would expect
|
||||
* to see A/D _explicitly_ before encountering
|
||||
* A/D/E.
|
||||
*/
|
||||
assert(j == ctrl_path_elements(s->pathname) - 1);
|
||||
|
||||
c = strrchr(s->pathname, '/');
|
||||
if (!c)
|
||||
c = s->pathname;
|
||||
else
|
||||
c++;
|
||||
|
||||
item = treeview_insert(&tvfaff, j, c, s->pathname);
|
||||
if (!hfirst)
|
||||
hfirst = item;
|
||||
|
||||
path = s->pathname;
|
||||
}
|
||||
|
||||
/*
|
||||
* Put the treeview selection on to the Session panel.
|
||||
* This should also cause creation of the relevant
|
||||
* controls.
|
||||
*/
|
||||
TreeView_SelectItem(treeview, hfirst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set focus into the first available control.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
struct winctrl *c;
|
||||
|
||||
for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL;
|
||||
i++) {
|
||||
if (c->ctrl) {
|
||||
dlg_set_focus(c->ctrl, &dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetWindowLong(hwnd, GWL_USERDATA, 1);
|
||||
return 0;
|
||||
case WM_LBUTTONUP:
|
||||
/*
|
||||
* Button release should trigger WM_OK if there was a
|
||||
* previous double click on the session list.
|
||||
*/
|
||||
ReleaseCapture();
|
||||
if (dp.ended)
|
||||
SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
|
||||
break;
|
||||
case WM_NOTIFY:
|
||||
if (LOWORD(wParam) == IDCX_TREEVIEW &&
|
||||
((LPNMHDR) lParam)->code == TVN_SELCHANGED) {
|
||||
HTREEITEM i =
|
||||
TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom);
|
||||
TVITEM item;
|
||||
char buffer[64];
|
||||
|
||||
SendMessage (hwnd, WM_SETREDRAW, FALSE, 0);
|
||||
|
||||
item.hItem = i;
|
||||
item.pszText = buffer;
|
||||
item.cchTextMax = sizeof(buffer);
|
||||
item.mask = TVIF_TEXT | TVIF_PARAM;
|
||||
TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item);
|
||||
{
|
||||
/* Destroy all controls in the currently visible panel. */
|
||||
int k;
|
||||
HWND item;
|
||||
struct winctrl *c;
|
||||
|
||||
while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) {
|
||||
for (k = 0; k < c->num_ids; k++) {
|
||||
item = GetDlgItem(hwnd, c->base_id + k);
|
||||
if (item)
|
||||
DestroyWindow(item);
|
||||
}
|
||||
winctrl_rem_shortcuts(&dp, c);
|
||||
winctrl_remove(&ctrls_panel, c);
|
||||
sfree(c->data);
|
||||
sfree(c);
|
||||
}
|
||||
}
|
||||
create_controls(hwnd, (char *)item.lParam);
|
||||
|
||||
dlg_refresh(NULL, &dp); /* set up control values */
|
||||
|
||||
SendMessage (hwnd, WM_SETREDRAW, TRUE, 0);
|
||||
InvalidateRect (hwnd, NULL, TRUE);
|
||||
|
||||
SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
case WM_DRAWITEM:
|
||||
default: /* also handle drag list msg here */
|
||||
/*
|
||||
* Only process WM_COMMAND once the dialog is fully formed.
|
||||
*/
|
||||
if (GetWindowLong(hwnd, GWL_USERDATA) == 1) {
|
||||
ret = winctrl_handle_command(&dp, msg, wParam, lParam);
|
||||
if (dp.ended && GetCapture() != hwnd)
|
||||
SaneEndDialog(hwnd, dp.endresult ? 1 : 0);
|
||||
} else
|
||||
ret = 0;
|
||||
return ret;
|
||||
case WM_HELP:
|
||||
if (help_path) {
|
||||
if (winctrl_context_help(&dp, hwnd,
|
||||
((LPHELPINFO)lParam)->iCtrlId))
|
||||
requested_help = TRUE;
|
||||
else
|
||||
MessageBeep(0);
|
||||
}
|
||||
break;
|
||||
case WM_CLOSE:
|
||||
if (requested_help) {
|
||||
WinHelp(hwnd, help_path, HELP_QUIT, 0);
|
||||
requested_help = FALSE;
|
||||
}
|
||||
SaneEndDialog(hwnd, 0);
|
||||
return 0;
|
||||
|
||||
/* Grrr Explorer will maximize Dialogs! */
|
||||
case WM_SIZE:
|
||||
if (wParam == SIZE_MAXIMIZED)
|
||||
force_normal(hwnd);
|
||||
return 0;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void modal_about_box(HWND hwnd)
|
||||
{
|
||||
EnableWindow(hwnd, 0);
|
||||
DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
|
||||
EnableWindow(hwnd, 1);
|
||||
SetActiveWindow(hwnd);
|
||||
}
|
||||
|
||||
void show_help(HWND hwnd)
|
||||
{
|
||||
if (help_path) {
|
||||
WinHelp(hwnd, help_path,
|
||||
help_has_contents ? HELP_FINDER : HELP_CONTENTS,
|
||||
0);
|
||||
requested_help = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void defuse_showwindow(void)
|
||||
{
|
||||
/*
|
||||
* Work around the fact that the app's first call to ShowWindow
|
||||
* will ignore the default in favour of the shell-provided
|
||||
* setting.
|
||||
*/
|
||||
{
|
||||
HWND hwnd;
|
||||
hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX),
|
||||
NULL, NullDlgProc);
|
||||
ShowWindow(hwnd, SW_HIDE);
|
||||
SetActiveWindow(hwnd);
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
int do_config(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ctrlbox = ctrl_new_box();
|
||||
setup_config_box(ctrlbox, &sesslist, FALSE, 0);
|
||||
win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), FALSE);
|
||||
dp_init(&dp);
|
||||
winctrl_init(&ctrls_base);
|
||||
winctrl_init(&ctrls_panel);
|
||||
dp_add_tree(&dp, &ctrls_base);
|
||||
dp_add_tree(&dp, &ctrls_panel);
|
||||
dp.wintitle = dupprintf("%s Configuration", appname);
|
||||
dp.errtitle = dupprintf("%s Error", appname);
|
||||
dp.data = &cfg;
|
||||
dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
|
||||
|
||||
get_sesslist(&sesslist, TRUE);
|
||||
ret =
|
||||
SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
|
||||
GenericMainDlgProc);
|
||||
get_sesslist(&sesslist, FALSE);
|
||||
|
||||
ctrl_free_box(ctrlbox);
|
||||
winctrl_cleanup(&ctrls_panel);
|
||||
winctrl_cleanup(&ctrls_base);
|
||||
dp_cleanup(&dp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int do_reconfig(HWND hwnd)
|
||||
{
|
||||
Config backup_cfg;
|
||||
int ret;
|
||||
|
||||
backup_cfg = cfg; /* structure copy */
|
||||
|
||||
ctrlbox = ctrl_new_box();
|
||||
setup_config_box(ctrlbox, NULL, TRUE, cfg.protocol);
|
||||
win_setup_config_box(ctrlbox, &dp.hwnd, (help_path != NULL), TRUE);
|
||||
dp_init(&dp);
|
||||
winctrl_init(&ctrls_base);
|
||||
winctrl_init(&ctrls_panel);
|
||||
dp_add_tree(&dp, &ctrls_base);
|
||||
dp_add_tree(&dp, &ctrls_panel);
|
||||
dp.wintitle = dupprintf("%s Reconfiguration", appname);
|
||||
dp.errtitle = dupprintf("%s Error", appname);
|
||||
dp.data = &cfg;
|
||||
dp.shortcuts['g'] = TRUE; /* the treeview: `Cate&gory' */
|
||||
|
||||
ret = SaneDialogBox(hinst, MAKEINTRESOURCE(IDD_MAINBOX), NULL,
|
||||
GenericMainDlgProc);
|
||||
|
||||
ctrl_free_box(ctrlbox);
|
||||
winctrl_cleanup(&ctrls_base);
|
||||
winctrl_cleanup(&ctrls_panel);
|
||||
dp_cleanup(&dp);
|
||||
|
||||
if (!ret)
|
||||
cfg = backup_cfg; /* structure copy */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void logevent(void *frontend, const char *string)
|
||||
{
|
||||
char timebuf[40];
|
||||
time_t t;
|
||||
|
||||
log_eventlog(logctx, string);
|
||||
|
||||
if (nevents >= negsize) {
|
||||
negsize += 64;
|
||||
events = sresize(events, negsize, char *);
|
||||
}
|
||||
|
||||
time(&t);
|
||||
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t",
|
||||
localtime(&t));
|
||||
|
||||
events[nevents] = snewn(strlen(timebuf) + strlen(string) + 1, char);
|
||||
strcpy(events[nevents], timebuf);
|
||||
strcat(events[nevents], string);
|
||||
if (logbox) {
|
||||
int count;
|
||||
SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING,
|
||||
0, (LPARAM) events[nevents]);
|
||||
count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0);
|
||||
SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0);
|
||||
}
|
||||
nevents++;
|
||||
}
|
||||
|
||||
void showeventlog(HWND hwnd)
|
||||
{
|
||||
if (!logbox) {
|
||||
logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX),
|
||||
hwnd, LogProc);
|
||||
ShowWindow(logbox, SW_SHOWNORMAL);
|
||||
}
|
||||
SetActiveWindow(logbox);
|
||||
}
|
||||
|
||||
void showabout(HWND hwnd)
|
||||
{
|
||||
DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc);
|
||||
}
|
||||
|
||||
void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype,
|
||||
char *keystr, char *fingerprint)
|
||||
{
|
||||
int ret;
|
||||
|
||||
static const char absentmsg[] =
|
||||
"The server's host key is not cached in the registry. You\n"
|
||||
"have no guarantee that the server is the computer you\n"
|
||||
"think it is.\n"
|
||||
"The server's %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"If you trust this host, hit Yes to add the key to\n"
|
||||
"%s's cache and carry on connecting.\n"
|
||||
"If you want to carry on connecting just once, without\n"
|
||||
"adding the key to the cache, hit No.\n"
|
||||
"If you do not trust this host, hit Cancel to abandon the\n"
|
||||
"connection.\n";
|
||||
|
||||
static const char wrongmsg[] =
|
||||
"WARNING - POTENTIAL SECURITY BREACH!\n"
|
||||
"\n"
|
||||
"The server's host key does not match the one %s has\n"
|
||||
"cached in the registry. This means that either the\n"
|
||||
"server administrator has changed the host key, or you\n"
|
||||
"have actually connected to another computer pretending\n"
|
||||
"to be the server.\n"
|
||||
"The new %s key fingerprint is:\n"
|
||||
"%s\n"
|
||||
"If you were expecting this change and trust the new key,\n"
|
||||
"hit Yes to update %s's cache and continue connecting.\n"
|
||||
"If you want to carry on connecting but without updating\n"
|
||||
"the cache, hit No.\n"
|
||||
"If you want to abandon the connection completely, hit\n"
|
||||
"Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n";
|
||||
|
||||
static const char mbtitle[] = "%s Security Alert";
|
||||
|
||||
/*
|
||||
* Verify the key against the registry.
|
||||
*/
|
||||
ret = verify_host_key(host, port, keytype, keystr);
|
||||
|
||||
if (ret == 0) /* success - key matched OK */
|
||||
return;
|
||||
if (ret == 2) { /* key was different */
|
||||
int mbret;
|
||||
char *message, *title;
|
||||
message = dupprintf(wrongmsg, appname, keytype, fingerprint, appname);
|
||||
title = dupprintf(mbtitle, appname);
|
||||
mbret = MessageBox(NULL, message, title,
|
||||
MB_ICONWARNING | MB_YESNOCANCEL);
|
||||
sfree(message);
|
||||
sfree(title);
|
||||
if (mbret == IDYES)
|
||||
store_host_key(host, port, keytype, keystr);
|
||||
if (mbret == IDCANCEL)
|
||||
cleanup_exit(0);
|
||||
}
|
||||
if (ret == 1) { /* key was absent */
|
||||
int mbret;
|
||||
char *message, *title;
|
||||
message = dupprintf(absentmsg, keytype, fingerprint, appname);
|
||||
title = dupprintf(mbtitle, appname);
|
||||
mbret = MessageBox(NULL, message, title,
|
||||
MB_ICONWARNING | MB_YESNOCANCEL);
|
||||
sfree(message);
|
||||
sfree(title);
|
||||
if (mbret == IDYES)
|
||||
store_host_key(host, port, keytype, keystr);
|
||||
if (mbret == IDCANCEL)
|
||||
cleanup_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask whether the selected cipher is acceptable (since it was
|
||||
* below the configured 'warn' threshold).
|
||||
* cs: 0 = both ways, 1 = client->server, 2 = server->client
|
||||
*/
|
||||
void askcipher(void *frontend, char *ciphername, int cs)
|
||||
{
|
||||
static const char mbtitle[] = "%s Security Alert";
|
||||
static const char msg[] =
|
||||
"The first %.35scipher supported by the server\n"
|
||||
"is %.64s, which is below the configured\n"
|
||||
"warning threshold.\n"
|
||||
"Do you want to continue with this connection?\n";
|
||||
char *message, *title;
|
||||
int mbret;
|
||||
|
||||
message = dupprintf(msg, ((cs == 0) ? "" :
|
||||
(cs == 1) ? "client-to-server " :
|
||||
"server-to-client "), ciphername);
|
||||
title = dupprintf(mbtitle, appname);
|
||||
mbret = MessageBox(NULL, message, title,
|
||||
MB_ICONWARNING | MB_YESNO);
|
||||
sfree(message);
|
||||
sfree(title);
|
||||
if (mbret == IDYES)
|
||||
return;
|
||||
else
|
||||
cleanup_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ask whether to wipe a session log file before writing to it.
|
||||
* Returns 2 for wipe, 1 for append, 0 for cancel (don't log).
|
||||
*/
|
||||
int askappend(void *frontend, Filename filename)
|
||||
{
|
||||
static const char msgtemplate[] =
|
||||
"The session log file \"%.*s\" already exists.\n"
|
||||
"You can overwrite it with a new session log,\n"
|
||||
"append your session log to the end of it,\n"
|
||||
"or disable session logging for this session.\n"
|
||||
"Hit Yes to wipe the file, No to append to it,\n"
|
||||
"or Cancel to disable logging.";
|
||||
char *message;
|
||||
char *mbtitle;
|
||||
int mbret;
|
||||
|
||||
message = dupprintf(msgtemplate, FILENAME_MAX, filename.path);
|
||||
mbtitle = dupprintf("%s Log to File", appname);
|
||||
|
||||
mbret = MessageBox(NULL, message, mbtitle,
|
||||
MB_ICONQUESTION | MB_YESNOCANCEL);
|
||||
|
||||
sfree(message);
|
||||
sfree(mbtitle);
|
||||
|
||||
if (mbret == IDYES)
|
||||
return 2;
|
||||
else if (mbret == IDNO)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Warn about the obsolescent key file format.
|
||||
*
|
||||
* Uniquely among these functions, this one does _not_ expect a
|
||||
* frontend handle. This means that if PuTTY is ported to a
|
||||
* platform which requires frontend handles, this function will be
|
||||
* an anomaly. Fortunately, the problem it addresses will not have
|
||||
* been present on that platform, so it can plausibly be
|
||||
* implemented as an empty function.
|
||||
*/
|
||||
void old_keyfile_warning(void)
|
||||
{
|
||||
static const char mbtitle[] = "%s Key File Warning";
|
||||
static const char message[] =
|
||||
"You are loading an SSH 2 private key which has an\n"
|
||||
"old version of the file format. This means your key\n"
|
||||
"file is not fully tamperproof. Future versions of\n"
|
||||
"%s may stop supporting this private key format,\n"
|
||||
"so we recommend you convert your key to the new\n"
|
||||
"format.\n"
|
||||
"\n"
|
||||
"You can perform this conversion by loading the key\n"
|
||||
"into PuTTYgen and then saving it again.";
|
||||
|
||||
char *msg, *title;
|
||||
msg = dupprintf(message, appname);
|
||||
title = dupprintf(mbtitle, appname);
|
||||
|
||||
MessageBox(NULL, msg, title, MB_OK);
|
||||
|
||||
sfree(msg);
|
||||
sfree(title);
|
||||
}
|
4967
windows/window.c
Normal file
4967
windows/window.c
Normal file
File diff suppressed because it is too large
Load Diff
118
windows/winhelp.h
Normal file
118
windows/winhelp.h
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* winhelp.h - define Windows Help context names for the controls
|
||||
* in the PuTTY config box.
|
||||
*/
|
||||
|
||||
#define HELPCTX(x) P(WINHELP_CTX_ ## x)
|
||||
|
||||
#define WINHELP_CTX_no_help NULL
|
||||
|
||||
#define WINHELP_CTX_session_hostname "session.hostname"
|
||||
#define WINHELP_CTX_session_saved "session.saved"
|
||||
#define WINHELP_CTX_session_coe "session.coe"
|
||||
#define WINHELP_CTX_logging_main "logging.main"
|
||||
#define WINHELP_CTX_logging_filename "logging.filename"
|
||||
#define WINHELP_CTX_logging_exists "logging.exists"
|
||||
#define WINHELP_CTX_logging_ssh_omit_password "logging.ssh.omitpassword"
|
||||
#define WINHELP_CTX_logging_ssh_omit_data "logging.ssh.omitdata"
|
||||
#define WINHELP_CTX_keyboard_backspace "keyboard.backspace"
|
||||
#define WINHELP_CTX_keyboard_homeend "keyboard.homeend"
|
||||
#define WINHELP_CTX_keyboard_funkeys "keyboard.funkeys"
|
||||
#define WINHELP_CTX_keyboard_appkeypad "keyboard.appkeypad"
|
||||
#define WINHELP_CTX_keyboard_appcursor "keyboard.appcursor"
|
||||
#define WINHELP_CTX_keyboard_nethack "keyboard.nethack"
|
||||
#define WINHELP_CTX_keyboard_compose "keyboard.compose"
|
||||
#define WINHELP_CTX_keyboard_ctrlalt "keyboard.ctrlalt"
|
||||
#define WINHELP_CTX_features_application "features.application"
|
||||
#define WINHELP_CTX_features_mouse "features.mouse"
|
||||
#define WINHELP_CTX_features_resize "features.resize"
|
||||
#define WINHELP_CTX_features_altscreen "features.altscreen"
|
||||
#define WINHELP_CTX_features_retitle "features.retitle"
|
||||
#define WINHELP_CTX_features_qtitle "features.qtitle"
|
||||
#define WINHELP_CTX_features_dbackspace "features.dbackspace"
|
||||
#define WINHELP_CTX_features_charset "features.charset"
|
||||
#define WINHELP_CTX_features_arabicshaping "features.arabicshaping"
|
||||
#define WINHELP_CTX_features_bidi "features.bidi"
|
||||
#define WINHELP_CTX_terminal_autowrap "terminal.autowrap"
|
||||
#define WINHELP_CTX_terminal_decom "terminal.decom"
|
||||
#define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr"
|
||||
#define WINHELP_CTX_terminal_bce "terminal.bce"
|
||||
#define WINHELP_CTX_terminal_blink "terminal.blink"
|
||||
#define WINHELP_CTX_terminal_answerback "terminal.answerback"
|
||||
#define WINHELP_CTX_terminal_localecho "terminal.localecho"
|
||||
#define WINHELP_CTX_terminal_localedit "terminal.localedit"
|
||||
#define WINHELP_CTX_terminal_printing "terminal.printing"
|
||||
#define WINHELP_CTX_bell_style "bell.style"
|
||||
#define WINHELP_CTX_bell_taskbar "bell.taskbar"
|
||||
#define WINHELP_CTX_bell_overload "bell.overload"
|
||||
#define WINHELP_CTX_window_size "window.size"
|
||||
#define WINHELP_CTX_window_resize "window.resize"
|
||||
#define WINHELP_CTX_window_scrollback "window.scrollback"
|
||||
#define WINHELP_CTX_window_erased "window.erased"
|
||||
#define WINHELP_CTX_behaviour_closewarn "behaviour.closewarn"
|
||||
#define WINHELP_CTX_behaviour_altf4 "behaviour.altf4"
|
||||
#define WINHELP_CTX_behaviour_altspace "behaviour.altspace"
|
||||
#define WINHELP_CTX_behaviour_altonly "behaviour.altonly"
|
||||
#define WINHELP_CTX_behaviour_alwaysontop "behaviour.alwaysontop"
|
||||
#define WINHELP_CTX_behaviour_altenter "behaviour.altenter"
|
||||
#define WINHELP_CTX_appearance_cursor "appearance.cursor"
|
||||
#define WINHELP_CTX_appearance_font "appearance.font"
|
||||
#define WINHELP_CTX_appearance_title "appearance.title"
|
||||
#define WINHELP_CTX_appearance_hidemouse "appearance.hidemouse"
|
||||
#define WINHELP_CTX_appearance_border "appearance.border"
|
||||
#define WINHELP_CTX_connection_termtype "connection.termtype"
|
||||
#define WINHELP_CTX_connection_termspeed "connection.termspeed"
|
||||
#define WINHELP_CTX_connection_username "connection.username"
|
||||
#define WINHELP_CTX_connection_keepalive "connection.keepalive"
|
||||
#define WINHELP_CTX_connection_nodelay "connection.nodelay"
|
||||
#define WINHELP_CTX_connection_tcpkeepalive "connection.tcpkeepalive"
|
||||
#define WINHELP_CTX_proxy_type "proxy.type"
|
||||
#define WINHELP_CTX_proxy_main "proxy.main"
|
||||
#define WINHELP_CTX_proxy_exclude "proxy.exclude"
|
||||
#define WINHELP_CTX_proxy_dns "proxy.dns"
|
||||
#define WINHELP_CTX_proxy_auth "proxy.auth"
|
||||
#define WINHELP_CTX_proxy_command "proxy.command"
|
||||
#define WINHELP_CTX_proxy_socksver "proxy.socksver"
|
||||
#define WINHELP_CTX_telnet_environ "telnet.environ"
|
||||
#define WINHELP_CTX_telnet_oldenviron "telnet.oldenviron"
|
||||
#define WINHELP_CTX_telnet_passive "telnet.passive"
|
||||
#define WINHELP_CTX_telnet_specialkeys "telnet.specialkeys"
|
||||
#define WINHELP_CTX_telnet_newline "telnet.newline"
|
||||
#define WINHELP_CTX_rlogin_localuser "rlogin.localuser"
|
||||
#define WINHELP_CTX_ssh_nopty "ssh.nopty"
|
||||
#define WINHELP_CTX_ssh_noshell "ssh.noshell"
|
||||
#define WINHELP_CTX_ssh_ciphers "ssh.ciphers"
|
||||
#define WINHELP_CTX_ssh_protocol "ssh.protocol"
|
||||
#define WINHELP_CTX_ssh_command "ssh.command"
|
||||
#define WINHELP_CTX_ssh_compress "ssh.compress"
|
||||
#define WINHELP_CTX_ssh_auth_privkey "ssh.auth.privkey"
|
||||
#define WINHELP_CTX_ssh_auth_agentfwd "ssh.auth.agentfwd"
|
||||
#define WINHELP_CTX_ssh_auth_changeuser "ssh.auth.changeuser"
|
||||
#define WINHELP_CTX_ssh_auth_tis "ssh.auth.tis"
|
||||
#define WINHELP_CTX_ssh_auth_ki "ssh.auth.ki"
|
||||
#define WINHELP_CTX_selection_buttons "selection.buttons"
|
||||
#define WINHELP_CTX_selection_shiftdrag "selection.shiftdrag"
|
||||
#define WINHELP_CTX_selection_rect "selection.rect"
|
||||
#define WINHELP_CTX_selection_charclasses "selection.charclasses"
|
||||
#define WINHELP_CTX_selection_linedraw "selection.linedraw"
|
||||
#define WINHELP_CTX_selection_rtf "selection.rtf"
|
||||
#define WINHELP_CTX_colours_ansi "colours.ansi"
|
||||
#define WINHELP_CTX_colours_bold "colours.bold"
|
||||
#define WINHELP_CTX_colours_system "colours.system"
|
||||
#define WINHELP_CTX_colours_logpal "colours.logpal"
|
||||
#define WINHELP_CTX_colours_config "colours.config"
|
||||
#define WINHELP_CTX_translation_codepage "translation.codepage"
|
||||
#define WINHELP_CTX_translation_cyrillic "translation.cyrillic"
|
||||
#define WINHELP_CTX_translation_linedraw "translation.linedraw"
|
||||
#define WINHELP_CTX_ssh_tunnels_x11 "ssh.tunnels.x11"
|
||||
#define WINHELP_CTX_ssh_tunnels_x11auth "ssh.tunnels.x11auth"
|
||||
#define WINHELP_CTX_ssh_tunnels_portfwd "ssh.tunnels.portfwd"
|
||||
#define WINHELP_CTX_ssh_tunnels_portfwd_localhost "ssh.tunnels.portfwd.localhost"
|
||||
#define WINHELP_CTX_ssh_bugs_ignore1 "ssh.bugs.ignore1"
|
||||
#define WINHELP_CTX_ssh_bugs_plainpw1 "ssh.bugs.plainpw1"
|
||||
#define WINHELP_CTX_ssh_bugs_rsa1 "ssh.bugs.rsa1"
|
||||
#define WINHELP_CTX_ssh_bugs_hmac2 "ssh.bugs.hmac2"
|
||||
#define WINHELP_CTX_ssh_bugs_derivekey2 "ssh.bugs.derivekey2"
|
||||
#define WINHELP_CTX_ssh_bugs_rsapad2 "ssh.bugs.rsapad2"
|
||||
#define WINHELP_CTX_ssh_bugs_dhgex2 "ssh.bugs.dhgex2"
|
||||
#define WINHELP_CTX_ssh_bugs_pksessid2 "ssh.bugs.pksessid2"
|
366
windows/winmisc.c
Normal file
366
windows/winmisc.c
Normal file
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* winmisc.c: miscellaneous Windows-specific things
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "putty.h"
|
||||
|
||||
OSVERSIONINFO osVersion;
|
||||
|
||||
void platform_get_x11_auth(char *display, int *proto,
|
||||
unsigned char *data, int *datalen)
|
||||
{
|
||||
/* We don't support this at all under Windows. */
|
||||
}
|
||||
|
||||
const char platform_x11_best_transport[] = "localhost";
|
||||
|
||||
char *platform_get_x_display(void) {
|
||||
/* We may as well check for DISPLAY in case it's useful. */
|
||||
return dupstr(getenv("DISPLAY"));
|
||||
}
|
||||
|
||||
Filename filename_from_str(const char *str)
|
||||
{
|
||||
Filename ret;
|
||||
strncpy(ret.path, str, sizeof(ret.path));
|
||||
ret.path[sizeof(ret.path)-1] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *filename_to_str(const Filename *fn)
|
||||
{
|
||||
return fn->path;
|
||||
}
|
||||
|
||||
int filename_equal(Filename f1, Filename f2)
|
||||
{
|
||||
return !strcmp(f1.path, f2.path);
|
||||
}
|
||||
|
||||
int filename_is_null(Filename fn)
|
||||
{
|
||||
return !*fn.path;
|
||||
}
|
||||
|
||||
char *get_username(void)
|
||||
{
|
||||
DWORD namelen;
|
||||
char *user;
|
||||
|
||||
namelen = 0;
|
||||
if (GetUserName(NULL, &namelen) == FALSE)
|
||||
return NULL;
|
||||
|
||||
user = snewn(namelen, char);
|
||||
GetUserName(user, &namelen);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
int SaneDialogBox(HINSTANCE hinst,
|
||||
LPCTSTR tmpl,
|
||||
HWND hwndparent,
|
||||
DLGPROC lpDialogFunc)
|
||||
{
|
||||
WNDCLASS wc;
|
||||
HWND hwnd;
|
||||
MSG msg;
|
||||
int flags;
|
||||
int ret;
|
||||
int gm;
|
||||
|
||||
wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
|
||||
wc.lpfnWndProc = DefDlgProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = DLGWINDOWEXTRA + 8;
|
||||
wc.hInstance = hinst;
|
||||
wc.hIcon = NULL;
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = "PuTTYConfigBox";
|
||||
RegisterClass(&wc);
|
||||
|
||||
hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc);
|
||||
|
||||
SetWindowLong(hwnd, BOXFLAGS, 0); /* flags */
|
||||
SetWindowLong(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */
|
||||
|
||||
while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
|
||||
flags=GetWindowLong(hwnd, BOXFLAGS);
|
||||
if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg))
|
||||
DispatchMessage(&msg);
|
||||
if (flags & DF_END)
|
||||
break;
|
||||
}
|
||||
|
||||
if (gm == 0)
|
||||
PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
|
||||
|
||||
ret=GetWindowLong(hwnd, BOXRESULT);
|
||||
DestroyWindow(hwnd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SaneEndDialog(HWND hwnd, int ret)
|
||||
{
|
||||
SetWindowLong(hwnd, BOXRESULT, ret);
|
||||
SetWindowLong(hwnd, BOXFLAGS, DF_END);
|
||||
}
|
||||
|
||||
BOOL init_winver(void)
|
||||
{
|
||||
ZeroMemory(&osVersion, sizeof(osVersion));
|
||||
osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
||||
return GetVersionEx ( (OSVERSIONINFO *) &osVersion);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static FILE *debug_fp = NULL;
|
||||
static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
|
||||
static int debug_got_console = 0;
|
||||
|
||||
void dputs(char *buf)
|
||||
{
|
||||
DWORD dw;
|
||||
|
||||
if (!debug_got_console) {
|
||||
if (AllocConsole()) {
|
||||
debug_got_console = 1;
|
||||
debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
}
|
||||
if (!debug_fp) {
|
||||
debug_fp = fopen("debug.log", "w");
|
||||
}
|
||||
|
||||
if (debug_hdl != INVALID_HANDLE_VALUE) {
|
||||
WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
|
||||
}
|
||||
fputs(buf, debug_fp);
|
||||
fflush(debug_fp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MINEFIELD
|
||||
/*
|
||||
* Minefield - a Windows equivalent for Electric Fence
|
||||
*/
|
||||
|
||||
#define PAGESIZE 4096
|
||||
|
||||
/*
|
||||
* Design:
|
||||
*
|
||||
* We start by reserving as much virtual address space as Windows
|
||||
* will sensibly (or not sensibly) let us have. We flag it all as
|
||||
* invalid memory.
|
||||
*
|
||||
* Any allocation attempt is satisfied by committing one or more
|
||||
* pages, with an uncommitted page on either side. The returned
|
||||
* memory region is jammed up against the _end_ of the pages.
|
||||
*
|
||||
* Freeing anything causes instantaneous decommitment of the pages
|
||||
* involved, so stale pointers are caught as soon as possible.
|
||||
*/
|
||||
|
||||
static int minefield_initialised = 0;
|
||||
static void *minefield_region = NULL;
|
||||
static long minefield_size = 0;
|
||||
static long minefield_npages = 0;
|
||||
static long minefield_curpos = 0;
|
||||
static unsigned short *minefield_admin = NULL;
|
||||
static void *minefield_pages = NULL;
|
||||
|
||||
static void minefield_admin_hide(int hide)
|
||||
{
|
||||
int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
|
||||
VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
|
||||
}
|
||||
|
||||
static void minefield_init(void)
|
||||
{
|
||||
int size;
|
||||
int admin_size;
|
||||
int i;
|
||||
|
||||
for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
|
||||
minefield_region = VirtualAlloc(NULL, size,
|
||||
MEM_RESERVE, PAGE_NOACCESS);
|
||||
if (minefield_region)
|
||||
break;
|
||||
}
|
||||
minefield_size = size;
|
||||
|
||||
/*
|
||||
* Firstly, allocate a section of that to be the admin block.
|
||||
* We'll need a two-byte field for each page.
|
||||
*/
|
||||
minefield_admin = minefield_region;
|
||||
minefield_npages = minefield_size / PAGESIZE;
|
||||
admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
|
||||
minefield_npages = (minefield_size - admin_size) / PAGESIZE;
|
||||
minefield_pages = (char *) minefield_region + admin_size;
|
||||
|
||||
/*
|
||||
* Commit the admin region.
|
||||
*/
|
||||
VirtualAlloc(minefield_admin, minefield_npages * 2,
|
||||
MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
/*
|
||||
* Mark all pages as unused (0xFFFF).
|
||||
*/
|
||||
for (i = 0; i < minefield_npages; i++)
|
||||
minefield_admin[i] = 0xFFFF;
|
||||
|
||||
/*
|
||||
* Hide the admin region.
|
||||
*/
|
||||
minefield_admin_hide(1);
|
||||
|
||||
minefield_initialised = 1;
|
||||
}
|
||||
|
||||
static void minefield_bomb(void)
|
||||
{
|
||||
div(1, *(int *) minefield_pages);
|
||||
}
|
||||
|
||||
static void *minefield_alloc(int size)
|
||||
{
|
||||
int npages;
|
||||
int pos, lim, region_end, region_start;
|
||||
int start;
|
||||
int i;
|
||||
|
||||
npages = (size + PAGESIZE - 1) / PAGESIZE;
|
||||
|
||||
minefield_admin_hide(0);
|
||||
|
||||
/*
|
||||
* Search from current position until we find a contiguous
|
||||
* bunch of npages+2 unused pages.
|
||||
*/
|
||||
pos = minefield_curpos;
|
||||
lim = minefield_npages;
|
||||
while (1) {
|
||||
/* Skip over used pages. */
|
||||
while (pos < lim && minefield_admin[pos] != 0xFFFF)
|
||||
pos++;
|
||||
/* Count unused pages. */
|
||||
start = pos;
|
||||
while (pos < lim && pos - start < npages + 2 &&
|
||||
minefield_admin[pos] == 0xFFFF)
|
||||
pos++;
|
||||
if (pos - start == npages + 2)
|
||||
break;
|
||||
/* If we've reached the limit, reset the limit or stop. */
|
||||
if (pos >= lim) {
|
||||
if (lim == minefield_npages) {
|
||||
/* go round and start again at zero */
|
||||
lim = minefield_curpos;
|
||||
pos = 0;
|
||||
} else {
|
||||
minefield_admin_hide(1);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
minefield_curpos = pos - 1;
|
||||
|
||||
/*
|
||||
* We have npages+2 unused pages starting at start. We leave
|
||||
* the first and last of these alone and use the rest.
|
||||
*/
|
||||
region_end = (start + npages + 1) * PAGESIZE;
|
||||
region_start = region_end - size;
|
||||
/* FIXME: could align here if we wanted */
|
||||
|
||||
/*
|
||||
* Update the admin region.
|
||||
*/
|
||||
for (i = start + 2; i < start + npages + 1; i++)
|
||||
minefield_admin[i] = 0xFFFE; /* used but no region starts here */
|
||||
minefield_admin[start + 1] = region_start % PAGESIZE;
|
||||
|
||||
minefield_admin_hide(1);
|
||||
|
||||
VirtualAlloc((char *) minefield_pages + region_start, size,
|
||||
MEM_COMMIT, PAGE_READWRITE);
|
||||
return (char *) minefield_pages + region_start;
|
||||
}
|
||||
|
||||
static void minefield_free(void *ptr)
|
||||
{
|
||||
int region_start, i, j;
|
||||
|
||||
minefield_admin_hide(0);
|
||||
|
||||
region_start = (char *) ptr - (char *) minefield_pages;
|
||||
i = region_start / PAGESIZE;
|
||||
if (i < 0 || i >= minefield_npages ||
|
||||
minefield_admin[i] != region_start % PAGESIZE)
|
||||
minefield_bomb();
|
||||
for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
|
||||
minefield_admin[j] = 0xFFFF;
|
||||
}
|
||||
|
||||
VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
|
||||
|
||||
minefield_admin_hide(1);
|
||||
}
|
||||
|
||||
static int minefield_get_size(void *ptr)
|
||||
{
|
||||
int region_start, i, j;
|
||||
|
||||
minefield_admin_hide(0);
|
||||
|
||||
region_start = (char *) ptr - (char *) minefield_pages;
|
||||
i = region_start / PAGESIZE;
|
||||
if (i < 0 || i >= minefield_npages ||
|
||||
minefield_admin[i] != region_start % PAGESIZE)
|
||||
minefield_bomb();
|
||||
for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
|
||||
|
||||
minefield_admin_hide(1);
|
||||
|
||||
return j * PAGESIZE - region_start;
|
||||
}
|
||||
|
||||
void *minefield_c_malloc(size_t size)
|
||||
{
|
||||
if (!minefield_initialised)
|
||||
minefield_init();
|
||||
return minefield_alloc(size);
|
||||
}
|
||||
|
||||
void minefield_c_free(void *p)
|
||||
{
|
||||
if (!minefield_initialised)
|
||||
minefield_init();
|
||||
minefield_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* realloc _always_ moves the chunk, for rapid detection of code
|
||||
* that assumes it won't.
|
||||
*/
|
||||
void *minefield_c_realloc(void *p, size_t size)
|
||||
{
|
||||
size_t oldsize;
|
||||
void *q;
|
||||
if (!minefield_initialised)
|
||||
minefield_init();
|
||||
q = minefield_alloc(size);
|
||||
oldsize = minefield_get_size(p);
|
||||
memcpy(q, p, (oldsize < size ? oldsize : size));
|
||||
minefield_free(p);
|
||||
return q;
|
||||
}
|
||||
|
||||
#endif /* MINEFIELD */
|
1372
windows/winnet.c
Normal file
1372
windows/winnet.c
Normal file
File diff suppressed because it is too large
Load Diff
124
windows/winnoise.c
Normal file
124
windows/winnoise.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Noise generation for PuTTY's cryptographic random number
|
||||
* generator.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "storage.h"
|
||||
|
||||
/*
|
||||
* This function is called once, at PuTTY startup, and will do some
|
||||
* seriously silly things like listing directories and getting disk
|
||||
* free space and a process snapshot.
|
||||
*/
|
||||
|
||||
void noise_get_heavy(void (*func) (void *, int))
|
||||
{
|
||||
HANDLE srch;
|
||||
WIN32_FIND_DATA finddata;
|
||||
char winpath[MAX_PATH + 3];
|
||||
|
||||
GetWindowsDirectory(winpath, sizeof(winpath));
|
||||
strcat(winpath, "\\*");
|
||||
srch = FindFirstFile(winpath, &finddata);
|
||||
if (srch != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
func(&finddata, sizeof(finddata));
|
||||
} while (FindNextFile(srch, &finddata));
|
||||
FindClose(srch);
|
||||
}
|
||||
|
||||
read_random_seed(func);
|
||||
/* Update the seed immediately, in case another instance uses it. */
|
||||
random_save_seed();
|
||||
}
|
||||
|
||||
void random_save_seed(void)
|
||||
{
|
||||
int len;
|
||||
void *data;
|
||||
|
||||
if (random_active) {
|
||||
random_get_savedata(&data, &len);
|
||||
write_random_seed(data, len);
|
||||
sfree(data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called every time the random pool needs
|
||||
* stirring, and will acquire the system time in all available
|
||||
* forms and the battery status.
|
||||
*/
|
||||
void noise_get_light(void (*func) (void *, int))
|
||||
{
|
||||
SYSTEMTIME systime;
|
||||
DWORD adjust[2];
|
||||
BOOL rubbish;
|
||||
|
||||
GetSystemTime(&systime);
|
||||
func(&systime, sizeof(systime));
|
||||
|
||||
GetSystemTimeAdjustment(&adjust[0], &adjust[1], &rubbish);
|
||||
func(&adjust, sizeof(adjust));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called on a timer, and it will monitor
|
||||
* frequently changing quantities such as the state of physical and
|
||||
* virtual memory, the state of the process's message queue, which
|
||||
* window is in the foreground, which owns the clipboard, etc.
|
||||
*/
|
||||
void noise_regular(void)
|
||||
{
|
||||
HWND w;
|
||||
DWORD z;
|
||||
POINT pt;
|
||||
MEMORYSTATUS memstat;
|
||||
FILETIME times[4];
|
||||
|
||||
w = GetForegroundWindow();
|
||||
random_add_noise(&w, sizeof(w));
|
||||
w = GetCapture();
|
||||
random_add_noise(&w, sizeof(w));
|
||||
w = GetClipboardOwner();
|
||||
random_add_noise(&w, sizeof(w));
|
||||
z = GetQueueStatus(QS_ALLEVENTS);
|
||||
random_add_noise(&z, sizeof(z));
|
||||
|
||||
GetCursorPos(&pt);
|
||||
random_add_noise(&pt, sizeof(pt));
|
||||
|
||||
GlobalMemoryStatus(&memstat);
|
||||
random_add_noise(&memstat, sizeof(memstat));
|
||||
|
||||
GetThreadTimes(GetCurrentThread(), times, times + 1, times + 2,
|
||||
times + 3);
|
||||
random_add_noise(×, sizeof(times));
|
||||
GetProcessTimes(GetCurrentProcess(), times, times + 1, times + 2,
|
||||
times + 3);
|
||||
random_add_noise(×, sizeof(times));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called on every keypress or mouse move, and
|
||||
* will add the current Windows time and performance monitor
|
||||
* counter to the noise pool. It gets the scan code or mouse
|
||||
* position passed in.
|
||||
*/
|
||||
void noise_ultralight(unsigned long data)
|
||||
{
|
||||
DWORD wintime;
|
||||
LARGE_INTEGER perftime;
|
||||
|
||||
random_add_noise(&data, sizeof(DWORD));
|
||||
|
||||
wintime = GetTickCount();
|
||||
random_add_noise(&wintime, sizeof(DWORD));
|
||||
|
||||
if (QueryPerformanceCounter(&perftime))
|
||||
random_add_noise(&perftime, sizeof(perftime));
|
||||
}
|
1451
windows/winpgen.c
Normal file
1451
windows/winpgen.c
Normal file
File diff suppressed because it is too large
Load Diff
2217
windows/winpgnt.c
Normal file
2217
windows/winpgnt.c
Normal file
File diff suppressed because it is too large
Load Diff
144
windows/winpgntc.c
Normal file
144
windows/winpgntc.c
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Pageant client code.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
|
||||
#define AGENT_COPYDATA_ID 0x804e50ba /* random goop */
|
||||
#define AGENT_MAX_MSGLEN 8192
|
||||
|
||||
#define GET_32BIT(cp) \
|
||||
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
|
||||
((unsigned long)(unsigned char)(cp)[1] << 16) | \
|
||||
((unsigned long)(unsigned char)(cp)[2] << 8) | \
|
||||
((unsigned long)(unsigned char)(cp)[3]))
|
||||
|
||||
int agent_exists(void)
|
||||
{
|
||||
HWND hwnd;
|
||||
hwnd = FindWindow("Pageant", "Pageant");
|
||||
if (!hwnd)
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unfortunately, this asynchronous agent request mechanism doesn't
|
||||
* appear to work terribly well. I'm going to comment it out for
|
||||
* the moment, and see if I can come up with a better one :-/
|
||||
*/
|
||||
#ifdef WINDOWS_ASYNC_AGENT
|
||||
|
||||
struct agent_query_data {
|
||||
COPYDATASTRUCT cds;
|
||||
unsigned char *mapping;
|
||||
HANDLE handle;
|
||||
char *mapname;
|
||||
HWND hwnd;
|
||||
void (*callback)(void *, void *, int);
|
||||
void *callback_ctx;
|
||||
};
|
||||
|
||||
DWORD WINAPI agent_query_thread(LPVOID param)
|
||||
{
|
||||
struct agent_query_data *data = (struct agent_query_data *)param;
|
||||
unsigned char *ret;
|
||||
int id, retlen;
|
||||
|
||||
id = SendMessage(data->hwnd, WM_COPYDATA, (WPARAM) NULL,
|
||||
(LPARAM) &data->cds);
|
||||
ret = NULL;
|
||||
if (id > 0) {
|
||||
retlen = 4 + GET_32BIT(data->mapping);
|
||||
ret = snewn(retlen, unsigned char);
|
||||
if (ret) {
|
||||
memcpy(ret, data->mapping, retlen);
|
||||
}
|
||||
}
|
||||
if (!ret)
|
||||
retlen = 0;
|
||||
UnmapViewOfFile(data->mapping);
|
||||
CloseHandle(data->handle);
|
||||
sfree(data->mapname);
|
||||
|
||||
agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int agent_query(void *in, int inlen, void **out, int *outlen,
|
||||
void (*callback)(void *, void *, int), void *callback_ctx)
|
||||
{
|
||||
HWND hwnd;
|
||||
char *mapname;
|
||||
HANDLE filemap;
|
||||
unsigned char *p, *ret;
|
||||
int id, retlen;
|
||||
COPYDATASTRUCT cds;
|
||||
|
||||
*out = NULL;
|
||||
*outlen = 0;
|
||||
|
||||
hwnd = FindWindow("Pageant", "Pageant");
|
||||
if (!hwnd)
|
||||
return 1; /* *out == NULL, so failure */
|
||||
mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
|
||||
filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
|
||||
0, AGENT_MAX_MSGLEN, mapname);
|
||||
if (!filemap)
|
||||
return 1; /* *out == NULL, so failure */
|
||||
p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0);
|
||||
memcpy(p, in, inlen);
|
||||
cds.dwData = AGENT_COPYDATA_ID;
|
||||
cds.cbData = 1 + strlen(mapname);
|
||||
cds.lpData = mapname;
|
||||
#ifdef WINDOWS_ASYNC_AGENT
|
||||
if (callback != NULL && !(flags & FLAG_SYNCAGENT)) {
|
||||
/*
|
||||
* We need an asynchronous Pageant request. Since I know of
|
||||
* no way to stop SendMessage from blocking the thread it's
|
||||
* called in, I see no option but to start a fresh thread.
|
||||
* When we're done we'll PostMessage the result back to our
|
||||
* main window, so that the callback is done in the primary
|
||||
* thread to avoid concurrency.
|
||||
*/
|
||||
struct agent_query_data *data = snew(struct agent_query_data);
|
||||
DWORD threadid;
|
||||
data->mapping = p;
|
||||
data->handle = filemap;
|
||||
data->mapname = mapname;
|
||||
data->callback = callback;
|
||||
data->callback_ctx = callback_ctx;
|
||||
data->cds = cds; /* structure copy */
|
||||
data->hwnd = hwnd;
|
||||
if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid))
|
||||
return 0;
|
||||
sfree(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The user either passed a null callback (indicating that the
|
||||
* query is required to be synchronous) or CreateThread failed.
|
||||
* Either way, we need a synchronous request.
|
||||
*/
|
||||
id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) & cds);
|
||||
if (id > 0) {
|
||||
retlen = 4 + GET_32BIT(p);
|
||||
ret = snewn(retlen, unsigned char);
|
||||
if (ret) {
|
||||
memcpy(ret, p, retlen);
|
||||
*out = ret;
|
||||
*outlen = retlen;
|
||||
}
|
||||
}
|
||||
UnmapViewOfFile(p);
|
||||
CloseHandle(filemap);
|
||||
return 1;
|
||||
}
|
789
windows/winplink.c
Normal file
789
windows/winplink.c
Normal file
@ -0,0 +1,789 @@
|
||||
/*
|
||||
* PLink - a Windows command-line (stdin/stdout) variant of PuTTY.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
|
||||
#include "putty.h"
|
||||
#include "storage.h"
|
||||
#include "tree234.h"
|
||||
|
||||
#define WM_AGENT_CALLBACK (WM_XUSER + 4)
|
||||
|
||||
#define MAX_STDIN_BACKLOG 4096
|
||||
|
||||
struct agent_callback {
|
||||
void (*callback)(void *, void *, int);
|
||||
void *callback_ctx;
|
||||
void *data;
|
||||
int len;
|
||||
};
|
||||
|
||||
void fatalbox(char *p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
fprintf(stderr, "FATAL ERROR: ");
|
||||
va_start(ap, p);
|
||||
vfprintf(stderr, p, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
void modalfatalbox(char *p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
fprintf(stderr, "FATAL ERROR: ");
|
||||
va_start(ap, p);
|
||||
vfprintf(stderr, p, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
void connection_fatal(void *frontend, char *p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
fprintf(stderr, "FATAL ERROR: ");
|
||||
va_start(ap, p);
|
||||
vfprintf(stderr, p, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
cleanup_exit(1);
|
||||
}
|
||||
void cmdline_error(char *p, ...)
|
||||
{
|
||||
va_list ap;
|
||||
fprintf(stderr, "plink: ");
|
||||
va_start(ap, p);
|
||||
vfprintf(stderr, p, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
HANDLE inhandle, outhandle, errhandle;
|
||||
DWORD orig_console_mode;
|
||||
|
||||
WSAEVENT netevent;
|
||||
|
||||
static Backend *back;
|
||||
static void *backhandle;
|
||||
static Config cfg;
|
||||
|
||||
int term_ldisc(Terminal *term, int mode)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
void ldisc_update(void *frontend, int echo, int edit)
|
||||
{
|
||||
/* Update stdin read mode to reflect changes in line discipline. */
|
||||
DWORD mode;
|
||||
|
||||
mode = ENABLE_PROCESSED_INPUT;
|
||||
if (echo)
|
||||
mode = mode | ENABLE_ECHO_INPUT;
|
||||
else
|
||||
mode = mode & ~ENABLE_ECHO_INPUT;
|
||||
if (edit)
|
||||
mode = mode | ENABLE_LINE_INPUT;
|
||||
else
|
||||
mode = mode & ~ENABLE_LINE_INPUT;
|
||||
SetConsoleMode(inhandle, mode);
|
||||
}
|
||||
|
||||
struct input_data {
|
||||
DWORD len;
|
||||
char buffer[4096];
|
||||
HANDLE event, eventback;
|
||||
};
|
||||
|
||||
static DWORD WINAPI stdin_read_thread(void *param)
|
||||
{
|
||||
struct input_data *idata = (struct input_data *) param;
|
||||
HANDLE inhandle;
|
||||
|
||||
inhandle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
while (ReadFile(inhandle, idata->buffer, sizeof(idata->buffer),
|
||||
&idata->len, NULL) && idata->len > 0) {
|
||||
SetEvent(idata->event);
|
||||
WaitForSingleObject(idata->eventback, INFINITE);
|
||||
}
|
||||
|
||||
idata->len = 0;
|
||||
SetEvent(idata->event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct output_data {
|
||||
DWORD len, lenwritten;
|
||||
int writeret;
|
||||
char *buffer;
|
||||
int is_stderr, done;
|
||||
HANDLE event, eventback;
|
||||
int busy;
|
||||
};
|
||||
|
||||
static DWORD WINAPI stdout_write_thread(void *param)
|
||||
{
|
||||
struct output_data *odata = (struct output_data *) param;
|
||||
HANDLE outhandle, errhandle;
|
||||
|
||||
outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
errhandle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
while (1) {
|
||||
WaitForSingleObject(odata->eventback, INFINITE);
|
||||
if (odata->done)
|
||||
break;
|
||||
odata->writeret =
|
||||
WriteFile(odata->is_stderr ? errhandle : outhandle,
|
||||
odata->buffer, odata->len, &odata->lenwritten, NULL);
|
||||
SetEvent(odata->event);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bufchain stdout_data, stderr_data;
|
||||
struct output_data odata, edata;
|
||||
|
||||
void try_output(int is_stderr)
|
||||
{
|
||||
struct output_data *data = (is_stderr ? &edata : &odata);
|
||||
void *senddata;
|
||||
int sendlen;
|
||||
|
||||
if (!data->busy) {
|
||||
bufchain_prefix(is_stderr ? &stderr_data : &stdout_data,
|
||||
&senddata, &sendlen);
|
||||
data->buffer = senddata;
|
||||
data->len = sendlen;
|
||||
SetEvent(data->eventback);
|
||||
data->busy = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int from_backend(void *frontend_handle, int is_stderr,
|
||||
const char *data, int len)
|
||||
{
|
||||
int osize, esize;
|
||||
|
||||
if (is_stderr) {
|
||||
bufchain_add(&stderr_data, data, len);
|
||||
try_output(1);
|
||||
} else {
|
||||
bufchain_add(&stdout_data, data, len);
|
||||
try_output(0);
|
||||
}
|
||||
|
||||
osize = bufchain_size(&stdout_data);
|
||||
esize = bufchain_size(&stderr_data);
|
||||
|
||||
return osize + esize;
|
||||
}
|
||||
|
||||
static DWORD main_thread_id;
|
||||
|
||||
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;
|
||||
PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c);
|
||||
}
|
||||
|
||||
/*
|
||||
* Short description of parameters.
|
||||
*/
|
||||
static void usage(void)
|
||||
{
|
||||
printf("PuTTY Link: command-line connection utility\n");
|
||||
printf("%s\n", ver);
|
||||
printf("Usage: plink [options] [user@]host [command]\n");
|
||||
printf(" (\"host\" can also be a PuTTY saved session name)\n");
|
||||
printf("Options:\n");
|
||||
printf(" -V print version information\n");
|
||||
printf(" -v show verbose messages\n");
|
||||
printf(" -load sessname Load settings from saved session\n");
|
||||
printf(" -ssh -telnet -rlogin -raw\n");
|
||||
printf(" force use of a particular protocol\n");
|
||||
printf(" -P port connect to specified port\n");
|
||||
printf(" -l user connect with specified username\n");
|
||||
printf(" -m file read remote command(s) from file\n");
|
||||
printf(" -batch disable all interactive prompts\n");
|
||||
printf("The following options only apply to SSH connections:\n");
|
||||
printf(" -pw passw login with specified password\n");
|
||||
printf(" -D [listen-IP:]listen-port\n");
|
||||
printf(" Dynamic SOCKS-based port forwarding\n");
|
||||
printf(" -L [listen-IP:]listen-port:host:port\n");
|
||||
printf(" Forward local port to remote address\n");
|
||||
printf(" -R [listen-IP:]listen-port:host:port\n");
|
||||
printf(" Forward remote port to local address\n");
|
||||
printf(" -X -x enable / disable X11 forwarding\n");
|
||||
printf(" -A -a enable / disable agent forwarding\n");
|
||||
printf(" -t -T enable / disable pty allocation\n");
|
||||
printf(" -1 -2 force use of particular protocol version\n");
|
||||
printf(" -C enable compression\n");
|
||||
printf(" -i key private key file for authentication\n");
|
||||
printf(" -s remote command is an SSH subsystem (SSH-2 only)\n");
|
||||
printf(" -N don't start a shell/command (SSH-2 only)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void version(void)
|
||||
{
|
||||
printf("plink: %s\n", ver);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *do_select(SOCKET skt, int startup)
|
||||
{
|
||||
int events;
|
||||
if (startup) {
|
||||
events = (FD_CONNECT | FD_READ | FD_WRITE |
|
||||
FD_OOB | FD_CLOSE | FD_ACCEPT);
|
||||
} else {
|
||||
events = 0;
|
||||
}
|
||||
if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
|
||||
switch (p_WSAGetLastError()) {
|
||||
case WSAENETDOWN:
|
||||
return "Network is down";
|
||||
default:
|
||||
return "WSAEventSelect(): unknown error";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
WSAEVENT stdinevent, stdoutevent, stderrevent;
|
||||
HANDLE handles[4];
|
||||
DWORD in_threadid, out_threadid, err_threadid;
|
||||
struct input_data idata;
|
||||
int reading;
|
||||
int sending;
|
||||
int portnumber = -1;
|
||||
SOCKET *sklist;
|
||||
int skcount, sksize;
|
||||
int connopen;
|
||||
int exitcode;
|
||||
int errors;
|
||||
int use_subsystem = 0;
|
||||
|
||||
ssh_get_line = console_get_line;
|
||||
|
||||
sklist = NULL;
|
||||
skcount = sksize = 0;
|
||||
/*
|
||||
* Initialise port and protocol to sensible defaults. (These
|
||||
* will be overridden by more or less anything.)
|
||||
*/
|
||||
default_protocol = PROT_SSH;
|
||||
default_port = 22;
|
||||
|
||||
flags = FLAG_STDERR;
|
||||
/*
|
||||
* Process the command line.
|
||||
*/
|
||||
do_defaults(NULL, &cfg);
|
||||
loaded_session = FALSE;
|
||||
default_protocol = cfg.protocol;
|
||||
default_port = cfg.port;
|
||||
errors = 0;
|
||||
{
|
||||
/*
|
||||
* Override the default protocol if PLINK_PROTOCOL is set.
|
||||
*/
|
||||
char *p = getenv("PLINK_PROTOCOL");
|
||||
int i;
|
||||
if (p) {
|
||||
for (i = 0; backends[i].backend != NULL; i++) {
|
||||
if (!strcmp(backends[i].name, p)) {
|
||||
default_protocol = cfg.protocol = backends[i].protocol;
|
||||
default_port = cfg.port =
|
||||
backends[i].backend->default_port;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (--argc) {
|
||||
char *p = *++argv;
|
||||
if (*p == '-') {
|
||||
int ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
|
||||
1, &cfg);
|
||||
if (ret == -2) {
|
||||
fprintf(stderr,
|
||||
"plink: option \"%s\" requires an argument\n", p);
|
||||
errors = 1;
|
||||
} else if (ret == 2) {
|
||||
--argc, ++argv;
|
||||
} else if (ret == 1) {
|
||||
continue;
|
||||
} else if (!strcmp(p, "-batch")) {
|
||||
console_batch_mode = 1;
|
||||
} else if (!strcmp(p, "-s")) {
|
||||
/* Save status to write to cfg later. */
|
||||
use_subsystem = 1;
|
||||
} else if (!strcmp(p, "-V")) {
|
||||
version();
|
||||
} else {
|
||||
fprintf(stderr, "plink: unknown option \"%s\"\n", p);
|
||||
errors = 1;
|
||||
}
|
||||
} else if (*p) {
|
||||
if (!*cfg.host) {
|
||||
char *q = p;
|
||||
/*
|
||||
* If the hostname starts with "telnet:", set the
|
||||
* protocol to Telnet and process the string as a
|
||||
* Telnet URL.
|
||||
*/
|
||||
if (!strncmp(q, "telnet:", 7)) {
|
||||
char c;
|
||||
|
||||
q += 7;
|
||||
if (q[0] == '/' && q[1] == '/')
|
||||
q += 2;
|
||||
cfg.protocol = PROT_TELNET;
|
||||
p = q;
|
||||
while (*p && *p != ':' && *p != '/')
|
||||
p++;
|
||||
c = *p;
|
||||
if (*p)
|
||||
*p++ = '\0';
|
||||
if (c == ':')
|
||||
cfg.port = atoi(p);
|
||||
else
|
||||
cfg.port = -1;
|
||||
strncpy(cfg.host, q, sizeof(cfg.host) - 1);
|
||||
cfg.host[sizeof(cfg.host) - 1] = '\0';
|
||||
} else {
|
||||
char *r, *user, *host;
|
||||
/*
|
||||
* Before we process the [user@]host string, we
|
||||
* first check for the presence of a protocol
|
||||
* prefix (a protocol name followed by ",").
|
||||
*/
|
||||
r = strchr(p, ',');
|
||||
if (r) {
|
||||
int i, j;
|
||||
for (i = 0; backends[i].backend != NULL; i++) {
|
||||
j = strlen(backends[i].name);
|
||||
if (j == r - p &&
|
||||
!memcmp(backends[i].name, p, j)) {
|
||||
default_protocol = cfg.protocol =
|
||||
backends[i].protocol;
|
||||
portnumber =
|
||||
backends[i].backend->default_port;
|
||||
p = r + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A nonzero length string followed by an @ is treated
|
||||
* as a username. (We discount an _initial_ @.) The
|
||||
* rest of the string (or the whole string if no @)
|
||||
* is treated as a session name and/or hostname.
|
||||
*/
|
||||
r = strrchr(p, '@');
|
||||
if (r == p)
|
||||
p++, r = NULL; /* discount initial @ */
|
||||
if (r) {
|
||||
*r++ = '\0';
|
||||
user = p, host = r;
|
||||
} else {
|
||||
user = NULL, host = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now attempt to load a saved session with the
|
||||
* same name as the hostname.
|
||||
*/
|
||||
{
|
||||
Config cfg2;
|
||||
do_defaults(host, &cfg2);
|
||||
if (loaded_session || cfg2.host[0] == '\0') {
|
||||
/* No settings for this host; use defaults */
|
||||
/* (or session was already loaded with -load) */
|
||||
strncpy(cfg.host, host, sizeof(cfg.host) - 1);
|
||||
cfg.host[sizeof(cfg.host) - 1] = '\0';
|
||||
cfg.port = default_port;
|
||||
} else {
|
||||
cfg = cfg2;
|
||||
/* Ick: patch up internal pointer after copy */
|
||||
cfg.remote_cmd_ptr = cfg.remote_cmd;
|
||||
}
|
||||
}
|
||||
|
||||
if (user) {
|
||||
/* Patch in specified username. */
|
||||
strncpy(cfg.username, user,
|
||||
sizeof(cfg.username) - 1);
|
||||
cfg.username[sizeof(cfg.username) - 1] = '\0';
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
char *command;
|
||||
int cmdlen, cmdsize;
|
||||
cmdlen = cmdsize = 0;
|
||||
command = NULL;
|
||||
|
||||
while (argc) {
|
||||
while (*p) {
|
||||
if (cmdlen >= cmdsize) {
|
||||
cmdsize = cmdlen + 512;
|
||||
command = sresize(command, cmdsize, char);
|
||||
}
|
||||
command[cmdlen++]=*p++;
|
||||
}
|
||||
if (cmdlen >= cmdsize) {
|
||||
cmdsize = cmdlen + 512;
|
||||
command = sresize(command, cmdsize, char);
|
||||
}
|
||||
command[cmdlen++]=' '; /* always add trailing space */
|
||||
if (--argc) p = *++argv;
|
||||
}
|
||||
if (cmdlen) command[--cmdlen]='\0';
|
||||
/* change trailing blank to NUL */
|
||||
cfg.remote_cmd_ptr = command;
|
||||
cfg.remote_cmd_ptr2 = NULL;
|
||||
cfg.nopty = TRUE; /* command => no terminal */
|
||||
|
||||
break; /* done with cmdline */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors)
|
||||
return 1;
|
||||
|
||||
if (!*cfg.host) {
|
||||
usage();
|
||||
}
|
||||
|
||||
/*
|
||||
* Trim leading whitespace off the hostname if it's there.
|
||||
*/
|
||||
{
|
||||
int space = strspn(cfg.host, " \t");
|
||||
memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
|
||||
}
|
||||
|
||||
/* See if host is of the form user@host */
|
||||
if (cfg.host[0] != '\0') {
|
||||
char *atsign = strrchr(cfg.host, '@');
|
||||
/* Make sure we're not overflowing the user field */
|
||||
if (atsign) {
|
||||
if (atsign - cfg.host < sizeof cfg.username) {
|
||||
strncpy(cfg.username, cfg.host, atsign - cfg.host);
|
||||
cfg.username[atsign - cfg.host] = '\0';
|
||||
}
|
||||
memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform command-line overrides on session configuration.
|
||||
*/
|
||||
cmdline_run_saved(&cfg);
|
||||
|
||||
/*
|
||||
* Apply subsystem status.
|
||||
*/
|
||||
if (use_subsystem)
|
||||
cfg.ssh_subsys = TRUE;
|
||||
|
||||
/*
|
||||
* Trim a colon suffix off the hostname if it's there.
|
||||
*/
|
||||
cfg.host[strcspn(cfg.host, ":")] = '\0';
|
||||
|
||||
/*
|
||||
* Remove any remaining whitespace from the hostname.
|
||||
*/
|
||||
{
|
||||
int p1 = 0, p2 = 0;
|
||||
while (cfg.host[p2] != '\0') {
|
||||
if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
|
||||
cfg.host[p1] = cfg.host[p2];
|
||||
p1++;
|
||||
}
|
||||
p2++;
|
||||
}
|
||||
cfg.host[p1] = '\0';
|
||||
}
|
||||
|
||||
if (!*cfg.remote_cmd_ptr)
|
||||
flags |= FLAG_INTERACTIVE;
|
||||
|
||||
/*
|
||||
* Select protocol. This is farmed out into a table in a
|
||||
* separate file to enable an ssh-free variant.
|
||||
*/
|
||||
{
|
||||
int i;
|
||||
back = NULL;
|
||||
for (i = 0; backends[i].backend != NULL; i++)
|
||||
if (backends[i].protocol == cfg.protocol) {
|
||||
back = backends[i].backend;
|
||||
break;
|
||||
}
|
||||
if (back == NULL) {
|
||||
fprintf(stderr,
|
||||
"Internal fault: Unsupported protocol found\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Select port.
|
||||
*/
|
||||
if (portnumber != -1)
|
||||
cfg.port = portnumber;
|
||||
|
||||
sk_init();
|
||||
if (p_WSAEventSelect == NULL) {
|
||||
fprintf(stderr, "Plink requires WinSock 2\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start up the connection.
|
||||
*/
|
||||
netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
{
|
||||
const char *error;
|
||||
char *realhost;
|
||||
/* nodelay is only useful if stdin is a character device (console) */
|
||||
int nodelay = cfg.tcp_nodelay &&
|
||||
(GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
|
||||
|
||||
error = back->init(NULL, &backhandle, &cfg, cfg.host, cfg.port,
|
||||
&realhost, nodelay, cfg.tcp_keepalives);
|
||||
if (error) {
|
||||
fprintf(stderr, "Unable to open connection:\n%s", error);
|
||||
return 1;
|
||||
}
|
||||
logctx = log_init(NULL, &cfg);
|
||||
back->provide_logctx(backhandle, logctx);
|
||||
console_provide_logctx(logctx);
|
||||
sfree(realhost);
|
||||
}
|
||||
connopen = 1;
|
||||
|
||||
stdinevent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
stdoutevent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
stderrevent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
inhandle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
outhandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
errhandle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
GetConsoleMode(inhandle, &orig_console_mode);
|
||||
SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT);
|
||||
|
||||
main_thread_id = GetCurrentThreadId();
|
||||
|
||||
/*
|
||||
* Turn off ECHO and LINE input modes. We don't care if this
|
||||
* call fails, because we know we aren't necessarily running in
|
||||
* a console.
|
||||
*/
|
||||
handles[0] = netevent;
|
||||
handles[1] = stdinevent;
|
||||
handles[2] = stdoutevent;
|
||||
handles[3] = stderrevent;
|
||||
sending = FALSE;
|
||||
|
||||
/*
|
||||
* Create spare threads to write to stdout and stderr, so we
|
||||
* can arrange asynchronous writes.
|
||||
*/
|
||||
odata.event = stdoutevent;
|
||||
odata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
odata.is_stderr = 0;
|
||||
odata.busy = odata.done = 0;
|
||||
if (!CreateThread(NULL, 0, stdout_write_thread,
|
||||
&odata, 0, &out_threadid)) {
|
||||
fprintf(stderr, "Unable to create output thread\n");
|
||||
cleanup_exit(1);
|
||||
}
|
||||
edata.event = stderrevent;
|
||||
edata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
edata.is_stderr = 1;
|
||||
edata.busy = edata.done = 0;
|
||||
if (!CreateThread(NULL, 0, stdout_write_thread,
|
||||
&edata, 0, &err_threadid)) {
|
||||
fprintf(stderr, "Unable to create error output thread\n");
|
||||
cleanup_exit(1);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int n;
|
||||
|
||||
if (!sending && back->sendok(backhandle)) {
|
||||
/*
|
||||
* Create a separate thread to read from stdin. This is
|
||||
* a total pain, but I can't find another way to do it:
|
||||
*
|
||||
* - an overlapped ReadFile or ReadFileEx just doesn't
|
||||
* happen; we get failure from ReadFileEx, and
|
||||
* ReadFile blocks despite being given an OVERLAPPED
|
||||
* structure. Perhaps we can't do overlapped reads
|
||||
* on consoles. WHY THE HELL NOT?
|
||||
*
|
||||
* - WaitForMultipleObjects(netevent, console) doesn't
|
||||
* work, because it signals the console when
|
||||
* _anything_ happens, including mouse motions and
|
||||
* other things that don't cause data to be readable
|
||||
* - so we're back to ReadFile blocking.
|
||||
*/
|
||||
idata.event = stdinevent;
|
||||
idata.eventback = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (!CreateThread(NULL, 0, stdin_read_thread,
|
||||
&idata, 0, &in_threadid)) {
|
||||
fprintf(stderr, "Unable to create input thread\n");
|
||||
cleanup_exit(1);
|
||||
}
|
||||
sending = TRUE;
|
||||
}
|
||||
|
||||
n = MsgWaitForMultipleObjects(4, handles, FALSE, INFINITE,
|
||||
QS_POSTMESSAGE);
|
||||
if (n == 0) {
|
||||
WSANETWORKEVENTS things;
|
||||
SOCKET socket;
|
||||
extern SOCKET first_socket(int *), next_socket(int *);
|
||||
extern int select_result(WPARAM, LPARAM);
|
||||
int i, socketstate;
|
||||
|
||||
/*
|
||||
* We must not call select_result() for any socket
|
||||
* until we have finished enumerating within the tree.
|
||||
* This is because select_result() may close the socket
|
||||
* and modify the tree.
|
||||
*/
|
||||
/* Count the active sockets. */
|
||||
i = 0;
|
||||
for (socket = first_socket(&socketstate);
|
||||
socket != INVALID_SOCKET;
|
||||
socket = next_socket(&socketstate)) i++;
|
||||
|
||||
/* Expand the buffer if necessary. */
|
||||
if (i > sksize) {
|
||||
sksize = i + 16;
|
||||
sklist = sresize(sklist, sksize, SOCKET);
|
||||
}
|
||||
|
||||
/* Retrieve the sockets into sklist. */
|
||||
skcount = 0;
|
||||
for (socket = first_socket(&socketstate);
|
||||
socket != INVALID_SOCKET;
|
||||
socket = next_socket(&socketstate)) {
|
||||
sklist[skcount++] = socket;
|
||||
}
|
||||
|
||||
/* Now we're done enumerating; go through the list. */
|
||||
for (i = 0; i < skcount; i++) {
|
||||
WPARAM wp;
|
||||
socket = sklist[i];
|
||||
wp = (WPARAM) socket;
|
||||
if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {
|
||||
static const struct { int bit, mask; } eventtypes[] = {
|
||||
{FD_CONNECT_BIT, FD_CONNECT},
|
||||
{FD_READ_BIT, FD_READ},
|
||||
{FD_CLOSE_BIT, FD_CLOSE},
|
||||
{FD_OOB_BIT, FD_OOB},
|
||||
{FD_WRITE_BIT, FD_WRITE},
|
||||
{FD_ACCEPT_BIT, FD_ACCEPT},
|
||||
};
|
||||
int e;
|
||||
|
||||
noise_ultralight(socket);
|
||||
noise_ultralight(things.lNetworkEvents);
|
||||
|
||||
for (e = 0; e < lenof(eventtypes); e++)
|
||||
if (things.lNetworkEvents & eventtypes[e].mask) {
|
||||
LPARAM lp;
|
||||
int err = things.iErrorCode[eventtypes[e].bit];
|
||||
lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
|
||||
connopen &= select_result(wp, lp);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (n == 1) {
|
||||
reading = 0;
|
||||
noise_ultralight(idata.len);
|
||||
if (connopen && back->socket(backhandle) != NULL) {
|
||||
if (idata.len > 0) {
|
||||
back->send(backhandle, idata.buffer, idata.len);
|
||||
} else {
|
||||
back->special(backhandle, TS_EOF);
|
||||
}
|
||||
}
|
||||
} else if (n == 2) {
|
||||
odata.busy = 0;
|
||||
if (!odata.writeret) {
|
||||
fprintf(stderr, "Unable to write to standard output\n");
|
||||
cleanup_exit(0);
|
||||
}
|
||||
bufchain_consume(&stdout_data, odata.lenwritten);
|
||||
if (bufchain_size(&stdout_data) > 0)
|
||||
try_output(0);
|
||||
if (connopen && back->socket(backhandle) != NULL) {
|
||||
back->unthrottle(backhandle, bufchain_size(&stdout_data) +
|
||||
bufchain_size(&stderr_data));
|
||||
}
|
||||
} else if (n == 3) {
|
||||
edata.busy = 0;
|
||||
if (!edata.writeret) {
|
||||
fprintf(stderr, "Unable to write to standard output\n");
|
||||
cleanup_exit(0);
|
||||
}
|
||||
bufchain_consume(&stderr_data, edata.lenwritten);
|
||||
if (bufchain_size(&stderr_data) > 0)
|
||||
try_output(1);
|
||||
if (connopen && back->socket(backhandle) != NULL) {
|
||||
back->unthrottle(backhandle, bufchain_size(&stdout_data) +
|
||||
bufchain_size(&stderr_data));
|
||||
}
|
||||
} else if (n == 4) {
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, INVALID_HANDLE_VALUE,
|
||||
WM_AGENT_CALLBACK, WM_AGENT_CALLBACK,
|
||||
PM_REMOVE)) {
|
||||
struct agent_callback *c = (struct agent_callback *)msg.lParam;
|
||||
c->callback(c->callback_ctx, c->data, c->len);
|
||||
sfree(c);
|
||||
}
|
||||
}
|
||||
if (!reading && back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
|
||||
SetEvent(idata.eventback);
|
||||
reading = 1;
|
||||
}
|
||||
if ((!connopen || back->socket(backhandle) == NULL) &&
|
||||
bufchain_size(&stdout_data) == 0 &&
|
||||
bufchain_size(&stderr_data) == 0)
|
||||
break; /* we closed the connection */
|
||||
}
|
||||
exitcode = back->exitcode(backhandle);
|
||||
if (exitcode < 0) {
|
||||
fprintf(stderr, "Remote process exit code unavailable\n");
|
||||
exitcode = 1; /* this is an error condition */
|
||||
}
|
||||
cleanup_exit(exitcode);
|
||||
return 0; /* placate compiler warning */
|
||||
}
|
185
windows/winprint.c
Normal file
185
windows/winprint.c
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Printing interface for PuTTY.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include <winspool.h>
|
||||
|
||||
struct printer_enum_tag {
|
||||
int nprinters;
|
||||
DWORD enum_level;
|
||||
union {
|
||||
LPPRINTER_INFO_4 i4;
|
||||
LPPRINTER_INFO_5 i5;
|
||||
} info;
|
||||
};
|
||||
|
||||
struct printer_job_tag {
|
||||
HANDLE hprinter;
|
||||
};
|
||||
|
||||
static char *printer_add_enum(int param, DWORD level, char *buffer,
|
||||
int offset, int *nprinters_ptr)
|
||||
{
|
||||
DWORD needed, nprinters;
|
||||
|
||||
buffer = sresize(buffer, offset+512, char);
|
||||
|
||||
/*
|
||||
* Exploratory call to EnumPrinters to determine how much space
|
||||
* we'll need for the output. Discard the return value since it
|
||||
* will almost certainly be a failure due to lack of space.
|
||||
*/
|
||||
EnumPrinters(param, NULL, level, buffer+offset, 512,
|
||||
&needed, &nprinters);
|
||||
|
||||
if (needed < 512)
|
||||
needed = 512;
|
||||
|
||||
buffer = sresize(buffer, offset+needed, char);
|
||||
|
||||
if (EnumPrinters(param, NULL, level, buffer+offset,
|
||||
needed, &needed, &nprinters) == 0)
|
||||
return NULL;
|
||||
|
||||
*nprinters_ptr += nprinters;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
printer_enum *printer_start_enum(int *nprinters_ptr)
|
||||
{
|
||||
printer_enum *ret = snew(printer_enum);
|
||||
char *buffer = NULL, *retval;
|
||||
|
||||
*nprinters_ptr = 0; /* default return value */
|
||||
buffer = snewn(512, char);
|
||||
|
||||
/*
|
||||
* Determine what enumeration level to use.
|
||||
* When enumerating printers, we need to use PRINTER_INFO_4 on
|
||||
* NT-class systems to avoid Windows looking too hard for them and
|
||||
* slowing things down; and we need to avoid PRINTER_INFO_5 as
|
||||
* we've seen network printers not show up.
|
||||
* On 9x-class systems, PRINTER_INFO_4 isn't available and
|
||||
* PRINTER_INFO_5 is recommended.
|
||||
* Bletch.
|
||||
*/
|
||||
if (osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) {
|
||||
ret->enum_level = 5;
|
||||
} else {
|
||||
ret->enum_level = 4;
|
||||
}
|
||||
|
||||
retval = printer_add_enum(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS,
|
||||
ret->enum_level, buffer, 0, nprinters_ptr);
|
||||
if (!retval)
|
||||
goto error;
|
||||
else
|
||||
buffer = retval;
|
||||
|
||||
switch (ret->enum_level) {
|
||||
case 4:
|
||||
ret->info.i4 = (LPPRINTER_INFO_4)buffer;
|
||||
break;
|
||||
case 5:
|
||||
ret->info.i5 = (LPPRINTER_INFO_5)buffer;
|
||||
break;
|
||||
}
|
||||
ret->nprinters = *nprinters_ptr;
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
sfree(buffer);
|
||||
sfree(ret);
|
||||
*nprinters_ptr = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *printer_get_name(printer_enum *pe, int i)
|
||||
{
|
||||
if (!pe)
|
||||
return NULL;
|
||||
if (i < 0 || i >= pe->nprinters)
|
||||
return NULL;
|
||||
switch (pe->enum_level) {
|
||||
case 4:
|
||||
return pe->info.i4[i].pPrinterName;
|
||||
case 5:
|
||||
return pe->info.i5[i].pPrinterName;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void printer_finish_enum(printer_enum *pe)
|
||||
{
|
||||
if (!pe)
|
||||
return;
|
||||
switch (pe->enum_level) {
|
||||
case 4:
|
||||
sfree(pe->info.i4);
|
||||
break;
|
||||
case 5:
|
||||
sfree(pe->info.i5);
|
||||
break;
|
||||
}
|
||||
sfree(pe);
|
||||
}
|
||||
|
||||
printer_job *printer_start_job(char *printer)
|
||||
{
|
||||
printer_job *ret = snew(printer_job);
|
||||
DOC_INFO_1 docinfo;
|
||||
int jobstarted = 0, pagestarted = 0;
|
||||
|
||||
ret->hprinter = NULL;
|
||||
if (!OpenPrinter(printer, &ret->hprinter, NULL))
|
||||
goto error;
|
||||
|
||||
docinfo.pDocName = "PuTTY remote printer output";
|
||||
docinfo.pOutputFile = NULL;
|
||||
docinfo.pDatatype = "RAW";
|
||||
|
||||
if (!StartDocPrinter(ret->hprinter, 1, (LPSTR)&docinfo))
|
||||
goto error;
|
||||
jobstarted = 1;
|
||||
|
||||
if (!StartPagePrinter(ret->hprinter))
|
||||
goto error;
|
||||
pagestarted = 1;
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
if (pagestarted)
|
||||
EndPagePrinter(ret->hprinter);
|
||||
if (jobstarted)
|
||||
EndDocPrinter(ret->hprinter);
|
||||
if (ret->hprinter)
|
||||
ClosePrinter(ret->hprinter);
|
||||
sfree(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void printer_job_data(printer_job *pj, void *data, int len)
|
||||
{
|
||||
DWORD written;
|
||||
|
||||
if (!pj)
|
||||
return;
|
||||
|
||||
WritePrinter(pj->hprinter, data, len, &written);
|
||||
}
|
||||
|
||||
void printer_finish_job(printer_job *pj)
|
||||
{
|
||||
if (!pj)
|
||||
return;
|
||||
|
||||
EndPagePrinter(pj->hprinter);
|
||||
EndDocPrinter(pj->hprinter);
|
||||
ClosePrinter(pj->hprinter);
|
||||
sfree(pj);
|
||||
}
|
503
windows/winsftp.c
Normal file
503
windows/winsftp.c
Normal file
@ -0,0 +1,503 @@
|
||||
/*
|
||||
* winsftp.c: the Windows-specific parts of PSFTP and PSCP.
|
||||
*/
|
||||
|
||||
#include "putty.h"
|
||||
#include "psftp.h"
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Interface to GUI driver program.
|
||||
*/
|
||||
|
||||
/* This is just a base value from which the main message numbers are
|
||||
* derived. */
|
||||
#define WM_APP_BASE 0x8000
|
||||
|
||||
/* These two pass a single character value in wParam. They represent
|
||||
* the visible output from PSCP. */
|
||||
#define WM_STD_OUT_CHAR ( WM_APP_BASE+400 )
|
||||
#define WM_STD_ERR_CHAR ( WM_APP_BASE+401 )
|
||||
|
||||
/* These pass a transfer status update. WM_STATS_CHAR passes a single
|
||||
* character in wParam, and is called repeatedly to pass the name of
|
||||
* the file, terminated with "\n". WM_STATS_SIZE passes the size of
|
||||
* the file being transferred in wParam. WM_STATS_ELAPSED is called
|
||||
* to pass the elapsed time (in seconds) in wParam, and
|
||||
* WM_STATS_PERCENT passes the percentage of the transfer which is
|
||||
* complete, also in wParam. */
|
||||
#define WM_STATS_CHAR ( WM_APP_BASE+402 )
|
||||
#define WM_STATS_SIZE ( WM_APP_BASE+403 )
|
||||
#define WM_STATS_PERCENT ( WM_APP_BASE+404 )
|
||||
#define WM_STATS_ELAPSED ( WM_APP_BASE+405 )
|
||||
|
||||
/* These are used at the end of a run to pass an error code in
|
||||
* wParam: zero means success, nonzero means failure. WM_RET_ERR_CNT
|
||||
* is used after a copy, and WM_LS_RET_ERR_CNT is used after a file
|
||||
* list operation. */
|
||||
#define WM_RET_ERR_CNT ( WM_APP_BASE+406 )
|
||||
#define WM_LS_RET_ERR_CNT ( WM_APP_BASE+407 )
|
||||
|
||||
/* More transfer status update messages. WM_STATS_DONE passes the
|
||||
* number of bytes sent so far in wParam. WM_STATS_ETA passes the
|
||||
* estimated time to completion (in seconds). WM_STATS_RATEBS passes
|
||||
* the average transfer rate (in bytes per second). */
|
||||
#define WM_STATS_DONE ( WM_APP_BASE+408 )
|
||||
#define WM_STATS_ETA ( WM_APP_BASE+409 )
|
||||
#define WM_STATS_RATEBS ( WM_APP_BASE+410 )
|
||||
|
||||
#define NAME_STR_MAX 2048
|
||||
static char statname[NAME_STR_MAX + 1];
|
||||
static unsigned long statsize = 0;
|
||||
static unsigned long statdone = 0;
|
||||
static unsigned long stateta = 0;
|
||||
static unsigned long statratebs = 0;
|
||||
static int statperct = 0;
|
||||
static unsigned long statelapsed = 0;
|
||||
|
||||
static HWND gui_hwnd = NULL;
|
||||
|
||||
static void send_msg(HWND h, UINT message, WPARAM wParam)
|
||||
{
|
||||
while (!PostMessage(h, message, wParam, 0))
|
||||
SleepEx(1000, TRUE);
|
||||
}
|
||||
|
||||
void gui_send_char(int is_stderr, int c)
|
||||
{
|
||||
unsigned int msg_id = WM_STD_OUT_CHAR;
|
||||
if (is_stderr)
|
||||
msg_id = WM_STD_ERR_CHAR;
|
||||
send_msg(gui_hwnd, msg_id, (WPARAM) c);
|
||||
}
|
||||
|
||||
void gui_send_errcount(int list, int errs)
|
||||
{
|
||||
unsigned int msg_id = WM_RET_ERR_CNT;
|
||||
if (list)
|
||||
msg_id = WM_LS_RET_ERR_CNT;
|
||||
while (!PostMessage(gui_hwnd, msg_id, (WPARAM) errs, 0))
|
||||
SleepEx(1000, TRUE);
|
||||
}
|
||||
|
||||
void gui_update_stats(char *name, unsigned long size,
|
||||
int percentage, unsigned long elapsed,
|
||||
unsigned long done, unsigned long eta,
|
||||
unsigned long ratebs)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (strcmp(name, statname) != 0) {
|
||||
for (i = 0; i < strlen(name); ++i)
|
||||
send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) name[i]);
|
||||
send_msg(gui_hwnd, WM_STATS_CHAR, (WPARAM) '\n');
|
||||
strcpy(statname, name);
|
||||
}
|
||||
if (statsize != size) {
|
||||
send_msg(gui_hwnd, WM_STATS_SIZE, (WPARAM) size);
|
||||
statsize = size;
|
||||
}
|
||||
if (statdone != done) {
|
||||
send_msg(gui_hwnd, WM_STATS_DONE, (WPARAM) done);
|
||||
statdone = done;
|
||||
}
|
||||
if (stateta != eta) {
|
||||
send_msg(gui_hwnd, WM_STATS_ETA, (WPARAM) eta);
|
||||
stateta = eta;
|
||||
}
|
||||
if (statratebs != ratebs) {
|
||||
send_msg(gui_hwnd, WM_STATS_RATEBS, (WPARAM) ratebs);
|
||||
statratebs = ratebs;
|
||||
}
|
||||
if (statelapsed != elapsed) {
|
||||
send_msg(gui_hwnd, WM_STATS_ELAPSED, (WPARAM) elapsed);
|
||||
statelapsed = elapsed;
|
||||
}
|
||||
if (statperct != percentage) {
|
||||
send_msg(gui_hwnd, WM_STATS_PERCENT, (WPARAM) percentage);
|
||||
statperct = percentage;
|
||||
}
|
||||
}
|
||||
|
||||
void gui_enable(char *arg)
|
||||
{
|
||||
gui_hwnd = (HWND) atoi(arg);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* File access abstraction.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Set local current directory. Returns NULL on success, or else an
|
||||
* error message which must be freed after printing.
|
||||
*/
|
||||
char *psftp_lcd(char *dir)
|
||||
{
|
||||
char *ret = NULL;
|
||||
|
||||
if (!SetCurrentDirectory(dir)) {
|
||||
LPVOID message;
|
||||
int i;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&message, 0, NULL);
|
||||
i = strcspn((char *)message, "\n");
|
||||
ret = dupprintf("%.*s", i, (LPCTSTR)message);
|
||||
LocalFree(message);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get local current directory. Returns a string which must be
|
||||
* freed.
|
||||
*/
|
||||
char *psftp_getcwd(void)
|
||||
{
|
||||
char *ret = snewn(256, char);
|
||||
int len = GetCurrentDirectory(256, ret);
|
||||
if (len > 256)
|
||||
ret = sresize(ret, len, char);
|
||||
GetCurrentDirectory(len, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define TIME_POSIX_TO_WIN(t, ft) (*(LONGLONG*)&(ft) = \
|
||||
((LONGLONG) (t) + (LONGLONG) 11644473600) * (LONGLONG) 10000000)
|
||||
#define TIME_WIN_TO_POSIX(ft, t) ((t) = (unsigned long) \
|
||||
((*(LONGLONG*)&(ft)) / (LONGLONG) 10000000 - (LONGLONG) 11644473600))
|
||||
|
||||
struct RFile {
|
||||
HANDLE h;
|
||||
};
|
||||
|
||||
RFile *open_existing_file(char *name, unsigned long *size,
|
||||
unsigned long *mtime, unsigned long *atime)
|
||||
{
|
||||
HANDLE h;
|
||||
RFile *ret;
|
||||
|
||||
h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
ret = snew(RFile);
|
||||
ret->h = h;
|
||||
|
||||
if (size)
|
||||
*size = GetFileSize(h, NULL);
|
||||
|
||||
if (mtime || atime) {
|
||||
FILETIME actime, wrtime;
|
||||
GetFileTime(h, NULL, &actime, &wrtime);
|
||||
if (atime)
|
||||
TIME_WIN_TO_POSIX(actime, *atime);
|
||||
if (mtime)
|
||||
TIME_WIN_TO_POSIX(wrtime, *mtime);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int read_from_file(RFile *f, void *buffer, int length)
|
||||
{
|
||||
int ret, read;
|
||||
ret = ReadFile(f->h, buffer, length, &read, NULL);
|
||||
if (!ret)
|
||||
return -1; /* error */
|
||||
else
|
||||
return read;
|
||||
}
|
||||
|
||||
void close_rfile(RFile *f)
|
||||
{
|
||||
CloseHandle(f->h);
|
||||
sfree(f);
|
||||
}
|
||||
|
||||
struct WFile {
|
||||
HANDLE h;
|
||||
};
|
||||
|
||||
WFile *open_new_file(char *name)
|
||||
{
|
||||
HANDLE h;
|
||||
WFile *ret;
|
||||
|
||||
h = CreateFile(name, GENERIC_WRITE, 0, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
ret = snew(WFile);
|
||||
ret->h = h;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int write_to_file(WFile *f, void *buffer, int length)
|
||||
{
|
||||
int ret, written;
|
||||
ret = WriteFile(f->h, buffer, length, &written, NULL);
|
||||
if (!ret)
|
||||
return -1; /* error */
|
||||
else
|
||||
return written;
|
||||
}
|
||||
|
||||
void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)
|
||||
{
|
||||
FILETIME actime, wrtime;
|
||||
TIME_POSIX_TO_WIN(atime, actime);
|
||||
TIME_POSIX_TO_WIN(mtime, wrtime);
|
||||
SetFileTime(f->h, NULL, &actime, &wrtime);
|
||||
}
|
||||
|
||||
void close_wfile(WFile *f)
|
||||
{
|
||||
CloseHandle(f->h);
|
||||
sfree(f);
|
||||
}
|
||||
|
||||
int file_type(char *name)
|
||||
{
|
||||
DWORD attr;
|
||||
attr = GetFileAttributes(name);
|
||||
/* We know of no `weird' files under Windows. */
|
||||
if (attr == (DWORD)-1)
|
||||
return FILE_TYPE_NONEXISTENT;
|
||||
else if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return FILE_TYPE_DIRECTORY;
|
||||
else
|
||||
return FILE_TYPE_FILE;
|
||||
}
|
||||
|
||||
struct DirHandle {
|
||||
HANDLE h;
|
||||
char *name;
|
||||
};
|
||||
|
||||
DirHandle *open_directory(char *name)
|
||||
{
|
||||
HANDLE h;
|
||||
WIN32_FIND_DATA fdat;
|
||||
char *findfile;
|
||||
DirHandle *ret;
|
||||
|
||||
/* Enumerate files in dir `foo'. */
|
||||
findfile = dupcat(name, "/*", NULL);
|
||||
h = FindFirstFile(findfile, &fdat);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
sfree(findfile);
|
||||
|
||||
ret = snew(DirHandle);
|
||||
ret->h = h;
|
||||
ret->name = dupstr(fdat.cFileName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *read_filename(DirHandle *dir)
|
||||
{
|
||||
while (!dir->name) {
|
||||
WIN32_FIND_DATA fdat;
|
||||
int ok = FindNextFile(dir->h, &fdat);
|
||||
|
||||
if (!ok)
|
||||
return NULL;
|
||||
|
||||
if (fdat.cFileName[0] == '.' &&
|
||||
(fdat.cFileName[1] == '\0' ||
|
||||
(fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
|
||||
dir->name = NULL;
|
||||
else
|
||||
dir->name = dupstr(fdat.cFileName);
|
||||
}
|
||||
|
||||
if (dir->name) {
|
||||
char *ret = dir->name;
|
||||
dir->name = NULL;
|
||||
return ret;
|
||||
} else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void close_directory(DirHandle *dir)
|
||||
{
|
||||
FindClose(dir->h);
|
||||
if (dir->name)
|
||||
sfree(dir->name);
|
||||
sfree(dir);
|
||||
}
|
||||
|
||||
int test_wildcard(char *name, int cmdline)
|
||||
{
|
||||
HANDLE fh;
|
||||
WIN32_FIND_DATA fdat;
|
||||
|
||||
/* First see if the exact name exists. */
|
||||
if (GetFileAttributes(name) != (DWORD)-1)
|
||||
return WCTYPE_FILENAME;
|
||||
|
||||
/* Otherwise see if a wildcard match finds anything. */
|
||||
fh = FindFirstFile(name, &fdat);
|
||||
if (fh == INVALID_HANDLE_VALUE)
|
||||
return WCTYPE_NONEXISTENT;
|
||||
|
||||
FindClose(fh);
|
||||
return WCTYPE_WILDCARD;
|
||||
}
|
||||
|
||||
struct WildcardMatcher {
|
||||
HANDLE h;
|
||||
char *name;
|
||||
char *srcpath;
|
||||
};
|
||||
|
||||
/*
|
||||
* Return a pointer to the portion of str that comes after the last
|
||||
* slash (or backslash or colon, if `local' is TRUE).
|
||||
*/
|
||||
static char *stripslashes(char *str, int local)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (local) {
|
||||
p = strchr(str, ':');
|
||||
if (p) str = p+1;
|
||||
}
|
||||
|
||||
p = strrchr(str, '/');
|
||||
if (p) str = p+1;
|
||||
|
||||
if (local) {
|
||||
p = strrchr(str, '\\');
|
||||
if (p) str = p+1;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
WildcardMatcher *begin_wildcard_matching(char *name)
|
||||
{
|
||||
HANDLE h;
|
||||
WIN32_FIND_DATA fdat;
|
||||
WildcardMatcher *ret;
|
||||
char *last;
|
||||
|
||||
h = FindFirstFile(name, &fdat);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return NULL;
|
||||
|
||||
ret = snew(WildcardMatcher);
|
||||
ret->h = h;
|
||||
ret->srcpath = dupstr(name);
|
||||
last = stripslashes(ret->srcpath, 1);
|
||||
*last = '\0';
|
||||
if (fdat.cFileName[0] == '.' &&
|
||||
(fdat.cFileName[1] == '\0' ||
|
||||
(fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
|
||||
ret->name = NULL;
|
||||
else
|
||||
ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *wildcard_get_filename(WildcardMatcher *dir)
|
||||
{
|
||||
while (!dir->name) {
|
||||
WIN32_FIND_DATA fdat;
|
||||
int ok = FindNextFile(dir->h, &fdat);
|
||||
|
||||
if (!ok)
|
||||
return NULL;
|
||||
|
||||
if (fdat.cFileName[0] == '.' &&
|
||||
(fdat.cFileName[1] == '\0' ||
|
||||
(fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
|
||||
dir->name = NULL;
|
||||
else
|
||||
dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL);
|
||||
}
|
||||
|
||||
if (dir->name) {
|
||||
char *ret = dir->name;
|
||||
dir->name = NULL;
|
||||
return ret;
|
||||
} else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void finish_wildcard_matching(WildcardMatcher *dir)
|
||||
{
|
||||
FindClose(dir->h);
|
||||
if (dir->name)
|
||||
sfree(dir->name);
|
||||
sfree(dir->srcpath);
|
||||
sfree(dir);
|
||||
}
|
||||
|
||||
int create_directory(char *name)
|
||||
{
|
||||
return CreateDirectory(name, NULL) != 0;
|
||||
}
|
||||
|
||||
char *dir_file_cat(char *dir, char *file)
|
||||
{
|
||||
return dupcat(dir, "\\", file, NULL);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Platform-specific network handling.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Be told what socket we're supposed to be using.
|
||||
*/
|
||||
static SOCKET sftp_ssh_socket;
|
||||
char *do_select(SOCKET skt, int startup)
|
||||
{
|
||||
if (startup)
|
||||
sftp_ssh_socket = skt;
|
||||
else
|
||||
sftp_ssh_socket = INVALID_SOCKET;
|
||||
return NULL;
|
||||
}
|
||||
extern int select_result(WPARAM, LPARAM);
|
||||
|
||||
/*
|
||||
* Wait for some network data and process it.
|
||||
*/
|
||||
int ssh_sftp_loop_iteration(void)
|
||||
{
|
||||
fd_set readfds;
|
||||
|
||||
if (sftp_ssh_socket == INVALID_SOCKET)
|
||||
return -1; /* doom */
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sftp_ssh_socket, &readfds);
|
||||
if (p_select(1, &readfds, NULL, NULL, NULL) < 0)
|
||||
return -1; /* doom */
|
||||
|
||||
select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Main program. Parse arguments etc.
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = psftp_main(argc, argv);
|
||||
|
||||
return ret;
|
||||
}
|
565
windows/winstore.c
Normal file
565
windows/winstore.c
Normal file
@ -0,0 +1,565 @@
|
||||
/*
|
||||
* winstore.c: Windows-specific implementation of the interface
|
||||
* defined in storage.h.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include "putty.h"
|
||||
#include "storage.h"
|
||||
|
||||
static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
|
||||
|
||||
static char seedpath[2 * MAX_PATH + 10] = "\0";
|
||||
|
||||
static const char hex[16] = "0123456789ABCDEF";
|
||||
|
||||
static void mungestr(const char *in, char *out)
|
||||
{
|
||||
int candot = 0;
|
||||
|
||||
while (*in) {
|
||||
if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
|
||||
*in == '%' || *in < ' ' || *in > '~' || (*in == '.'
|
||||
&& !candot)) {
|
||||
*out++ = '%';
|
||||
*out++ = hex[((unsigned char) *in) >> 4];
|
||||
*out++ = hex[((unsigned char) *in) & 15];
|
||||
} else
|
||||
*out++ = *in;
|
||||
in++;
|
||||
candot = 1;
|
||||
}
|
||||
*out = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
static void unmungestr(const char *in, char *out, int outlen)
|
||||
{
|
||||
while (*in) {
|
||||
if (*in == '%' && in[1] && in[2]) {
|
||||
int i, j;
|
||||
|
||||
i = in[1] - '0';
|
||||
i -= (i > 9 ? 7 : 0);
|
||||
j = in[2] - '0';
|
||||
j -= (j > 9 ? 7 : 0);
|
||||
|
||||
*out++ = (i << 4) + j;
|
||||
if (!--outlen)
|
||||
return;
|
||||
in += 3;
|
||||
} else {
|
||||
*out++ = *in++;
|
||||
if (!--outlen)
|
||||
return;
|
||||
}
|
||||
}
|
||||
*out = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
void *open_settings_w(const char *sessionname, char **errmsg)
|
||||
{
|
||||
HKEY subkey1, sesskey;
|
||||
int ret;
|
||||
char *p;
|
||||
|
||||
*errmsg = NULL;
|
||||
|
||||
if (!sessionname || !*sessionname)
|
||||
sessionname = "Default Settings";
|
||||
|
||||
p = snewn(3 * strlen(sessionname) + 1, char);
|
||||
mungestr(sessionname, p);
|
||||
|
||||
ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1);
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
sfree(p);
|
||||
*errmsg = dupprintf("Unable to create registry key\n"
|
||||
"HKEY_CURRENT_USER%s", puttystr);
|
||||
return NULL;
|
||||
}
|
||||
ret = RegCreateKey(subkey1, p, &sesskey);
|
||||
sfree(p);
|
||||
RegCloseKey(subkey1);
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
*errmsg = dupprintf("Unable to create registry key\n"
|
||||
"HKEY_CURRENT_USER%s\\%s", puttystr, p);
|
||||
return NULL;
|
||||
}
|
||||
return (void *) sesskey;
|
||||
}
|
||||
|
||||
void write_setting_s(void *handle, const char *key, const char *value)
|
||||
{
|
||||
if (handle)
|
||||
RegSetValueEx((HKEY) handle, key, 0, REG_SZ, value,
|
||||
1 + strlen(value));
|
||||
}
|
||||
|
||||
void write_setting_i(void *handle, const char *key, int value)
|
||||
{
|
||||
if (handle)
|
||||
RegSetValueEx((HKEY) handle, key, 0, REG_DWORD,
|
||||
(CONST BYTE *) & value, sizeof(value));
|
||||
}
|
||||
|
||||
void close_settings_w(void *handle)
|
||||
{
|
||||
RegCloseKey((HKEY) handle);
|
||||
}
|
||||
|
||||
void *open_settings_r(const char *sessionname)
|
||||
{
|
||||
HKEY subkey1, sesskey;
|
||||
char *p;
|
||||
|
||||
if (!sessionname || !*sessionname)
|
||||
sessionname = "Default Settings";
|
||||
|
||||
p = snewn(3 * strlen(sessionname) + 1, char);
|
||||
mungestr(sessionname, p);
|
||||
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) {
|
||||
sesskey = NULL;
|
||||
} else {
|
||||
if (RegOpenKey(subkey1, p, &sesskey) != ERROR_SUCCESS) {
|
||||
sesskey = NULL;
|
||||
}
|
||||
RegCloseKey(subkey1);
|
||||
}
|
||||
|
||||
sfree(p);
|
||||
|
||||
return (void *) sesskey;
|
||||
}
|
||||
|
||||
char *read_setting_s(void *handle, const char *key, char *buffer, int buflen)
|
||||
{
|
||||
DWORD type, size;
|
||||
size = buflen;
|
||||
|
||||
if (!handle ||
|
||||
RegQueryValueEx((HKEY) handle, key, 0,
|
||||
&type, buffer, &size) != ERROR_SUCCESS ||
|
||||
type != REG_SZ) return NULL;
|
||||
else
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int read_setting_i(void *handle, const char *key, int defvalue)
|
||||
{
|
||||
DWORD type, val, size;
|
||||
size = sizeof(val);
|
||||
|
||||
if (!handle ||
|
||||
RegQueryValueEx((HKEY) handle, key, 0, &type,
|
||||
(BYTE *) & val, &size) != ERROR_SUCCESS ||
|
||||
size != sizeof(val) || type != REG_DWORD)
|
||||
return defvalue;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
int read_setting_fontspec(void *handle, const char *name, FontSpec *result)
|
||||
{
|
||||
char *settingname;
|
||||
FontSpec ret;
|
||||
|
||||
if (!read_setting_s(handle, name, ret.name, sizeof(ret.name)))
|
||||
return 0;
|
||||
settingname = dupcat(name, "IsBold", NULL);
|
||||
ret.isbold = read_setting_i(handle, settingname, -1);
|
||||
sfree(settingname);
|
||||
if (ret.isbold == -1) return 0;
|
||||
settingname = dupcat(name, "CharSet", NULL);
|
||||
ret.charset = read_setting_i(handle, settingname, -1);
|
||||
sfree(settingname);
|
||||
if (ret.charset == -1) return 0;
|
||||
settingname = dupcat(name, "Height", NULL);
|
||||
ret.height = read_setting_i(handle, settingname, INT_MIN);
|
||||
sfree(settingname);
|
||||
if (ret.height == INT_MIN) return 0;
|
||||
*result = ret;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void write_setting_fontspec(void *handle, const char *name, FontSpec font)
|
||||
{
|
||||
char *settingname;
|
||||
|
||||
write_setting_s(handle, name, font.name);
|
||||
settingname = dupcat(name, "IsBold", NULL);
|
||||
write_setting_i(handle, settingname, font.isbold);
|
||||
sfree(settingname);
|
||||
settingname = dupcat(name, "CharSet", NULL);
|
||||
write_setting_i(handle, settingname, font.charset);
|
||||
sfree(settingname);
|
||||
settingname = dupcat(name, "Height", NULL);
|
||||
write_setting_i(handle, settingname, font.height);
|
||||
sfree(settingname);
|
||||
}
|
||||
|
||||
int read_setting_filename(void *handle, const char *name, Filename *result)
|
||||
{
|
||||
return !!read_setting_s(handle, name, result->path, sizeof(result->path));
|
||||
}
|
||||
|
||||
void write_setting_filename(void *handle, const char *name, Filename result)
|
||||
{
|
||||
write_setting_s(handle, name, result.path);
|
||||
}
|
||||
|
||||
void close_settings_r(void *handle)
|
||||
{
|
||||
RegCloseKey((HKEY) handle);
|
||||
}
|
||||
|
||||
void del_settings(const char *sessionname)
|
||||
{
|
||||
HKEY subkey1;
|
||||
char *p;
|
||||
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
p = snewn(3 * strlen(sessionname) + 1, char);
|
||||
mungestr(sessionname, p);
|
||||
RegDeleteKey(subkey1, p);
|
||||
sfree(p);
|
||||
|
||||
RegCloseKey(subkey1);
|
||||
}
|
||||
|
||||
struct enumsettings {
|
||||
HKEY key;
|
||||
int i;
|
||||
};
|
||||
|
||||
void *enum_settings_start(void)
|
||||
{
|
||||
struct enumsettings *ret;
|
||||
HKEY key;
|
||||
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS)
|
||||
return NULL;
|
||||
|
||||
ret = snew(struct enumsettings);
|
||||
if (ret) {
|
||||
ret->key = key;
|
||||
ret->i = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *enum_settings_next(void *handle, char *buffer, int buflen)
|
||||
{
|
||||
struct enumsettings *e = (struct enumsettings *) handle;
|
||||
char *otherbuf;
|
||||
otherbuf = snewn(3 * buflen, char);
|
||||
if (RegEnumKey(e->key, e->i++, otherbuf, 3 * buflen) == ERROR_SUCCESS) {
|
||||
unmungestr(otherbuf, buffer, buflen);
|
||||
sfree(otherbuf);
|
||||
return buffer;
|
||||
} else {
|
||||
sfree(otherbuf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void enum_settings_finish(void *handle)
|
||||
{
|
||||
struct enumsettings *e = (struct enumsettings *) handle;
|
||||
RegCloseKey(e->key);
|
||||
sfree(e);
|
||||
}
|
||||
|
||||
static void hostkey_regname(char *buffer, const char *hostname,
|
||||
int port, const char *keytype)
|
||||
{
|
||||
int len;
|
||||
strcpy(buffer, keytype);
|
||||
strcat(buffer, "@");
|
||||
len = strlen(buffer);
|
||||
len += sprintf(buffer + len, "%d:", port);
|
||||
mungestr(hostname, buffer + strlen(buffer));
|
||||
}
|
||||
|
||||
int verify_host_key(const char *hostname, int port,
|
||||
const char *keytype, const char *key)
|
||||
{
|
||||
char *otherstr, *regname;
|
||||
int len;
|
||||
HKEY rkey;
|
||||
DWORD readlen;
|
||||
DWORD type;
|
||||
int ret, compare;
|
||||
|
||||
len = 1 + strlen(key);
|
||||
|
||||
/*
|
||||
* Now read a saved key in from the registry and see what it
|
||||
* says.
|
||||
*/
|
||||
otherstr = snewn(len, char);
|
||||
regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);
|
||||
|
||||
hostkey_regname(regname, hostname, port, keytype);
|
||||
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
|
||||
&rkey) != ERROR_SUCCESS)
|
||||
return 1; /* key does not exist in registry */
|
||||
|
||||
readlen = len;
|
||||
ret = RegQueryValueEx(rkey, regname, NULL, &type, otherstr, &readlen);
|
||||
|
||||
if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA &&
|
||||
!strcmp(keytype, "rsa")) {
|
||||
/*
|
||||
* Key didn't exist. If the key type is RSA, we'll try
|
||||
* another trick, which is to look up the _old_ key format
|
||||
* under just the hostname and translate that.
|
||||
*/
|
||||
char *justhost = regname + 1 + strcspn(regname, ":");
|
||||
char *oldstyle = snewn(len + 10, char); /* safety margin */
|
||||
readlen = len;
|
||||
ret = RegQueryValueEx(rkey, justhost, NULL, &type,
|
||||
oldstyle, &readlen);
|
||||
|
||||
if (ret == ERROR_SUCCESS && type == REG_SZ) {
|
||||
/*
|
||||
* The old format is two old-style bignums separated by
|
||||
* a slash. An old-style bignum is made of groups of
|
||||
* four hex digits: digits are ordered in sensible
|
||||
* (most to least significant) order within each group,
|
||||
* but groups are ordered in silly (least to most)
|
||||
* order within the bignum. The new format is two
|
||||
* ordinary C-format hex numbers (0xABCDEFG...XYZ, with
|
||||
* A nonzero except in the special case 0x0, which
|
||||
* doesn't appear anyway in RSA keys) separated by a
|
||||
* comma. All hex digits are lowercase in both formats.
|
||||
*/
|
||||
char *p = otherstr;
|
||||
char *q = oldstyle;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int ndigits, nwords;
|
||||
*p++ = '0';
|
||||
*p++ = 'x';
|
||||
ndigits = strcspn(q, "/"); /* find / or end of string */
|
||||
nwords = ndigits / 4;
|
||||
/* now trim ndigits to remove leading zeros */
|
||||
while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1)
|
||||
ndigits--;
|
||||
/* now move digits over to new string */
|
||||
for (j = 0; j < ndigits; j++)
|
||||
p[ndigits - 1 - j] = q[j ^ 3];
|
||||
p += ndigits;
|
||||
q += nwords * 4;
|
||||
if (*q) {
|
||||
q++; /* eat the slash */
|
||||
*p++ = ','; /* add a comma */
|
||||
}
|
||||
*p = '\0'; /* terminate the string */
|
||||
}
|
||||
|
||||
/*
|
||||
* Now _if_ this key matches, we'll enter it in the new
|
||||
* format. If not, we'll assume something odd went
|
||||
* wrong, and hyper-cautiously do nothing.
|
||||
*/
|
||||
if (!strcmp(otherstr, key))
|
||||
RegSetValueEx(rkey, regname, 0, REG_SZ, otherstr,
|
||||
strlen(otherstr) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
RegCloseKey(rkey);
|
||||
|
||||
compare = strcmp(otherstr, key);
|
||||
|
||||
sfree(otherstr);
|
||||
sfree(regname);
|
||||
|
||||
if (ret == ERROR_MORE_DATA ||
|
||||
(ret == ERROR_SUCCESS && type == REG_SZ && compare))
|
||||
return 2; /* key is different in registry */
|
||||
else if (ret != ERROR_SUCCESS || type != REG_SZ)
|
||||
return 1; /* key does not exist in registry */
|
||||
else
|
||||
return 0; /* key matched OK in registry */
|
||||
}
|
||||
|
||||
void store_host_key(const char *hostname, int port,
|
||||
const char *keytype, const char *key)
|
||||
{
|
||||
char *regname;
|
||||
HKEY rkey;
|
||||
|
||||
regname = snewn(3 * (strlen(hostname) + strlen(keytype)) + 15, char);
|
||||
|
||||
hostkey_regname(regname, hostname, port, keytype);
|
||||
|
||||
if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys",
|
||||
&rkey) != ERROR_SUCCESS)
|
||||
return; /* key does not exist in registry */
|
||||
RegSetValueEx(rkey, regname, 0, REG_SZ, key, strlen(key) + 1);
|
||||
RegCloseKey(rkey);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the random seed file path and store it in `seedpath'.
|
||||
*/
|
||||
static void get_seedpath(void)
|
||||
{
|
||||
HKEY rkey;
|
||||
DWORD type, size;
|
||||
|
||||
size = sizeof(seedpath);
|
||||
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) ==
|
||||
ERROR_SUCCESS) {
|
||||
int ret = RegQueryValueEx(rkey, "RandSeedFile",
|
||||
0, &type, seedpath, &size);
|
||||
if (ret != ERROR_SUCCESS || type != REG_SZ)
|
||||
seedpath[0] = '\0';
|
||||
RegCloseKey(rkey);
|
||||
} else
|
||||
seedpath[0] = '\0';
|
||||
|
||||
if (!seedpath[0]) {
|
||||
int len, ret;
|
||||
|
||||
len =
|
||||
GetEnvironmentVariable("HOMEDRIVE", seedpath,
|
||||
sizeof(seedpath));
|
||||
ret =
|
||||
GetEnvironmentVariable("HOMEPATH", seedpath + len,
|
||||
sizeof(seedpath) - len);
|
||||
if (ret == 0) { /* probably win95; store in \WINDOWS */
|
||||
GetWindowsDirectory(seedpath, sizeof(seedpath));
|
||||
len = strlen(seedpath);
|
||||
} else
|
||||
len += ret;
|
||||
strcpy(seedpath + len, "\\PUTTY.RND");
|
||||
}
|
||||
}
|
||||
|
||||
void read_random_seed(noise_consumer_t consumer)
|
||||
{
|
||||
HANDLE seedf;
|
||||
|
||||
if (!seedpath[0])
|
||||
get_seedpath();
|
||||
|
||||
seedf = CreateFile(seedpath, GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, 0, NULL);
|
||||
|
||||
if (seedf != INVALID_HANDLE_VALUE) {
|
||||
while (1) {
|
||||
char buf[1024];
|
||||
DWORD len;
|
||||
|
||||
if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len)
|
||||
consumer(buf, len);
|
||||
else
|
||||
break;
|
||||
}
|
||||
CloseHandle(seedf);
|
||||
}
|
||||
}
|
||||
|
||||
void write_random_seed(void *data, int len)
|
||||
{
|
||||
HANDLE seedf;
|
||||
|
||||
if (!seedpath[0])
|
||||
get_seedpath();
|
||||
|
||||
seedf = CreateFile(seedpath, GENERIC_WRITE, 0,
|
||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (seedf != INVALID_HANDLE_VALUE) {
|
||||
DWORD lenwritten;
|
||||
|
||||
WriteFile(seedf, data, len, &lenwritten, NULL);
|
||||
CloseHandle(seedf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively delete a registry key and everything under it.
|
||||
*/
|
||||
static void registry_recursive_remove(HKEY key)
|
||||
{
|
||||
DWORD i;
|
||||
char name[MAX_PATH + 1];
|
||||
HKEY subkey;
|
||||
|
||||
i = 0;
|
||||
while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) {
|
||||
if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) {
|
||||
registry_recursive_remove(subkey);
|
||||
RegCloseKey(subkey);
|
||||
}
|
||||
RegDeleteKey(key, name);
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_all(void)
|
||||
{
|
||||
HKEY key;
|
||||
int ret;
|
||||
char name[MAX_PATH + 1];
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Wipe out the random seed file.
|
||||
*/
|
||||
if (!seedpath[0])
|
||||
get_seedpath();
|
||||
remove(seedpath);
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* Destroy all registry information associated with PuTTY.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Open the main PuTTY registry key and remove everything in it.
|
||||
*/
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) ==
|
||||
ERROR_SUCCESS) {
|
||||
registry_recursive_remove(key);
|
||||
RegCloseKey(key);
|
||||
}
|
||||
/*
|
||||
* Now open the parent key and remove the PuTTY main key. Once
|
||||
* we've done that, see if the parent key has any other
|
||||
* children.
|
||||
*/
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT,
|
||||
&key) == ERROR_SUCCESS) {
|
||||
RegDeleteKey(key, PUTTY_REG_PARENT_CHILD);
|
||||
ret = RegEnumKey(key, 0, name, sizeof(name));
|
||||
RegCloseKey(key);
|
||||
/*
|
||||
* If the parent key had no other children, we must delete
|
||||
* it in its turn. That means opening the _grandparent_
|
||||
* key.
|
||||
*/
|
||||
if (ret != ERROR_SUCCESS) {
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT,
|
||||
&key) == ERROR_SUCCESS) {
|
||||
RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD);
|
||||
RegCloseKey(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Now we're done.
|
||||
*/
|
||||
}
|
373
windows/winstuff.h
Normal file
373
windows/winstuff.h
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
* winstuff.h: Windows-specific inter-module stuff.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_WINSTUFF_H
|
||||
#define PUTTY_WINSTUFF_H
|
||||
|
||||
#ifndef AUTO_WINSOCK
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <stdio.h> /* for FILENAME_MAX */
|
||||
|
||||
#include "tree234.h"
|
||||
|
||||
#include "winhelp.h"
|
||||
|
||||
struct Filename {
|
||||
char path[FILENAME_MAX];
|
||||
};
|
||||
#define f_open(filename, mode) ( fopen((filename).path, (mode)) )
|
||||
|
||||
struct FontSpec {
|
||||
char name[64];
|
||||
int isbold;
|
||||
int height;
|
||||
int charset;
|
||||
};
|
||||
|
||||
#define BOXFLAGS DLGWINDOWEXTRA
|
||||
#define BOXRESULT DLGWINDOWEXTRA + 4
|
||||
#define DF_END 0x0001
|
||||
|
||||
/*
|
||||
* Global variables. Most modules declare these `extern', but
|
||||
* window.c will do `#define PUTTY_DO_GLOBALS' before including this
|
||||
* module, and so will get them properly defined.
|
||||
*/
|
||||
#ifndef GLOBAL
|
||||
#ifdef PUTTY_DO_GLOBALS
|
||||
#define GLOBAL
|
||||
#else
|
||||
#define GLOBAL extern
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DONE_TYPEDEFS
|
||||
#define DONE_TYPEDEFS
|
||||
typedef struct config_tag Config;
|
||||
typedef struct backend_tag Backend;
|
||||
typedef struct terminal_tag Terminal;
|
||||
#endif
|
||||
|
||||
#define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY"
|
||||
#define PUTTY_REG_PARENT "Software\\SimonTatham"
|
||||
#define PUTTY_REG_PARENT_CHILD "PuTTY"
|
||||
#define PUTTY_REG_GPARENT "Software"
|
||||
#define PUTTY_REG_GPARENT_CHILD "SimonTatham"
|
||||
|
||||
#define GETTICKCOUNT GetTickCount
|
||||
#define CURSORBLINK GetCaretBlinkTime()
|
||||
#define TICKSPERSEC 1000 /* GetTickCount returns milliseconds */
|
||||
|
||||
#define DEFAULT_CODEPAGE CP_ACP
|
||||
|
||||
typedef HDC Context;
|
||||
|
||||
/*
|
||||
* Window handles for the dialog boxes that can be running during a
|
||||
* PuTTY session.
|
||||
*/
|
||||
GLOBAL HWND logbox;
|
||||
|
||||
/*
|
||||
* The all-important instance handle.
|
||||
*/
|
||||
GLOBAL HINSTANCE hinst;
|
||||
|
||||
/*
|
||||
* Details of the help file.
|
||||
*/
|
||||
GLOBAL char *help_path;
|
||||
GLOBAL int help_has_contents;
|
||||
|
||||
/*
|
||||
* The terminal and logging context are notionally local to the
|
||||
* Windows front end, but they must be shared between window.c and
|
||||
* windlg.c. Likewise the saved-sessions list.
|
||||
*/
|
||||
GLOBAL Terminal *term;
|
||||
GLOBAL void *logctx;
|
||||
|
||||
/*
|
||||
* I've just looked in the windows standard headr files for WM_USER, there
|
||||
* are hundreds of flags defined using the form WM_USER+123 so I've
|
||||
* renumbered this NETEVENT value and the two in window.c
|
||||
*/
|
||||
#define WM_XUSER (WM_USER + 0x2000)
|
||||
#define WM_NETEVENT (WM_XUSER + 5)
|
||||
|
||||
/*
|
||||
* On Windows, we send MA_2CLK as the only event marking the second
|
||||
* press of a mouse button. Compare unix.h.
|
||||
*/
|
||||
#define MULTICLICK_ONLY_EVENT 1
|
||||
|
||||
/*
|
||||
* On Windows, data written to the clipboard must be NUL-terminated.
|
||||
*/
|
||||
#define SELECTION_NUL_TERMINATED 1
|
||||
|
||||
/*
|
||||
* On Windows, copying to the clipboard terminates lines with CRLF.
|
||||
*/
|
||||
#define SEL_NL { 13, 10 }
|
||||
|
||||
/*
|
||||
* sk_getxdmdata() does not exist under Windows (not that I
|
||||
* couldn't write it if I wanted to, but I haven't bothered), so
|
||||
* it's a macro which always returns FALSE. With any luck this will
|
||||
* cause the compiler to notice it can optimise away the
|
||||
* implementation of XDM-AUTHORIZATION-1 in x11fwd.c :-)
|
||||
*/
|
||||
#define sk_getxdmdata(socket, ip, port) (0)
|
||||
|
||||
/*
|
||||
* File-selector filter strings used in the config box. On Windows,
|
||||
* these strings are of exactly the type needed to go in
|
||||
* `lpstrFilter' in an OPENFILENAME structure.
|
||||
*/
|
||||
#define FILTER_KEY_FILES ("PuTTY Private Key Files (*.ppk)\0*.ppk\0" \
|
||||
"All Files (*.*)\0*\0\0\0")
|
||||
#define FILTER_WAVE_FILES ("Wave Files (*.wav)\0*.WAV\0" \
|
||||
"All Files (*.*)\0*\0\0\0")
|
||||
|
||||
/*
|
||||
* winnet.c dynamically loads WinSock 2 or WinSock 1 depending on
|
||||
* what it can get, which means any WinSock routines used outside
|
||||
* that module must be exported from it as function pointers. So
|
||||
* here they are.
|
||||
*/
|
||||
extern int (WINAPI *p_WSAAsyncSelect)
|
||||
(SOCKET s, HWND hWnd, u_int wMsg, long lEvent);
|
||||
extern int (WINAPI *p_WSAEventSelect)
|
||||
(SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
|
||||
extern int (WINAPI *p_select)
|
||||
(int nfds, fd_set FAR * readfds, fd_set FAR * writefds,
|
||||
fd_set FAR *exceptfds, const struct timeval FAR * timeout);
|
||||
extern int (WINAPI *p_WSAGetLastError)(void);
|
||||
extern int (WINAPI *p_WSAEnumNetworkEvents)
|
||||
(SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
|
||||
|
||||
/*
|
||||
* Exports from winctrls.c.
|
||||
*/
|
||||
|
||||
struct ctlpos {
|
||||
HWND hwnd;
|
||||
WPARAM font;
|
||||
int dlu4inpix;
|
||||
int ypos, width;
|
||||
int xoff;
|
||||
int boxystart, boxid;
|
||||
char *boxtext;
|
||||
};
|
||||
|
||||
/*
|
||||
* Exports from winutils.c.
|
||||
*/
|
||||
void split_into_argv(char *, int *, char ***, char ***);
|
||||
|
||||
/*
|
||||
* Private structure for prefslist state. Only in the header file
|
||||
* so that we can delegate allocation to callers.
|
||||
*/
|
||||
struct prefslist {
|
||||
int listid, upbid, dnbid;
|
||||
int srcitem;
|
||||
int dummyitem;
|
||||
int dragging;
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is passed to event handler functions as the `dlg'
|
||||
* parameter, and hence is passed back to winctrls access functions.
|
||||
*/
|
||||
struct dlgparam {
|
||||
HWND hwnd; /* the hwnd of the dialog box */
|
||||
struct winctrls *controltrees[8]; /* can have several of these */
|
||||
int nctrltrees;
|
||||
char *wintitle; /* title of actual window */
|
||||
char *errtitle; /* title of error sub-messageboxes */
|
||||
void *data; /* data to pass in refresh events */
|
||||
union control *focused, *lastfocused; /* which ctrl has focus now/before */
|
||||
char shortcuts[128]; /* track which shortcuts in use */
|
||||
int coloursel_wanted; /* has an event handler asked for
|
||||
* a colour selector? */
|
||||
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? */
|
||||
};
|
||||
|
||||
/*
|
||||
* Exports from winctrls.c.
|
||||
*/
|
||||
void ctlposinit(struct ctlpos *cp, HWND hwnd,
|
||||
int leftborder, int rightborder, int topborder);
|
||||
HWND doctl(struct ctlpos *cp, RECT r,
|
||||
char *wclass, int wstyle, int exstyle, char *wtext, int wid);
|
||||
void bartitle(struct ctlpos *cp, char *name, int id);
|
||||
void beginbox(struct ctlpos *cp, char *name, int idbox);
|
||||
void endbox(struct ctlpos *cp);
|
||||
void multiedit(struct ctlpos *cp, int password, ...);
|
||||
void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...);
|
||||
void bareradioline(struct ctlpos *cp, int nacross, ...);
|
||||
void radiobig(struct ctlpos *cp, char *text, int id, ...);
|
||||
void checkbox(struct ctlpos *cp, char *text, int id);
|
||||
void statictext(struct ctlpos *cp, char *text, int lines, int id);
|
||||
void staticbtn(struct ctlpos *cp, char *stext, int sid,
|
||||
char *btext, int bid);
|
||||
void static2btn(struct ctlpos *cp, char *stext, int sid,
|
||||
char *btext1, int bid1, char *btext2, int bid2);
|
||||
void staticedit(struct ctlpos *cp, char *stext,
|
||||
int sid, int eid, int percentedit);
|
||||
void staticddl(struct ctlpos *cp, char *stext,
|
||||
int sid, int lid, int percentlist);
|
||||
void combobox(struct ctlpos *cp, char *text, int staticid, int listid);
|
||||
void staticpassedit(struct ctlpos *cp, char *stext,
|
||||
int sid, int eid, int percentedit);
|
||||
void bigeditctrl(struct ctlpos *cp, char *stext,
|
||||
int sid, int eid, int lines);
|
||||
void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id);
|
||||
void editbutton(struct ctlpos *cp, char *stext, int sid,
|
||||
int eid, char *btext, int bid);
|
||||
void sesssaver(struct ctlpos *cp, char *text,
|
||||
int staticid, int editid, int listid, ...);
|
||||
void envsetter(struct ctlpos *cp, char *stext, int sid,
|
||||
char *e1stext, int e1sid, int e1id,
|
||||
char *e2stext, int e2sid, int e2id,
|
||||
int listid, char *b1text, int b1id, char *b2text, int b2id);
|
||||
void charclass(struct ctlpos *cp, char *stext, int sid, int listid,
|
||||
char *btext, int bid, int eid, char *s2text, int s2id);
|
||||
void colouredit(struct ctlpos *cp, char *stext, int sid, int listid,
|
||||
char *btext, int bid, ...);
|
||||
void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines,
|
||||
char *stext, int sid, int listid, int upbid, int dnbid);
|
||||
int handle_prefslist(struct prefslist *hdl,
|
||||
int *array, int maxmemb,
|
||||
int is_dlmsg, HWND hwnd,
|
||||
WPARAM wParam, LPARAM lParam);
|
||||
void progressbar(struct ctlpos *cp, int id);
|
||||
void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid,
|
||||
char *e1stext, int e1sid, int e1id,
|
||||
char *e2stext, int e2sid, int e2id,
|
||||
char *btext, int bid,
|
||||
char *r1text, int r1id, char *r2text, int r2id);
|
||||
|
||||
#define MAX_SHORTCUTS_PER_CTRL 16
|
||||
|
||||
/*
|
||||
* This structure is what's stored for each `union control' in the
|
||||
* portable-dialog interface.
|
||||
*/
|
||||
struct winctrl {
|
||||
union control *ctrl;
|
||||
/*
|
||||
* The control may have several components at the Windows
|
||||
* level, with different dialog IDs. To avoid needing N
|
||||
* separate platformsidectrl structures (which could be stored
|
||||
* separately in a tree234 so that lookup by ID worked), we
|
||||
* impose the constraint that those IDs must be in a contiguous
|
||||
* block.
|
||||
*/
|
||||
int base_id;
|
||||
int num_ids;
|
||||
/*
|
||||
* Remember what keyboard shortcuts were used by this control,
|
||||
* so that when we remove it again we can take them out of the
|
||||
* list in the dlgparam.
|
||||
*/
|
||||
char shortcuts[MAX_SHORTCUTS_PER_CTRL];
|
||||
/*
|
||||
* Some controls need a piece of allocated memory in which to
|
||||
* store temporary data about the control.
|
||||
*/
|
||||
void *data;
|
||||
};
|
||||
/*
|
||||
* And this structure holds a set of the above, in two separate
|
||||
* tree234s so that it can find an item by `union control' or by
|
||||
* dialog ID.
|
||||
*/
|
||||
struct winctrls {
|
||||
tree234 *byctrl, *byid;
|
||||
};
|
||||
struct controlset;
|
||||
struct controlbox;
|
||||
|
||||
void winctrl_init(struct winctrls *);
|
||||
void winctrl_cleanup(struct winctrls *);
|
||||
void winctrl_add(struct winctrls *, struct winctrl *);
|
||||
void winctrl_remove(struct winctrls *, struct winctrl *);
|
||||
struct winctrl *winctrl_findbyctrl(struct winctrls *, union control *);
|
||||
struct winctrl *winctrl_findbyid(struct winctrls *, int);
|
||||
struct winctrl *winctrl_findbyindex(struct winctrls *, int);
|
||||
void winctrl_layout(struct dlgparam *dp, struct winctrls *wc,
|
||||
struct ctlpos *cp, struct controlset *s, int *id);
|
||||
int winctrl_handle_command(struct dlgparam *dp, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam);
|
||||
void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c);
|
||||
int winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id);
|
||||
|
||||
void dp_init(struct dlgparam *dp);
|
||||
void dp_add_tree(struct dlgparam *dp, struct winctrls *tree);
|
||||
void dp_cleanup(struct dlgparam *dp);
|
||||
|
||||
/*
|
||||
* Exports from wincfg.c.
|
||||
*/
|
||||
void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help,
|
||||
int midsession);
|
||||
|
||||
/*
|
||||
* Exports from windlg.c.
|
||||
*/
|
||||
void defuse_showwindow(void);
|
||||
int do_config(void);
|
||||
int do_reconfig(HWND);
|
||||
void showeventlog(HWND);
|
||||
void showabout(HWND);
|
||||
void force_normal(HWND hwnd);
|
||||
void modal_about_box(HWND hwnd);
|
||||
void show_help(HWND hwnd);
|
||||
|
||||
/*
|
||||
* Exports from winmisc.c.
|
||||
*/
|
||||
|
||||
int SaneDialogBox(HINSTANCE hinst,
|
||||
LPCTSTR tmpl,
|
||||
HWND hwndparent,
|
||||
DLGPROC lpDialogFunc);
|
||||
|
||||
void SaneEndDialog(HWND hwnd, int ret);
|
||||
|
||||
extern OSVERSIONINFO osVersion;
|
||||
BOOL init_winver(void);
|
||||
|
||||
/*
|
||||
* Exports from sizetip.c.
|
||||
*/
|
||||
void UpdateSizeTip(HWND src, int cx, int cy);
|
||||
void EnableSizeTip(int bEnable);
|
||||
|
||||
/*
|
||||
* Exports from unicode.c.
|
||||
*/
|
||||
struct unicode_data;
|
||||
void init_ucs(Config *, struct unicode_data *);
|
||||
|
||||
/*
|
||||
* pageantc.c needs to schedule callbacks for asynchronous agent
|
||||
* requests. This has to be done differently in GUI and console, so
|
||||
* there's an exported function used for the purpose.
|
||||
*
|
||||
* Also, we supply FLAG_SYNCAGENT to force agent requests to be
|
||||
* synchronous in pscp and psftp.
|
||||
*/
|
||||
void agent_schedule_callback(void (*callback)(void *, void *, int),
|
||||
void *callback_ctx, void *data, int len);
|
||||
#define FLAG_SYNCAGENT 0x1000
|
||||
|
||||
#endif
|
1251
windows/winucs.c
Normal file
1251
windows/winucs.c
Normal file
File diff suppressed because it is too large
Load Diff
463
windows/winutils.c
Normal file
463
windows/winutils.c
Normal file
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* winutils.c: miscellaneous Windows utilities for GUI apps
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
#ifdef TESTMODE
|
||||
/* Definitions to allow this module to be compiled standalone for testing. */
|
||||
#define smalloc malloc
|
||||
#define srealloc realloc
|
||||
#define sfree free
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Split a complete command line into argc/argv, attempting to do
|
||||
* it exactly the same way Windows itself would do it (so that
|
||||
* console utilities, which receive argc and argv from Windows,
|
||||
* will have their command lines processed in the same way as GUI
|
||||
* utilities which get a whole command line and must break it
|
||||
* themselves).
|
||||
*
|
||||
* Does not modify the input command line.
|
||||
*
|
||||
* The final parameter (argstart) is used to return a second array
|
||||
* of char * pointers, the same length as argv, each one pointing
|
||||
* at the start of the corresponding element of argv in the
|
||||
* original command line. So if you get half way through processing
|
||||
* your command line in argc/argv form and then decide you want to
|
||||
* treat the rest as a raw string, you can. If you don't want to,
|
||||
* `argstart' can be safely left NULL.
|
||||
*/
|
||||
void split_into_argv(char *cmdline, int *argc, char ***argv,
|
||||
char ***argstart)
|
||||
{
|
||||
char *p;
|
||||
char *outputline, *q;
|
||||
char **outputargv, **outputargstart;
|
||||
int outputargc;
|
||||
|
||||
/*
|
||||
* At first glance the rules appeared to be:
|
||||
*
|
||||
* - Single quotes are not special characters.
|
||||
*
|
||||
* - Double quotes are removed, but within them spaces cease
|
||||
* to be special.
|
||||
*
|
||||
* - Backslashes are _only_ special when a sequence of them
|
||||
* appear just before a double quote. In this situation,
|
||||
* they are treated like C backslashes: so \" just gives a
|
||||
* literal quote, \\" gives a literal backslash and then
|
||||
* opens or closes a double-quoted segment, \\\" gives a
|
||||
* literal backslash and then a literal quote, \\\\" gives
|
||||
* two literal backslashes and then opens/closes a
|
||||
* double-quoted segment, and so forth. Note that this
|
||||
* behaviour is identical inside and outside double quotes.
|
||||
*
|
||||
* - Two successive double quotes become one literal double
|
||||
* quote, but only _inside_ a double-quoted segment.
|
||||
* Outside, they just form an empty double-quoted segment
|
||||
* (which may cause an empty argument word).
|
||||
*
|
||||
* - That only leaves the interesting question of what happens
|
||||
* when one or more backslashes precedes two or more double
|
||||
* quotes, starting inside a double-quoted string. And the
|
||||
* answer to that appears somewhat bizarre. Here I tabulate
|
||||
* number of backslashes (across the top) against number of
|
||||
* quotes (down the left), and indicate how many backslashes
|
||||
* are output, how many quotes are output, and whether a
|
||||
* quoted segment is open at the end of the sequence:
|
||||
*
|
||||
* backslashes
|
||||
*
|
||||
* 0 1 2 3 4
|
||||
*
|
||||
* 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y
|
||||
* --------+-----------------------------
|
||||
* 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n
|
||||
* q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n
|
||||
* u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y
|
||||
* o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n
|
||||
* t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n
|
||||
* e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y
|
||||
* s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n
|
||||
* 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n
|
||||
* 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y
|
||||
* 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n
|
||||
* 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n
|
||||
*
|
||||
*
|
||||
* [Test fragment was of the form "a\\\"""b c" d.]
|
||||
*
|
||||
* There is very weird mod-3 behaviour going on here in the
|
||||
* number of quotes, and it even applies when there aren't any
|
||||
* backslashes! How ghastly.
|
||||
*
|
||||
* With a bit of thought, this extremely odd diagram suddenly
|
||||
* coalesced itself into a coherent, if still ghastly, model of
|
||||
* how things work:
|
||||
*
|
||||
* - As before, backslashes are only special when one or more
|
||||
* of them appear contiguously before at least one double
|
||||
* quote. In this situation the backslashes do exactly what
|
||||
* you'd expect: each one quotes the next thing in front of
|
||||
* it, so you end up with n/2 literal backslashes (if n is
|
||||
* even) or (n-1)/2 literal backslashes and a literal quote
|
||||
* (if n is odd). In the latter case the double quote
|
||||
* character right after the backslashes is used up.
|
||||
*
|
||||
* - After that, any remaining double quotes are processed. A
|
||||
* string of contiguous unescaped double quotes has a mod-3
|
||||
* behaviour:
|
||||
*
|
||||
* * inside a quoted segment, a quote ends the segment.
|
||||
* * _immediately_ after ending a quoted segment, a quote
|
||||
* simply produces a literal quote.
|
||||
* * otherwise, outside a quoted segment, a quote begins a
|
||||
* quoted segment.
|
||||
*
|
||||
* So, for example, if we started inside a quoted segment
|
||||
* then two contiguous quotes would close the segment and
|
||||
* produce a literal quote; three would close the segment,
|
||||
* produce a literal quote, and open a new segment. If we
|
||||
* started outside a quoted segment, then two contiguous
|
||||
* quotes would open and then close a segment, producing no
|
||||
* output (but potentially creating a zero-length argument);
|
||||
* but three quotes would open and close a segment and then
|
||||
* produce a literal quote.
|
||||
*/
|
||||
|
||||
/*
|
||||
* First deal with the simplest of all special cases: if there
|
||||
* aren't any arguments, return 0,NULL,NULL.
|
||||
*/
|
||||
while (*cmdline && isspace(*cmdline)) cmdline++;
|
||||
if (!*cmdline) {
|
||||
if (argc) *argc = 0;
|
||||
if (argv) *argv = NULL;
|
||||
if (argstart) *argstart = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will guaranteeably be big enough; we can realloc it
|
||||
* down later.
|
||||
*/
|
||||
outputline = snewn(1+strlen(cmdline), char);
|
||||
outputargv = snewn(strlen(cmdline)+1 / 2, char *);
|
||||
outputargstart = snewn(strlen(cmdline)+1 / 2, char *);
|
||||
|
||||
p = cmdline; q = outputline; outputargc = 0;
|
||||
|
||||
while (*p) {
|
||||
int quote;
|
||||
|
||||
/* Skip whitespace searching for start of argument. */
|
||||
while (*p && isspace(*p)) p++;
|
||||
if (!*p) break;
|
||||
|
||||
/* We have an argument; start it. */
|
||||
outputargv[outputargc] = q;
|
||||
outputargstart[outputargc] = p;
|
||||
outputargc++;
|
||||
quote = 0;
|
||||
|
||||
/* Copy data into the argument until it's finished. */
|
||||
while (*p) {
|
||||
if (!quote && isspace(*p))
|
||||
break; /* argument is finished */
|
||||
|
||||
if (*p == '"' || *p == '\\') {
|
||||
/*
|
||||
* We have a sequence of zero or more backslashes
|
||||
* followed by a sequence of zero or more quotes.
|
||||
* Count up how many of each, and then deal with
|
||||
* them as appropriate.
|
||||
*/
|
||||
int i, slashes = 0, quotes = 0;
|
||||
while (*p == '\\') slashes++, p++;
|
||||
while (*p == '"') quotes++, p++;
|
||||
|
||||
if (!quotes) {
|
||||
/*
|
||||
* Special case: if there are no quotes,
|
||||
* slashes are not special at all, so just copy
|
||||
* n slashes to the output string.
|
||||
*/
|
||||
while (slashes--) *q++ = '\\';
|
||||
} else {
|
||||
/* Slashes annihilate in pairs. */
|
||||
while (slashes >= 2) slashes -= 2, *q++ = '\\';
|
||||
|
||||
/* One remaining slash takes out the first quote. */
|
||||
if (slashes) quotes--, *q++ = '"';
|
||||
|
||||
if (quotes > 0) {
|
||||
/* Outside a quote segment, a quote starts one. */
|
||||
if (!quote) quotes--, quote = 1;
|
||||
|
||||
/* Now we produce (n+1)/3 literal quotes... */
|
||||
for (i = 3; i <= quotes+1; i += 3) *q++ = '"';
|
||||
|
||||
/* ... and end in a quote segment iff 3 divides n. */
|
||||
quote = (quotes % 3 == 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*q++ = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* At the end of an argument, just append a trailing NUL. */
|
||||
*q++ = '\0';
|
||||
}
|
||||
|
||||
outputargv = sresize(outputargv, outputargc, char *);
|
||||
outputargstart = sresize(outputargstart, outputargc, char *);
|
||||
|
||||
if (argc) *argc = outputargc;
|
||||
if (argv) *argv = outputargv; else sfree(outputargv);
|
||||
if (argstart) *argstart = outputargstart; else sfree(outputargstart);
|
||||
}
|
||||
|
||||
#ifdef TESTMODE
|
||||
|
||||
const struct argv_test {
|
||||
const char *cmdline;
|
||||
const char *argv[10];
|
||||
} argv_tests[] = {
|
||||
/*
|
||||
* We generate this set of tests by invoking ourself with
|
||||
* `-generate'.
|
||||
*/
|
||||
{"ab c\" d", {"ab", "c d", NULL}},
|
||||
{"a\"b c\" d", {"ab c", "d", NULL}},
|
||||
{"a\"\"b c\" d", {"ab", "c d", NULL}},
|
||||
{"a\"\"\"b c\" d", {"a\"b", "c d", NULL}},
|
||||
{"a\"\"\"\"b c\" d", {"a\"b c", "d", NULL}},
|
||||
{"a\"\"\"\"\"b c\" d", {"a\"b", "c d", NULL}},
|
||||
{"a\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},
|
||||
{"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"a\\b c\" d", {"a\\b", "c d", NULL}},
|
||||
{"a\\\"b c\" d", {"a\"b", "c d", NULL}},
|
||||
{"a\\\"\"b c\" d", {"a\"b c", "d", NULL}},
|
||||
{"a\\\"\"\"b c\" d", {"a\"b", "c d", NULL}},
|
||||
{"a\\\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"a\\\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},
|
||||
{"a\\\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},
|
||||
{"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}},
|
||||
{"a\\\\b c\" d", {"a\\\\b", "c d", NULL}},
|
||||
{"a\\\\\"b c\" d", {"a\\b c", "d", NULL}},
|
||||
{"a\\\\\"\"b c\" d", {"a\\b", "c d", NULL}},
|
||||
{"a\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},
|
||||
{"a\\\\\"\"\"\"b c\" d", {"a\\\"b c", "d", NULL}},
|
||||
{"a\\\\\"\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},
|
||||
{"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},
|
||||
{"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"a\\\\\\b c\" d", {"a\\\\\\b", "c d", NULL}},
|
||||
{"a\\\\\\\"b c\" d", {"a\\\"b", "c d", NULL}},
|
||||
{"a\\\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}},
|
||||
{"a\\\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},
|
||||
{"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},
|
||||
{"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},
|
||||
{"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}},
|
||||
{"a\\\\\\\\b c\" d", {"a\\\\\\\\b", "c d", NULL}},
|
||||
{"a\\\\\\\\\"b c\" d", {"a\\\\b c", "d", NULL}},
|
||||
{"a\\\\\\\\\"\"b c\" d", {"a\\\\b", "c d", NULL}},
|
||||
{"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},
|
||||
{"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}},
|
||||
{"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},
|
||||
{"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},
|
||||
{"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}},
|
||||
{"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},
|
||||
{"\"ab c\" d", {"ab c", "d", NULL}},
|
||||
{"\"a\"b c\" d", {"ab", "c d", NULL}},
|
||||
{"\"a\"\"b c\" d", {"a\"b", "c d", NULL}},
|
||||
{"\"a\"\"\"b c\" d", {"a\"b c", "d", NULL}},
|
||||
{"\"a\"\"\"\"b c\" d", {"a\"b", "c d", NULL}},
|
||||
{"\"a\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"\"a\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},
|
||||
{"\"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"\"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},
|
||||
{"\"a\\b c\" d", {"a\\b c", "d", NULL}},
|
||||
{"\"a\\\"b c\" d", {"a\"b c", "d", NULL}},
|
||||
{"\"a\\\"\"b c\" d", {"a\"b", "c d", NULL}},
|
||||
{"\"a\\\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"\"a\\\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}},
|
||||
{"\"a\\\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}},
|
||||
{"\"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},
|
||||
{"\"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}},
|
||||
{"\"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\b c\" d", {"a\\\\b c", "d", NULL}},
|
||||
{"\"a\\\\\"b c\" d", {"a\\b", "c d", NULL}},
|
||||
{"\"a\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}},
|
||||
{"\"a\\\\\"\"\"b c\" d", {"a\\\"b c", "d", NULL}},
|
||||
{"\"a\\\\\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}},
|
||||
{"\"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},
|
||||
{"\"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\b c\" d", {"a\\\\\\b c", "d", NULL}},
|
||||
{"\"a\\\\\\\"b c\" d", {"a\\\"b c", "d", NULL}},
|
||||
{"\"a\\\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}},
|
||||
{"\"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}},
|
||||
{"\"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\\b c\" d", {"a\\\\\\\\b c", "d", NULL}},
|
||||
{"\"a\\\\\\\\\"b c\" d", {"a\\\\b", "c d", NULL}},
|
||||
{"\"a\\\\\\\\\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}},
|
||||
{"\"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}},
|
||||
{"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}},
|
||||
{"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}},
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (argc > 1) {
|
||||
/*
|
||||
* Generation of tests.
|
||||
*
|
||||
* Given `-splat <args>', we print out a C-style
|
||||
* representation of each argument (in the form "a", "b",
|
||||
* NULL), backslash-escaping each backslash and double
|
||||
* quote.
|
||||
*
|
||||
* Given `-split <string>', we first doctor `string' by
|
||||
* turning forward slashes into backslashes, single quotes
|
||||
* into double quotes and underscores into spaces; and then
|
||||
* we feed the resulting string to ourself with `-splat'.
|
||||
*
|
||||
* Given `-generate', we concoct a variety of fun test
|
||||
* cases, encode them in quote-safe form (mapping \, " and
|
||||
* space to /, ' and _ respectively) and feed each one to
|
||||
* `-split'.
|
||||
*/
|
||||
if (!strcmp(argv[1], "-splat")) {
|
||||
int i;
|
||||
char *p;
|
||||
for (i = 2; i < argc; i++) {
|
||||
putchar('"');
|
||||
for (p = argv[i]; *p; p++) {
|
||||
if (*p == '\\' || *p == '"')
|
||||
putchar('\\');
|
||||
putchar(*p);
|
||||
}
|
||||
printf("\", ");
|
||||
}
|
||||
printf("NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "-split") && argc > 2) {
|
||||
char *str = malloc(20 + strlen(argv[0]) + strlen(argv[2]));
|
||||
char *p, *q;
|
||||
|
||||
q = str + sprintf(str, "%s -splat ", argv[0]);
|
||||
printf(" {\"");
|
||||
for (p = argv[2]; *p; p++, q++) {
|
||||
switch (*p) {
|
||||
case '/': printf("\\\\"); *q = '\\'; break;
|
||||
case '\'': printf("\\\""); *q = '"'; break;
|
||||
case '_': printf(" "); *q = ' '; break;
|
||||
default: putchar(*p); *q = *p; break;
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
printf("\", {");
|
||||
fflush(stdout);
|
||||
|
||||
system(str);
|
||||
|
||||
printf("}},\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "-generate")) {
|
||||
char *teststr, *p;
|
||||
int i, initialquote, backslashes, quotes;
|
||||
|
||||
teststr = malloc(200 + strlen(argv[0]));
|
||||
|
||||
for (initialquote = 0; initialquote <= 1; initialquote++) {
|
||||
for (backslashes = 0; backslashes < 5; backslashes++) {
|
||||
for (quotes = 0; quotes < 9; quotes++) {
|
||||
p = teststr + sprintf(teststr, "%s -split ", argv[0]);
|
||||
if (initialquote) *p++ = '\'';
|
||||
*p++ = 'a';
|
||||
for (i = 0; i < backslashes; i++) *p++ = '/';
|
||||
for (i = 0; i < quotes; i++) *p++ = '\'';
|
||||
*p++ = 'b';
|
||||
*p++ = '_';
|
||||
*p++ = 'c';
|
||||
*p++ = '\'';
|
||||
*p++ = '_';
|
||||
*p++ = 'd';
|
||||
*p = '\0';
|
||||
|
||||
system(teststr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "unrecognised option: \"%s\"\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get here, we were invoked with no arguments, so just
|
||||
* run the tests.
|
||||
*/
|
||||
|
||||
for (i = 0; i < lenof(argv_tests); i++) {
|
||||
int ac;
|
||||
char **av;
|
||||
|
||||
split_into_argv(argv_tests[i].cmdline, &ac, &av);
|
||||
|
||||
for (j = 0; j < ac && argv_tests[i].argv[j]; j++) {
|
||||
if (strcmp(av[j], argv_tests[i].argv[j])) {
|
||||
printf("failed test %d (|%s|) arg %d: |%s| should be |%s|\n",
|
||||
i, argv_tests[i].cmdline,
|
||||
j, av[j], argv_tests[i].argv[j]);
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
else {
|
||||
printf("test %d (|%s|) arg %d: |%s| == |%s|\n",
|
||||
i, argv_tests[i].cmdline,
|
||||
j, av[j], argv_tests[i].argv[j]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (j < ac)
|
||||
printf("failed test %d (|%s|): %d args returned, should be %d\n",
|
||||
i, argv_tests[i].cmdline, ac, j);
|
||||
if (argv_tests[i].argv[j])
|
||||
printf("failed test %d (|%s|): %d args returned, should be more\n",
|
||||
i, argv_tests[i].cmdline, ac);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user