add view system + bug fix

This commit is contained in:
speyrefitte
2014-07-28 18:25:40 +02:00
parent dca9613707
commit a518963253
7 changed files with 228 additions and 49 deletions

View File

@@ -29,7 +29,7 @@ sys.path.insert(1, os.path.join(sys.path[0], '..'))
from rdpy.base import log, error
from rdpy.protocol.rdp import rdp
from rdpy.ui import widget
from rdpy.ui import view
from twisted.internet import reactor
from PyQt4 import QtCore, QtGui
@@ -42,6 +42,8 @@ class IProxyClient(object):
pass
def sendPointerEvent(self, x, y, button, isPressed):
pass
def sendRefreshOrder(self, left, top, right, bottom):
pass
class ProxyServer(rdp.RDPServerObserver):
"""
@@ -69,21 +71,27 @@ class ProxyServer(rdp.RDPServerObserver):
Event use to inform state of server stack
Use to connect client
"""
domain, username, password = self._controller.getCredentials()
if self._credentialProvider.isAdmin(domain, username, password):
self.clientConnected(ProxyAdmin(self))
return
try:
dstIp, dstPort, dstDomain, dstUsername, dstPassword = self._credentialProvider.getCredentials(domain, username, password)
except error.InvalidExpectedDataException as e:
log.info(e.message)
#self._controller.close()
return
width, height = self._controller.getScreen()
reactor.connectTCP(dstIp, dstPort, ProxyClientFactory(self, width, height, dstDomain, dstUsername, dstPassword, "%s/%s -> %s %s/%s"%(domain, username, dstIp, dstDomain, dstUsername)))
if self._client is None:
#try a connection
domain, username, password = self._controller.getCredentials()
if self._credentialProvider.isAdmin(domain, username, password):
self.clientConnected(ProxyAdmin(self))
return
try:
dstIp, dstPort, dstDomain, dstUsername, dstPassword = self._credentialProvider.getCredentials(domain, username, password)
except error.InvalidExpectedDataException as e:
log.info(e.message)
#self._controller.close()
return
width, height = self._controller.getScreen()
reactor.connectTCP(dstIp, dstPort, ProxyClientFactory(self, width, height, dstDomain, dstUsername, dstPassword, "%s/%s -> %s %s/%s"%(domain, username, dstIp, dstDomain, dstUsername)))
else:
#refresh client
width, height = self._controller.getScreen()
self._client.sendRefreshOrder(0, 0, width, height)
def onKeyEventScancode(self, code, isPressed):
"""
@@ -159,14 +167,21 @@ class ProxyClient(rdp.RDPClientObserver, IProxyClient):
@param data: bitmap data
"""
self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data)
def getColorDepth(self):
return self._controller.getColorDepth()
def sendKeyEventScancode(self, code, isPressed):
self._controller.sendKeyEventScancode(code, isPressed)
def sendKeyEventUnicode(self, code, isPressed):
self._controller.sendKeyEventUnicode(code, isPressed)
def sendPointerEvent(self, x, y, button, isPressed):
self._controller.sendPointerEvent(x, y, button, isPressed)
def sendRefreshOrder(self, left, top, right, bottom):
self._controller.sendRefreshOrder(left, top, right, bottom)
class ProxyServerFactory(rdp.ServerFactory):
"""
@@ -177,7 +192,7 @@ class ProxyServerFactory(rdp.ServerFactory):
@param config: rdp-proxy configuration
@param credentialProvider: CredentialProvider
"""
rdp.ServerFactory.__init__(self, "/home/sylvain/dev/certificate/rdpy.key", "/home/sylvain/dev/certificate/rdpy.crt", 16)
rdp.ServerFactory.__init__(self, "/home/speyrefitte/dev/certificate/rdpy.key", "/home/speyrefitte/dev/certificate/rdpy.crt", 16)
self._credentialProvider = credentialProvider
def buildObserver(self, controller):
@@ -277,19 +292,28 @@ class CredentialProvider(object):
return self._config['admin']['domain'] == domain and self._config['admin']['username'] == username and self._config['admin']['password'] == password
class ProxyAdmin(IProxyClient):
"""
Use to manage client side of admin session
Add GUI to select wich session to see
"""
def __init__(self, server):
self._server = server
#self._list = widget.List(ProxyClientFactory._CLIENT_PROXY_.keys(), 100, 100, self.onSelect, widget.Anchor(0, 0, widget.RDPWidgetListener(self._server._controller)))
self._list = widget.List(["salut les copains"], 300, 300, self.onSelect, widget.Anchor(0, 0, widget.RDPWidgetListener(self._server._controller)))
self._list = view.ListView(ProxyClientFactory._CLIENT_PROXY_.keys(), 300, 300, self.onSelect)
self._render = view.RDPRenderer(self._server._controller)
self._list.update(self._render)
def getColorDepth(self):
return 16
def sendKeyEventScancode(self, code, isPressed):
if isPressed:
self._list.keyEvent(code)
self._list.update(self._render)
def sendKeyEventUnicode(self, code, isPressed):
pass
def sendPointerEvent(self, x, y, button, isPressed):
pass
def sendRefreshOrder(self, left, top, right, bottom):
self._list.keyEvent(code)
self._list.update(self._render)
def onSelect(self, name):
if ProxyClientFactory._CLIENT_PROXY_.has_key(name):
self._server.clientConnected(ProxyClient(ProxyClientFactory._CLIENT_PROXY_[name]._controller, self._server))

View File

@@ -668,7 +668,7 @@ class String(Type, CallableValue):
"""
String network type
"""
def __init__(self, value = "", readLen = None, conditional = lambda:True, optional = False, constant = False, unicode = False):
def __init__(self, value = "", readLen = None, conditional = lambda:True, optional = False, constant = False, unicode = False, until = None):
"""
@param value: python string use for inner value
@param readLen: length use to read in stream (SimpleType) if 0 read entire stream
@@ -676,12 +676,14 @@ class String(Type, CallableValue):
@param optional: boolean check before read if there is still data in stream
@param constant: if true check any changement of object during reading
@param unicode: Encode and decode value as unicode
@param until: read until sequence is readed or write sequence at the end of string
"""
Type.__init__(self, conditional = conditional, optional = optional, constant = constant)
CallableValue.__init__(self, value)
#type use to know read length
self._readLen = readLen
self._unicode = unicode
self._until = until
def __eq__(self, other):
'''
@@ -710,6 +712,11 @@ class String(Type, CallableValue):
Write the entire raw value
@param s: Stream
"""
toWrite = self.value
if not self._until is None:
toWrite += self._until
if self._unicode:
s.write(encodeUnicode(self.value))
else:
@@ -722,7 +729,12 @@ class String(Type, CallableValue):
@param s: Stream
"""
if self._readLen is None:
self.value = s.getvalue()
if self._until is None:
self.value = s.getvalue()
else:
self.value = ""
while self.value[-len(self._until):] != self._until or s.dataLen() == 0:
self.value += s.read(1)
else:
self.value = s.read(self._readLen.value)

View File

@@ -576,7 +576,7 @@ class Server(PDULayer, tpkt.IFastPathListener):
self._transport.close()
def recvFastPath(self, fastPathS):
"""
"""r
Implement IFastPathListener interface
Fast path is needed by RDP 8.0
@param fastPathS: Stream that contain fast path data

View File

@@ -247,7 +247,6 @@ class RDPServerController(pdu.layer.PDUServerListener):
@param colorDepth: 15, 16, 24
"""
self._isReady = False
self._sendReady = False
#list of observer
self._serverObserver = []
#build RDP protocol stack
@@ -342,9 +341,6 @@ class RDPServerController(pdu.layer.PDUServerListener):
RDP stack is now ready
"""
self._isReady = True
if self._sendReady:
return
self._sendReady = True
for observer in self._serverObserver:
observer.onReady()
@@ -490,6 +486,7 @@ class RDPServerObserver(object):
def onReady(self):
"""
Stack is ready and connected
May be called after an setColorDepth too
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RDPServerObserver"))

View File

@@ -25,7 +25,7 @@ RDP basic security is not supported by RDPY (because is not a true security laye
"""
from rdpy.network.layer import LayerAutomata, IStreamSender
from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof
from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof, String
from rdpy.base.error import InvalidExpectedDataException
class MessageType(object):
@@ -66,17 +66,30 @@ class NegotiationFailureCode(object):
HYBRID_REQUIRED_BY_SERVER = 0x00000005
SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 0x00000006
class TPDUConnectMessage(CompositeType):
class ClientConnectionRequestPDU(CompositeType):
"""
Header of TPDU connection messages
Connection request
client -> server
@see: http://msdn.microsoft.com/en-us/library/cc240470.aspx
"""
def __init__(self, code):
"""
@param code: MessageType
"""
def __init__(self):
CompositeType.__init__(self)
self.len = UInt8(lambda:sizeof(self) - 1)
self.code = UInt8(code, constant = True)
self.code = UInt8(MessageType.X224_TPDU_CONNECTION_REQUEST, constant = True)
self.padding = (UInt16Be(), UInt16Be(), UInt8())
self.cookie = String(until = "\x0d\x0a", conditional = lambda:(self.len._is_readed and self.len.value > 14))
#read if there is enough data
self.protocolNeg = Negotiation(optional = True)
class ServerConnectionConfirm(CompositeType):
"""
Server response
@see: http://msdn.microsoft.com/en-us/library/cc240506.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.len = UInt8(lambda:sizeof(self) - 1)
self.code = UInt8(MessageType.X224_TPDU_CONNECTION_CONFIRM, constant = True)
self.padding = (UInt16Be(), UInt16Be(), UInt8())
#read if there is enough data
self.protocolNeg = Negotiation(optional = True)
@@ -103,7 +116,7 @@ class Negotiation(CompositeType):
self.code = UInt8()
self.flag = UInt8(0)
#always 8
self.len = UInt16Le(0x0008)#not constant because freerdp send me random value...
self.len = UInt16Le(0x0008, constant = True)#not constant because freerdp send me random value...
self.selectedProtocol = UInt32Le(conditional = lambda: (self.code.value != NegociationType.TYPE_RDP_NEG_FAILURE))
self.failureCode = UInt32Le(conditional = lambda: (self.code.value == NegociationType.TYPE_RDP_NEG_FAILURE))
@@ -164,7 +177,7 @@ class Client(TPDULayer):
Next state is recvConnectionConfirm
@see: http://msdn.microsoft.com/en-us/library/cc240500.aspx
"""
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST)
message = ClientConnectionRequestPDU()
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_REQ
message.protocolNeg.selectedProtocol.value = self._requestedProtocol
self._transport.send(message)
@@ -179,7 +192,7 @@ class Client(TPDULayer):
@see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
@see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
"""
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_CONFIRM)
message = ServerConnectionConfirm()
data.readType(message)
#check presence of negotiation response
@@ -231,7 +244,7 @@ class Server(TPDULayer):
@param data: Stream
@see : http://msdn.microsoft.com/en-us/library/cc240470.aspx
"""
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST)
message = ClientConnectionRequestPDU()
data.readType(message)
if not message.protocolNeg._is_readed or message.protocolNeg.failureCode._is_readed:
@@ -241,8 +254,7 @@ class Server(TPDULayer):
if not self._requestedProtocol & Protocols.PROTOCOL_SSL:
#send error message and quit
message = TPDUConnectMessage()
message.code.value = MessageType.X224_TPDU_CONNECTION_CONFIRM
message = ServerConnectionConfirm()
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_FAILURE
message.protocolNeg.failureCode.value = NegotiationFailureCode.SSL_REQUIRED_BY_SERVER
self._transport.send(message)
@@ -258,7 +270,7 @@ class Server(TPDULayer):
Next state is recvData
@see : http://msdn.microsoft.com/en-us/library/cc240501.aspx
"""
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_CONFIRM)
message = ServerConnectionConfirm()
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_RSP
message.protocolNeg.selectedProtocol.value = self._selectedProtocol
self._transport.send(message)

View File

@@ -179,7 +179,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
@param e: QKeyEvent
@param isPressed: event come from press or release action
"""
self._controller.sendKeyEventUnicode(ord(unicode(e.text().toUtf8(), encoding="UTF-8")), isPressed)
self._controller.sendKeyEventScancode(e.nativeScanCode(), isPressed)
def closeEvent(self, e):
"""
@@ -207,28 +207,28 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB555)
data = rle.bitmap_decompress(image.bits(), width, height, data, len(data), 2)
else:
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB555)
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB555).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
elif bitsPerPixel == 16:
if isCompress:
image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB16)
data = rle.bitmap_decompress(image.bits(), width, height, data, len(data), 2)
else:
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB16)
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB16).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
elif bitsPerPixel == 24:
if isCompress:
image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB24)
data = rle.bitmap_decompress(image.bits(), width, height, data, len(data), 3)
else:
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB24)
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))
elif bitsPerPixel == 32:
if isCompress:
image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
data = rle.bitmap_decompress(image.bits(), width, height, data, len(data), 4)
else:
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB32)
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:
log.error("Receive image in bad format")
return
@@ -288,9 +288,6 @@ class QRemoteDesktop(QtGui.QWidget):
Call when Qt renderer engine estimate that is needed
@param e: QEvent
"""
#if there is no refresh -> done
if self._refresh == []:
return
#fill buffer image
with QtGui.QPainter(self._buffer) as qp:
#draw image

137
rdpy/ui/view.py Normal file
View File

@@ -0,0 +1,137 @@
#
# 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/>.
#
"""
Fake widget
"""
from rdpy.base.error import CallPureVirtualFuntion
from PyQt4 import QtGui, QtCore
class KeyCode(object):
ENTER = 28
UP = 328
DOWN = 336
class IRender(object):
def translate(self, dx, dy):
pass
def drawImage(self, image):
pass
class IView(object):
def keyEvent(self, code):
pass
def pointerEvent(self, x, y, button):
pass
def update(self, render):
pass
class AnchorView(IView):
def __init__(self, x, y, view):
self._x = x
self._y = y
self._view = view
def keyEvent(self, code):
self._view.keyEvent(code)
def pointerEvent(self, x, y, button):
self._view.pointerEvent(x - self._x, y - self._y)
def update(self, render):
render.translate(self._x, self._y)
self._view.update(self._view, render)
render.translate(- self._x, - self._y)
class ListView(IView):
"""
List widget simulate by QT painter
"""
def __init__(self, labels, width, height, callback):
self._labels = labels
self._width = width
self._height = height
self._cellHeight = 25
self._backGroudColor = QtGui.QColor(24, 93, 123)
self._fontSize = 14
self._current = 0
self._callback = callback
def keyEvent(self, code):
#enter key
if len(self._labels) == 0:
return
if code == KeyCode.ENTER:
self._callback(self._labels[self._current])
elif code == KeyCode.DOWN:
self._current = min(len(self._labels) - 1, self._current + 1)
elif code == KeyCode.UP:
self._current = max(0, self._current - 1)
def pointerEvent(self, x, y, button):
pass
def update(self, render):
"""
Draw GUI that list active session
"""
i = 0
drawArea = QtGui.QImage(self._width, self._height, QtGui.QImage.Format_RGB16)
#fill with background Color
drawArea.fill(self._backGroudColor)
with QtGui.QPainter(drawArea) as qp:
for label in self._labels:
rect = QtCore.QRect(0, i * self._cellHeight, self._width, self._cellHeight)
if i == self._current:
qp.setPen(QtCore.Qt.darkGreen)
qp.drawRoundedRect(rect, 0.2, 0.2)
qp.setPen(QtCore.Qt.white)
qp.setFont(QtGui.QFont('arial', self._fontSize, QtGui.QFont.Bold))
qp.drawText(rect, QtCore.Qt.AlignCenter, label)
i += 1
render.drawImage(drawArea)
class RDPRenderer(object):
def __init__(self, server):
"""
@param server: RDPServerController
"""
self._server = server
self._dx = 0
self._dy = 0
self._renderSize = 64
def translate(self, dx, dy):
self._dx += dx
self._dy += dy
def drawImage(self, image):
"""
Render of widget
"""
nbWidth = image.width() / self._renderSize + 1
nbHeight = image.height() / self._renderSize + 1
for i in range(0, nbWidth):
for j in range(0, nbHeight):
tmp = image.copy(i * self._renderSize, j * self._renderSize, self._renderSize, self._renderSize)
#in RDP image or bottom top encoded
tmp = tmp.transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
ptr = tmp.bits()
ptr.setsize(tmp.byteCount())
self._server.sendUpdate(i * self._renderSize + self._dx, j * self._renderSize + self._dy, min((i + 1) * self._renderSize, image.width()) + self._dx - 1, min((j + 1) * self._renderSize, image.height()) + self._dy - 1, tmp.width(), tmp.height(), 16, False, ptr.asstring())