Add user inputs for RDP stack
This commit is contained in:
@@ -1,15 +1,33 @@
|
||||
'''
|
||||
@author: sylvain
|
||||
'''
|
||||
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof
|
||||
from rdpy.network.const import ConstAttributes, TypeAttributes
|
||||
#
|
||||
# Copyright (c) 2014 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
RDP extended license
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241880.aspx
|
||||
"""
|
||||
|
||||
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof
|
||||
|
||||
@ConstAttributes
|
||||
@TypeAttributes(UInt8)
|
||||
class MessageType(object):
|
||||
'''
|
||||
"""
|
||||
License packet message type
|
||||
'''
|
||||
"""
|
||||
LICENSE_REQUEST = 0x01
|
||||
PLATFORM_CHALLENGE = 0x02
|
||||
NEW_LICENSE = 0x03
|
||||
@@ -19,12 +37,11 @@ class MessageType(object):
|
||||
PLATFORM_CHALLENGE_RESPONSE = 0x15
|
||||
ERROR_ALERT = 0xFF
|
||||
|
||||
@ConstAttributes
|
||||
@TypeAttributes(UInt32Le)
|
||||
|
||||
class ErrorCode(object):
|
||||
'''
|
||||
license error message code
|
||||
'''
|
||||
"""
|
||||
License error message code
|
||||
"""
|
||||
ERR_INVALID_SERVER_CERTIFICATE = 0x00000001
|
||||
ERR_NO_LICENSE = 0x00000002
|
||||
ERR_INVALID_SCOPE = 0x00000004
|
||||
@@ -34,22 +51,20 @@ class ErrorCode(object):
|
||||
ERR_INVALID_PRODUCTID = 0x0000000B
|
||||
ERR_INVALID_MESSAGE_LEN = 0x0000000C
|
||||
ERR_INVALID_MAC = 0x00000003
|
||||
|
||||
@ConstAttributes
|
||||
@TypeAttributes(UInt32Le)
|
||||
|
||||
class StateTransition(object):
|
||||
'''
|
||||
automata state transition
|
||||
'''
|
||||
"""
|
||||
Automata state transition
|
||||
"""
|
||||
ST_TOTAL_ABORT = 0x00000001
|
||||
ST_NO_TRANSITION = 0x00000002
|
||||
ST_RESET_PHASE_TO_START = 0x00000003
|
||||
ST_RESEND_LAST_MESSAGE = 0x00000004
|
||||
|
||||
class LicenceBinaryBlob(CompositeType):
|
||||
'''
|
||||
blob use by license manager to echange security data
|
||||
'''
|
||||
"""
|
||||
Blob use by license manager to echange security data
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.wBlobType = UInt16Le()
|
||||
@@ -57,9 +72,9 @@ class LicenceBinaryBlob(CompositeType):
|
||||
self.blobData = String(readLen = self.wBlobLen, conditional = lambda:self.wBlobLen.value > 0)
|
||||
|
||||
class LicensingErrorMessage(CompositeType):
|
||||
'''
|
||||
license error message
|
||||
'''
|
||||
"""
|
||||
License error message
|
||||
"""
|
||||
def __init__(self, conditional = lambda:True):
|
||||
CompositeType.__init__(self, conditional = conditional)
|
||||
self.dwErrorCode = UInt32Le()
|
||||
@@ -67,14 +82,14 @@ class LicensingErrorMessage(CompositeType):
|
||||
self.blob = LicenceBinaryBlob()
|
||||
|
||||
class LicPacket(CompositeType):
|
||||
'''
|
||||
a license packet
|
||||
'''
|
||||
"""
|
||||
A license packet
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
#preambule
|
||||
self.bMsgtype = UInt8()
|
||||
self.flag = UInt8()
|
||||
self.wMsgSize = UInt16Le(lambda: sizeof(self))
|
||||
self.errorMessage = LicensingErrorMessage(conditional = lambda:self.bMsgtype == MessageType.ERROR_ALERT)
|
||||
self.errorMessage = LicensingErrorMessage(conditional = lambda:self.bMsgtype.value == MessageType.ERROR_ALERT)
|
||||
|
||||
@@ -23,9 +23,9 @@ Implement the main graphic layer
|
||||
In this layer are managed all mains bitmap update orders end user inputs
|
||||
"""
|
||||
|
||||
from rdpy.network.layer import LayerAutomata
|
||||
from rdpy.network.layer import LayerAutomata, LayerMode
|
||||
from rdpy.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
from rdpy.network.error import InvalidExpectedDataException, ErrorReportedFromPeer
|
||||
from rdpy.network.error import InvalidExpectedDataException, ErrorReportedFromPeer, CallPureVirtualFuntion, InvalidType
|
||||
|
||||
import gcc, lic, caps
|
||||
|
||||
@@ -189,7 +189,7 @@ class UpdateType(object):
|
||||
|
||||
class InputMessageType(object):
|
||||
"""
|
||||
Use in slowpath input PDU
|
||||
Use in slow-path input PDU
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240583.aspx
|
||||
"""
|
||||
INPUT_EVENT_SYNC = 0x0000
|
||||
@@ -199,6 +199,30 @@ class InputMessageType(object):
|
||||
INPUT_EVENT_MOUSE = 0x8001
|
||||
INPUT_EVENT_MOUSEX = 0x8002
|
||||
|
||||
class PointerFlag(object):
|
||||
"""
|
||||
Use in Pointer event
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240586.aspx
|
||||
"""
|
||||
PTRFLAGS_HWHEEL = 0x0400
|
||||
PTRFLAGS_WHEEL = 0x0200
|
||||
PTRFLAGS_WHEEL_NEGATIVE = 0x0100
|
||||
WheelRotationMask = 0x01FF
|
||||
PTRFLAGS_MOVE = 0x0800
|
||||
PTRFLAGS_DOWN = 0x8000
|
||||
PTRFLAGS_BUTTON1 = 0x1000
|
||||
PTRFLAGS_BUTTON2 = 0x2000
|
||||
PTRFLAGS_BUTTON3 = 0x4000
|
||||
|
||||
class KeyboardFlag(object):
|
||||
"""
|
||||
Use in scancode key event
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240584.aspx
|
||||
"""
|
||||
KBDFLAGS_EXTENDED = 0x0100
|
||||
KBDFLAGS_DOWN = 0x4000
|
||||
KBDFLAGS_RELEASE = 0x8000
|
||||
|
||||
class ErrorInfo(object):
|
||||
"""
|
||||
Error code use in Error info PDU
|
||||
@@ -643,9 +667,9 @@ class UpdateDataPDU(CompositeType):
|
||||
for example
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240608.aspx
|
||||
"""
|
||||
def __init__(self, updateType = UInt16Le(), updateData = None):
|
||||
def __init__(self, updateType = 0, updateData = None):
|
||||
CompositeType.__init__(self)
|
||||
self.updateType = updateType
|
||||
self.updateType = UInt16Le(updateType)
|
||||
|
||||
def UpdateDataFactory():
|
||||
if self.updateType.value == UpdateType.UPDATETYPE_BITMAP:
|
||||
@@ -665,29 +689,8 @@ class BitmapUpdateDataPDU(CompositeType):
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.numberRectangles = UInt16Le()
|
||||
self.numberRectangles = UInt16Le(lambda:len(self.rectangles._array))
|
||||
self.rectangles = ArrayType(BitmapData, readLen = self.numberRectangles)
|
||||
|
||||
class ClientInputEventPDU(CompositeType):
|
||||
"""
|
||||
PDU use to send client inputs in slow path mode
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc746160.aspx
|
||||
"""
|
||||
def __init__(self, userId = 0, shareId = 0):
|
||||
CompositeType.__init__(self)
|
||||
self.shareDataHeader = ShareDataHeader(lambda:sizeof(self), PDUType2.PDUTYPE2_INPUT, userId, shareId)
|
||||
self.numEvents = UInt16Le()
|
||||
self.pad2Octets = UInt16Le()
|
||||
|
||||
class SlowPathInputData(CompositeType):
|
||||
"""
|
||||
PDU use in slowpath sending client inputs
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240583.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.eventTime = UInt32Le()
|
||||
self.messageType = UInt16Le()
|
||||
|
||||
class BitmapCompressedDataHeader(CompositeType):
|
||||
"""
|
||||
@@ -720,17 +723,114 @@ class BitmapData(CompositeType):
|
||||
self.bitmapLength = UInt16Le()
|
||||
self.bitmapComprHdr = BitmapCompressedDataHeader(conditional = lambda:(not (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR)))
|
||||
self.bitmapDataStream = String(readLen = UInt16Le(lambda:(self.bitmapLength.value if (self.flags.value | BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
|
||||
|
||||
class ClientInputEventPDU(CompositeType):
|
||||
"""
|
||||
PDU use to send client inputs in slow path mode
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc746160.aspx
|
||||
"""
|
||||
def __init__(self, userId = 0, shareId = 0):
|
||||
CompositeType.__init__(self)
|
||||
self.shareDataHeader = ShareDataHeader(lambda:sizeof(self), PDUType2.PDUTYPE2_INPUT, userId, shareId)
|
||||
self.numEvents = UInt16Le(lambda:len(self.slowPathInputEvents._array))
|
||||
self.pad2Octets = UInt16Le()
|
||||
self.slowPathInputEvents = ArrayType(SlowPathInputEvent, readLen = self.numEvents)
|
||||
|
||||
class SlowPathInputEvent(CompositeType):
|
||||
"""
|
||||
PDU use in slow-path sending client inputs
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240583.aspx
|
||||
"""
|
||||
def __init__(self, messageData = None):
|
||||
CompositeType.__init__(self)
|
||||
self.eventTime = UInt32Le()
|
||||
|
||||
def MessageTypeFactory(event):
|
||||
"""
|
||||
retrieve message type from event type
|
||||
"""
|
||||
if isinstance(event, PointerEvent):
|
||||
return InputMessageType.INPUT_EVENT_MOUSE
|
||||
elif isinstance(event, ScancodeKeyEvent):
|
||||
return InputMessageType.INPUT_EVENT_SCANCODE
|
||||
elif isinstance(event, UnicodeKeyEvent):
|
||||
return InputMessageType.INPUT_EVENT_UNICODE
|
||||
else:
|
||||
return None
|
||||
|
||||
self.messageType = UInt16Le(lambda:MessageTypeFactory(self.slowPathInputData._value))
|
||||
|
||||
def SlowPathInputDataFactory():
|
||||
if self.messageType.value == InputMessageType.INPUT_EVENT_MOUSE:
|
||||
return PointerEvent()
|
||||
|
||||
if messageData is None:
|
||||
messageData = SlowPathInputDataFactory
|
||||
|
||||
self.slowPathInputData = FactoryType(messageData)
|
||||
|
||||
class PointerEvent(CompositeType):
|
||||
"""
|
||||
Event use to communicate mouse position
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240586.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.pointerFlags = UInt16Le()
|
||||
self.xPos = UInt16Le()
|
||||
self.yPos = UInt16Le()
|
||||
|
||||
class ScancodeKeyEvent(CompositeType):
|
||||
"""
|
||||
Event use to communicate keyboard informations
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240584.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.keyboardFlags = UInt16Le()
|
||||
self.keyCode = UInt16Le()
|
||||
self.pad2Octets = UInt16Le()
|
||||
|
||||
class UnicodeKeyEvent(CompositeType):
|
||||
"""
|
||||
Event use to communicate keyboard informations
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240585.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.keyboardFlags = UInt16Le()
|
||||
self.unicode = UInt16Le()
|
||||
self.pad2Octets = UInt16Le()
|
||||
|
||||
|
||||
class PDUClientListener(object):
|
||||
"""
|
||||
Interface for PDU client automata listener
|
||||
"""
|
||||
def recvBitmapUpdateDataPDU(self, rectangles):
|
||||
"""
|
||||
call when a bitmap data is received from update PDU
|
||||
@param rectangles: [pdu.BitmapData] struct
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener"))
|
||||
|
||||
class PDU(LayerAutomata):
|
||||
"""
|
||||
Global channel for mcs that handle session
|
||||
identification user, licensing management, and capabilities exchange
|
||||
"""
|
||||
def __init__(self, mode, controller):
|
||||
def __init__(self, listener):
|
||||
"""
|
||||
@param mode: LayerMode
|
||||
@param controller: controller use to inform orders
|
||||
@param listener: listener use to inform orders
|
||||
"""
|
||||
mode = None
|
||||
if isinstance(listener, PDUClientListener):
|
||||
mode = LayerMode.CLIENT
|
||||
#set client listener
|
||||
self._clientListener = listener
|
||||
else:
|
||||
raise InvalidType("PDU Layer expect PDUClientListener as listener")
|
||||
|
||||
LayerAutomata.__init__(self, mode, None)
|
||||
#logon info send from client to server
|
||||
self._info = RDPInfo(extendedInfoConditional = lambda:self._transport.getGCCServerSettings().core.rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)
|
||||
@@ -768,8 +868,7 @@ class PDU(LayerAutomata):
|
||||
#share id between client and server
|
||||
self._shareId = 0
|
||||
|
||||
#rdp controller
|
||||
self._controller = controller
|
||||
self._isConnected = False
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
@@ -809,9 +908,9 @@ class PDU(LayerAutomata):
|
||||
data.readType(validClientPdu)
|
||||
|
||||
if not validClientPdu.errorMessage._is_readed:
|
||||
raise InvalidExpectedDataException("Waiting valid client pdu : rdpy doesn't support licensing neg")
|
||||
raise InvalidExpectedDataException("Waiting valid client PDU : rdpy doesn't support licensing nego")
|
||||
|
||||
if not (validClientPdu.errorMessage.dwErrorCode == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.errorMessage.dwStateTransition == lic.StateTransition.ST_NO_TRANSITION):
|
||||
if not (validClientPdu.errorMessage.dwErrorCode.value == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.errorMessage.dwStateTransition.value == lic.StateTransition.ST_NO_TRANSITION):
|
||||
raise InvalidExpectedDataException("Server refuse licensing negotiation")
|
||||
|
||||
self.setNextState(self.recvDemandActivePDU)
|
||||
@@ -865,7 +964,7 @@ class PDU(LayerAutomata):
|
||||
|
||||
def recvServerControlCooperatePDU(self, data):
|
||||
"""
|
||||
Receive control cooperate pdu from server
|
||||
Receive control cooperate PDU from server
|
||||
@param data: Stream from transport layer
|
||||
"""
|
||||
dataPDU = self.readDataPDU(data)
|
||||
@@ -891,9 +990,9 @@ class PDU(LayerAutomata):
|
||||
dataPDU = self.readDataPDU(data)
|
||||
if dataPDU.shareDataHeader.pduType2.value != PDUType2.PDUTYPE2_FONTMAP:
|
||||
raise InvalidExpectedDataException("Error in PDU layer automata : expected fontMapPDU")
|
||||
print "client is now connected"
|
||||
if not self._presentation is None:
|
||||
self._presentation.connect()
|
||||
|
||||
#here i'm connected
|
||||
self._isConnected = True
|
||||
self.setNextState(self.recvDataPDU)
|
||||
|
||||
def recvDataPDU(self, data):
|
||||
@@ -903,7 +1002,7 @@ class PDU(LayerAutomata):
|
||||
"""
|
||||
dataPDU = self.readDataPDU(data)
|
||||
if dataPDU.shareDataHeader.pduType2.value == PDUType2.PDUTYPE2_UPDATE and dataPDU.pduData._value.updateType.value == UpdateType.UPDATETYPE_BITMAP:
|
||||
self._controller.recvBitmapUpdateDataPDU(dataPDU.pduData._value.updateData._value)
|
||||
self._clientListener.recvBitmapUpdateDataPDU(dataPDU.pduData._value.updateData._value.rectangles._array)
|
||||
|
||||
|
||||
def sendConfirmActivePDU(self):
|
||||
@@ -958,14 +1057,23 @@ class PDU(LayerAutomata):
|
||||
controlRequestPDU = DataPDU(PDUType2.PDUTYPE2_CONTROL, ControlDataPDU(Action.CTRLACTION_REQUEST_CONTROL), self._transport.getUserId(), self._shareId)
|
||||
self._transport.send(controlRequestPDU)
|
||||
|
||||
#send persistent list pdu I don't know why this packet is rejected maybe beacause we made a 0 size bitmapcache capability
|
||||
#send persistent list pdu I don't know why this packet is rejected maybe because we made a 0 size bitmapcache capability
|
||||
#persistentListPDU = PersistentListPDU(self._transport.getUserId(), self._shareId)
|
||||
#persistentListPDU.bitMask = UInt16Le(PersistentKeyListFlag.PERSIST_FIRST_PDU | PersistentKeyListFlag.PERSIST_LAST_PDU)
|
||||
#self._transport.send(persistentListPDU)
|
||||
|
||||
#deprecated font list pdu
|
||||
fontListPDU = DataPDU(PDUType2.PDUTYPE2_FONTLIST, FontListDataPDU(), self._transport.getUserId(), self._shareId)
|
||||
self._transport.send(fontListPDU)
|
||||
|
||||
self.setNextState(self.recvServerSynchronizePDU)
|
||||
|
||||
def sendInputEvents(self, pointerEvents):
|
||||
"""
|
||||
send client input events
|
||||
@param pointerEvents: list of pointer events
|
||||
"""
|
||||
pdu = ClientInputEventPDU(self._transport.getUserId(), self._shareId)
|
||||
pdu.slowPathInputEvents._array = [SlowPathInputEvent(x) for x in pointerEvents]
|
||||
self._transport.send(pdu)
|
||||
|
||||
|
||||
@@ -3,46 +3,126 @@
|
||||
'''
|
||||
from twisted.internet import protocol
|
||||
from rdpy.network.layer import LayerMode
|
||||
from rdpy.network.error import CallPureVirtualFuntion
|
||||
from rdpy.network.error import CallPureVirtualFuntion, InvalidValue
|
||||
import tpkt, tpdu, mcs, pdu
|
||||
|
||||
class RDPController(object):
|
||||
class RDPController(pdu.PDUClientListener):
|
||||
"""
|
||||
use to decode and dispatch to observer PDU messages and orders
|
||||
"""
|
||||
def __init__(self, mode):
|
||||
'''
|
||||
@param mode: mode of generate layer by controller
|
||||
"""
|
||||
@param mode: mode of generate layer by listener
|
||||
@param observer: observer
|
||||
'''
|
||||
"""
|
||||
#list of observer
|
||||
self._clientObserver = []
|
||||
#transport layer
|
||||
self._pduLayer = pdu.PDU(mode, self)
|
||||
self._pduLayer = pdu.PDU(self)
|
||||
|
||||
def getPDULayer(self):
|
||||
"""
|
||||
@return: pdu layer use by controller
|
||||
@return: PDU layer use by controller
|
||||
"""
|
||||
return self._pduLayer
|
||||
|
||||
def addClientObserver(self, observer):
|
||||
'''
|
||||
add observer to rdp protocol
|
||||
"""
|
||||
add observer to RDP protocol
|
||||
@param observer: new observer to add
|
||||
'''
|
||||
"""
|
||||
self._clientObserver.append(observer)
|
||||
observer._controller = self
|
||||
observer._clientListener = self
|
||||
|
||||
def recvBitmapUpdateDataPDU(self, bitmapUpdateData):
|
||||
'''
|
||||
call when a bitmap data is received from update pdu
|
||||
@param bitmapData: pdu.BitmapData struct
|
||||
'''
|
||||
def recvBitmapUpdateDataPDU(self, rectangles):
|
||||
"""
|
||||
call when a bitmap data is received from update PDU
|
||||
@param rectangles: [pdu.BitmapData] struct
|
||||
"""
|
||||
for observer in self._clientObserver:
|
||||
#for each rectangle in update PDU
|
||||
for rectangle in bitmapUpdateData.rectangles._array:
|
||||
for rectangle in rectangles:
|
||||
observer.onBitmapUpdate(rectangle.destLeft.value, rectangle.destTop.value, rectangle.destRight.value, rectangle.destBottom.value, rectangle.width.value, rectangle.height.value, rectangle.bitsPerPixel.value, rectangle.flags.value & pdu.BitmapFlag.BITMAP_COMPRESSION, rectangle.bitmapDataStream.value)
|
||||
|
||||
def sendPointerEvent(self, x, y, button, isPressed):
|
||||
"""
|
||||
send pointer events
|
||||
@param x: x position of pointer
|
||||
@param y: y position of pointer
|
||||
@param button: 1 or 2 or 3
|
||||
@param isPressed: true if button is pressed or false if it's released
|
||||
"""
|
||||
if not self._pduLayer._isConnected:
|
||||
return
|
||||
|
||||
try:
|
||||
event = pdu.PointerEvent()
|
||||
if isPressed:
|
||||
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_DOWN
|
||||
|
||||
if button == 1:
|
||||
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON1
|
||||
elif button == 2:
|
||||
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON2
|
||||
elif button == 3:
|
||||
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_BUTTON3
|
||||
else:
|
||||
event.pointerFlags.value |= pdu.PointerFlag.PTRFLAGS_MOVE
|
||||
|
||||
#position
|
||||
event.xPos.value = x
|
||||
event.yPos.value = y
|
||||
|
||||
#send proper event
|
||||
self._pduLayer.sendInputEvents([event])
|
||||
|
||||
except InvalidValue:
|
||||
print "try send pointer event with incorrect position"
|
||||
|
||||
def sendKeyEventScancode(self, code, isPressed):
|
||||
"""
|
||||
Send a scan code to RDP stack
|
||||
@param code: scan code
|
||||
@param isPressed: True if key is pressed and false if it's released
|
||||
"""
|
||||
if not self._pduLayer._isConnected:
|
||||
return
|
||||
|
||||
try:
|
||||
event = pdu.ScancodeKeyEvent()
|
||||
event.keyCode.value = code
|
||||
if isPressed:
|
||||
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_DOWN
|
||||
else:
|
||||
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE
|
||||
|
||||
#send event
|
||||
self._pduLayer.sendInputEvents([event])
|
||||
|
||||
except InvalidValue:
|
||||
print "try send bad key event"
|
||||
|
||||
def sendKeyEventUnicode(self, code, isPressed):
|
||||
"""
|
||||
Send a scan code to RDP stack
|
||||
@param code: unicode
|
||||
@param isPressed: True if key is pressed and false if it's released
|
||||
"""
|
||||
if not self._pduLayer._isConnected:
|
||||
return
|
||||
|
||||
try:
|
||||
event = pdu.UnicodeKeyEvent()
|
||||
event.unicode.value = code
|
||||
if not isPressed:
|
||||
event.keyboardFlags.value |= pdu.KeyboardFlag.KBDFLAGS_RELEASE
|
||||
|
||||
#send event
|
||||
self._pduLayer.sendInputEvents([event])
|
||||
|
||||
except InvalidValue:
|
||||
print "try send bad key event"
|
||||
|
||||
|
||||
class ClientFactory(protocol.Factory):
|
||||
"""
|
||||
@@ -82,18 +162,18 @@ class ServerFactory(protocol.Factory):
|
||||
Factory of Serrve RDP protocol
|
||||
'''
|
||||
def __init__(self, privateKeyFileName, certificateFileName):
|
||||
'''
|
||||
"""
|
||||
@param privateKeyFileName: file contain server private key
|
||||
@param certficiateFileName: file that contain publi key
|
||||
'''
|
||||
"""
|
||||
self._privateKeyFileName = privateKeyFileName
|
||||
self._certificateFileName = certificateFileName
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
'''
|
||||
"""
|
||||
Function call from twisted and build rdp protocol stack
|
||||
@param addr: destination address
|
||||
'''
|
||||
"""
|
||||
pduLayer = pdu.PDU(LayerMode.SERVER)
|
||||
#pduLayer.getController().addObserver(self.buildObserver())
|
||||
return tpkt.TPKT(tpdu.createServer(mcs.createServer(pduLayer), self._privateKeyFileName, self._certificateFileName));
|
||||
@@ -110,11 +190,12 @@ class RDPClientObserver(object):
|
||||
'''
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@param controller: RDP controller use to interact with protocol
|
||||
@param listener: RDP listener use to interact with protocol
|
||||
"""
|
||||
self._controller = controller
|
||||
self._controller.addClientObserver(self)
|
||||
|
||||
|
||||
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
'''
|
||||
notify bitmap update
|
||||
|
||||
852
rdpy/protocol/rfb/pyDes.py
Normal file
852
rdpy/protocol/rfb/pyDes.py
Normal file
@@ -0,0 +1,852 @@
|
||||
#############################################################################
|
||||
# Documentation #
|
||||
#############################################################################
|
||||
|
||||
# Author: Todd Whiteman
|
||||
# Date: 16th March, 2009
|
||||
# Verion: 2.0.0
|
||||
# License: Public Domain - free to do as you wish
|
||||
# Homepage: http://twhiteman.netfirms.com/des.html
|
||||
#
|
||||
# This is a pure python implementation of the DES encryption algorithm.
|
||||
# It's pure python to avoid portability issues, since most DES
|
||||
# implementations are programmed in C (for performance reasons).
|
||||
#
|
||||
# Triple DES class is also implemented, utilising the DES base. Triple DES
|
||||
# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
|
||||
#
|
||||
# See the README.txt that should come with this python module for the
|
||||
# implementation methods used.
|
||||
#
|
||||
# Thanks to:
|
||||
# * David Broadwell for ideas, comments and suggestions.
|
||||
# * Mario Wolff for pointing out and debugging some triple des CBC errors.
|
||||
# * Santiago Palladino for providing the PKCS5 padding technique.
|
||||
# * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
|
||||
#
|
||||
"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.
|
||||
|
||||
Class initialization
|
||||
--------------------
|
||||
pyDes.des(key, [mode], [IV], [pad], [padmode])
|
||||
pyDes.triple_des(key, [mode], [IV], [pad], [padmode])
|
||||
|
||||
key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
|
||||
for Triple DES
|
||||
mode -> Optional argument for encryption type, can be either
|
||||
pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
|
||||
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
|
||||
Length must be 8 bytes.
|
||||
pad -> Optional argument, set the pad character (PAD_NORMAL) to use during
|
||||
all encrypt/decrpt operations done with this instance.
|
||||
padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
|
||||
to use during all encrypt/decrpt operations done with this instance.
|
||||
|
||||
I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
|
||||
padding issues, as the padding can be removed unambiguously upon decrypting
|
||||
data that was encrypted using PAD_PKCS5 padmode.
|
||||
|
||||
Common methods
|
||||
--------------
|
||||
encrypt(data, [pad], [padmode])
|
||||
decrypt(data, [pad], [padmode])
|
||||
|
||||
data -> Bytes to be encrypted/decrypted
|
||||
pad -> Optional argument. Only when using padmode of PAD_NORMAL. For
|
||||
encryption, adds this characters to the end of the data block when
|
||||
data is not a multiple of 8 bytes. For decryption, will remove the
|
||||
trailing characters that match this pad character from the last 8
|
||||
bytes of the unencrypted data block.
|
||||
padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
|
||||
or PAD_PKCS5). Defaults to PAD_NORMAL.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
from pyDes import *
|
||||
|
||||
data = "Please encrypt my data"
|
||||
k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
|
||||
# For Python3, you'll need to use bytes, i.e.:
|
||||
# data = b"Please encrypt my data"
|
||||
# k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
|
||||
d = k.encrypt(data)
|
||||
print "Encrypted: %r" % d
|
||||
print "Decrypted: %r" % k.decrypt(d)
|
||||
assert k.decrypt(d, padmode=PAD_PKCS5) == data
|
||||
|
||||
|
||||
See the module source (pyDes.py) for more examples of use.
|
||||
You can also run the pyDes.py file without and arguments to see a simple test.
|
||||
|
||||
Note: This code was not written for high-end systems needing a fast
|
||||
implementation, but rather a handy portable solution with small usage.
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
# _pythonMajorVersion is used to handle Python2 and Python3 differences.
|
||||
_pythonMajorVersion = sys.version_info[0]
|
||||
|
||||
# Modes of crypting / cyphering
|
||||
ECB = 0
|
||||
CBC = 1
|
||||
|
||||
# Modes of padding
|
||||
PAD_NORMAL = 1
|
||||
PAD_PKCS5 = 2
|
||||
|
||||
# PAD_PKCS5: is a method that will unambiguously remove all padding
|
||||
# characters after decryption, when originally encrypted with
|
||||
# this padding mode.
|
||||
# For a good description of the PKCS5 padding technique, see:
|
||||
# http://www.faqs.org/rfcs/rfc1423.html
|
||||
|
||||
# The base class shared by des and triple des.
|
||||
class _baseDes(object):
|
||||
def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
|
||||
if IV:
|
||||
IV = self._guardAgainstUnicode(IV)
|
||||
if pad:
|
||||
pad = self._guardAgainstUnicode(pad)
|
||||
self.block_size = 8
|
||||
# Sanity checking of arguments.
|
||||
if pad and padmode == PAD_PKCS5:
|
||||
raise ValueError("Cannot use a pad character with PAD_PKCS5")
|
||||
if IV and len(IV) != self.block_size:
|
||||
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
|
||||
|
||||
# Set the passed in variables
|
||||
self._mode = mode
|
||||
self._iv = IV
|
||||
self._padding = pad
|
||||
self._padmode = padmode
|
||||
|
||||
def getKey(self):
|
||||
"""getKey() -> bytes"""
|
||||
return self.__key
|
||||
|
||||
def setKey(self, key):
|
||||
"""Will set the crypting key for this object."""
|
||||
key = self._guardAgainstUnicode(key)
|
||||
self.__key = key
|
||||
|
||||
def getMode(self):
|
||||
"""getMode() -> pyDes.ECB or pyDes.CBC"""
|
||||
return self._mode
|
||||
|
||||
def setMode(self, mode):
|
||||
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
|
||||
self._mode = mode
|
||||
|
||||
def getPadding(self):
|
||||
"""getPadding() -> bytes of length 1. Padding character."""
|
||||
return self._padding
|
||||
|
||||
def setPadding(self, pad):
|
||||
"""setPadding() -> bytes of length 1. Padding character."""
|
||||
if pad is not None:
|
||||
pad = self._guardAgainstUnicode(pad)
|
||||
self._padding = pad
|
||||
|
||||
def getPadMode(self):
|
||||
"""getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
|
||||
return self._padmode
|
||||
|
||||
def setPadMode(self, mode):
|
||||
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
|
||||
self._padmode = mode
|
||||
|
||||
def getIV(self):
|
||||
"""getIV() -> bytes"""
|
||||
return self._iv
|
||||
|
||||
def setIV(self, IV):
|
||||
"""Will set the Initial Value, used in conjunction with CBC mode"""
|
||||
if not IV or len(IV) != self.block_size:
|
||||
raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
|
||||
IV = self._guardAgainstUnicode(IV)
|
||||
self._iv = IV
|
||||
|
||||
def _padData(self, data, pad, padmode):
|
||||
# Pad data depending on the mode
|
||||
if padmode is None:
|
||||
# Get the default padding mode.
|
||||
padmode = self.getPadMode()
|
||||
if pad and padmode == PAD_PKCS5:
|
||||
raise ValueError("Cannot use a pad character with PAD_PKCS5")
|
||||
|
||||
if padmode == PAD_NORMAL:
|
||||
if len(data) % self.block_size == 0:
|
||||
# No padding required.
|
||||
return data
|
||||
|
||||
if not pad:
|
||||
# Get the default padding.
|
||||
pad = self.getPadding()
|
||||
if not pad:
|
||||
raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
|
||||
data += (self.block_size - (len(data) % self.block_size)) * pad
|
||||
|
||||
elif padmode == PAD_PKCS5:
|
||||
pad_len = 8 - (len(data) % self.block_size)
|
||||
if _pythonMajorVersion < 3:
|
||||
data += pad_len * chr(pad_len)
|
||||
else:
|
||||
data += bytes([pad_len] * pad_len)
|
||||
|
||||
return data
|
||||
|
||||
def _unpadData(self, data, pad, padmode):
|
||||
# Unpad data depending on the mode.
|
||||
if not data:
|
||||
return data
|
||||
if pad and padmode == PAD_PKCS5:
|
||||
raise ValueError("Cannot use a pad character with PAD_PKCS5")
|
||||
if padmode is None:
|
||||
# Get the default padding mode.
|
||||
padmode = self.getPadMode()
|
||||
|
||||
if padmode == PAD_NORMAL:
|
||||
if not pad:
|
||||
# Get the default padding.
|
||||
pad = self.getPadding()
|
||||
if pad:
|
||||
data = data[:-self.block_size] + \
|
||||
data[-self.block_size:].rstrip(pad)
|
||||
|
||||
elif padmode == PAD_PKCS5:
|
||||
if _pythonMajorVersion < 3:
|
||||
pad_len = ord(data[-1])
|
||||
else:
|
||||
pad_len = data[-1]
|
||||
data = data[:-pad_len]
|
||||
|
||||
return data
|
||||
|
||||
def _guardAgainstUnicode(self, data):
|
||||
# Only accept byte strings or ascii unicode values, otherwise
|
||||
# there is no way to correctly decode the data into bytes.
|
||||
if _pythonMajorVersion < 3:
|
||||
if isinstance(data, unicode):
|
||||
raise ValueError("pyDes can only work with bytes, not Unicode strings.")
|
||||
else:
|
||||
if isinstance(data, str):
|
||||
# Only accept ascii unicode values.
|
||||
try:
|
||||
return data.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
raise ValueError("pyDes can only work with encoded strings, not Unicode.")
|
||||
return data
|
||||
|
||||
#############################################################################
|
||||
# DES #
|
||||
#############################################################################
|
||||
class des(_baseDes):
|
||||
"""DES encryption/decrytpion class
|
||||
|
||||
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
|
||||
|
||||
pyDes.des(key,[mode], [IV])
|
||||
|
||||
key -> Bytes containing the encryption key, must be exactly 8 bytes
|
||||
mode -> Optional argument for encryption type, can be either pyDes.ECB
|
||||
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
|
||||
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
|
||||
Must be 8 bytes in length.
|
||||
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
|
||||
during all encrypt/decrpt operations done with this instance.
|
||||
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
|
||||
PAD_PKCS5) to use during all encrypt/decrpt operations done
|
||||
with this instance.
|
||||
"""
|
||||
|
||||
|
||||
# Permutation and translation tables for DES
|
||||
__pc1 = [56, 48, 40, 32, 24, 16, 8,
|
||||
0, 57, 49, 41, 33, 25, 17,
|
||||
9, 1, 58, 50, 42, 34, 26,
|
||||
18, 10, 2, 59, 51, 43, 35,
|
||||
62, 54, 46, 38, 30, 22, 14,
|
||||
6, 61, 53, 45, 37, 29, 21,
|
||||
13, 5, 60, 52, 44, 36, 28,
|
||||
20, 12, 4, 27, 19, 11, 3
|
||||
]
|
||||
|
||||
# number left rotations of pc1
|
||||
__left_rotations = [
|
||||
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
|
||||
]
|
||||
|
||||
# permuted choice key (table 2)
|
||||
__pc2 = [
|
||||
13, 16, 10, 23, 0, 4,
|
||||
2, 27, 14, 5, 20, 9,
|
||||
22, 18, 11, 3, 25, 7,
|
||||
15, 6, 26, 19, 12, 1,
|
||||
40, 51, 30, 36, 46, 54,
|
||||
29, 39, 50, 44, 32, 47,
|
||||
43, 48, 38, 55, 33, 52,
|
||||
45, 41, 49, 35, 28, 31
|
||||
]
|
||||
|
||||
# initial permutation IP
|
||||
__ip = [57, 49, 41, 33, 25, 17, 9, 1,
|
||||
59, 51, 43, 35, 27, 19, 11, 3,
|
||||
61, 53, 45, 37, 29, 21, 13, 5,
|
||||
63, 55, 47, 39, 31, 23, 15, 7,
|
||||
56, 48, 40, 32, 24, 16, 8, 0,
|
||||
58, 50, 42, 34, 26, 18, 10, 2,
|
||||
60, 52, 44, 36, 28, 20, 12, 4,
|
||||
62, 54, 46, 38, 30, 22, 14, 6
|
||||
]
|
||||
|
||||
# Expansion table for turning 32 bit blocks into 48 bits
|
||||
__expansion_table = [
|
||||
31, 0, 1, 2, 3, 4,
|
||||
3, 4, 5, 6, 7, 8,
|
||||
7, 8, 9, 10, 11, 12,
|
||||
11, 12, 13, 14, 15, 16,
|
||||
15, 16, 17, 18, 19, 20,
|
||||
19, 20, 21, 22, 23, 24,
|
||||
23, 24, 25, 26, 27, 28,
|
||||
27, 28, 29, 30, 31, 0
|
||||
]
|
||||
|
||||
# The (in)famous S-boxes
|
||||
__sbox = [
|
||||
# S1
|
||||
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
|
||||
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
|
||||
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
|
||||
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
|
||||
|
||||
# S2
|
||||
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
|
||||
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
|
||||
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
|
||||
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
|
||||
|
||||
# S3
|
||||
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
|
||||
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
|
||||
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
|
||||
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
|
||||
|
||||
# S4
|
||||
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
|
||||
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
|
||||
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
|
||||
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
|
||||
|
||||
# S5
|
||||
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
|
||||
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
|
||||
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
|
||||
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
|
||||
|
||||
# S6
|
||||
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
|
||||
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
|
||||
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
|
||||
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
|
||||
|
||||
# S7
|
||||
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
|
||||
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
|
||||
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
|
||||
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
|
||||
|
||||
# S8
|
||||
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
|
||||
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
|
||||
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
|
||||
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
|
||||
]
|
||||
|
||||
|
||||
# 32-bit permutation function P used on the output of the S-boxes
|
||||
__p = [
|
||||
15, 6, 19, 20, 28, 11,
|
||||
27, 16, 0, 14, 22, 25,
|
||||
4, 17, 30, 9, 1, 7,
|
||||
23,13, 31, 26, 2, 8,
|
||||
18, 12, 29, 5, 21, 10,
|
||||
3, 24
|
||||
]
|
||||
|
||||
# final permutation IP^-1
|
||||
__fp = [
|
||||
39, 7, 47, 15, 55, 23, 63, 31,
|
||||
38, 6, 46, 14, 54, 22, 62, 30,
|
||||
37, 5, 45, 13, 53, 21, 61, 29,
|
||||
36, 4, 44, 12, 52, 20, 60, 28,
|
||||
35, 3, 43, 11, 51, 19, 59, 27,
|
||||
34, 2, 42, 10, 50, 18, 58, 26,
|
||||
33, 1, 41, 9, 49, 17, 57, 25,
|
||||
32, 0, 40, 8, 48, 16, 56, 24
|
||||
]
|
||||
|
||||
# Type of crypting being done
|
||||
ENCRYPT = 0x00
|
||||
DECRYPT = 0x01
|
||||
|
||||
# Initialisation
|
||||
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
|
||||
# Sanity checking of arguments.
|
||||
if len(key) != 8:
|
||||
raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
|
||||
_baseDes.__init__(self, mode, IV, pad, padmode)
|
||||
self.key_size = 8
|
||||
|
||||
self.L = []
|
||||
self.R = []
|
||||
self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16)
|
||||
self.final = []
|
||||
|
||||
self.setKey(key)
|
||||
|
||||
def setKey(self, key):
|
||||
"""Will set the crypting key for this object. Must be 8 bytes."""
|
||||
_baseDes.setKey(self, key)
|
||||
self.__create_sub_keys()
|
||||
|
||||
def __String_to_BitList(self, data):
|
||||
"""Turn the string data, into a list of bits (1, 0)'s"""
|
||||
if _pythonMajorVersion < 3:
|
||||
# Turn the strings into integers. Python 3 uses a bytes
|
||||
# class, which already has this behaviour.
|
||||
data = [ord(c) for c in data]
|
||||
l = len(data) * 8
|
||||
result = [0] * l
|
||||
pos = 0
|
||||
for ch in data:
|
||||
i = 7
|
||||
while i >= 0:
|
||||
if ch & (1 << i) != 0:
|
||||
result[pos] = 1
|
||||
else:
|
||||
result[pos] = 0
|
||||
pos += 1
|
||||
i -= 1
|
||||
|
||||
return result
|
||||
|
||||
def __BitList_to_String(self, data):
|
||||
"""Turn the list of bits -> data, into a string"""
|
||||
result = []
|
||||
pos = 0
|
||||
c = 0
|
||||
while pos < len(data):
|
||||
c += data[pos] << (7 - (pos % 8))
|
||||
if (pos % 8) == 7:
|
||||
result.append(c)
|
||||
c = 0
|
||||
pos += 1
|
||||
|
||||
if _pythonMajorVersion < 3:
|
||||
return ''.join([ chr(c) for c in result ])
|
||||
else:
|
||||
return bytes(result)
|
||||
|
||||
def __permutate(self, table, block):
|
||||
"""Permutate this block with the specified table"""
|
||||
return list(map(lambda x: block[x], table))
|
||||
|
||||
# Transform the secret key, so that it is ready for data processing
|
||||
# Create the 16 subkeys, K[1] - K[16]
|
||||
def __create_sub_keys(self):
|
||||
"""Create the 16 subkeys K[1] to K[16] from the given key"""
|
||||
key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
|
||||
i = 0
|
||||
# Split into Left and Right sections
|
||||
self.L = key[:28]
|
||||
self.R = key[28:]
|
||||
while i < 16:
|
||||
j = 0
|
||||
# Perform circular left shifts
|
||||
while j < des.__left_rotations[i]:
|
||||
self.L.append(self.L[0])
|
||||
del self.L[0]
|
||||
|
||||
self.R.append(self.R[0])
|
||||
del self.R[0]
|
||||
|
||||
j += 1
|
||||
|
||||
# Create one of the 16 subkeys through pc2 permutation
|
||||
self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
|
||||
|
||||
i += 1
|
||||
|
||||
# Main part of the encryption algorithm, the number cruncher :)
|
||||
def __des_crypt(self, block, crypt_type):
|
||||
"""Crypt the block of data through DES bit-manipulation"""
|
||||
block = self.__permutate(des.__ip, block)
|
||||
self.L = block[:32]
|
||||
self.R = block[32:]
|
||||
|
||||
# Encryption starts from Kn[1] through to Kn[16]
|
||||
if crypt_type == des.ENCRYPT:
|
||||
iteration = 0
|
||||
iteration_adjustment = 1
|
||||
# Decryption starts from Kn[16] down to Kn[1]
|
||||
else:
|
||||
iteration = 15
|
||||
iteration_adjustment = -1
|
||||
|
||||
i = 0
|
||||
while i < 16:
|
||||
# Make a copy of R[i-1], this will later become L[i]
|
||||
tempR = self.R[:]
|
||||
|
||||
# Permutate R[i - 1] to start creating R[i]
|
||||
self.R = self.__permutate(des.__expansion_table, self.R)
|
||||
|
||||
# Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
|
||||
self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
|
||||
B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
|
||||
# Optimization: Replaced below commented code with above
|
||||
#j = 0
|
||||
#B = []
|
||||
#while j < len(self.R):
|
||||
# self.R[j] = self.R[j] ^ self.Kn[iteration][j]
|
||||
# j += 1
|
||||
# if j % 6 == 0:
|
||||
# B.append(self.R[j-6:j])
|
||||
|
||||
# Permutate B[1] to B[8] using the S-Boxes
|
||||
j = 0
|
||||
Bn = [0] * 32
|
||||
pos = 0
|
||||
while j < 8:
|
||||
# Work out the offsets
|
||||
m = (B[j][0] << 1) + B[j][5]
|
||||
n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
|
||||
|
||||
# Find the permutation value
|
||||
v = des.__sbox[j][(m << 4) + n]
|
||||
|
||||
# Turn value into bits, add it to result: Bn
|
||||
Bn[pos] = (v & 8) >> 3
|
||||
Bn[pos + 1] = (v & 4) >> 2
|
||||
Bn[pos + 2] = (v & 2) >> 1
|
||||
Bn[pos + 3] = v & 1
|
||||
|
||||
pos += 4
|
||||
j += 1
|
||||
|
||||
# Permutate the concatination of B[1] to B[8] (Bn)
|
||||
self.R = self.__permutate(des.__p, Bn)
|
||||
|
||||
# Xor with L[i - 1]
|
||||
self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
|
||||
# Optimization: This now replaces the below commented code
|
||||
#j = 0
|
||||
#while j < len(self.R):
|
||||
# self.R[j] = self.R[j] ^ self.L[j]
|
||||
# j += 1
|
||||
|
||||
# L[i] becomes R[i - 1]
|
||||
self.L = tempR
|
||||
|
||||
i += 1
|
||||
iteration += iteration_adjustment
|
||||
|
||||
# Final permutation of R[16]L[16]
|
||||
self.final = self.__permutate(des.__fp, self.R + self.L)
|
||||
return self.final
|
||||
|
||||
|
||||
# Data to be encrypted/decrypted
|
||||
def crypt(self, data, crypt_type):
|
||||
"""Crypt the data in blocks, running it through des_crypt()"""
|
||||
|
||||
# Error check the data
|
||||
if not data:
|
||||
return ''
|
||||
if len(data) % self.block_size != 0:
|
||||
if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
|
||||
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
|
||||
if not self.getPadding():
|
||||
raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
|
||||
else:
|
||||
data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
|
||||
# print "Len of data: %f" % (len(data) / self.block_size)
|
||||
|
||||
if self.getMode() == CBC:
|
||||
if self.getIV():
|
||||
iv = self.__String_to_BitList(self.getIV())
|
||||
else:
|
||||
raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
|
||||
|
||||
# Split the data into blocks, crypting each one seperately
|
||||
i = 0
|
||||
dict = {}
|
||||
result = []
|
||||
#cached = 0
|
||||
#lines = 0
|
||||
while i < len(data):
|
||||
# Test code for caching encryption results
|
||||
#lines += 1
|
||||
#if dict.has_key(data[i:i+8]):
|
||||
#print "Cached result for: %s" % data[i:i+8]
|
||||
# cached += 1
|
||||
# result.append(dict[data[i:i+8]])
|
||||
# i += 8
|
||||
# continue
|
||||
|
||||
block = self.__String_to_BitList(data[i:i+8])
|
||||
|
||||
# Xor with IV if using CBC mode
|
||||
if self.getMode() == CBC:
|
||||
if crypt_type == des.ENCRYPT:
|
||||
block = list(map(lambda x, y: x ^ y, block, iv))
|
||||
#j = 0
|
||||
#while j < len(block):
|
||||
# block[j] = block[j] ^ iv[j]
|
||||
# j += 1
|
||||
|
||||
processed_block = self.__des_crypt(block, crypt_type)
|
||||
|
||||
if crypt_type == des.DECRYPT:
|
||||
processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
|
||||
#j = 0
|
||||
#while j < len(processed_block):
|
||||
# processed_block[j] = processed_block[j] ^ iv[j]
|
||||
# j += 1
|
||||
iv = block
|
||||
else:
|
||||
iv = processed_block
|
||||
else:
|
||||
processed_block = self.__des_crypt(block, crypt_type)
|
||||
|
||||
|
||||
# Add the resulting crypted block to our list
|
||||
#d = self.__BitList_to_String(processed_block)
|
||||
#result.append(d)
|
||||
result.append(self.__BitList_to_String(processed_block))
|
||||
#dict[data[i:i+8]] = d
|
||||
i += 8
|
||||
|
||||
# print "Lines: %d, cached: %d" % (lines, cached)
|
||||
|
||||
# Return the full crypted string
|
||||
if _pythonMajorVersion < 3:
|
||||
return ''.join(result)
|
||||
else:
|
||||
return bytes.fromhex('').join(result)
|
||||
|
||||
def encrypt(self, data, pad=None, padmode=None):
|
||||
"""encrypt(data, [pad], [padmode]) -> bytes
|
||||
|
||||
data : Bytes to be encrypted
|
||||
pad : Optional argument for encryption padding. Must only be one byte
|
||||
padmode : Optional argument for overriding the padding mode.
|
||||
|
||||
The data must be a multiple of 8 bytes and will be encrypted
|
||||
with the already specified key. Data does not have to be a
|
||||
multiple of 8 bytes if the padding character is supplied, or
|
||||
the padmode is set to PAD_PKCS5, as bytes will then added to
|
||||
ensure the be padded data is a multiple of 8 bytes.
|
||||
"""
|
||||
data = self._guardAgainstUnicode(data)
|
||||
if pad is not None:
|
||||
pad = self._guardAgainstUnicode(pad)
|
||||
data = self._padData(data, pad, padmode)
|
||||
return self.crypt(data, des.ENCRYPT)
|
||||
|
||||
def decrypt(self, data, pad=None, padmode=None):
|
||||
"""decrypt(data, [pad], [padmode]) -> bytes
|
||||
|
||||
data : Bytes to be encrypted
|
||||
pad : Optional argument for decryption padding. Must only be one byte
|
||||
padmode : Optional argument for overriding the padding mode.
|
||||
|
||||
The data must be a multiple of 8 bytes and will be decrypted
|
||||
with the already specified key. In PAD_NORMAL mode, if the
|
||||
optional padding character is supplied, then the un-encrypted
|
||||
data will have the padding characters removed from the end of
|
||||
the bytes. This pad removal only occurs on the last 8 bytes of
|
||||
the data (last data block). In PAD_PKCS5 mode, the special
|
||||
padding end markers will be removed from the data after decrypting.
|
||||
"""
|
||||
data = self._guardAgainstUnicode(data)
|
||||
if pad is not None:
|
||||
pad = self._guardAgainstUnicode(pad)
|
||||
data = self.crypt(data, des.DECRYPT)
|
||||
return self._unpadData(data, pad, padmode)
|
||||
|
||||
|
||||
|
||||
#############################################################################
|
||||
# Triple DES #
|
||||
#############################################################################
|
||||
class triple_des(_baseDes):
|
||||
"""Triple DES encryption/decrytpion class
|
||||
|
||||
This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
|
||||
the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
|
||||
Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
|
||||
|
||||
pyDes.des(key, [mode], [IV])
|
||||
|
||||
key -> Bytes containing the encryption key, must be either 16 or
|
||||
24 bytes long
|
||||
mode -> Optional argument for encryption type, can be either pyDes.ECB
|
||||
(Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
|
||||
IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
|
||||
Must be 8 bytes in length.
|
||||
pad -> Optional argument, set the pad character (PAD_NORMAL) to use
|
||||
during all encrypt/decrpt operations done with this instance.
|
||||
padmode -> Optional argument, set the padding mode (PAD_NORMAL or
|
||||
PAD_PKCS5) to use during all encrypt/decrpt operations done
|
||||
with this instance.
|
||||
"""
|
||||
def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
|
||||
_baseDes.__init__(self, mode, IV, pad, padmode)
|
||||
self.setKey(key)
|
||||
|
||||
def setKey(self, key):
|
||||
"""Will set the crypting key for this object. Either 16 or 24 bytes long."""
|
||||
self.key_size = 24 # Use DES-EDE3 mode
|
||||
if len(key) != self.key_size:
|
||||
if len(key) == 16: # Use DES-EDE2 mode
|
||||
self.key_size = 16
|
||||
else:
|
||||
raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
|
||||
if self.getMode() == CBC:
|
||||
if not self.getIV():
|
||||
# Use the first 8 bytes of the key
|
||||
self._iv = key[:self.block_size]
|
||||
if len(self.getIV()) != self.block_size:
|
||||
raise ValueError("Invalid IV, must be 8 bytes in length")
|
||||
self.__key1 = des(key[:8], self._mode, self._iv,
|
||||
self._padding, self._padmode)
|
||||
self.__key2 = des(key[8:16], self._mode, self._iv,
|
||||
self._padding, self._padmode)
|
||||
if self.key_size == 16:
|
||||
self.__key3 = self.__key1
|
||||
else:
|
||||
self.__key3 = des(key[16:], self._mode, self._iv,
|
||||
self._padding, self._padmode)
|
||||
_baseDes.setKey(self, key)
|
||||
|
||||
# Override setter methods to work on all 3 keys.
|
||||
|
||||
def setMode(self, mode):
|
||||
"""Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
|
||||
_baseDes.setMode(self, mode)
|
||||
for key in (self.__key1, self.__key2, self.__key3):
|
||||
key.setMode(mode)
|
||||
|
||||
def setPadding(self, pad):
|
||||
"""setPadding() -> bytes of length 1. Padding character."""
|
||||
_baseDes.setPadding(self, pad)
|
||||
for key in (self.__key1, self.__key2, self.__key3):
|
||||
key.setPadding(pad)
|
||||
|
||||
def setPadMode(self, mode):
|
||||
"""Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
|
||||
_baseDes.setPadMode(self, mode)
|
||||
for key in (self.__key1, self.__key2, self.__key3):
|
||||
key.setPadMode(mode)
|
||||
|
||||
def setIV(self, IV):
|
||||
"""Will set the Initial Value, used in conjunction with CBC mode"""
|
||||
_baseDes.setIV(self, IV)
|
||||
for key in (self.__key1, self.__key2, self.__key3):
|
||||
key.setIV(IV)
|
||||
|
||||
def encrypt(self, data, pad=None, padmode=None):
|
||||
"""encrypt(data, [pad], [padmode]) -> bytes
|
||||
|
||||
data : bytes to be encrypted
|
||||
pad : Optional argument for encryption padding. Must only be one byte
|
||||
padmode : Optional argument for overriding the padding mode.
|
||||
|
||||
The data must be a multiple of 8 bytes and will be encrypted
|
||||
with the already specified key. Data does not have to be a
|
||||
multiple of 8 bytes if the padding character is supplied, or
|
||||
the padmode is set to PAD_PKCS5, as bytes will then added to
|
||||
ensure the be padded data is a multiple of 8 bytes.
|
||||
"""
|
||||
ENCRYPT = des.ENCRYPT
|
||||
DECRYPT = des.DECRYPT
|
||||
data = self._guardAgainstUnicode(data)
|
||||
if pad is not None:
|
||||
pad = self._guardAgainstUnicode(pad)
|
||||
# Pad the data accordingly.
|
||||
data = self._padData(data, pad, padmode)
|
||||
if self.getMode() == CBC:
|
||||
self.__key1.setIV(self.getIV())
|
||||
self.__key2.setIV(self.getIV())
|
||||
self.__key3.setIV(self.getIV())
|
||||
i = 0
|
||||
result = []
|
||||
while i < len(data):
|
||||
block = self.__key1.crypt(data[i:i+8], ENCRYPT)
|
||||
block = self.__key2.crypt(block, DECRYPT)
|
||||
block = self.__key3.crypt(block, ENCRYPT)
|
||||
self.__key1.setIV(block)
|
||||
self.__key2.setIV(block)
|
||||
self.__key3.setIV(block)
|
||||
result.append(block)
|
||||
i += 8
|
||||
if _pythonMajorVersion < 3:
|
||||
return ''.join(result)
|
||||
else:
|
||||
return bytes.fromhex('').join(result)
|
||||
else:
|
||||
data = self.__key1.crypt(data, ENCRYPT)
|
||||
data = self.__key2.crypt(data, DECRYPT)
|
||||
return self.__key3.crypt(data, ENCRYPT)
|
||||
|
||||
def decrypt(self, data, pad=None, padmode=None):
|
||||
"""decrypt(data, [pad], [padmode]) -> bytes
|
||||
|
||||
data : bytes to be encrypted
|
||||
pad : Optional argument for decryption padding. Must only be one byte
|
||||
padmode : Optional argument for overriding the padding mode.
|
||||
|
||||
The data must be a multiple of 8 bytes and will be decrypted
|
||||
with the already specified key. In PAD_NORMAL mode, if the
|
||||
optional padding character is supplied, then the un-encrypted
|
||||
data will have the padding characters removed from the end of
|
||||
the bytes. This pad removal only occurs on the last 8 bytes of
|
||||
the data (last data block). In PAD_PKCS5 mode, the special
|
||||
padding end markers will be removed from the data after
|
||||
decrypting, no pad character is required for PAD_PKCS5.
|
||||
"""
|
||||
ENCRYPT = des.ENCRYPT
|
||||
DECRYPT = des.DECRYPT
|
||||
data = self._guardAgainstUnicode(data)
|
||||
if pad is not None:
|
||||
pad = self._guardAgainstUnicode(pad)
|
||||
if self.getMode() == CBC:
|
||||
self.__key1.setIV(self.getIV())
|
||||
self.__key2.setIV(self.getIV())
|
||||
self.__key3.setIV(self.getIV())
|
||||
i = 0
|
||||
result = []
|
||||
while i < len(data):
|
||||
iv = data[i:i+8]
|
||||
block = self.__key3.crypt(iv, DECRYPT)
|
||||
block = self.__key2.crypt(block, ENCRYPT)
|
||||
block = self.__key1.crypt(block, DECRYPT)
|
||||
self.__key1.setIV(iv)
|
||||
self.__key2.setIV(iv)
|
||||
self.__key3.setIV(iv)
|
||||
result.append(block)
|
||||
i += 8
|
||||
if _pythonMajorVersion < 3:
|
||||
data = ''.join(result)
|
||||
else:
|
||||
data = bytes.fromhex('').join(result)
|
||||
else:
|
||||
data = self.__key3.crypt(data, DECRYPT)
|
||||
data = self.__key2.crypt(data, ENCRYPT)
|
||||
data = self.__key1.crypt(data, DECRYPT)
|
||||
return self._unpadData(data, pad, padmode)
|
||||
@@ -171,11 +171,13 @@ class RFB(RawLayer):
|
||||
def __init__(self, listener):
|
||||
"""
|
||||
@param mode: LayerMode client or server
|
||||
@param controller: controller use to inform new orders
|
||||
@param listener: listener use to inform new orders
|
||||
"""
|
||||
mode = None
|
||||
if isinstance(listener, RFBClientListener):
|
||||
mode = LayerMode.CLIENT
|
||||
#set client listener
|
||||
self._clientListener = listener
|
||||
else:
|
||||
raise InvalidType("RFB Layer expect RFBClientListener as listener")
|
||||
|
||||
@@ -199,8 +201,6 @@ class RFB(RawLayer):
|
||||
self._nbRect = 0
|
||||
#current rectangle header
|
||||
self._currentRect = Rectangle()
|
||||
#client or server adaptor
|
||||
self._listener = listener
|
||||
#ready to send events
|
||||
self._ready = False
|
||||
|
||||
@@ -390,7 +390,7 @@ class RFB(RawLayer):
|
||||
Read body of rectangle update
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
self._listener.recvRectangle(self._currentRect, self._pixelFormat, data.getvalue())
|
||||
self._clientListener.recvRectangle(self._currentRect, self._pixelFormat, data.getvalue())
|
||||
|
||||
self._nbRect = self._nbRect - 1
|
||||
#if there is another rect to read
|
||||
@@ -489,7 +489,7 @@ class RFBController(RFBClientListener):
|
||||
@param observer: new observer
|
||||
"""
|
||||
self._clientObservers.append(observer)
|
||||
observer._controller = self
|
||||
observer._clientListener = self
|
||||
|
||||
def recvRectangle(self, rectangle, pixelFormat, data):
|
||||
"""
|
||||
@@ -579,7 +579,7 @@ class RFBClientObserver(object):
|
||||
"""
|
||||
Send a key event
|
||||
@param isPressed: state of key
|
||||
@param key: ascii code of key
|
||||
@param key: ASCII code of key
|
||||
"""
|
||||
self._controller.sendKeyEvent(isPressed, key)
|
||||
|
||||
|
||||
194
rdpy/ui/qt4.py
194
rdpy/ui/qt4.py
@@ -26,58 +26,58 @@ QRemoteDesktop is a widget use for render in rdpy
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from rdpy.protocol.rfb.rfb import RFBClientObserver
|
||||
from rdpy.protocol.rdp.rdp import RDPClientObserver
|
||||
from rdpy.network.error import CallPureVirtualFuntion
|
||||
import rle
|
||||
|
||||
|
||||
class QAdaptor(object):
|
||||
'''
|
||||
adaptor model with link between protocol
|
||||
and qt widget
|
||||
'''
|
||||
|
||||
def sendMouseEvent(self, e):
|
||||
'''
|
||||
interface to send mouse event
|
||||
to protocol stack
|
||||
@param e: qEvent
|
||||
'''
|
||||
pass
|
||||
|
||||
def sendKeyEvent(self, e):
|
||||
'''
|
||||
interface to send key event
|
||||
to protocol stack
|
||||
@param e: qEvent
|
||||
'''
|
||||
pass
|
||||
"""
|
||||
Adaptor model with link between protocol
|
||||
And Qt widget
|
||||
"""
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
"""
|
||||
Interface to send mouse event to protocol stack
|
||||
@param e: QMouseEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendMouseEvent", "QAdaptor"))
|
||||
|
||||
def sendKeyEvent(self, e, isPressed):
|
||||
"""
|
||||
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"))
|
||||
|
||||
def getWidget(self):
|
||||
'''
|
||||
"""
|
||||
@return: widget use for render
|
||||
'''
|
||||
pass
|
||||
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getWidget", "QAdaptor"))
|
||||
|
||||
class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
'''
|
||||
"""
|
||||
QAdaptor for specific RFB protocol stack
|
||||
is to an RFB observer
|
||||
'''
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@param controller: controller for obser
|
||||
@param controller: controller for observer
|
||||
"""
|
||||
RFBClientObserver.__init__(self, controller)
|
||||
self._widget = QRemoteDesktop(self)
|
||||
|
||||
def getWidget(self):
|
||||
'''
|
||||
"""
|
||||
@return: widget use for render
|
||||
'''
|
||||
"""
|
||||
return self._widget
|
||||
|
||||
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
|
||||
'''
|
||||
implement RFBClientObserver interface
|
||||
"""
|
||||
Implement RFBClientObserver interface
|
||||
@param width: width of new image
|
||||
@param height: height of new image
|
||||
@param x: x position of new image
|
||||
@@ -85,7 +85,7 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
@param pixelFormat: pixefFormat structure in rfb.message.PixelFormat
|
||||
@param encoding: encoding type rfb.message.Encoding
|
||||
@param data: image data in accordance with pixel format and encoding
|
||||
'''
|
||||
"""
|
||||
imageFormat = None
|
||||
if pixelFormat.BitsPerPixel.value == 32 and pixelFormat.RedShift.value == 16:
|
||||
imageFormat = QtGui.QImage.Format_RGB32
|
||||
@@ -96,11 +96,12 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
image = QtGui.QImage(data, width, height, imageFormat)
|
||||
self._widget.notifyImage(x, y, image)
|
||||
|
||||
def sendMouseEvent(self, e):
|
||||
'''
|
||||
convert qt mouse event to RFB mouse event
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
"""
|
||||
Convert Qt mouse event to RFB mouse event
|
||||
@param e: qMouseEvent
|
||||
'''
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
button = e.button()
|
||||
buttonNumber = 0
|
||||
if button == QtCore.Qt.LeftButton:
|
||||
@@ -111,18 +112,19 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
buttonNumber = 3
|
||||
self.mouseEvent(buttonNumber, e.pos().x(), e.pos().y())
|
||||
|
||||
def sendKeyEvent(self, e):
|
||||
'''
|
||||
convert Qt key press event to RFB press event
|
||||
def sendKeyEvent(self, e, isPressed):
|
||||
"""
|
||||
Convert Qt key press event to RFB press event
|
||||
@param e: qKeyEvent
|
||||
'''
|
||||
self.keyEvent(True, e.nativeVirtualKey())
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
self.keyEvent(isPressed, e.nativeVirtualKey())
|
||||
|
||||
|
||||
class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
'''
|
||||
"""
|
||||
Adaptor for RDP client
|
||||
'''
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@param controller: RDP controller
|
||||
@@ -131,14 +133,38 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
self._widget = QRemoteDesktop(self)
|
||||
|
||||
def getWidget(self):
|
||||
'''
|
||||
"""
|
||||
@return: widget use for render
|
||||
'''
|
||||
"""
|
||||
return self._widget
|
||||
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
"""
|
||||
Convert Qt mouse event to RDP mouse event
|
||||
@param e: qMouseEvent
|
||||
@param isPressed: event come from press(true) or release(false) action
|
||||
"""
|
||||
button = e.button()
|
||||
buttonNumber = 0
|
||||
if button == QtCore.Qt.LeftButton:
|
||||
buttonNumber = 1
|
||||
elif button == QtCore.Qt.MidButton:
|
||||
buttonNumber = 2
|
||||
elif button == QtCore.Qt.RightButton:
|
||||
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
|
||||
@param e: QKeyEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
self._controller.sendKeyEventUnicode(ord(unicode(e.text().toUtf8(), encoding="UTF-8")), isPressed)
|
||||
|
||||
def onBitmapUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
'''
|
||||
notify bitmap update
|
||||
"""
|
||||
Notify bitmap update
|
||||
@param destLeft: xmin position
|
||||
@param destTop: ymin position
|
||||
@param destRight: xmax position because RDP can send bitmap with padding
|
||||
@@ -148,7 +174,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
@param bitsPerPixel: number of bit per pixel
|
||||
@param isCompress: use RLE compression
|
||||
@param data: bitmap data
|
||||
'''
|
||||
"""
|
||||
image = None
|
||||
if bitsPerPixel == 16:
|
||||
if isCompress:
|
||||
@@ -178,13 +204,13 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
|
||||
|
||||
class QRemoteDesktop(QtGui.QWidget):
|
||||
'''
|
||||
qt display widget
|
||||
'''
|
||||
"""
|
||||
Qt display widget
|
||||
"""
|
||||
def __init__(self, adaptor):
|
||||
'''
|
||||
constructor
|
||||
'''
|
||||
"""
|
||||
@param adaptor: QAdaptor
|
||||
"""
|
||||
super(QRemoteDesktop, self).__init__()
|
||||
#adaptor use to send
|
||||
self._adaptor = adaptor
|
||||
@@ -198,22 +224,22 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
self.setMouseTracking(True)
|
||||
|
||||
def notifyImage(self, x, y, qimage):
|
||||
'''
|
||||
function call from Qadaptor
|
||||
"""
|
||||
Function call from QAdaptor
|
||||
@param x: x position of new image
|
||||
@param y: y position of new image
|
||||
@param qimage: new qimage
|
||||
'''
|
||||
@param qimage: new QImage
|
||||
"""
|
||||
#save in refresh list (order is important)
|
||||
self._refresh.append({"x" : x, "y" : y, "image" : qimage})
|
||||
#force update
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, e):
|
||||
'''
|
||||
call when QT renderer engine estimate that is needed
|
||||
@param e: qevent
|
||||
'''
|
||||
"""
|
||||
Call when Qt renderer engine estimate that is needed
|
||||
@param e: QEvent
|
||||
"""
|
||||
#if there is no refresh -> done
|
||||
if self._refresh == []:
|
||||
return
|
||||
@@ -228,25 +254,39 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
self._lastReceive = []
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
'''
|
||||
call when mouse move
|
||||
@param event: qMouseEvent
|
||||
'''
|
||||
"""
|
||||
Call when mouse move
|
||||
@param event: QMouseEvent
|
||||
"""
|
||||
if self._adaptor is None:
|
||||
print "No adaptor to send mouse move event"
|
||||
self._adaptor.sendMouseEvent(event)
|
||||
self._adaptor.sendMouseEvent(event, False)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
'''
|
||||
call when button mouse is pressed
|
||||
@param event: qMouseEvent
|
||||
'''
|
||||
self._adaptor.sendMouseEvent(event)
|
||||
"""
|
||||
Call when button mouse is pressed
|
||||
@param event: QMouseEvent
|
||||
"""
|
||||
self._adaptor.sendMouseEvent(event, True)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
"""
|
||||
Call when button mouse is released
|
||||
@param event: QMouseEvent
|
||||
"""
|
||||
self._adaptor.sendMouseEvent(event, False)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
'''
|
||||
call when button key is pressed
|
||||
@param event: qKeyEvent
|
||||
'''
|
||||
self._adaptor.sendKeyEvent(event)
|
||||
"""
|
||||
Call when button key is pressed
|
||||
@param event: QKeyEvent
|
||||
"""
|
||||
self._adaptor.sendKeyEvent(event, True)
|
||||
|
||||
def keyReleaseEvent(self, event):
|
||||
"""
|
||||
Call when button key is released
|
||||
@param event: QKeyEvent
|
||||
"""
|
||||
self._adaptor.sendKeyEvent(event, False)
|
||||
|
||||
Reference in New Issue
Block a user