terminal/src/terminal/adapter/adaptDispatch.hpp
James Holderness 6742965bb8
Disable the acceptance of C1 control codes by default (#11690)
There are some code pages with "unmapped" code points in the C1 range,
which results in them being translated into Unicode C1 control codes,
even though that is not their intended use. To avoid having these
characters triggering unintentional escape sequences, this PR now
disables C1 controls by default.

Switching to ISO-2022 encoding will re-enable them, though, since that
is the most likely scenario in which they would be required. They can
also be explicitly enabled, even in UTF-8 mode, with the `DECAC1` escape
sequence.

What I've done is add a new mode to the `StateMachine` class that
controls whether C1 code points are interpreted as control characters or
not. When disabled, these code points are simply dropped from the
output, similar to the way a `NUL` is interpreted.

This isn't exactly the way they were handled in the v1 console (which I
think replaces them with the font _notdef_ glyph), but it matches the
XTerm behavior, which seems more appropriate considering this is in VT
mode. And it's worth noting that Windows Explorer seems to work the same
way.

As mentioned above, the mode can be enabled by designating the ISO-2022
coding system with a `DOCS` sequence, and it will be disabled again when
UTF-8 is designated. You can also enable it explicitly with a `DECAC1`
sequence (originally this was actually a DEC printer sequence, but it
doesn't seem unreasonable to use it in a terminal).

I've also extended the operations that save and restore "cursor state"
(e.g. `DECSC` and `DECRC`) to include the state of the C1 parser mode,
since it's closely tied to the code page and character sets which are
also saved there. Similarly, when a `DECSTR` sequence resets the code
page and character sets, I've now made it reset the C1 mode as well.

I should note that the new `StateMachine` mode is controlled via a
generic `SetParserMode` method (with a matching API in the `ConGetSet`
interface) to allow for easier addition of other modes in the future.
And I've reimplemented the existing ANSI/VT52 mode in terms of these
generic methods instead of it having to have its own separate APIs.

## Validation Steps Performed

Some of the unit tests for OSC sequences were using a C1 `0x9C` for the
string terminator, which doesn't work by default anymore. Since that's
not a good practice anyway, I thought it best to change those to a
standard 7-bit terminator. However, in tests that were explicitly
validating the C1 controls, I've just enabled the C1 parser mode at the
start of the tests in order to get them working again.

There were also some ANSI mode adapter tests that had to be updated to
account for the fact that it has now been reimplemented in terms of the
`SetParserMode` API.

I've added a new state machine test to validate the changes in behavior
when the C1 parser mode is enabled or disabled. And I've added an
adapter test to verify that the `DesignateCodingSystems` and
`AcceptC1Controls` methods toggle the C1 parser mode as expected.

I've manually verified the test cases in #10069 and #10310 to confirm
that they're no longer triggering control sequences by default.
Although, as I explained above, the C1 code points are completely
dropped from the output rather than displayed as _notdef_ glyphs. I
think this is a reasonable compromise though.

Closes #10069
Closes #10310
2021-11-17 23:40:31 +00:00

229 lines
11 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- adaptDispatch.hpp
Abstract:
- This serves as the Windows Console API-specific implementation of the callbacks from our generic Virtual Terminal parser.
Author(s):
- Michael Niksa (MiNiksa) 30-July-2015
--*/
#pragma once
#include "termDispatch.hpp"
#include "DispatchCommon.hpp"
#include "conGetSet.hpp"
#include "adaptDefaults.hpp"
#include "FontBuffer.hpp"
#include "terminalOutput.hpp"
#include "..\..\types\inc\sgrStack.hpp"
namespace Microsoft::Console::VirtualTerminal
{
class AdaptDispatch : public ITermDispatch
{
public:
AdaptDispatch(std::unique_ptr<ConGetSet> pConApi,
std::unique_ptr<AdaptDefaults> pDefaults);
void Execute(const wchar_t wchControl) override
{
_pDefaults->Execute(wchControl);
}
void PrintString(const std::wstring_view string) override;
void Print(const wchar_t wchPrintable) override;
bool CursorUp(const size_t distance) override; // CUU
bool CursorDown(const size_t distance) override; // CUD
bool CursorForward(const size_t distance) override; // CUF
bool CursorBackward(const size_t distance) override; // CUB, BS
bool CursorNextLine(const size_t distance) override; // CNL
bool CursorPrevLine(const size_t distance) override; // CPL
bool CursorHorizontalPositionAbsolute(const size_t column) override; // HPA, CHA
bool VerticalLinePositionAbsolute(const size_t line) override; // VPA
bool HorizontalPositionRelative(const size_t distance) override; // HPR
bool VerticalPositionRelative(const size_t distance) override; // VPR
bool CursorPosition(const size_t line, const size_t column) override; // CUP, HVP
bool CursorSaveState() override; // DECSC
bool CursorRestoreState() override; // DECRC
bool CursorVisibility(const bool isVisible) override; // DECTCEM
bool EraseInDisplay(const DispatchTypes::EraseType eraseType) override; // ED
bool EraseInLine(const DispatchTypes::EraseType eraseType) override; // EL
bool EraseCharacters(const size_t numChars) override; // ECH
bool InsertCharacter(const size_t count) override; // ICH
bool DeleteCharacter(const size_t count) override; // DCH
bool SetGraphicsRendition(const VTParameters options) override; // SGR
bool SetLineRendition(const LineRendition rendition) override; // DECSWL, DECDWL, DECDHL
bool PushGraphicsRendition(const VTParameters options) override; // XTPUSHSGR
bool PopGraphicsRendition() override; // XTPOPSGR
bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) override; // DSR, DSR-OS, DSR-CPR
bool DeviceAttributes() override; // DA1
bool SecondaryDeviceAttributes() override; // DA2
bool TertiaryDeviceAttributes() override; // DA3
bool Vt52DeviceAttributes() override; // VT52 Identify
bool RequestTerminalParameters(const DispatchTypes::ReportingPermission permission) override; // DECREQTPARM
bool ScrollUp(const size_t distance) override; // SU
bool ScrollDown(const size_t distance) override; // SD
bool InsertLine(const size_t distance) override; // IL
bool DeleteLine(const size_t distance) override; // DL
bool SetColumns(const size_t columns) override; // DECCOLM
bool SetMode(const DispatchTypes::ModeParams param) override; // DECSET
bool ResetMode(const DispatchTypes::ModeParams param) override; // DECRST
bool SetCursorKeysMode(const bool applicationMode) override; // DECCKM
bool SetKeypadMode(const bool applicationMode) override; // DECKPAM, DECKPNM
bool EnableWin32InputMode(const bool win32InputMode) override; // win32-input-mode
bool EnableCursorBlinking(const bool enable) override; // ATT610
bool SetAnsiMode(const bool ansiMode) override; // DECANM
bool SetScreenMode(const bool reverseMode) override; // DECSCNM
bool SetOriginMode(const bool relativeMode) noexcept override; // DECOM
bool SetAutoWrapMode(const bool wrapAtEOL) override; // DECAWM
bool SetTopBottomScrollingMargins(const size_t topMargin,
const size_t bottomMargin) override; // DECSTBM
bool WarningBell() override; // BEL
bool CarriageReturn() override; // CR
bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) override; // IND, NEL, LF, FF, VT
bool ReverseLineFeed() override; // RI
bool SetWindowTitle(const std::wstring_view title) override; // OSCWindowTitle
bool UseAlternateScreenBuffer() override; // ASBSET
bool UseMainScreenBuffer() override; // ASBRST
bool HorizontalTabSet() override; // HTS
bool ForwardTab(const size_t numTabs) override; // CHT, HT
bool BackwardsTab(const size_t numTabs) override; // CBT
bool TabClear(const DispatchTypes::TabClearType clearType) override; // TBC
bool DesignateCodingSystem(const VTID codingSystem) override; // DOCS
bool Designate94Charset(const size_t gsetNumber, const VTID charset) override; // SCS
bool Designate96Charset(const size_t gsetNumber, const VTID charset) override; // SCS
bool LockingShift(const size_t gsetNumber) override; // LS0, LS1, LS2, LS3
bool LockingShiftRight(const size_t gsetNumber) override; // LS1R, LS2R, LS3R
bool SingleShift(const size_t gsetNumber) override; // SS2, SS3
bool AcceptC1Controls(const bool enabled) override; // DECAC1
bool SoftReset() override; // DECSTR
bool HardReset() override; // RIS
bool ScreenAlignmentPattern() override; // DECALN
bool EnableDECCOLMSupport(const bool enabled) noexcept override; // ?40
bool EnableVT200MouseMode(const bool enabled) override; // ?1000
bool EnableUTF8ExtendedMouseMode(const bool enabled) override; // ?1005
bool EnableSGRExtendedMouseMode(const bool enabled) override; // ?1006
bool EnableButtonEventMouseMode(const bool enabled) override; // ?1002
bool EnableAnyEventMouseMode(const bool enabled) override; // ?1003
bool EnableAlternateScroll(const bool enabled) override; // ?1007
bool EnableXtermBracketedPasteMode(const bool enabled) noexcept override; // ?2004
bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
bool SetCursorColor(const COLORREF cursorColor) override;
bool SetClipboard(const std::wstring_view content) noexcept override; // OSCSetClipboard
bool SetColorTableEntry(const size_t tableIndex,
const DWORD color) override; // OSCColorTable
bool SetDefaultForeground(const DWORD color) override; // OSCDefaultForeground
bool SetDefaultBackground(const DWORD color) override; // OSCDefaultBackground
bool WindowManipulation(const DispatchTypes::WindowManipulationType function,
const VTParameter parameter1,
const VTParameter parameter2) override; // DTTERM_WindowManipulation
bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) override;
bool EndHyperlink() override;
bool DoConEmuAction(const std::wstring_view string) noexcept override;
StringHandler DownloadDRCS(const size_t fontNumber,
const VTParameter startChar,
const DispatchTypes::DrcsEraseControl eraseControl,
const DispatchTypes::DrcsCellMatrix cellMatrix,
const DispatchTypes::DrcsFontSet fontSet,
const DispatchTypes::DrcsFontUsage fontUsage,
const VTParameter cellHeight,
const DispatchTypes::DrcsCharsetSize charsetSize) override; // DECDLD
StringHandler RequestSetting() override; // DECRQSS
private:
enum class ScrollDirection
{
Up,
Down
};
struct CursorState
{
unsigned int Row = 1;
unsigned int Column = 1;
bool IsOriginModeRelative = false;
TextAttribute Attributes = {};
TerminalOutput TermOutput = {};
bool C1ControlsAccepted = false;
unsigned int CodePage = 0;
};
struct Offset
{
int Value;
bool IsAbsolute;
// VT origin is at 1,1 so we need to subtract 1 from absolute positions.
static constexpr Offset Absolute(const size_t value) { return { gsl::narrow_cast<int>(value) - 1, true }; };
static constexpr Offset Forward(const size_t value) { return { gsl::narrow_cast<int>(value), false }; };
static constexpr Offset Backward(const size_t value) { return { -gsl::narrow_cast<int>(value), false }; };
static constexpr Offset Unchanged() { return Forward(0); };
};
bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) const;
bool _EraseSingleLineHelper(const CONSOLE_SCREEN_BUFFER_INFOEX& csbiex,
const DispatchTypes::EraseType eraseType,
const size_t lineId) const;
bool _EraseScrollback();
bool _EraseAll();
bool _InsertDeleteHelper(const size_t count, const bool isInsert) const;
bool _ScrollMovement(const ScrollDirection dir, const size_t distance) const;
bool _DoSetTopBottomScrollingMargins(const size_t topMargin,
const size_t bottomMargin);
bool _OperatingStatus() const;
bool _CursorPositionReport() const;
bool _WriteResponse(const std::wstring_view reply) const;
bool _ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable);
bool _DoDECCOLMHelper(const size_t columns);
bool _ClearSingleTabStop();
bool _ClearAllTabStops() noexcept;
void _ResetTabStops() noexcept;
void _InitTabStopsForWidth(const size_t width);
void _ReportSGRSetting() const;
void _ReportDECSTBMSetting() const;
std::vector<bool> _tabStopColumns;
bool _initDefaultTabStops = true;
std::unique_ptr<ConGetSet> _pConApi;
std::unique_ptr<AdaptDefaults> _pDefaults;
TerminalOutput _termOutput;
std::unique_ptr<FontBuffer> _fontBuffer;
std::optional<unsigned int> _initialCodePage;
// We have two instances of the saved cursor state, because we need
// one for the main buffer (at index 0), and another for the alt buffer
// (at index 1). The _usingAltBuffer property keeps tracks of which
// buffer is active, so can be used as an index into this array to
// obtain the saved state that should be currently active.
std::array<CursorState, 2> _savedCursorState;
bool _usingAltBuffer;
SMALL_RECT _scrollMargins;
bool _isOriginModeRelative;
bool _isDECCOLMAllowed;
SgrStack _sgrStack;
size_t _SetRgbColorsHelper(const VTParameters options,
TextAttribute& attr,
const bool isForeground) noexcept;
};
}