bug fix on gui + add license neg (almost)
This commit is contained in:
@@ -930,7 +930,7 @@ class OrderUpdateDataPDU(CompositeType):
|
||||
self.pad2OctetsA = UInt16Le()
|
||||
self.numberOrders = UInt16Le(lambda:len(self.orderData._array))
|
||||
self.pad2OctetsB = UInt16Le()
|
||||
self.orderData = ArrayType(order.DrawingOrder, readLen = self.numberOrders)
|
||||
self.orderData = ArrayType(order.PrimaryDrawingOrder, readLen = self.numberOrders)
|
||||
|
||||
class BitmapCompressedDataHeader(CompositeType):
|
||||
"""
|
||||
|
||||
@@ -136,6 +136,8 @@ class Client(PDULayer, tpkt.IFastPathListener):
|
||||
self._listener = listener
|
||||
#enable or not fast path
|
||||
self._fastPathSender = None
|
||||
#todo generate hostname
|
||||
self._licenceManager = lic.LicenseManager(self, self._info.userName.value, "wav-glw-009")
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
@@ -177,18 +179,9 @@ class Client(PDULayer, tpkt.IFastPathListener):
|
||||
if not (securityFlag.value & data.SecurityFlag.SEC_LICENSE_PKT):
|
||||
raise InvalidExpectedDataException("Waiting license packet")
|
||||
|
||||
validClientPdu = lic.LicPacket()
|
||||
s.readType(validClientPdu)
|
||||
|
||||
if validClientPdu.bMsgtype.value == lic.MessageType.ERROR_ALERT and validClientPdu.licensingMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.licensingMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION:
|
||||
if self._licenceManager.recv(s):
|
||||
self.setNextState(self.recvDemandActivePDU)
|
||||
#not tested because i can't buy RDP license server
|
||||
elif validClientPdu.bMsgtype.value == lic.MessageType.LICENSE_REQUEST:
|
||||
newLicenseReq = lic.createNewLicenseRequest(validClientPdu.licensingMessage)
|
||||
self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), newLicenseReq))
|
||||
else:
|
||||
raise InvalidExpectedDataException("Not a valid license packet")
|
||||
|
||||
|
||||
def recvDemandActivePDU(self, s):
|
||||
"""
|
||||
Receive demand active PDU which contains
|
||||
@@ -343,6 +336,13 @@ class Client(PDULayer, tpkt.IFastPathListener):
|
||||
"""
|
||||
self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info))
|
||||
|
||||
def sendLicensePacket(self, licPkt):
|
||||
"""
|
||||
@summary: send license packet
|
||||
@param licPktr: license packet
|
||||
"""
|
||||
self._transport.send((UInt16Le(data.SecurityFlag.SEC_LICENSE_PKT), UInt16Le(), licPkt))
|
||||
|
||||
def sendConfirmActivePDU(self):
|
||||
"""
|
||||
Send all client capabilities
|
||||
|
||||
@@ -18,17 +18,20 @@
|
||||
#
|
||||
|
||||
"""
|
||||
RDP extended license
|
||||
@summary: RDP extended license
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241880.aspx
|
||||
"""
|
||||
|
||||
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType
|
||||
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType,\
|
||||
Stream
|
||||
from rdpy.base.error import InvalidExpectedDataException
|
||||
import rdpy.base.log as log
|
||||
import rdpy.protocol.rdp.sec as sec
|
||||
import rdpy.protocol.rdp.rc4 as rc4
|
||||
|
||||
class MessageType(object):
|
||||
"""
|
||||
License packet message type
|
||||
@summary: License packet message type
|
||||
"""
|
||||
LICENSE_REQUEST = 0x01
|
||||
PLATFORM_CHALLENGE = 0x02
|
||||
@@ -42,7 +45,7 @@ class MessageType(object):
|
||||
|
||||
class ErrorCode(object):
|
||||
"""
|
||||
License error message code
|
||||
@summary: License error message code
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
"""
|
||||
ERR_INVALID_SERVER_CERTIFICATE = 0x00000001
|
||||
@@ -57,7 +60,7 @@ class ErrorCode(object):
|
||||
|
||||
class StateTransition(object):
|
||||
"""
|
||||
Automata state transition
|
||||
@summary: Automata state transition
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
"""
|
||||
ST_TOTAL_ABORT = 0x00000001
|
||||
@@ -67,9 +70,10 @@ class StateTransition(object):
|
||||
|
||||
class BinaryBlobType(object):
|
||||
"""
|
||||
Binary blob data type
|
||||
@summary: Binary blob data type
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
"""
|
||||
BB_ANY_BLOB = 0x0000
|
||||
BB_DATA_BLOB = 0x0001
|
||||
BB_RANDOM_BLOB = 0x0002
|
||||
BB_CERTIFICATE_BLOB = 0x0003
|
||||
@@ -82,25 +86,25 @@ class BinaryBlobType(object):
|
||||
|
||||
class Preambule(object):
|
||||
"""
|
||||
Preambule version
|
||||
@summary: Preambule version
|
||||
"""
|
||||
PREAMBLE_VERSION_2_0 = 0x2
|
||||
PREAMBLE_VERSION_3_0 = 0x3
|
||||
|
||||
class LicenseBinaryBlob(CompositeType):
|
||||
"""
|
||||
Blob use by license manager to exchange security data
|
||||
@summary: Blob use by license manager to exchange security data
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240481.aspx
|
||||
"""
|
||||
def __init__(self, blobType = 0):
|
||||
def __init__(self, blobType = BinaryBlobType.BB_ANY_BLOB):
|
||||
CompositeType.__init__(self)
|
||||
self.wBlobType = UInt16Le(blobType, constant = True)
|
||||
self.wBlobType = UInt16Le(blobType, constant = True if blobType != BinaryBlobType.BB_ANY_BLOB else False)
|
||||
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
|
||||
self.blobData = String(readLen = self.wBlobLen)
|
||||
|
||||
class LicensingErrorMessage(CompositeType):
|
||||
"""
|
||||
License error message
|
||||
@summary: License error message
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240482.aspx
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.ERROR_ALERT
|
||||
@@ -113,7 +117,7 @@ class LicensingErrorMessage(CompositeType):
|
||||
|
||||
class ProductInformation(CompositeType):
|
||||
"""
|
||||
License server product information
|
||||
@summary: License server product information
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241915.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
@@ -121,15 +125,15 @@ class ProductInformation(CompositeType):
|
||||
self.dwVersion = UInt32Le()
|
||||
self.cbCompanyName = UInt32Le(lambda:sizeof(self.pbCompanyName))
|
||||
#may contain "Microsoft Corporation" from server microsoft
|
||||
self.pbCompanyName = String(readLen = self.cbCompanyName)
|
||||
self.pbCompanyName = String(readLen = self.cbCompanyName, unicode = True)
|
||||
self.cbProductId = UInt32Le(lambda:sizeof(self.pbProductId))
|
||||
#may contain "A02" from microsoft license server
|
||||
self.pbProductId = String(readLen = self.cbProductId)
|
||||
self.pbProductId = String(readLen = self.cbProductId, unicode = True)
|
||||
|
||||
|
||||
class Scope(CompositeType):
|
||||
"""
|
||||
Use in license nego
|
||||
@summary: Use in license nego
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241917.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
@@ -138,7 +142,7 @@ class Scope(CompositeType):
|
||||
|
||||
class ScopeList(CompositeType):
|
||||
"""
|
||||
Use in license nego
|
||||
@summary: Use in license nego
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241916.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
@@ -148,8 +152,8 @@ class ScopeList(CompositeType):
|
||||
|
||||
class ServerLicenseRequest(CompositeType):
|
||||
"""
|
||||
Send by server to signal license request
|
||||
server -> client
|
||||
@summary: Send by server to signal license request
|
||||
server -> client
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241914.aspx
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.LICENSE_REQUEST
|
||||
@@ -164,8 +168,8 @@ class ServerLicenseRequest(CompositeType):
|
||||
|
||||
class ClientNewLicenseRequest(CompositeType):
|
||||
"""
|
||||
Send by client to ask new license for client.
|
||||
RDPY doesn'support license reuse, need it in futur version
|
||||
@summary: Send by client to ask new license for client.
|
||||
RDPY doesn'support license reuse, need it in futur version
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.NEW_LICENSE_REQUEST
|
||||
@@ -181,6 +185,32 @@ class ClientNewLicenseRequest(CompositeType):
|
||||
self.encryptedPreMasterSecret = LicenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB)
|
||||
self.ClientUserName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB)
|
||||
self.ClientMachineName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
|
||||
|
||||
class ServerPlatformChallenge(CompositeType):
|
||||
"""
|
||||
@summary: challenge send from server to client
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241921.aspx
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE
|
||||
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.connectFlags = UInt32Le()
|
||||
self.encryptedPlatformChallenge = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||
self.MACData = String(readLen = UInt8(16))
|
||||
|
||||
class ClientPLatformChallengeResponse(CompositeType):
|
||||
"""
|
||||
@summary: client challenge response
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241922.aspx
|
||||
"""
|
||||
_MESSAGE_TYPE_ = MessageType.PLATFORM_CHALLENGE_RESPONSE
|
||||
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.encryptedPlatformChallengeResponse = LicenseBinaryBlob(BinaryBlobType.BB_ENCRYPTED_DATA_BLOB)
|
||||
self.encryptedHWID = LicenseBinaryBlob(BinaryBlobType.BB_ENCRYPTED_DATA_BLOB)
|
||||
self.MACData = String(readLen = UInt8(16))
|
||||
|
||||
class LicPacket(CompositeType):
|
||||
"""
|
||||
@@ -198,7 +228,7 @@ class LicPacket(CompositeType):
|
||||
factory for message nego
|
||||
Use in read mode
|
||||
"""
|
||||
for c in [LicensingErrorMessage, ServerLicenseRequest, ClientNewLicenseRequest]:
|
||||
for c in [LicensingErrorMessage, ServerLicenseRequest, ClientNewLicenseRequest, ServerPlatformChallenge, ClientPLatformChallengeResponse]:
|
||||
if self.bMsgtype.value == c._MESSAGE_TYPE_:
|
||||
return c()
|
||||
log.debug("unknown license message : %s"%self.bMsgtype.value)
|
||||
@@ -210,23 +240,94 @@ class LicPacket(CompositeType):
|
||||
raise InvalidExpectedDataException("Try to send an invalid license message")
|
||||
|
||||
self.licensingMessage = message
|
||||
|
||||
|
||||
def createValidClientLicensingErrorMessage():
|
||||
"""
|
||||
@summary: Create a licensing error message that accept client
|
||||
server automata message
|
||||
"""
|
||||
message = LicensingErrorMessage()
|
||||
message.dwErrorCode.value = ErrorCode.STATUS_VALID_CLIENT
|
||||
message.dwStateTransition.value = StateTransition.ST_NO_TRANSITION
|
||||
return LicPacket(message = message)
|
||||
|
||||
class LicenseManager(object):
|
||||
"""
|
||||
Create a licensing error message that accept client
|
||||
server automata message
|
||||
@summary: handle license automata
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241890.aspx
|
||||
"""
|
||||
message = LicensingErrorMessage()
|
||||
message.dwErrorCode.value = ErrorCode.STATUS_VALID_CLIENT
|
||||
message.dwStateTransition.value = StateTransition.ST_NO_TRANSITION
|
||||
return LicPacket(message = message)
|
||||
|
||||
def createNewLicenseRequest(serverLicenseRequest):
|
||||
"""
|
||||
Create new license request in response to server license request
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241989.aspx
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||
"""
|
||||
message = ClientNewLicenseRequest()
|
||||
def __init__(self, transport, username, hostname):
|
||||
"""
|
||||
@param transport: layer use to send packet
|
||||
"""
|
||||
self._clientRandom = "\x00" * 32
|
||||
self._serverRandom = None
|
||||
self._serverEncryptedChallenge = None
|
||||
self._transport = transport
|
||||
self._username = username
|
||||
self._hostname = hostname
|
||||
|
||||
def generateKeys(self):
|
||||
"""
|
||||
@summary: generate key for license session
|
||||
"""
|
||||
self._masterSecret = sec.generateMicrosoftKey("\x00" * 64, self._clientRandom, self._serverRandom)
|
||||
self._sessionKeyBlob = sec.generateMicrosoftKey(self._masterSecret, self._serverRandom, self._clientRandom)
|
||||
self._macSalt = self._sessionKeyBlob[:16]
|
||||
self._licenseKey = sec.md5_16_32_32(self._sessionKeyBlob[16:], self._clientRandom, self._serverRandom)
|
||||
|
||||
def recv(self, s):
|
||||
"""
|
||||
@summary: receive license packet from PDU layer
|
||||
@return true when license automata is finish
|
||||
"""
|
||||
licPacket = LicPacket()
|
||||
s.readType(licPacket)
|
||||
|
||||
#end of automata
|
||||
if licPacket.bMsgtype.value == MessageType.ERROR_ALERT and licPacket.licensingMessage.dwErrorCode.value == ErrorCode.STATUS_VALID_CLIENT and licPacket.licensingMessage.dwStateTransition.value == StateTransition.ST_NO_TRANSITION:
|
||||
return True
|
||||
|
||||
elif licPacket.bMsgtype.value == MessageType.LICENSE_REQUEST:
|
||||
self._serverRandom = licPacket.licensingMessage.serverRandom.value
|
||||
self.generateKeys()
|
||||
self.sendClientNewLicenseRequest()
|
||||
|
||||
elif licPacket.bMsgtype.value == MessageType.PLATFORM_CHALLENGE:
|
||||
self._serverEncryptedChallenge = licPacket.licensingMessage.encryptedPlatformChallenge.blobData.value
|
||||
self.sendClientChallengeResponse()
|
||||
|
||||
else:
|
||||
raise InvalidExpectedDataException("Not a valid license packet")
|
||||
|
||||
|
||||
return LicPacket(message)
|
||||
def sendClientNewLicenseRequest(self):
|
||||
"""
|
||||
@summary: Create new license request in response to server license request
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241989.aspx
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||
"""
|
||||
message = ClientNewLicenseRequest()
|
||||
message.clientRandom.value = self._clientRandom
|
||||
message.encryptedPreMasterSecret.blobData = String("\x00" * (64 + 8))
|
||||
message.ClientMachineName.blobData = String(self._hostname + "\x00")
|
||||
message.ClientUserName.blobData = String(self._username + "\x00")
|
||||
self._transport.sendLicensePacket(LicPacket(message))
|
||||
|
||||
def sendClientChallengeResponse(self):
|
||||
#it should be TEST in unicode format
|
||||
serverChallenge = rc4.crypt(self._licenseKey, self._serverEncryptedChallenge)
|
||||
|
||||
#generate hwid
|
||||
s = Stream()
|
||||
s.writeType((UInt32Le(2), String(self._username + self._hostname + "\x00" * 20)))
|
||||
hwid = s.getvalue()[:20]
|
||||
|
||||
signature = sec.macData(self._macSalt, serverChallenge + hwid)
|
||||
|
||||
message = ClientPLatformChallengeResponse()
|
||||
message.encryptedPlatformChallengeResponse.blobData.value = self._serverEncryptedChallenge
|
||||
message.encryptedHWID.blobData.value = rc4.crypt(self._licenseKey, hwid)
|
||||
message.MACData.value = signature
|
||||
|
||||
self._transport.sendLicensePacket(LicPacket(message))
|
||||
56
rdpy/protocol/rdp/rc4.py
Normal file
56
rdpy/protocol/rdp/rc4.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
Copyright (C) 2012 Bo Zhu http://about.bozhu.me
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
|
||||
|
||||
def KSA(key):
|
||||
keylength = len(key)
|
||||
|
||||
S = range(256)
|
||||
|
||||
j = 0
|
||||
for i in range(256):
|
||||
j = (j + S[i] + key[i % keylength]) % 256
|
||||
S[i], S[j] = S[j], S[i] # swap
|
||||
|
||||
return S
|
||||
|
||||
|
||||
def PRGA(S):
|
||||
i = 0
|
||||
j = 0
|
||||
while True:
|
||||
i = (i + 1) % 256
|
||||
j = (j + S[i]) % 256
|
||||
S[i], S[j] = S[j], S[i] # swap
|
||||
|
||||
K = S[(S[i] + S[j]) % 256]
|
||||
yield K
|
||||
|
||||
|
||||
def RC4(key):
|
||||
S = KSA(key)
|
||||
return PRGA(S)
|
||||
|
||||
def crypt(key, plaintext):
|
||||
keystream = RC4([ord(c) for c in key])
|
||||
|
||||
return "".join([chr(ord(c) ^ keystream.next()) for c in plaintext])
|
||||
@@ -68,13 +68,13 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def setPerformanceSession(self):
|
||||
"""
|
||||
Set particular flag in RDP stack to avoid wall-paper, theme, menu animation etc...
|
||||
@summary: Set particular flag in RDP stack to avoid wall-paper, theme, menu animation etc...
|
||||
"""
|
||||
self._pduLayer._info.extendedInfo.performanceFlags.value = pdu.data.PerfFlag.PERF_DISABLE_WALLPAPER | pdu.data.PerfFlag.PERF_DISABLE_MENUANIMATIONS | pdu.data.PerfFlag.PERF_DISABLE_CURSOR_SHADOW | pdu.data.PerfFlag.PERF_DISABLE_THEMING | pdu.data.PerfFlag.PERF_DISABLE_FULLWINDOWDRAG
|
||||
|
||||
def setScreen(self, width, height):
|
||||
"""
|
||||
Set screen dim of session
|
||||
@summary: Set screen dim of session
|
||||
@param width: width in pixel of screen
|
||||
@param height: height in pixel of screen
|
||||
"""
|
||||
@@ -84,7 +84,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def setUsername(self, username):
|
||||
"""
|
||||
Set the username for session
|
||||
@summary: Set the username for session
|
||||
@param username: username of session
|
||||
"""
|
||||
#username in PDU info packet
|
||||
@@ -92,7 +92,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def setPassword(self, password):
|
||||
"""
|
||||
Set password for session
|
||||
@summary: Set password for session
|
||||
@param password: password of session
|
||||
"""
|
||||
self.setAutologon()
|
||||
@@ -100,7 +100,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def setDomain(self, domain):
|
||||
"""
|
||||
Set the windows domain of session
|
||||
@summary: Set the windows domain of session
|
||||
@param domain: domain of session
|
||||
"""
|
||||
self._pduLayer._info.domain.value = domain
|
||||
@@ -113,14 +113,14 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def addClientObserver(self, observer):
|
||||
"""
|
||||
Add observer to RDP protocol
|
||||
@summary: Add observer to RDP protocol
|
||||
@param observer: new observer to add
|
||||
"""
|
||||
self._clientObserver.append(observer)
|
||||
|
||||
def removeClientObserver(self, observer):
|
||||
"""
|
||||
Remove observer to RDP protocol stack
|
||||
@summary: Remove observer to RDP protocol stack
|
||||
@param observer: observer to remove
|
||||
"""
|
||||
for i in range(0, len(self._clientObserver)):
|
||||
@@ -130,7 +130,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def onUpdate(self, rectangles):
|
||||
"""
|
||||
Call when a bitmap data is received from update PDU
|
||||
@summary: Call when a bitmap data is received from update PDU
|
||||
@param rectangles: [pdu.BitmapData] struct
|
||||
"""
|
||||
for observer in self._clientObserver:
|
||||
@@ -140,7 +140,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
Call when PDU layer is connected
|
||||
@summary: Call when PDU layer is connected
|
||||
"""
|
||||
self._isReady = True
|
||||
#signal all listener
|
||||
@@ -149,7 +149,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
Event call when RDP stack is closed
|
||||
@summary: Event call when RDP stack is closed
|
||||
"""
|
||||
self._isReady = False
|
||||
for observer in self._clientObserver:
|
||||
@@ -157,7 +157,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def sendPointerEvent(self, x, y, button, isPressed):
|
||||
"""
|
||||
send pointer events
|
||||
@summary: send pointer events
|
||||
@param x: x position of pointer
|
||||
@param y: y position of pointer
|
||||
@param button: 1 or 2 or 3
|
||||
@@ -189,10 +189,44 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
except InvalidValue:
|
||||
log.info("try send pointer event with incorrect position")
|
||||
|
||||
def sendWheelEvent(self, x, y, step, isNegative = False, isHorizontal = False):
|
||||
"""
|
||||
@summary: Send a mouse wheel event
|
||||
@param x: x position of pointer
|
||||
@param y: y position of pointer
|
||||
@param step: number of step rolled
|
||||
@param isHorizontal: horizontal wheel (default is vertical)
|
||||
@param isNegative: is upper (default down)
|
||||
"""
|
||||
if not self._isReady:
|
||||
return
|
||||
|
||||
try:
|
||||
event = pdu.data.PointerEvent()
|
||||
if isHorizontal:
|
||||
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_HWHEEL
|
||||
else:
|
||||
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL
|
||||
|
||||
if isNegative:
|
||||
event.pointerFlags.value |= pdu.data.PointerFlag.PTRFLAGS_WHEEL_NEGATIVE
|
||||
|
||||
event.pointerFlags.value |= (step & pdu.data.PointerFlag.WheelRotationMask)
|
||||
|
||||
#position
|
||||
event.xPos.value = x
|
||||
event.yPos.value = y
|
||||
|
||||
#send proper event
|
||||
self._pduLayer.sendInputEvents([event])
|
||||
|
||||
except InvalidValue:
|
||||
log.info("try send wheel event with incorrect position")
|
||||
|
||||
def sendKeyEventScancode(self, code, isPressed):
|
||||
"""
|
||||
Send a scan code to RDP stack
|
||||
@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
|
||||
"""
|
||||
@@ -215,7 +249,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def sendKeyEventUnicode(self, code, isPressed):
|
||||
"""
|
||||
Send a scan code to RDP stack
|
||||
@summary: Send a scan code to RDP stack
|
||||
@param code: unicode
|
||||
@param isPressed: True if key is pressed and false if it's released
|
||||
"""
|
||||
@@ -236,7 +270,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def sendRefreshOrder(self, left, top, right, bottom):
|
||||
"""
|
||||
Force server to resend a particular zone
|
||||
@summary: Force server to resend a particular zone
|
||||
@param left: left coordinate
|
||||
@param top: top coordinate
|
||||
@param right: right coordinate
|
||||
@@ -253,13 +287,13 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close protocol stack
|
||||
@summary: Close protocol stack
|
||||
"""
|
||||
self._pduLayer.close()
|
||||
|
||||
class RDPServerController(pdu.layer.PDUServerListener):
|
||||
"""
|
||||
Controller use in server side mode
|
||||
@summary: Controller use in server side mode
|
||||
"""
|
||||
def __init__(self, privateKeyFileName, certificateFileName, colorDepth):
|
||||
"""
|
||||
@@ -283,7 +317,7 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close protocol stack
|
||||
@summary: Close protocol stack
|
||||
"""
|
||||
self._pduLayer.close()
|
||||
|
||||
@@ -296,28 +330,28 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
def getUsername(self):
|
||||
"""
|
||||
Must be call after on ready event else always empty string
|
||||
@summary: Must be call after on ready event else always empty string
|
||||
@return: username send by client may be an empty string
|
||||
"""
|
||||
return self._pduLayer._info.userName.value
|
||||
|
||||
def getPassword(self):
|
||||
"""
|
||||
Must be call after on ready event else always empty string
|
||||
@summary: Must be call after on ready event else always empty string
|
||||
@return: password send by client may be an empty string
|
||||
"""
|
||||
return self._pduLayer._info.password.value
|
||||
|
||||
def getDomain(self):
|
||||
"""
|
||||
Must be call after on ready event else always empty string
|
||||
@summary: Must be call after on ready event else always empty string
|
||||
@return: domain send by client may be an empty string
|
||||
"""
|
||||
return self._pduLayer._info.domain.value
|
||||
|
||||
def getCredentials(self):
|
||||
"""
|
||||
Must be call after on ready event else always empty string
|
||||
@summary: Must be call after on ready event else always empty string
|
||||
@return: tuple(domain, username, password)
|
||||
"""
|
||||
return (self.getDomain(), self.getUsername(), self.getPassword())
|
||||
@@ -337,15 +371,15 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
def addServerObserver(self, observer):
|
||||
"""
|
||||
Add observer to RDP protocol
|
||||
@summary: Add observer to RDP protocol
|
||||
@param observer: new observer to add
|
||||
"""
|
||||
self._serverObserver.append(observer)
|
||||
|
||||
def setColorDepth(self, colorDepth):
|
||||
"""
|
||||
Set color depth of session
|
||||
if PDU stack is already connected send a deactive-reactive sequence
|
||||
@summary: Set color depth of session
|
||||
if PDU stack is already connected send a deactive-reactive sequence
|
||||
@param colorDepth: depth of session (15, 16, 24)
|
||||
"""
|
||||
self._colorDepth = colorDepth
|
||||
@@ -357,13 +391,13 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
def setKeyEventUnicodeSupport(self):
|
||||
"""
|
||||
Enable key event in unicode format
|
||||
@summary: Enable key event in unicode format
|
||||
"""
|
||||
self._pduLayer._serverCapabilities[pdu.caps.CapsType.CAPSTYPE_INPUT].capability.inputFlags.value |= pdu.caps.InputFlags.INPUT_FLAG_UNICODE
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
RDP stack is now ready
|
||||
@summary: RDP stack is now ready
|
||||
"""
|
||||
self._isReady = True
|
||||
for observer in self._serverObserver:
|
||||
@@ -371,7 +405,7 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
Event call when RDP stack is closed
|
||||
@summary: Event call when RDP stack is closed
|
||||
"""
|
||||
self._isReady = False
|
||||
for observer in self._serverObserver:
|
||||
@@ -379,7 +413,7 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
def onSlowPathInput(self, slowPathInputEvents):
|
||||
"""
|
||||
Event call when slow path input are available
|
||||
@summary: Event call when slow path input are available
|
||||
@param slowPathInputEvents: [data.SlowPathInputEvent]
|
||||
"""
|
||||
for observer in self._serverObserver:
|
||||
@@ -404,7 +438,7 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
def sendUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
send bitmap update
|
||||
@summary: send bitmap update
|
||||
@param destLeft: xmin position
|
||||
@param destTop: ymin position
|
||||
@param destRight: xmax position because RDP can send bitmap with padding
|
||||
@@ -454,7 +488,7 @@ class ClientFactory(layer.RawLayerClientFactory):
|
||||
|
||||
class ServerFactory(layer.RawLayerServerFactory):
|
||||
"""
|
||||
Factory of Server RDP protocol
|
||||
@summary: Factory of Server RDP protocol
|
||||
"""
|
||||
def __init__(self, privateKeyFileName, certificateFileName, colorDepth):
|
||||
"""
|
||||
@@ -476,7 +510,7 @@ class ServerFactory(layer.RawLayerServerFactory):
|
||||
|
||||
def buildRawLayer(self, addr):
|
||||
"""
|
||||
Function call from twisted and build rdp protocol stack
|
||||
@summary: Function call from twisted and build rdp protocol stack
|
||||
@param addr: destination address
|
||||
"""
|
||||
controller = RDPServerController(self._privateKeyFileName, self._certificateFileName, self._colorDepth)
|
||||
@@ -485,7 +519,7 @@ class ServerFactory(layer.RawLayerServerFactory):
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
Build observer use for connection
|
||||
@summary: Build observer use for connection
|
||||
@param controller: RDP stack controller
|
||||
@param addr: destination address
|
||||
"""
|
||||
@@ -493,7 +527,7 @@ class ServerFactory(layer.RawLayerServerFactory):
|
||||
|
||||
class RDPClientObserver(object):
|
||||
"""
|
||||
Class use to inform all RDP event handle by RDPY
|
||||
@summary: Class use to inform all RDP event handle by RDPY
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@@ -504,19 +538,19 @@ class RDPClientObserver(object):
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
Stack is ready and connected
|
||||
@summary: Stack is ready and connected
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPClientObserver"))
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
Stack is closes
|
||||
@summary: Stack is closes
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver"))
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
Notify bitmap update
|
||||
@summary: Notify bitmap update
|
||||
@param destLeft: xmin position
|
||||
@param destTop: ymin position
|
||||
@param destRight: xmax position because RDP can send bitmap with padding
|
||||
@@ -531,7 +565,7 @@ class RDPClientObserver(object):
|
||||
|
||||
class RDPServerObserver(object):
|
||||
"""
|
||||
Class use to inform all RDP event handle by RDPY
|
||||
@summary: Class use to inform all RDP event handle by RDPY
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@@ -542,20 +576,20 @@ class RDPServerObserver(object):
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
Stack is ready and connected
|
||||
@summary: Stack is ready and connected
|
||||
May be called after an setColorDepth too
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver"))
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
Stack is closes
|
||||
@summary: Stack is closes
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RDPClientObserver"))
|
||||
|
||||
def onKeyEventScancode(self, code, isPressed):
|
||||
"""
|
||||
Event call when a keyboard event is catch in scan code format
|
||||
@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
|
||||
"""
|
||||
@@ -563,7 +597,7 @@ class RDPServerObserver(object):
|
||||
|
||||
def onKeyEventUnicode(self, code, isPressed):
|
||||
"""
|
||||
Event call when a keyboard event is catch in unicode format
|
||||
@summary: Event call when a keyboard event is catch in unicode format
|
||||
@param code: unicode of key
|
||||
@param isPressed: True if key is down
|
||||
"""
|
||||
@@ -571,7 +605,7 @@ class RDPServerObserver(object):
|
||||
|
||||
def onPointerEvent(self, x, y, button, isPressed):
|
||||
"""
|
||||
Event call on mouse event
|
||||
@summary: Event call on mouse event
|
||||
@param x: x position
|
||||
@param y: y position
|
||||
@param button: 1, 2 or 3 button
|
||||
|
||||
@@ -22,6 +22,7 @@ Some use full methods for security in RDP
|
||||
"""
|
||||
|
||||
import sha, md5
|
||||
from rdpy.network.type import Stream, UInt32Le
|
||||
|
||||
def saltedHash(inputData, salt, salt1, salt2):
|
||||
"""
|
||||
@@ -47,6 +48,46 @@ def saltedHash(inputData, salt, salt1, salt2):
|
||||
|
||||
return md5Digest.digest()
|
||||
|
||||
def masterSecret(preMasterSecret, clientRandom, serverRandom):
|
||||
def md5_16_32_32(in0, in1, in2):
|
||||
"""
|
||||
"""
|
||||
@summary: MD5(in0[:16] + in1[:32] + in2[:32])
|
||||
@param in0: in 16
|
||||
@param in1: in 32
|
||||
@param in2: in 32
|
||||
@return MD5(in0[:16] + in1[:32] + in2[:32])
|
||||
"""
|
||||
md5Digest = md5.new()
|
||||
md5Digest.update(in0[:16])
|
||||
md5Digest.update(in1[:32])
|
||||
md5Digest.update(in2[:32])
|
||||
return md5Digest.digest()
|
||||
|
||||
def generateMicrosoftKey(secret, random1, random2):
|
||||
"""
|
||||
@summary: Generate master secret
|
||||
@param secret: secret
|
||||
@param clientRandom : client random
|
||||
@param serverRandom : server random
|
||||
"""
|
||||
return saltedHash("A", secret, random1, random2) + saltedHash("BB", secret, random1, random2) + saltedHash("CCC", secret, random1, random2)
|
||||
|
||||
def macData(macSaltKey, data):
|
||||
sha1Digest = sha.new()
|
||||
md5Digest = md5.new()
|
||||
|
||||
#encode length
|
||||
s = Stream()
|
||||
s.writeType(UInt32Le(len(data)))
|
||||
|
||||
sha1Digest.update(macSaltKey)
|
||||
sha1Digest.update("\x36" * 40)
|
||||
sha1Digest.update(s.getvalue())
|
||||
sha1Digest.update(data)
|
||||
|
||||
sha1Sig = sha1Digest.digest()
|
||||
|
||||
md5Digest.update(macSaltKey)
|
||||
md5Digest.update("\x5c" * 48)
|
||||
md5Digest.update(sha1Sig)
|
||||
|
||||
return md5Digest.digest()
|
||||
@@ -34,12 +34,12 @@ import rle
|
||||
|
||||
class QAdaptor(object):
|
||||
"""
|
||||
Adaptor model with link between protocol
|
||||
And Qt widget
|
||||
@summary: Adaptor model with link between protocol
|
||||
And Qt widget
|
||||
"""
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
"""
|
||||
Interface to send mouse event to protocol stack
|
||||
@summary: Interface to send mouse event to protocol stack
|
||||
@param e: QMouseEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
@@ -47,11 +47,18 @@ class QAdaptor(object):
|
||||
|
||||
def sendKeyEvent(self, e, isPressed):
|
||||
"""
|
||||
Interface to send key event to protocol stack
|
||||
@summary: Interface to send key event to protocol stack
|
||||
@param e: QEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEvent", "QAdaptor"))
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendKeyEvent", "QAdaptor"))
|
||||
|
||||
def sendWheelEvent(self, e):
|
||||
"""
|
||||
@summary: Interface to send wheel event to protocol stack
|
||||
@param e: QWheelEvent
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendWheelEvent", "QAdaptor"))
|
||||
|
||||
def getWidget(self):
|
||||
"""
|
||||
@@ -61,7 +68,7 @@ class QAdaptor(object):
|
||||
|
||||
def closeEvent(self, e):
|
||||
"""
|
||||
Call when you want to close connection
|
||||
@summary: Call when you want to close connection
|
||||
@param: QCloseEvent
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "closeEvent", "QAdaptor"))
|
||||
@@ -77,7 +84,7 @@ def qtImageFormatFromRFBPixelFormat(pixelFormat):
|
||||
|
||||
class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
"""
|
||||
QAdaptor for specific RFB protocol stack
|
||||
@summary: QAdaptor for specific RFB protocol stack
|
||||
is to an RFB observer
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
@@ -97,7 +104,7 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
|
||||
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
|
||||
"""
|
||||
Implement RFBClientObserver interface
|
||||
@summary: Implement RFBClientObserver interface
|
||||
@param width: width of new image
|
||||
@param height: height of new image
|
||||
@param x: x position of new image
|
||||
@@ -136,7 +143,7 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
"""
|
||||
Convert Qt mouse event to RFB mouse event
|
||||
@summary: Convert Qt mouse event to RFB mouse event
|
||||
@param e: qMouseEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
@@ -152,29 +159,37 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
|
||||
def sendKeyEvent(self, e, isPressed):
|
||||
"""
|
||||
Convert Qt key press event to RFB press event
|
||||
@summary: Convert Qt key press event to RFB press event
|
||||
@param e: qKeyEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
self.keyEvent(isPressed, e.nativeVirtualKey())
|
||||
|
||||
def sendWheelEvent(self, e):
|
||||
"""
|
||||
@summary: Convert Qt wheel event to RFB Wheel event
|
||||
@param e: QKeyEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
pass
|
||||
|
||||
def closeEvent(self, e):
|
||||
"""
|
||||
Call when you want to close connection
|
||||
@summary: Call when you want to close connection
|
||||
@param: QCloseEvent
|
||||
"""
|
||||
self._controller.close()
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
Call when stack is close
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
#do something maybe a message
|
||||
pass
|
||||
|
||||
def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
Bitmap transformation to Qt object
|
||||
@summary: Bitmap transformation to Qt object
|
||||
@param width: width of bitmap
|
||||
@param height: height of bitmap
|
||||
@param bitsPerPixel: number of bit per pixel
|
||||
@@ -204,15 +219,15 @@ def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data):
|
||||
if isCompress:
|
||||
buf = bytearray(width * height * 3)
|
||||
rle.bitmap_decompress(buf, width, height, data, 3)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB24)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB888)
|
||||
else:
|
||||
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB24).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB888).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
|
||||
elif bitsPerPixel == 32:
|
||||
if isCompress:
|
||||
buf = bytearray(width * height * 4)
|
||||
rle.bitmap_decompress(buf, width, height, data, 4)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB24)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB32)
|
||||
else:
|
||||
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB32).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
else:
|
||||
@@ -222,7 +237,7 @@ def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data):
|
||||
|
||||
class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
"""
|
||||
Adaptor for RDP client
|
||||
@summary: Adaptor for RDP client
|
||||
"""
|
||||
def __init__(self, controller, width, height):
|
||||
"""
|
||||
@@ -243,7 +258,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
"""
|
||||
Convert Qt mouse event to RDP mouse event
|
||||
@summary: Convert Qt mouse event to RDP mouse event
|
||||
@param e: qMouseEvent
|
||||
@param isPressed: event come from press(true) or release(false) action
|
||||
"""
|
||||
@@ -251,15 +266,15 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
buttonNumber = 0
|
||||
if button == QtCore.Qt.LeftButton:
|
||||
buttonNumber = 1
|
||||
elif button == QtCore.Qt.MidButton:
|
||||
buttonNumber = 2
|
||||
elif button == QtCore.Qt.RightButton:
|
||||
buttonNumber = 2
|
||||
elif button == QtCore.Qt.MidButton:
|
||||
buttonNumber = 3
|
||||
self._controller.sendPointerEvent(e.pos().x(), e.pos().y(), buttonNumber, isPressed)
|
||||
|
||||
def sendKeyEvent(self, e, isPressed):
|
||||
"""
|
||||
Convert Qt key press event to RFB press event
|
||||
@summary: Convert Qt key press event to RDP press event
|
||||
@param e: QKeyEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
@@ -267,17 +282,25 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
if sys.platform == "linux2":
|
||||
code -= 8
|
||||
self._controller.sendKeyEventScancode(code, isPressed)
|
||||
|
||||
def sendWheelEvent(self, e):
|
||||
"""
|
||||
@summary: Convert Qt wheel event to RDP Wheel event
|
||||
@param e: QKeyEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
self._controller.sendWheelEvent(e.pos().x(), e.pos().y(), (abs(e.delta()) / 8) / 15, e.delta() < 0, e.orientation() == QtCore.Qt.Horizontal)
|
||||
|
||||
def closeEvent(self, e):
|
||||
"""
|
||||
Convert Qt close widget event into close stack event
|
||||
@summary: Convert Qt close widget event into close stack event
|
||||
@param e: QCloseEvent
|
||||
"""
|
||||
self._controller.close()
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
Notify bitmap update
|
||||
@summary: Notify bitmap update
|
||||
@param destLeft: xmin position
|
||||
@param destTop: ymin position
|
||||
@param destRight: xmax position because RDP can send bitmap with padding
|
||||
@@ -295,14 +318,14 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
Call when stack is ready
|
||||
@summary: Call when stack is ready
|
||||
"""
|
||||
#do something maybe a loader
|
||||
pass
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
Call when stack is close
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
#do something maybe a message
|
||||
pass
|
||||
@@ -310,7 +333,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
|
||||
class QRemoteDesktop(QtGui.QWidget):
|
||||
"""
|
||||
Qt display widget
|
||||
@summary: Qt display widget
|
||||
"""
|
||||
def __init__(self, adaptor, width, height):
|
||||
"""
|
||||
@@ -334,7 +357,7 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
|
||||
def notifyImage(self, x, y, qimage, width, height):
|
||||
"""
|
||||
Function call from QAdaptor
|
||||
@summary: Function call from QAdaptor
|
||||
@param x: x position of new image
|
||||
@param y: y position of new image
|
||||
@param qimage: new QImage
|
||||
@@ -346,7 +369,7 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
|
||||
def paintEvent(self, e):
|
||||
"""
|
||||
Call when Qt renderer engine estimate that is needed
|
||||
@summary: Call when Qt renderer engine estimate that is needed
|
||||
@param e: QEvent
|
||||
"""
|
||||
#fill buffer image
|
||||
@@ -362,42 +385,49 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""
|
||||
Call when mouse move
|
||||
@summary: Call when mouse move
|
||||
@param event: QMouseEvent
|
||||
"""
|
||||
self._adaptor.sendMouseEvent(event, False)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""
|
||||
Call when button mouse is pressed
|
||||
@summary: Call when button mouse is pressed
|
||||
@param event: QMouseEvent
|
||||
"""
|
||||
self._adaptor.sendMouseEvent(event, True)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
"""
|
||||
Call when button mouse is released
|
||||
@summary: Call when button mouse is released
|
||||
@param event: QMouseEvent
|
||||
"""
|
||||
self._adaptor.sendMouseEvent(event, False)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
Call when button key is pressed
|
||||
@summary: Call when button key is pressed
|
||||
@param event: QKeyEvent
|
||||
"""
|
||||
self._adaptor.sendKeyEvent(event, True)
|
||||
|
||||
def keyReleaseEvent(self, event):
|
||||
"""
|
||||
Call when button key is released
|
||||
@summary: Call when button key is released
|
||||
@param event: QKeyEvent
|
||||
"""
|
||||
self._adaptor.sendKeyEvent(event, False)
|
||||
|
||||
def wheelEvent(self, event):
|
||||
"""
|
||||
@summary: Call on wheel event
|
||||
@param event: QWheelEvent
|
||||
"""
|
||||
self._adaptor.sendWheelEvent(event)
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
Call when widget is closed
|
||||
@summary: Call when widget is closed
|
||||
@param event: QCloseEvent
|
||||
"""
|
||||
self._adaptor.closeEvent(event)
|
||||
Reference in New Issue
Block a user