bug fix on rfb

This commit is contained in:
citronneur
2014-11-01 17:23:30 +01:00
parent d10bb9a760
commit 3559b13f50
6 changed files with 231 additions and 36 deletions

16
LICENSE Normal file
View File

@@ -0,0 +1,16 @@
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/>.

View File

@@ -80,7 +80,7 @@ RDPY can also be used as Qt widget throw rdpy.ui.qt4.QRemoteDesktop class. It ca
In a nutshell the RDPY can be used as a protocol library with a twisted engine.
The client code looks like this:
The RDP client code looks like this:
```
from rdpy.protocol.rdp import rdp
@@ -106,7 +106,7 @@ class MyRDPFactory(rdp.ClientFactory):
#mouse move and click at pixel 200x200
self._controller.sendPointerEvent(200, 200, 1, true)
def onClode(self):
def onClose(self):
pass
return MyObserver(controller)

View File

@@ -42,17 +42,13 @@ class RFBClientQtFactory(rfb.ClientFactory):
@param controller: build by factory
"""
#create client observer
client = RFBClientQt(controller)
client = RFBClientQt(controller, 1024, 800)
#create qt widget
self._w = client.getWidget()
self._w.resize(1024, 800)
self._w.setWindowTitle('rdpy-vncclient')
self._w.show()
return client
def startedConnecting(self, connector):
pass
def clientConnectionLost(self, connector, reason):
"""
Connection lost event

167
bin/rdpy-vncscreenshot Executable file
View File

@@ -0,0 +1,167 @@
#!/usr/bin/python
#
# 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/>.
#
"""
example of use rdpy
take screenshot of login page
"""
import sys, os, getopt
# Change path so we find rdpy
sys.path.insert(1, os.path.join(sys.path[0], '..'))
from PyQt4 import QtCore, QtGui
from rdpy.protocol.rfb import rfb
import rdpy.base.log as log
from twisted.internet import task
#set log level
log._LOG_LEVEL = log.Level.INFO
class RFBScreenShotFactory(rfb.ClientFactory):
"""
@summary: Factory for screenshot exemple
"""
def __init__(self, path):
"""
@param path: path of output screenshot
"""
self._path = path
def clientConnectionLost(self, connector, reason):
"""
@summary: Connection lost event
@param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection
"""
log.info("connection lost : %s"%reason)
reactor.stop()
app.exit()
def clientConnectionFailed(self, connector, reason):
"""
@summary: Connection failed event
@param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection
"""
log.info("connection failed : %s"%reason)
reactor.stop()
app.exit()
def buildObserver(self, controller, addr):
"""
@summary: build ScreenShot observer
@param controller: RFBClientController
@param addr: address of target
"""
class ScreenShotObserver(rfb.RFBClientObserver):
"""
@summary: observer that connect, cache every image received and save at deconnection
"""
def __init__(self, controller, path):
"""
@param controller: RFBClientController
@param path: path of output screenshot
"""
rdp.RDPClientObserver.__init__(self, controller)
self._buffer = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
self._path = path
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
"""
@summary: callback use when bitmap is received
"""
self._hasUpdated = True
image = RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data);
with QtGui.QPainter(self._buffer) as qp:
#draw image
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
def onReady(self):
"""
@summary: callback use when RDP stack is connected (just before received bitmap)
"""
log.info("connected %s"%addr)
def onClose(self):
"""
@summary: callback use when RDP stack is closed
"""
log.info("save screenshot into %s"%self._path)
self._buffer.save(self._path)
def checkUpdate(self):
if not self._hasUpdated:
log.info("close connection on timeout without updating orders")
self._controller.close();
return
self._hasUpdated = False
return ScreenShotObserver(controller, self._width, self._height, self._path, self._timeout)
def help():
print "Usage: rdpy-rdpscreenshot [options] ip[:port]"
print "\t-w: width of screen default value is 1024"
print "\t-l: height of screen default value is 800"
print "\t-o: file path of screenshot default(/tmp/rdpy-rdpscreenshot.jpg)"
print "\t-t: timeout of connection without any updating order (default is 2s)"
if __name__ == '__main__':
#default script argument
width = 1024
height = 800
path = "/tmp/rdpy-rdpscreenshot.jpg"
timeout = 2.0
try:
opts, args = getopt.getopt(sys.argv[1:], "hw:l:o:t:")
except getopt.GetoptError:
help()
for opt, arg in opts:
if opt == "-h":
help()
sys.exit()
elif opt == "-w":
width = int(arg)
elif opt == "-l":
height = int(arg)
elif opt == "-o":
path = arg
elif opt == "-t":
timeout = float(arg)
if ':' in args[0]:
ip, port = args[0].split(':')
else:
ip, port = args[0], "3389"
#create application
app = QtGui.QApplication(sys.argv)
#add qt4 reactor
import qt4reactor
qt4reactor.install()
from twisted.internet import reactor
reactor.connectTCP(ip, int(port), RDPScreenShotFactory(width, height, path, timeout))
reactor.runReturn()
app.exec_()

View File

@@ -26,10 +26,10 @@ Implement Remote FrameBuffer protocol use in VNC client and server
@todo: more encoding rectangle
"""
from twisted.internet import protocol
from rdpy.network.layer import RawLayer
from rdpy.network.layer import RawLayer, RawLayerClientFactory
from rdpy.network.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType
from rdpy.base.error import InvalidValue, CallPureVirtualFuntion
import rdpy.base.log as log
class ProtocolVersion(object):
"""
@@ -223,7 +223,7 @@ class RFB(RawLayer):
elif data.len == 4:
bodyLen = UInt32Be()
else:
print "invalid header length"
log.error("invalid header length")
return
data.readType(bodyLen)
self.expect(bodyLen.value, self._callbackBody)
@@ -253,7 +253,7 @@ class RFB(RawLayer):
"""
self.readProtocolVersion(data)
if self._version.value == ProtocolVersion.UNKNOWN:
print "Unknown protocol version %s send 003.008"%data.getvalue()
log.info("Unknown protocol version %s send 003.008"%data.getvalue())
#protocol version is unknown try best version we can handle
self._version.value = ProtocolVersion.RFB003008
#send same version of
@@ -302,11 +302,11 @@ class RFB(RawLayer):
result = UInt32Be()
data.readType(result)
if result == UInt32Be(1):
print "Authentification failed"
log.info("Authentification failed")
if self._version.value == ProtocolVersion.RFB003008:
self.expectWithHeader(4, self.recvSecurityFailed)
else:
print "Authentification OK"
log.debug("Authentification OK")
self.sendClientInit()
def recvSecurityFailed(self, data):
@@ -314,7 +314,7 @@ class RFB(RawLayer):
Send by server to inform reason of why it's refused client
@param data: Stream that contains well formed packet
"""
print "Security failed cause to %s"%data.getvalue()
log.info("Security failed cause to %s"%data.getvalue())
def recvServerInit(self, data):
"""
@@ -330,7 +330,7 @@ class RFB(RawLayer):
@param data: Stream that contains well formed packet
"""
data.readType(self._serverName)
print "Server name %s"%str(self._serverName)
log.info("Server name %s"%str(self._serverName))
#end of handshake
#send pixel format
self.sendPixelFormat(self._pixelFormat)
@@ -454,19 +454,16 @@ class RFBClientListener(object):
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recvRectangle", "RFBListener"))
class RFBController(RFBClientListener):
class RFBClientController(RFBClientListener):
"""
Class use to manage RFB order and dispatch throw observers
@summary: Class use to manage RFB order and dispatch throw observers for client side
"""
def __init__(self, mode):
"""
@param mode: mode of inner RFB layer
"""
def __init__(self):
self._clientObservers = []
#rfb layer to send client orders
self._rfbLayer = RFB(self)
def getRFBLayer(self):
def getProtocol(self):
"""
@return: RFB layer build by controller
"""
@@ -498,7 +495,7 @@ class RFBController(RFBClientListener):
@param key: ASCII code of key
"""
if not self._rfbLayer._ready:
print "Try to send key event on non ready layer"
log.info("Try to send key event on non ready layer")
return
try:
event = KeyEvent()
@@ -507,7 +504,7 @@ class RFBController(RFBClientListener):
self._rfbLayer.sendKeyEvent(event)
except InvalidValue:
print "Try to send an invalid key event"
log.debug("Try to send an invalid key event")
def sendPointerEvent(self, mask, x, y):
"""
@@ -517,7 +514,7 @@ class RFBController(RFBClientListener):
@param y: y pointer of mouse pointer
"""
if not self._rfbLayer._ready:
print "Try to send pointer event on non ready layer"
log.info("Try to send pointer event on non ready layer")
return
try:
event = PointerEvent()
@@ -527,10 +524,16 @@ class RFBController(RFBClientListener):
self._rfbLayer.sendPointerEvent(event)
except InvalidValue:
print "Try to send an invalid pointer event"
log.debug("Try to send an invalid pointer event")
def close(self):
"""
@summary: close rfb stack
"""
self._rfbLayer.close()
class ClientFactory(protocol.Factory):
class ClientFactory(RawLayerClientFactory):
"""
Twisted Factory of RFB protocol
"""
@@ -539,9 +542,9 @@ class ClientFactory(protocol.Factory):
Function call by twisted on connection
@param addr: address where client try to connect
"""
controller = RFBController()
controller = RFBClientController()
self.buildObserver(controller)
return controller.getRFBLayer()
return controller.getProtocol()
def buildObserver(self, controller):
"""
@@ -575,7 +578,7 @@ class RFBClientObserver(object):
def mouseEvent(self, button, x, y):
"""
Send a mouse event to RFB Layer
@param button: button number which is pressed (0,1,2,3,4,5,6,7,8)
@param button: button number which is pressed (0,1,2,3,4,5,6,7)
@param x: x coordinate of mouse pointer
@param y: y coordinate of mouse pointer
"""

View File

@@ -70,6 +70,15 @@ class QAdaptor(object):
"""
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "closeEvent", "QAdaptor"))
def qtImageFormatFromRFBPixelFormat(pixelFormat):
"""
@summary: convert RFB pixel format to QtGui.QImage format
"""
if pixelFormat.BitsPerPixel.value == 32:
return QtGui.QImage.Format_RGB32
elif pixelFormat.BitsPerPixel.value == 16:
return QtGui.QImage.Format_RGB16
class RFBClientQt(RFBClientObserver, QAdaptor):
"""
QAdaptor for specific RFB protocol stack
@@ -101,15 +110,13 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
@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
else:
imageFormat = qtImageFormatFromRFBPixelFormat(pixelFormat)
if imageFormat is None:
log.error("Receive image in bad format")
return
image = QtGui.QImage(data, width, height, imageFormat)
self._widget.notifyImage(x, y, image)
self._widget.notifyImage(x, y, image, width, height)
def sendMouseEvent(self, e, isPressed):
"""
@@ -135,6 +142,12 @@ class RFBClientQt(RFBClientObserver, QAdaptor):
"""
self.keyEvent(isPressed, e.nativeVirtualKey())
def closeEvent(self, e):
"""
Call when you want to close connection
@param: QCloseEvent
"""
self._controller.close()
def RDPBitmapToQtImage(destLeft, width, height, bitsPerPixel, isCompress, data):
"""