rethink proxy with no credentials

This commit is contained in:
speyrefitte
2014-07-31 10:52:17 +02:00
parent 54b5ef3d8a
commit a4f799d175
4 changed files with 127 additions and 219 deletions

View File

@@ -33,58 +33,6 @@ from rdpy.ui import view
from twisted.internet import reactor from twisted.internet import reactor
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
class IProxyClient(object):
"""
Interface use by Proxy server to interact with client
"""
def close(self):
"""
Close proxy client
"""
raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getColorDepth", "IProxyClient"))
def getColorDepth(self):
"""
Color depth client, Use to re-negociate color depth with server
"""
raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getColorDepth", "IProxyClient"))
def sendKeyEventScancode(self, code, isPressed):
"""
Key event as scan code
@param code: scan code of key
@param isPressed: True if key is down
"""
raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEventScancode", "IProxyClient"))
def sendKeyEventUnicode(self, code, isPressed):
"""
Key event as unicode
@param code: unicode of key
@param isPressed: True if key is down
"""
raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEventUnicode", "IProxyClient"))
def sendPointerEvent(self, x, y, button, isPressed):
"""
Mouse event
@param x: x position
@param y: y position
@param isPressed: True if button is down
"""
raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendPointerEvent", "IProxyClient"))
def sendRefreshOrder(self, left, top, right, bottom):
"""
Refresh zone
@param left: left postion
@param top: top position
@param right: right position
@param bottom: bottom position
"""
raise error.CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendRefreshOrder", "IProxyClient"))
class ProxyServer(rdp.RDPServerObserver): class ProxyServer(rdp.RDPServerObserver):
""" """
Server side of proxy Server side of proxy
@@ -104,21 +52,17 @@ class ProxyServer(rdp.RDPServerObserver):
@param client: ProxyClient @param client: ProxyClient
""" """
self._client = client self._client = client
self._controller.setColorDepth(self._client.getColorDepth()) self._controller.setColorDepth(self._client._controller.getColorDepth())
def onReady(self): def onReady(self):
""" """
Event use to inform state of server stack Event use to inform state of server stack
Use to connect client Use to connect client
On ready is not launch only on connection but after a reactivation process
""" """
if self._client is None: if self._client is None:
#try a connection #try a connection
domain, username, password = self._controller.getCredentials() domain, username, password = self._controller.getCredentials()
if self._credentialProvider.isAdmin(domain, username, password):
self.clientConnected(ProxyAdmin(self))
return
try: try:
ip, port = self._credentialProvider.getProxyPass(domain, username) ip, port = self._credentialProvider.getProxyPass(domain, username)
except error.InvalidExpectedDataException as e: except error.InvalidExpectedDataException as e:
@@ -130,14 +74,15 @@ class ProxyServer(rdp.RDPServerObserver):
else: else:
#refresh client #refresh client
width, height = self._controller.getScreen() width, height = self._controller.getScreen()
self._client.sendRefreshOrder(0, 0, width, height) self._client._controller.sendRefreshOrder(0, 0, width, height)
def onClose(self): def onClose(self):
""" """
Call when client close connection Call when client close connection
""" """
if not self._client is None: if self._client is None:
self._client.close() return
self._client._controller.close()
def onKeyEventScancode(self, code, isPressed): def onKeyEventScancode(self, code, isPressed):
""" """
@@ -148,8 +93,7 @@ class ProxyServer(rdp.RDPServerObserver):
#no client connected #no client connected
if self._client is None: if self._client is None:
return return
self._client._controller.sendKeyEventScancode(code, isPressed)
self._client.sendKeyEventScancode(code, isPressed)
def onKeyEventUnicode(self, code, isPressed): def onKeyEventUnicode(self, code, isPressed):
""" """
@@ -160,8 +104,7 @@ class ProxyServer(rdp.RDPServerObserver):
#no client connected domain #no client connected domain
if self._client is None: if self._client is None:
return return
self._client._controller.sendKeyEventUnicode(code, isPressed)
self._client.sendKeyEventUnicode(code, isPressed)
def onPointerEvent(self, x, y, button, isPressed): def onPointerEvent(self, x, y, button, isPressed):
""" """
@@ -174,10 +117,28 @@ class ProxyServer(rdp.RDPServerObserver):
#no client connected #no client connected
if self._client is None: if self._client is None:
return return
self._client._controller.sendPointerEvent(x, y, button, isPressed)
self._client.sendPointerEvent(x, y, button, isPressed) class ProxyServerFactory(rdp.ServerFactory):
"""
Factory on listening events
"""
def __init__(self, credentialProvider, privateKeyFilePath, certificateFilePath):
"""
@param config: rdp-proxy configuration
@param credentialProvider: CredentialProvider
"""
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
self._credentialProvider = credentialProvider
def buildObserver(self, controller):
"""
Implement rdp.ServerFactory
@param controller: rdp.RDPServerController
"""
return ProxyServer(controller, self._credentialProvider)
class ProxyClient(rdp.RDPClientObserver, IProxyClient): class ProxyClient(rdp.RDPClientObserver):
""" """
Client side of proxy Client side of proxy
""" """
@@ -209,12 +170,6 @@ class ProxyClient(rdp.RDPClientObserver, IProxyClient):
del ProxyClient._CONNECTED_[self._name] del ProxyClient._CONNECTED_[self._name]
self._server._controller.close() self._server._controller.close()
def close(self):
"""
Close proxy client
"""
self._controller.close()
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
""" """
Event use to inform bitmap update Event use to inform bitmap update
@@ -230,66 +185,6 @@ class ProxyClient(rdp.RDPClientObserver, IProxyClient):
@param data: bitmap data @param data: bitmap data
""" """
self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data) self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data)
def getColorDepth(self):
"""
Color depth client, Use to re-negociate color depth with server
"""
return self._controller.getColorDepth()
def sendKeyEventScancode(self, code, isPressed):
"""
Key event as scan code
@param code: scan code of key
@param isPressed: True if key is down
"""
self._controller.sendKeyEventScancode(code, isPressed)
def sendKeyEventUnicode(self, code, isPressed):
"""
Key event as uni code
@param code: uni code of key
@param isPressed: True if key is down
"""
self._controller.sendKeyEventUnicode(code, isPressed)
def sendPointerEvent(self, x, y, button, isPressed):
"""
Mouse event
@param x: x position
@param y: y position
@param isPressed: True if button is down
"""
self._controller.sendPointerEvent(x, y, button, isPressed)
def sendRefreshOrder(self, left, top, right, bottom):
"""
Refresh zone
@param left: left postion
@param top: top position
@param right: right position
@param bottom: bottom position
"""
self._controller.sendRefreshOrder(left, top, right, bottom)
class ProxyServerFactory(rdp.ServerFactory):
"""
Factory on listening events
"""
def __init__(self, credentialProvider, privateKeyFilePath, certificateFilePath):
"""
@param config: rdp-proxy configuration
@param credentialProvider: CredentialProvider
"""
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
self._credentialProvider = credentialProvider
def buildObserver(self, controller):
"""
Implement rdp.ServerFactory
@param controller: rdp.RDPServerController
"""
return ProxyServer(controller, self._credentialProvider)
class ProxyClientFactory(rdp.ClientFactory): class ProxyClientFactory(rdp.ClientFactory):
""" """
@@ -305,7 +200,7 @@ class ProxyClientFactory(rdp.ClientFactory):
@param password: password session @param password: password session
@param name: name of session @param name: name of session
""" """
self._server = server self._controller = server
self._width = width self._width = width
self._height = height self._height = height
self._domain = domain self._domain = domain
@@ -325,7 +220,7 @@ class ProxyClientFactory(rdp.ClientFactory):
controller.setDomain(self._domain) controller.setDomain(self._domain)
controller.setUsername(self._username) controller.setUsername(self._username)
controller.setPassword(self._password) controller.setPassword(self._password)
proxy = ProxyClient(controller, self._server, self._name) proxy = ProxyClient(controller, self._controller, self._name)
return proxy return proxy
def startedConnecting(self, connector): def startedConnecting(self, connector):
@@ -338,12 +233,12 @@ class ProxyClientFactory(rdp.ClientFactory):
pass pass
class ProxyAdmin(IProxyClient): class ProxyAdmin(rdp.RDPServerObserver):
""" """
Use to manage client side of admin session Use to manage client side of admin session
Add GUI to select which session to see Add GUI to select which session to see
And manage see session And manage see session
Just escape key is authorized during see session Just escape key is authorized during spy session
""" """
class State(object): class State(object):
""" """
@@ -353,45 +248,47 @@ class ProxyAdmin(IProxyClient):
GUI = 0 GUI = 0
SPY = 1 SPY = 1
def __init__(self, server): def __init__(self, controller):
""" """
@param server: rdp.RDPServerController @param server: rdp.RDPServerController
""" """
self._server = server rdp.RDPServerObserver.__init__(self, controller)
self._spyProxy = None self._controller = controller
self.initView() self._spy = None
self._state = ProxyAdmin.State.GUI self._state = ProxyAdmin.State.GUI
def initView(self): def initView(self):
""" """
Init GUI view Init GUI view
""" """
width, height = self._server._controller.getScreen() width, height = self._controller.getScreen()
self._window = view.WindowView(width, height, QtGui.QColor(24, 93, 123)) self._window = view.WindowView(width, height, QtGui.QColor(24, 93, 123))
self._list = view.ListView(ProxyClient._CONNECTED_.keys(), 500, 500, self.onSelect, QtGui.QColor(24, 93, 123)) self._list = view.ListView(ProxyClient._CONNECTED_.keys(), 500, 500, self.onSelect, QtGui.QColor(24, 93, 123))
self._window.addView(view.AnchorView(width / 2 - 250, height / 2 - 250, self._list)) self._window.addView(view.AnchorView(width / 2 - 250, height / 2 - 250, self._list))
self._render = view.RDPRenderer(self._server._controller) self._render = view.RDPRenderer(self._controller, self._controller.getColorDepth())
def close(self): def onReady(self):
""" """
Close proxy client Stack is ready and connected
May be called after an setColorDepth too
"""
if self._state == ProxyAdmin.State.GUI:
self.initView()
self._window.update(self._render, True)
elif self._state == ProxyAdmin.State.SPY:
#refresh client
width, height = self._controller.getScreen()
self._spy._controller.sendRefreshOrder(0, 0, width, height)
def onClose(self):
"""
Stack is closes
""" """
pass pass
def getColorDepth(self): def onKeyEventScancode(self, code, isPressed):
""" """
Use same Color depth as server init Event call when a keyboard event is catch in scan code format
@return color depth of GUI
"""
if self._state == ProxyAdmin.State.GUI:
return self._server._controller.getColorDepth()
elif self._state == ProxyAdmin.State.SPY:
return self._spyProxy.getColorDepth()
def sendKeyEventScancode(self, code, isPressed):
"""
IProxyClient implement is unauthorized during admin session
Only for GUI
@param code: scan code of key @param code: scan code of key
@param isPressed: True if key is down @param isPressed: True if key is down
""" """
@@ -402,40 +299,29 @@ class ProxyAdmin(IProxyClient):
self._window.update(self._render) self._window.update(self._render)
elif code == 1: elif code == 1:
#escape button refresh GUI #escape button refresh GUI
self._spyProxy._controller.removeClientObserver(self._spyProxy)
self._state = ProxyAdmin.State.GUI self._state = ProxyAdmin.State.GUI
self._list._labels = ProxyClient._CONNECTED_.keys() self._spy._controller.removeClientObserver(self._spy)
self._server.clientConnected(self) self.onReady()
def sendKeyEventUnicode(self, code, isPressed): def onKeyEventUnicode(self, code, isPressed):
""" """
Key event as uni code is unauthorized during admin session Event call when a keyboard event is catch in unicode format
@param code: uni code of key In admin mode is forbidden
@param code: unicode of key
@param isPressed: True if key is down @param isPressed: True if key is down
""" """
pass pass
def sendPointerEvent(self, x, y, button, isPressed): def onPointerEvent(self, x, y, button, isPressed):
""" """
Mouse event is unauthorized during admin session Event call on mouse event
In admin mode is forbidden
@param x: x position @param x: x position
@param y: y position @param y: y position
@param isPressed: True if button is down @param button: 1, 2 or 3 button
@param isPressed: True if mouse button is pressed
""" """
pass pass
def sendRefreshOrder(self, left, top, right, bottom):
"""
Refresh zone
@param left: left postion
@param top: top position
@param right: right position
@param bottom: bottom position
"""
if self._state == ProxyAdmin.State.GUI:
self._window.update(self._render, True)
elif self._state == ProxyAdmin.State.SPY:
self._spyProxy.sendRefreshOrder(left, top, right, bottom)
def onSelect(self, name): def onSelect(self, name):
""" """
@@ -444,10 +330,27 @@ class ProxyAdmin(IProxyClient):
""" """
if not ProxyClient._CONNECTED_.has_key(name): if not ProxyClient._CONNECTED_.has_key(name):
return return
self._spyProxy = ProxyClient(ProxyClient._CONNECTED_[name]._controller, self._server, "Admin")
self._state = ProxyAdmin.State.SPY self._state = ProxyAdmin.State.SPY
#reconnect me self._spy = ProxyClient(ProxyClient._CONNECTED_[name]._controller, self, "Admin")
self._server.clientConnected(self) self._controller.setColorDepth(self._spy._controller.getColorDepth())
class ProxyAdminFactory(rdp.ServerFactory):
"""
Factory for admin
"""
def __init__(self, privateKeyFilePath, certificateFilePath):
"""
@param privateKeyFilePath: private key for admin session
@param certificateFilePath: certificate for admin session
"""
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
def buildObserver(self, controller):
"""
Implement rdp.ServerFactory
@param controller: rdp.RDPServerController
"""
return ProxyAdmin(controller)
class CredentialProvider(object): class CredentialProvider(object):
""" """
@@ -475,13 +378,6 @@ class CredentialProvider(object):
if not account.has_key("ip") or not account.has_key("port"): if not account.has_key("ip") or not account.has_key("port"):
raise error.InvalidExpectedDataException("Invalid credentials declaration %s\\%s"%(domain, username)) raise error.InvalidExpectedDataException("Invalid credentials declaration %s\\%s"%(domain, username))
return str(account['ip']), account['port'] return str(account['ip']), account['port']
def isAdmin(self, domain, username, password):
"""
@return: True if credential match admin credential
"""
account = self.getAccount(domain, username)
return account.has_key("admin") and account["admin"] and account.has_key("password")# and str(account["password"]) == password
def help(): def help():
""" """
@@ -508,9 +404,10 @@ if __name__ == '__main__':
configFilePath = None configFilePath = None
privateKeyFilePath = None privateKeyFilePath = None
certificateFilePath = None certificateFilePath = None
adminInterface = None
try: try:
opts, args = getopt.getopt(sys.argv[1:], "hf:k:c:") opts, args = getopt.getopt(sys.argv[1:], "hf:k:c:i:")
except getopt.GetoptError: except getopt.GetoptError:
help() help()
for opt, arg in opts: for opt, arg in opts:
@@ -523,6 +420,8 @@ if __name__ == '__main__':
privateKeyFilePath = arg privateKeyFilePath = arg
elif opt == "-c": elif opt == "-c":
certificateFilePath = arg certificateFilePath = arg
elif opt == "-i":
adminInterface = arg
if configFilePath is None: if configFilePath is None:
print "Config file is mandatory" print "Config file is mandatory"
@@ -549,4 +448,12 @@ if __name__ == '__main__':
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
reactor.listenTCP(int(args[0]), ProxyServerFactory(CredentialProvider(config), privateKeyFilePath, certificateFilePath)) reactor.listenTCP(int(args[0]), ProxyServerFactory(CredentialProvider(config), privateKeyFilePath, certificateFilePath))
if not adminInterface is None:
if ':' in adminInterface:
adminInterface, adminPort = adminInterface.split(':')
else:
adminInterface, adminPort = adminInterface, "3390"
log.info("Admin listen on %s:%s"%(adminInterface, adminPort))
reactor.listenTCP(int(adminPort), ProxyAdminFactory(privateKeyFilePath, certificateFilePath), interface = adminInterface)
reactor.run() reactor.run()

View File

@@ -226,9 +226,9 @@ class Capability(CompositeType):
A capability A capability
@see: http://msdn.microsoft.com/en-us/library/cc240486.aspx @see: http://msdn.microsoft.com/en-us/library/cc240486.aspx
""" """
def __init__(self, capabilitySetType = 0, capability = None): def __init__(self, capability = None):
CompositeType.__init__(self) CompositeType.__init__(self)
self.capabilitySetType = UInt16Le(capabilitySetType, constant = (not capability is None)) self.capabilitySetType = UInt16Le(lambda:capability.__class__._TYPE_)
self.lengthCapability = UInt16Le(lambda:sizeof(self)) self.lengthCapability = UInt16Le(lambda:sizeof(self))
def CapabilityFactory(): def CapabilityFactory():

View File

@@ -83,29 +83,29 @@ class PDULayer(LayerAutomata):
self._info = data.RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) self._info = data.RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS))
#server capabilities #server capabilities
self._serverCapabilities = { self._serverCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()), caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()), caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()), caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.OrderCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()), caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()), caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.InputCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()), caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.CapsType.CAPSTYPE_FONT, caps.FontCapability()), caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.FontCapability()),
caps.CapsType.CAPSTYPE_COLORCACHE : caps.Capability(caps.CapsType.CAPSTYPE_COLORCACHE, caps.ColorCacheCapability()), caps.CapsType.CAPSTYPE_COLORCACHE : caps.Capability(caps.ColorCacheCapability()),
caps.CapsType.CAPSTYPE_SHARE : caps.Capability(caps.CapsType.CAPSTYPE_SHARE, caps.ShareCapability()) caps.CapsType.CAPSTYPE_SHARE : caps.Capability(caps.ShareCapability())
} }
#client capabilities #client capabilities
self._clientCapabilities = { self._clientCapabilities = {
caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()), caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.GeneralCapability()),
caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.CapsType.CAPSTYPE_BITMAP, caps.BitmapCapability()), caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.BitmapCapability()),
caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.CapsType.CAPSTYPE_ORDER, caps.OrderCapability()), caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.OrderCapability()),
caps.CapsType.CAPSTYPE_BITMAPCACHE : caps.Capability(caps.CapsType.CAPSTYPE_BITMAPCACHE, caps.BitmapCacheCapability()), caps.CapsType.CAPSTYPE_BITMAPCACHE : caps.Capability(caps.BitmapCacheCapability()),
caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.CapsType.CAPSTYPE_POINTER, caps.PointerCapability()), caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.PointerCapability()),
caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.CapsType.CAPSTYPE_INPUT, caps.InputCapability()), caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.InputCapability()),
caps.CapsType.CAPSTYPE_BRUSH : caps.Capability(caps.CapsType.CAPSTYPE_BRUSH, caps.BrushCapability()), caps.CapsType.CAPSTYPE_BRUSH : caps.Capability(caps.BrushCapability()),
caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.CapsType.CAPSTYPE_GLYPHCACHE, caps.GlyphCapability()), caps.CapsType.CAPSTYPE_GLYPHCACHE : caps.Capability(caps.GlyphCapability()),
caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.CapsType.CAPSTYPE_OFFSCREENCACHE, caps.OffscreenBitmapCacheCapability()), caps.CapsType.CAPSTYPE_OFFSCREENCACHE : caps.Capability(caps.OffscreenBitmapCacheCapability()),
caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.CapsType.CAPSTYPE_VIRTUALCHANNEL, caps.VirtualChannelCapability()), caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.VirtualChannelCapability()),
caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.CapsType.CAPSTYPE_SOUND, caps.SoundCapability()) caps.CapsType.CAPSTYPE_SOUND : caps.Capability(caps.SoundCapability())
} }
#share id between client and server #share id between client and server
self._shareId = 0x103EA self._shareId = 0x103EA

View File

@@ -183,12 +183,13 @@ class WindowView(IView):
view.update(render, force) view.update(render, force)
class RDPRenderer(object): class RDPRenderer(object):
def __init__(self, server): def __init__(self, controller, colorDepth):
""" """
@param server: RDPServerController @param server: RDPServerController
@param colorDepth: color depth
""" """
self._server = server self._controller = controller
self._colorDepth = self._server.getColorDepth() self._colorDepth = colorDepth
self._dx = 0 self._dx = 0
self._dy = 0 self._dy = 0
self._renderSize = 64 self._renderSize = 64
@@ -220,5 +221,5 @@ class RDPRenderer(object):
tmp = tmp.transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) tmp = tmp.transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
ptr = tmp.bits() ptr = tmp.bits()
ptr.setsize(tmp.byteCount()) ptr.setsize(tmp.byteCount())
self._server.sendUpdate(i * self._renderSize + self._dx, j * self._renderSize + self._dy, min((i + 1) * self._renderSize, image.width()) + self._dx - 1, min((j + 1) * self._renderSize, image.height()) + self._dy - 1, tmp.width(), tmp.height(), self._colorDepth, False, ptr.asstring()) self._controller.sendUpdate(i * self._renderSize + self._dx, j * self._renderSize + self._dy, min((i + 1) * self._renderSize, image.width()) + self._dx - 1, min((j + 1) * self._renderSize, image.height()) + self._dy - 1, tmp.width(), tmp.height(), self._colorDepth, False, ptr.asstring())