This commit is contained in:
speyrefitte
2015-06-01 18:16:39 +02:00
13 changed files with 174 additions and 88 deletions

View File

@@ -182,6 +182,11 @@ class MyRDPFactory(rdp.ClientFactory):
@param data: bitmap data
"""
def onSessionReady(self):
"""
@summary: Windows session is ready
"""
def onClose(self):
"""
@summary: Call when stack is close

View File

@@ -172,7 +172,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
connector.connect()
return
QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason)
log.info("Lost connection : %s"%reason)
reactor.stop()
app.exit()
@@ -182,7 +182,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
@param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection
"""
QtGui.QMessageBox.warning(self._w, "Warning", "Connection failed : %s"%reason)
log.info("Connection failed : %s"%reason)
reactor.stop()
app.exit()

View File

@@ -70,7 +70,7 @@ class HoneyPotServer(rdp.RDPServerObserver):
def onClose(self):
""" HoneyPot """
def onKeyEventScancode(self, code, isPressed):
def onKeyEventScancode(self, code, isPressed, isExtended):
""" HoneyPot """
def onKeyEventUnicode(self, code, isPressed):

View File

@@ -92,16 +92,17 @@ class ProxyServer(rdp.RDPServerObserver):
return
self._client._controller.close()
def onKeyEventScancode(self, code, isPressed):
def onKeyEventScancode(self, code, isPressed, isExtended):
"""
@summary: Event call when a keyboard event is catch in scan code format
@param code: {int} scan code of key
@param isPressed: {bool} True if key is down
@param code: {integer} scan code of key
@param isPressed: {boolean} True if key is down
@param isExtended: {boolean} True if a special key
@see: rdp.RDPServerObserver.onKeyEventScancode
"""
if self._client is None:
return
self._client._controller.sendKeyEventScancode(code, isPressed)
self._client._controller.sendKeyEventScancode(code, isPressed, isExtended)
self._rss.keyScancode(code, isPressed)
def onKeyEventUnicode(self, code, isPressed):
@@ -178,6 +179,13 @@ class ProxyClient(rdp.RDPClientObserver):
#maybe color depth change
self._server._controller.setColorDepth(self._controller.getColorDepth())
def onSessionReady(self):
"""
@summary: Windows session is ready
@see: rdp.RDPClientObserver.onSessionReady
"""
pass
def onClose(self):
"""
@summary: Event inform that stack is close

View File

@@ -136,6 +136,13 @@ class RDPScreenShotFactory(rdp.ClientFactory):
"""
log.info("connected %s"%addr)
def onSessionReady(self):
"""
@summary: Windows session is ready
@see: rdp.RDPClientObserver.onSessionReady
"""
pass
def onClose(self):
"""
@summary: callback use when RDP stack is closed

View File

@@ -477,7 +477,7 @@ class CompositeType(Type):
raise e
if not self._readLen is None and readLen < self._readLen.value:
log.debug("Still have correct data in packet %s, read it as padding"%self.__class__)
log.debug("Still have correct data in packet %s, read %s bytes as padding"%(self.__class__, self._readLen.value - readLen))
s.read(self._readLen.value - readLen)
def __write__(self, s):

View File

@@ -234,7 +234,7 @@ class LicPacket(CompositeType):
if self.bMsgtype.value == c._MESSAGE_TYPE_:
return c(readLen = self.wMsgSize - 4)
log.debug("unknown license message : %s"%self.bMsgtype.value)
return String()
return String(readLen = self.wMsgSize - 4)
if message is None:
message = FactoryType(LicensingMessageFactory)

View File

@@ -202,6 +202,16 @@ class Display(object):
SUPPRESS_DISPLAY_UPDATES = 0x00
ALLOW_DISPLAY_UPDATES = 0x01
class ToogleFlag(object):
"""
@summary: Use to known state of keyboard
@see: https://msdn.microsoft.com/en-us/library/cc240588.aspx
"""
TS_SYNC_SCROLL_LOCK = 0x00000001
TS_SYNC_NUM_LOCK = 0x00000002
TS_SYNC_CAPS_LOCK = 0x00000004
TS_SYNC_KANA_LOCK = 0x00000008
class ErrorInfo(object):
"""
@summary: Error code use in Error info PDU
@@ -413,8 +423,6 @@ class ErrorInfo(object):
ERRINFO_VCDATATOOLONG : "The size of a received Virtual Channel PDU (section 2.2.6.1) exceeds the chunking size specified in the Virtual Channel Capability Set (section 2.2.7.1.10).",
}
class ShareControlHeader(CompositeType):
"""
@summary: PDU share control header
@@ -461,10 +469,10 @@ class PDU(CompositeType):
"""
for c in [DemandActivePDU, ConfirmActivePDU, DataPDU, DeactiveAllPDU]:
if self.shareControlHeader.pduType.value == c._PDUTYPE_:
return c()
return c(readLen = CallableValue(self.shareControlHeader.totalLength.value - sizeof(self.shareControlHeader)))
log.debug("unknown PDU type : %s"%hex(self.shareControlHeader.pduType.value))
#read entire packet
return String()
return String(readLen = CallableValue(self.shareControlHeader.totalLength.value - sizeof(self.shareControlHeader)))
if pduMessage is None:
pduMessage = FactoryType(PDUMessageFactory)
@@ -481,8 +489,8 @@ class DemandActivePDU(CompositeType):
#may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_DEMANDACTIVEPDU
def __init__(self):
CompositeType.__init__(self)
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.shareId = UInt32Le()
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
self.lengthCombinedCapabilities = UInt16Le(lambda:(sizeof(self.numberCapabilities) + sizeof(self.pad2Octets) + sizeof(self.capabilitySets)))
@@ -500,8 +508,8 @@ class ConfirmActivePDU(CompositeType):
#may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_CONFIRMACTIVEPDU
def __init__(self):
CompositeType.__init__(self)
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.shareId = UInt32Le()
self.originatorId = UInt16Le(0x03EA, constant = True)
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
@@ -519,10 +527,10 @@ class DeactiveAllPDU(CompositeType):
#may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_DEACTIVATEALLPDU
def __init__(self):
def __init__(self, readLen = None):
#in old version this packet is empty i don't know
#and not specified
CompositeType.__init__(self, optional = True)
CompositeType.__init__(self, optional = True, readLen = readLen)
self.shareId = UInt32Le()
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
@@ -534,19 +542,19 @@ class DataPDU(CompositeType):
#may declare the PDU type
_PDUTYPE_ = PDUType.PDUTYPE_DATAPDU
def __init__(self, pduData = None, shareId = 0):
CompositeType.__init__(self)
def __init__(self, pduData = None, shareId = 0, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.shareDataHeader = ShareDataHeader(lambda:sizeof(self), lambda:self.pduData.__class__._PDUTYPE2_, shareId)
def PDUDataFactory():
"""
@summary: Create object in accordance self.shareDataHeader.pduType2 value
"""
for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU, SupressOutputDataPDU]:
for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU, SupressOutputDataPDU, SaveSessionInfoPDU]:
if self.shareDataHeader.pduType2.value == c._PDUTYPE2_:
return c()
return c(readLen = CallableValue(readLen.value - sizeof(self.shareDataHeader)))
log.debug("unknown PDU data type : %s"%hex(self.shareDataHeader.pduType2.value))
return String()
return String(readLen = CallableValue(readLen.value - sizeof(self.shareDataHeader)))
if pduData is None:
pduData = FactoryType(PDUDataFactory)
@@ -655,8 +663,8 @@ class PersistentListPDU(CompositeType):
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST
def __init__(self, userId = 0, shareId = 0):
CompositeType.__init__(self)
def __init__(self, userId = 0, shareId = 0, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.numEntriesCache0 = UInt16Le()
self.numEntriesCache1 = UInt16Le()
self.numEntriesCache2 = UInt16Le()
@@ -679,8 +687,8 @@ class ClientInputEventPDU(CompositeType):
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_INPUT
def __init__(self):
CompositeType.__init__(self)
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.numEvents = UInt16Le(lambda:len(self.slowPathInputEvents._array))
self.pad2Octets = UInt16Le()
self.slowPathInputEvents = ArrayType(SlowPathInputEvent, readLen = self.numEvents)
@@ -691,8 +699,8 @@ class ShutdownRequestPDU(CompositeType):
client -> server
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_REQUEST
def __init__(self):
CompositeType.__init__(self)
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
class ShutdownDeniedPDU(CompositeType):
"""
@@ -700,8 +708,8 @@ class ShutdownDeniedPDU(CompositeType):
server -> client
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_DENIED
def __init__(self):
CompositeType.__init__(self)
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
class InclusiveRectangle(CompositeType):
"""
@@ -761,9 +769,9 @@ class UpdateDataPDU(CompositeType):
"""
for c in [BitmapUpdateDataPDU]:
if self.updateType.value == c._UPDATE_TYPE_:
return c()
return c(readLen = CallableValue(readLen.value - 2))
log.debug("unknown PDU update data type : %s"%hex(self.updateType.value))
return String()
return String(readLen = CallableValue(readLen.value - 2))
if updateData is None:
updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE))
@@ -772,6 +780,18 @@ class UpdateDataPDU(CompositeType):
self.updateData = updateData
class SaveSessionInfoPDU(CompositeType):
"""
@see: https://msdn.microsoft.com/en-us/library/cc240636.aspx
"""
_PDUTYPE2_ = PDUType2.PDUTYPE2_SAVE_SESSION_INFO
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.infoType = UInt32Le()
#TODO parse info data
self.infoData = String()
class FastPathUpdatePDU(CompositeType):
"""
@summary: Fast path update PDU packet
@@ -789,9 +809,9 @@ class FastPathUpdatePDU(CompositeType):
"""
for c in [FastPathBitmapUpdateDataPDU]:
if (self.updateHeader.value & 0xf) == c._FASTPATH_UPDATE_TYPE_:
return c()
return c(readLen = self.size)
log.debug("unknown Fast Path PDU update data type : %s"%hex(self.updateHeader.value & 0xf))
return String()
return String(readLen = self.size)
if updateData is None:
updateData = FactoryType(UpdateDataFactory)
@@ -821,8 +841,8 @@ class OrderUpdateDataPDU(CompositeType):
@see: http://msdn.microsoft.com/en-us/library/cc241571.aspx
@todo: not implemented yet but need it
"""
def __init__(self):
CompositeType.__init__(self)
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.pad2OctetsA = UInt16Le()
self.numberOrders = UInt16Le(lambda:len(self.orderData._array))
self.pad2OctetsB = UInt16Le()
@@ -882,8 +902,8 @@ class FastPathBitmapUpdateDataPDU(CompositeType):
"""
_FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP
def __init__(self):
CompositeType.__init__(self)
def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen)
self.header = UInt16Le(FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP, constant = True)
self.numberRectangles = UInt16Le(lambda:len(self.rectangles._array))
self.rectangles = ArrayType(BitmapData, readLen = self.numberRectangles)
@@ -899,11 +919,10 @@ class SlowPathInputEvent(CompositeType):
self.messageType = UInt16Le(lambda:self.slowPathInputData.__class__._INPUT_MESSAGE_TYPE_)
def SlowPathInputDataFactory():
for c in [PointerEvent, ScancodeKeyEvent, UnicodeKeyEvent]:
for c in [PointerEvent, ScancodeKeyEvent, UnicodeKeyEvent, SynchronizeEvent]:
if self.messageType.value == c._INPUT_MESSAGE_TYPE_:
return c()
log.debug("unknown slow path input : %s"%hex(self.messageType.value))
return String()
raise InvalidExpectedDataException("unknown slow path input : %s"%hex(self.messageType.value))
if messageData is None:
messageData = FactoryType(SlowPathInputDataFactory)
@@ -912,6 +931,18 @@ class SlowPathInputEvent(CompositeType):
self.slowPathInputData = messageData
class SynchronizeEvent(CompositeType):
"""
@summary: Synchronize keyboard
@see: https://msdn.microsoft.com/en-us/library/cc240588.aspx
"""
_INPUT_MESSAGE_TYPE_ = InputMessageType.INPUT_EVENT_SYNC
def __init__(self):
CompositeType.__init__(self)
self.pad2Octets = UInt16Le()
self.toggleFlags = UInt32Le()
class PointerEvent(CompositeType):
"""
@summary: Event use to communicate mouse position

View File

@@ -25,6 +25,7 @@ In this layer are managed all mains bitmap update orders end user inputs
from rdpy.core.layer import LayerAutomata
from rdpy.core.error import CallPureVirtualFuntion
from rdpy.core.type import ArrayType
import rdpy.core.log as log
import rdpy.protocol.rdp.tpkt as tpkt
import data, caps
@@ -39,6 +40,13 @@ class PDUClientListener(object):
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener"))
def onSessionReady(self):
"""
@summary: Event call when Windows session is ready
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onSessionReady", "PDUClientListener"))
def onUpdate(self, rectangles):
"""
@summary: call when a bitmap data is received from update PDU
@@ -259,15 +267,16 @@ class Client(PDULayer):
@summary: Main receive function after connection sequence
@param s: Stream from transport layer
"""
pdu = data.PDU()
s.readType(pdu)
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage)
elif pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
#use in deactivation-reactivation sequence
#next state is either a capabilities re exchange or disconnection
#http://msdn.microsoft.com/en-us/library/cc240454.aspx
self.setNextState(self.recvDemandActivePDU)
pdus = ArrayType(data.PDU)
s.readType(pdus)
for pdu in pdus:
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
self.readDataPDU(pdu.pduMessage)
elif pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DEACTIVATEALLPDU:
#use in deactivation-reactivation sequence
#next state is either a capabilities re exchange or disconnection
#http://msdn.microsoft.com/en-us/library/cc240454.aspx
self.setNextState(self.recvDemandActivePDU)
def recvFastPath(self, secFlag, fastPathS):
"""
@@ -276,10 +285,11 @@ class Client(PDULayer):
@param fastPathS: {Stream} that contain fast path data
@param secFlag: {SecFlags}
"""
fastPathPDU = data.FastPathUpdatePDU()
fastPathS.readType(fastPathPDU)
if fastPathPDU.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._listener.onUpdate(fastPathPDU.updateData.rectangles._array)
updates = ArrayType(data.FastPathUpdatePDU)
fastPathS.readType(updates)
for update in updates:
if update.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
self._listener.onUpdate(update.updateData.rectangles._array)
def readDataPDU(self, dataPDU):
"""
@@ -298,6 +308,9 @@ class Client(PDULayer):
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_DENIED:
#may be an event to ask to user
self._transport.close()
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SAVE_SESSION_INFO:
#handle session event
self._listener.onSessionReady()
elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_UPDATE:
self.readUpdateDataPDU(dataPDU.pduData)

View File

@@ -100,7 +100,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
def setUsername(self, username):
"""
@summary: Set the username for session
@param username: username of session
@param username: {string} username of session
"""
#username in PDU info packet
self._secLayer._info.userName.value = username
@@ -109,7 +109,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
def setPassword(self, password):
"""
@summary: Set password for session
@param password: password of session
@param password: {string} password of session
"""
self.setAutologon()
self._secLayer._info.password.value = password
@@ -117,7 +117,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
def setDomain(self, domain):
"""
@summary: Set the windows domain of session
@param domain: domain of session
@param domain: {string} domain of session
"""
self._secLayer._info.domain.value = domain
@@ -127,6 +127,13 @@ class RDPClientController(pdu.layer.PDUClientListener):
"""
self._secLayer._info.flag |= sec.InfoFlag.INFO_AUTOLOGON
def setAlternateShell(self, appName):
"""
@summary: set application name of app which start at the begining of session
@param appName: {string} application name
"""
self._secLayer._info.alternateShell.value = appName
def setKeyboardLayout(self, layout):
"""
@summary: keyboard layout
@@ -192,6 +199,15 @@ class RDPClientController(pdu.layer.PDUClientListener):
for observer in self._clientObserver:
observer.onReady()
def onSessionReady(self):
"""
@summary: Call when Windows session is ready (connected)
"""
self._isReady = True
#signal all listener
for observer in self._clientObserver:
observer.onSessionReady()
def onClose(self):
"""
@summary: Event call when RDP stack is closed
@@ -269,11 +285,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
except InvalidValue:
log.info("try send wheel event with incorrect position")
def sendKeyEventScancode(self, code, isPressed):
def sendKeyEventScancode(self, code, isPressed, extended = False):
"""
@summary: Send a scan code to RDP stack
@param code: scan code
@param isPressed: True if key is pressed and false if it's released
@param extended: {boolean} extended scancode like ctr or win button
"""
if not self._isReady:
return
@@ -281,11 +298,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
try:
event = pdu.data.ScancodeKeyEvent()
event.keyCode.value = code
if isPressed:
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_DOWN
else:
if not isPressed:
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_RELEASE
if extended:
event.keyboardFlags.value |= pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED
#send event
self._pduLayer.sendInputEvents([event])
@@ -478,7 +496,7 @@ class RDPServerController(pdu.layer.PDUServerListener):
for event in slowPathInputEvents:
#scan code
if event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_SCANCODE:
observer.onKeyEventScancode(event.slowPathInputData.keyCode.value, not (event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_RELEASE))
observer.onKeyEventScancode(event.slowPathInputData.keyCode.value, not (event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_RELEASE), bool(event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_EXTENDED))
#unicode
elif event.messageType.value == pdu.data.InputMessageType.INPUT_EVENT_UNICODE:
observer.onKeyEventUnicode(event.slowPathInputData.unicode.value, not (event.slowPathInputData.keyboardFlags.value & pdu.data.KeyboardFlag.KBDFLAGS_RELEASE))
@@ -607,6 +625,12 @@ class RDPClientObserver(object):
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver"))
def onSessionReady(self):
"""
@summary: Windows session is ready
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onSessionReady", "RDPClientObserver"))
def onClose(self):
"""
@summary: Stack is closes
@@ -652,11 +676,12 @@ class RDPServerObserver(object):
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver"))
def onKeyEventScancode(self, code, isPressed):
def onKeyEventScancode(self, code, isPressed, isExtended):
"""
@summary: Event call when a keyboard event is catch in scan code format
@param code: scan code of key
@param isPressed: True if key is down
@param code: {integer} scan code of key
@param isPressed: {boolean} True if key is down
@param isExtended: {boolean} True if a special key
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onKeyEventScanCode", "RDPServerObserver"))

View File

@@ -323,7 +323,7 @@ class RDPInfo(CompositeType):
#code page
self.codePage = UInt32Le()
#support flag
self.flag = UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL)
self.flag = UInt32Le(InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_DISABLECTRLALTDEL | InfoFlag.INFO_ENABLEWINDOWSKEY)
self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2)
self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2)
self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2)

View File

@@ -303,7 +303,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
@param isCompress: {bool} use RLE compression
@param data: {str} bitmap data
"""
image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data);
image = RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data)
#if image need to be cut
#For bit alignement server may send more than image pixel
self._widget.notifyImage(destLeft, destTop, image, destRight - destLeft + 1, destBottom - destTop + 1)
@@ -311,12 +311,21 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
def onReady(self):
"""
@summary: Call when stack is ready
@see: rdp.RDPClientObserver.onReady
"""
#do something maybe a loader
def onSessionReady(self):
"""
@summary: Windows session is ready
@see: rdp.RDPClientObserver.onSessionReady
"""
pass
def onClose(self):
"""
@summary: Call when stack is close
@see: rdp.RDPClientObserver.onClose
"""
#do something maybe a message
@@ -336,12 +345,6 @@ class QRemoteDesktop(QtGui.QWidget):
self._adaptor = adaptor
#set correct size
self.resize(width, height)
#refresh stack of image
#because we can update image only in paint
#event function. When protocol receive image
#we will stock into refresh list
#and in paint event paint list of all refresh images
self._refresh = []
#bind mouse event
self.setMouseTracking(True)
#buffer image
@@ -354,8 +357,9 @@ class QRemoteDesktop(QtGui.QWidget):
@param y: y position of new image
@param qimage: new QImage
"""
#save in refresh list (order is important)
self._refresh.append((x, y, qimage, width, height))
#fill buffer image
with QtGui.QPainter(self._buffer) as qp:
qp.drawImage(x, y, qimage, 0, 0, width, height)
#force update
self.update()
@@ -373,17 +377,10 @@ class QRemoteDesktop(QtGui.QWidget):
@summary: Call when Qt renderer engine estimate that is needed
@param e: QEvent
"""
#fill buffer image
with QtGui.QPainter(self._buffer) as qp:
#draw image
for (x, y, image, width, height) in self._refresh:
qp.drawImage(x, y, image, 0, 0, width, height)
#draw in widget
with QtGui.QPainter(self) as qp:
qp.drawImage(0, 0, self._buffer)
self._refresh = []
def mouseMoveEvent(self, event):
"""
@summary: Call when mouse move

View File

@@ -4,7 +4,7 @@ import setuptools
from distutils.core import setup, Extension
setup(name='rdpy',
version='1.3.1',
version='1.3.2',
description='Remote Desktop Protocol in Python',
long_description="""
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (Client and Server side). RDPY is built over the event driven network engine Twisted.