diff --git a/bin/rdpy-rdpclient b/bin/rdpy-rdpclient index ff1a6c6..6ce5916 100755 --- a/bin/rdpy-rdpclient +++ b/bin/rdpy-rdpclient @@ -33,11 +33,15 @@ from rdpy.protocol.rdp import rdp class RDPClientQtFactory(rdp.ClientFactory): """ - Factory create a RDP GUI client + @summary: Factory create a RDP GUI client """ def __init__(self, width, height, username, password, domain): """ - init client with correct definition + @param width: width of client + @param heigth: heigth of client + @param username: username present to the server + @param password: password present to the server + @param domain: microsoft domain """ self._width = width self._height = height @@ -46,10 +50,13 @@ class RDPClientQtFactory(rdp.ClientFactory): self._domain = domain self._w = None - def buildObserver(self, controller): + def buildObserver(self, controller, addr): """ - Build RFB observer + @summary: Build RFB observer + We use a RDPClientQt as RDP observer @param controller: build factory and needed by observer + @param addr: destination address + @return: RDPClientQt """ #create client observer client = RDPClientQt(controller, self._width, self._height) @@ -70,7 +77,7 @@ class RDPClientQtFactory(rdp.ClientFactory): def clientConnectionLost(self, connector, reason): """ - Connection lost event + @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 """ @@ -80,7 +87,7 @@ class RDPClientQtFactory(rdp.ClientFactory): def clientConnectionFailed(self, connector, reason): """ - Connection failed event + @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 """ diff --git a/bin/rdpy-rdpproxy b/bin/rdpy-rdpproxy index 03c7c5b..72c94a4 100755 --- a/bin/rdpy-rdpproxy +++ b/bin/rdpy-rdpproxy @@ -19,8 +19,14 @@ # """ -RDP proxy recorder and spy function -Proxy RDP protocol +RDP proxy with spy capabilities + --------------------------- +Client RDP -> | ProxyServer | ProxyClient | -> Server RDP + --------------------------- + | ProxyAdmin | + ------------ + ^ +Admin ----------------------| """ import sys, os, getopt, json @@ -35,7 +41,7 @@ from PyQt4 import QtCore, QtGui class ProxyServer(rdp.RDPServerObserver): """ - Server side of proxy + @summary: Server side of proxy """ def __init__(self, controller, credentialProvider): """ @@ -48,42 +54,65 @@ class ProxyServer(rdp.RDPServerObserver): self._window = None def showSelectView(self, machines): - self._machines = dict([("%s:%s"%(ip, port), (ip, port)) for ip, port in machines]) + """ + @summary: Show select sever view to the client + @param machines: [(ip, port)] + """ + self._machines = machines width, height = self._controller.getScreen() - self._window = view.Window(width, height, QtGui.QColor(24, 93, 123)) - self._window.addView(view.Anchor(width / 2 - 250, 100, view.Label("Please select following server", 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), backgroundColor = QtGui.QColor(24, 93, 123)))) - self._window.addView(view.Anchor(width / 2 - 250, 150, view.List(self._machines.keys(), 500, 500, self.onSelectMachine, QtGui.QColor(24, 93, 123))), True) - self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True) + self._window = view.Window(width, height, QtGui.QColor(8, 24, 66)) + + self._window.addView(view.Anchor(width / 2 - 250, 100, view.Label("Please select following server", + 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), + backgroundColor = QtGui.QColor(8, 24, 66)))) + + self._window.addView(view.Anchor(width / 2 - 250, 150, view.List(["%s:%s"%(ip, port) for ip, port in machines], + 500, 500, self.onSelectMachine, + QtGui.QColor(8, 24, 66))), True) + self._window.update(view.RDPRenderer(self._controller), True) - def onSelectMachine(self, machine): - ip, port = self._machines[machine] + def onSelectMachine(self, index): + """ + @summary: Callback of view.List in Select server view + @param: machine str name of machine selected + @param: index in list + """ + ip, port = self._machines[index] width, height = self._controller.getScreen() domain, username, password = self._controller.getCredentials() - reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password, "%s\\%s on %s:%s"%(domain, username, ip, port))) + reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password)) def clientConnected(self, client): """ - Event throw by client when it's ready + @summary: Event throw by client when it's ready @param client: ProxyClient """ self._client = client + #need to reevaluate color depth self._controller.setColorDepth(self._client._controller.getColorDepth()) - def showErrorMessage(self, message): + def showMessage(self, message): """ - Print a message to the client + @summary: Print a message to the client @param message: string """ width, height = self._controller.getScreen() - popup = view.Window(width, height, QtGui.QColor(24, 93, 123)) - popup.addView(view.Anchor(width / 2 - 250, height / 2 - 25, view.Label(message, 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), backgroundColor = QtGui.QColor(24, 93, 123)))) - popup.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True) + + popup = view.Window(width, height, QtGui.QColor(8, 24, 66)) + popup.addView(view.Anchor(width / 2 - 250, height / 2 - 25, + view.Label(message, 500, 50, + QtGui.QFont('arial', 18, QtGui.QFont.Bold), + backgroundColor = QtGui.QColor(8, 24, 66)))) + popup.update(view.RDPRenderer(self._controller), True) def onReady(self): """ - Event use to inform state of server stack - Use to connect client - On ready is not launch only after connection sequence but after a reactivation sequence too + @summary: Event use to inform state of server stack + First time this event is called is when human client is connected + Second time is after color depth nego, because color depth nego + restart a connection sequence + Use to connect proxy client or show available server + @see: rdp.RDPServerObserver.onReady """ if self._client is None: #try a connection @@ -91,11 +120,13 @@ class ProxyServer(rdp.RDPServerObserver): machines = self._credentialProvider.getProxyPass(domain, username) if len(machines) == 0: - self.showErrorMessage("No servers attach to account %s\\%s"%(domain, username)) + self.showMessage("No servers attach to account %s\\%s"%(domain, username)) elif len(machines) == 1: ip, port = machines[0] width, height = self._controller.getScreen() - reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password, "%s\\%s on %s:%s"%(domain, username, ip, port))) + reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, + domain, username, password, + "%s\\%s on %s:%s"%(domain, username, ip, port))) else: self.showSelectView(machines) else: @@ -105,31 +136,35 @@ class ProxyServer(rdp.RDPServerObserver): def onClose(self): """ - Call when client close connection + @summary: Call when human client close connection + @see: rdp.RDPServerObserver.onClose """ if self._client is None: return + #close proxy client self._client._controller.close() def onKeyEventScancode(self, code, isPressed): """ - Event call when a keyboard event is catch in scan code format + @summary: Event call when a keyboard event is catch in scan code format @param code: scan code of key @param isPressed: True if key is down + @see: rdp.RDPServerObserver.onKeyEventScancode """ #no client connected if not self._client is None: self._client._controller.sendKeyEventScancode(code, isPressed) elif not self._window is None and isPressed: self._window.keyEvent(code) - self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth())) + self._window.update(view.RDPRenderer(self._controller)) def onKeyEventUnicode(self, code, isPressed): """ - Event call when a keyboard event is catch in unicode format + @summary: Event call when a keyboard event is catch in unicode format @param code: unicode of key @param isPressed: True if key is down + @see: rdp.RDPServerObserver.onKeyEventUnicode """ #no client connected domain if self._client is None: @@ -138,11 +173,12 @@ class ProxyServer(rdp.RDPServerObserver): def onPointerEvent(self, x, y, button, isPressed): """ - Event call on mouse event + @summary: Event call on mouse event @param x: x position @param y: y position @param button: 1, 2 or 3 button @param isPressed: True if mouse button is pressed + @see: rdp.RDPServerObserver.onPointerEvent """ #no client connected if self._client is None: @@ -151,33 +187,36 @@ class ProxyServer(rdp.RDPServerObserver): class ProxyServerFactory(rdp.ServerFactory): """ - Factory on listening events + @summary: Factory on listening events """ def __init__(self, credentialProvider, privateKeyFilePath, certificateFilePath): """ - @param config: rdp-proxy configuration @param credentialProvider: CredentialProvider + @param privateKeyFilePath: file contain server private key + @param certificateFilePath: file contain server certificate """ rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16) self._credentialProvider = credentialProvider - def buildObserver(self, controller): + def buildObserver(self, controller, addr): """ - Implement rdp.ServerFactory @param controller: rdp.RDPServerController + @param addr: destination address + @see: rdp.ServerFactory.buildObserver """ return ProxyServer(controller, self._credentialProvider) class ProxyClient(rdp.RDPClientObserver): """ - Client side of proxy + @summary: Client side of proxy """ - _CONNECTED_ = {} - def __init__(self, controller, server, name): + _CONNECTED_ = [] + def __init__(self, controller, server, name = None): """ - @param controller: RDPClientObserver + @param controller: rdp.RDPClientController @param server: ProxyServer - @param name: name of session + @param name: name of session None if you don't + want to spy this session """ rdp.RDPClientObserver.__init__(self, controller) self._server = server @@ -185,25 +224,24 @@ class ProxyClient(rdp.RDPClientObserver): def onReady(self): """ - Event use to signal that RDP stack is ready - Inform proxy server that i'm connected - implement RDPClientObserver + @summary: Event use to signal that RDP stack is ready + Inform ProxyServer that i'm connected + @see: rdp.RDPClientObserver.onReady """ - ProxyClient._CONNECTED_[self._name] = self + if not self._name is None: + ProxyClient._CONNECTED_.append(self) self._server.clientConnected(self) def onClose(self): """ - Stack is closes + @summary: Event inform that stack is close + @see: rdp.RDPClientObserver.onClose """ - if ProxyClient._CONNECTED_.has_key(self._name): - del ProxyClient._CONNECTED_[self._name] - self._server._controller.close() + ProxyClient._CONNECTED_.remove(self) def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data): """ - Event use to inform bitmap update - implement RDPClientObserver + @summary: Event use to inform bitmap update @param destLeft: xmin position @param destTop: ymin position @param destRight: xmax position because RDP can send bitmap with padding @@ -213,14 +251,15 @@ class ProxyClient(rdp.RDPClientObserver): @param bitsPerPixel: number of bit per pixel @param isCompress: use RLE compression @param data: bitmap data + @see: rdp.RDPClientObserver.onUpdate """ self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data) class ProxyClientFactory(rdp.ClientFactory): """ - Factory for proxy client + @summary: Factory for proxy client """ - def __init__(self, server, width, height, domain, username, password, name): + def __init__(self, server, width, height, domain, username, password): """ @param server: ProxyServer @param width: screen width @@ -228,7 +267,6 @@ class ProxyClientFactory(rdp.ClientFactory): @param domain: domain session @param username: username session @param password: password session - @param name: name of session """ self._controller = server self._width = width @@ -236,13 +274,14 @@ class ProxyClientFactory(rdp.ClientFactory): self._domain = domain self._username = username self._password = password - self._name = name - def buildObserver(self, controller): + def buildObserver(self, controller, addr): """ - Implement rdp.ClientFactory - Build observer (ProxyClient) + @summary: Build observer @param controller: rdp.RDPClientController + @param addr: destination address + @see: rdp.ClientFactory.buildObserver + @return: ProxyClient """ #set screen resolution controller.setScreen(self._width, self._height) @@ -250,7 +289,7 @@ class ProxyClientFactory(rdp.ClientFactory): controller.setDomain(self._domain) controller.setUsername(self._username) controller.setPassword(self._password) - proxy = ProxyClient(controller, self._controller, self._name) + proxy = ProxyClient(controller, self._controller, "%s\\%s on %s"%(self._domain, self._username, addr)) return proxy def startedConnecting(self, connector): @@ -265,18 +304,14 @@ class ProxyClientFactory(rdp.ClientFactory): class ProxyAdmin(rdp.RDPServerObserver): """ - Use to manage client side of admin session - Add GUI to select which session to see - And manage see session - Just escape key is authorized during spy session + @summary: Use to manage admin session + Add GUI to select which session to see + Just escape key is authorized during spy session + To switch from spy state to admin state """ class State(object): - """ - GUI state -> list of active session - SPY state -> watch active session - """ - GUI = 0 - SPY = 1 + GUI = 0 #->list of active session + SPY = 1 #->watch active session def __init__(self, controller): """ @@ -289,21 +324,32 @@ class ProxyAdmin(rdp.RDPServerObserver): def initView(self): """ - Init GUI view + @summary: Initialize Admin GUI view """ + self._sessions = list(ProxyClient._CONNECTED_) #copy at t time width, height = self._controller.getScreen() - self._window = view.Window(width, height, QtGui.QColor(24, 93, 123)) - self._window.addView(view.Anchor(width / 2 - 250, 100, view.Label("Please select following session", 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), backgroundColor = QtGui.QColor(24, 93, 123)))) - self._window.addView(view.Anchor(width / 2 - 250, 150, view.List(ProxyClient._CONNECTED_.keys(), 500, 500, self.onSelect, QtGui.QColor(24, 93, 123))), True) + self._window = view.Window(width, height, QtGui.QColor(8, 24, 66)) + self._window.addView(view.Anchor(width / 2 - 250, 100, view.Label("Please select following session", + 500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold), + backgroundColor = QtGui.QColor(8, 24, 66)))) + + self._window.addView(view.Anchor(width / 2 - 250, 150, view.List([p._name for p in self._sessions], + 500, 500, self.onSelect, + QtGui.QColor(8, 24, 66))), True) + + def clientConnected(self, client): + pass + def onReady(self): """ - Stack is ready and connected - May be called after an setColorDepth too + @summary: Stack is ready and connected + May be called after an setColorDepth too + @see: rdp.RDPServerObserver.onReady """ if self._state == ProxyAdmin.State.GUI: self.initView() - self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True) + self._window.update(view.RDPRenderer(self._controller), True) elif self._state == ProxyAdmin.State.SPY: #refresh client width, height = self._controller.getScreen() @@ -311,21 +357,24 @@ class ProxyAdmin(rdp.RDPServerObserver): def onClose(self): """ - Stack is closes + @summary: Stack is close + @see: rdp.RDPServerObserver.onClose """ pass def onKeyEventScancode(self, code, isPressed): """ - Event call when a keyboard event is catch in scan code format + @summary: Event call when a keyboard event + is catch in scan code format @param code: scan code of key @param isPressed: True if key is down + @see: rdp.RDPServerObserver.onKeyEventScancode """ if self._state == ProxyAdmin.State.GUI: if not isPressed: return self._window.keyEvent(code) - self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth())) + self._window.update(view.RDPRenderer(self._controller)) elif code == 1: #escape button refresh GUI self._state = ProxyAdmin.State.GUI @@ -334,38 +383,39 @@ class ProxyAdmin(rdp.RDPServerObserver): def onKeyEventUnicode(self, code, isPressed): """ - Event call when a keyboard event is catch in unicode format - In admin mode is forbidden + @summary: Event call when a keyboard event is catch in unicode format + Admin GUI add filter for this event @param code: unicode of key @param isPressed: True if key is down + @see: rdp.RDPServerObserver.onKeyEventUnicode """ pass def onPointerEvent(self, x, y, button, isPressed): """ - Event call on mouse event - In admin mode is forbidden + @summary: Event call on mouse event + Admin GUI add filter for this event @param x: x position @param y: y position @param button: 1, 2 or 3 button @param isPressed: True if mouse button is pressed + @see: rdp.RDPServerObserver.onPointerEvent """ pass - def onSelect(self, name): + def onSelect(self, index): """ - Call back of list view - @param name: name selected by user + @summary: Callback of list view of active session + Connect to select session + @param index: index in sessions array """ - if not ProxyClient._CONNECTED_.has_key(name): - return self._state = ProxyAdmin.State.SPY - self._spy = ProxyClient(ProxyClient._CONNECTED_[name]._controller, self, "Admin") + self._spy = ProxyClient(self._sessions[index]._controller, self) self._controller.setColorDepth(self._spy._controller.getColorDepth()) class ProxyAdminFactory(rdp.ServerFactory): """ - Factory for admin + @summary: Factory for admin session """ def __init__(self, privateKeyFilePath, certificateFilePath): """ @@ -374,16 +424,19 @@ class ProxyAdminFactory(rdp.ServerFactory): """ rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16) - def buildObserver(self, controller): + def buildObserver(self, controller, addr): """ - Implement rdp.ServerFactory + @summary: Build ProxyAdmin @param controller: rdp.RDPServerController + @param addr: destination address + @return: ProxyAdmin + @see: rdp.ServerFactory.buildObserver """ return ProxyAdmin(controller) class CredentialProvider(object): """ - Credential provider for proxy + @summary: Credential provider for proxy """ def __init__(self, config): """ @@ -392,12 +445,19 @@ class CredentialProvider(object): self._config = config def getAccount(self, domain, username): + """ + @summary: Find account that match domain::username in config file + @param domain: Windows domain + @param username: username for session + @return: [(unicode(ip), port] or None if not found + """ if not self._config.has_key(domain) or not self._config[domain].has_key(username): return None return self._config[domain][username] def getProxyPass(self, domain, username): """ + @summary: Find list of server available for thi account @param domain: domain to check @param username: username in domain @return: [(ip, port)] @@ -409,13 +469,13 @@ class CredentialProvider(object): def help(): """ - Print help in console + @summary: Print help in console """ print "Usage: rdpy-rdpproxy -f credential_file_path -k private_key_file_path -c certificate_file_path listen_port" def loadConfig(configFilePath): """ - Load and check config file + @summary: Load and check config file @param configFilePath: config file path """ if not os.path.isfile(configFilePath): diff --git a/rdpy/base/const.py b/rdpy/base/const.py index fa66ae2..b76fe54 100644 --- a/rdpy/base/const.py +++ b/rdpy/base/const.py @@ -24,42 +24,45 @@ Const it's use to create fake object enum in python from copy import deepcopy class Constant(object): - ''' - Constant descriptor that deep copy value on get - ''' + """ + @summary: Constant descriptor that deep copy value on get + """ def __init__(self, value): - ''' - Constructor keep value - ''' + """ + @param value: value to protect + """ self._value = value def __get__(self, obj, objType): - ''' - on get constant return deep copy of wrapped value - ''' + """ + @summary: on get constant return deep copy of wrapped value + @param obj: unknown + @param objType: unknown + """ return deepcopy(self._value) def __set__(self, obj, value): - ''' - set is forbidden - in python 2.7 this function work only - on instanciate object - ''' + """ + @summary: Try to set a protect value is forbidden + in python 2.7 this function work only + on instanciate object + @param obj: + """ raise Exception("can't assign constant") def __delete__(self, obj): - ''' - delete is forbidden on constant - ''' + """ + @summary: delete is forbidden on constant + """ raise Exception("can't delete constant") def TypeAttributes(typeClass): - ''' - call typeClass ctor on each attributes - to uniform atributes type on class + """ + @summary: Call typeClass ctor on each attributes + to uniform atributes type on class @param typeClass: class use to construct each class attributes @return: class decorator - ''' + """ def wrapper(cls): for c_name, c_value in cls.__dict__.iteritems(): if c_name[0] != '_' and not callable(c_value): @@ -68,11 +71,11 @@ def TypeAttributes(typeClass): return wrapper def ConstAttributes(cls): - ''' - copy on read attributes - transform all attributes of class - in constant attribute - only attributes which are not begining with '_' char - and are not callable - ''' + """ + @summary: Copy on read attributes + transform all attributes of class + in constant attribute + only attributes which are not begining with '_' char + and are not callable + """ return TypeAttributes(Constant)(cls) \ No newline at end of file diff --git a/rdpy/base/error.py b/rdpy/base/error.py index 46b0080..ce0d728 100644 --- a/rdpy/base/error.py +++ b/rdpy/base/error.py @@ -23,7 +23,7 @@ All exceptions error use in RDPY class CallPureVirtualFuntion(Exception): """ - Raise when a virtual function is called and not implemented + @summary: Raise when a virtual function is called and not implemented """ def __init__(self, message = ""): """ @@ -33,7 +33,7 @@ class CallPureVirtualFuntion(Exception): class InvalidValue(Exception): """ - Raise when invalid value type occurred + @summary: Raise when invalid value type occurred """ def __init__(self, message = ""): """ @@ -43,7 +43,7 @@ class InvalidValue(Exception): class InvalidExpectedDataException(Exception): """ - Raise when expected data on network is invalid + @summary: Raise when expected data on network is invalid """ def __init__(self, message = ""): """ @@ -53,7 +53,7 @@ class InvalidExpectedDataException(Exception): class NegotiationFailure(Exception): """ - Raise when negotiation failure in different protocols + @summary: Raise when negotiation failure in different protocols """ def __init__(self, message = ""): """ @@ -63,7 +63,7 @@ class NegotiationFailure(Exception): class InvalidType(Exception): """ - Raise when invalid value type occured + @summary: Raise when invalid value type occured """ def __init__(self, message = ""): """ @@ -73,7 +73,7 @@ class InvalidType(Exception): class InvalidSize(Exception): """ - Raise when invalid size is present in packet type occured + @summary: Raise when invalid size is present in packet type occured """ def __init__(self, message = ""): """ @@ -83,7 +83,7 @@ class InvalidSize(Exception): class ErrorReportedFromPeer(Exception): """ - Raise when peer send an error + @summary: Raise when peer send an error """ def __init__(self, message = ""): """ diff --git a/rdpy/base/log.py b/rdpy/base/log.py index a6433e3..e08c9a5 100644 --- a/rdpy/base/log.py +++ b/rdpy/base/log.py @@ -24,7 +24,7 @@ Actually very basic log engine class Level(object): """ - Level log + @summary: Level log """ DEBUG = 0 INFO = 1 @@ -34,24 +34,44 @@ class Level(object): _LOG_LEVEL = Level.DEBUG def log(message): + """ + @summary: Main log function + @param message: string to print + """ print message def error(message): + """ + @summary: Log error message + @param message: string to print as error log + """ if _LOG_LEVEL > Level.ERROR: return log("ERROR : %s"%message) def warning(message): + """ + @summary: Log warning message + @param message: string to print as warning log + """ if _LOG_LEVEL > Level.WARNING: return log("WARNING : %s"%message) def info(message): + """ + @summary: Log info message + @param message: string to print as info log + """ if _LOG_LEVEL > Level.INFO: return log("INFO : %s"%message) def debug(message): + """ + @summary: Log debug message + @param message: string to print as debug log + """ if _LOG_LEVEL > Level.DEBUG: return log("DEBUG : %s"%message) \ No newline at end of file diff --git a/rdpy/network/layer.py b/rdpy/network/layer.py index 7425e0f..4f0284d 100644 --- a/rdpy/network/layer.py +++ b/rdpy/network/layer.py @@ -27,32 +27,30 @@ from rdpy.base.error import CallPureVirtualFuntion class IStreamListener(object): """ - Interface use to inform that we can handle receive stream + @summary: Interface use to inform stream receiver capability """ def recv(self, s): """ - Signal that data is available for this layer - call by transport layer - default is to pass data to presentation layer - @param s: raw Stream receive from transport layer + @summary: Signal that data is available + @param s: Stream """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) class IStreamSender(object): """ - Interface use to show stream sender capability + @summary: Interface use to inform stream sender capability """ def send(self, data): - ''' - Send Stream on layer + """ + @summary: Send Stream on layer @param data: Type or tuple element handle by transport layer - ''' + """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "send", "IStreamSender")) class Layer(object): """ - A simple double linked list with presentation and transport layer - and a subset of event (connect and close) + @summary: A simple double linked list with presentation and transport layer + and a subset of event (connect and close) """ def __init__(self, presentation = None): """ @@ -68,24 +66,25 @@ class Layer(object): def connect(self): """ - Call when transport layer is connected - default is send connect event to presentation layer + @summary: Call when transport layer is connected + default is send connect event to presentation layer """ if not self._presentation is None: self._presentation.connect() def close(self): """ - Close layer event - default is sent to transport layer + @summary: Close layer event + default is sent to transport layer """ if not self._transport is None: self._transport.close() class LayerAutomata(Layer, IStreamListener): """ - Layer with automata state - we can set next recv function used for Stream packet + @summary: Layer with automata callback + we can set next recv function used for Stream packet + Usefull for event driven engine as twisted """ def __init__(self, presentation = None): """ @@ -96,10 +95,8 @@ class LayerAutomata(Layer, IStreamListener): def setNextState(self, callback = None): """ - Set receive function to next callback or - current self.recv function if it's None - @param callback: a callable object that can - receive Layer, Stream parameters + @summary: Set the next callback in automata + @param callback: a callable object """ if callback is None: callback = self.__class__.recv @@ -114,11 +111,11 @@ from type import Stream class RawLayerClientFactory(protocol.ClientFactory): """ - Abstract class for Raw layer client factory + @summary: Abstract class for Raw layer client factory """ def buildProtocol(self, addr): """ - Function call from twisted and build rdp protocol stack + @summary: Function call from twisted @param addr: destination address """ rawLayer = self.buildRawLayer(addr) @@ -127,24 +124,25 @@ class RawLayerClientFactory(protocol.ClientFactory): def buildRawLayer(self, addr): """ - Override this function to build raw layer + @summary: Override this function to build raw layer @param addr: destination address """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) def connectionLost(self, rawlayer): """ - Overirde this method to handle connection lost + @summary: Override this method to handle connection lost + @param rawlayer: rawLayer that cause connectionLost event """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) class RawLayerServerFactory(protocol.ClientFactory): """ - Abstract class for Raw layer server factory + @summary: Abstract class for Raw layer server factory """ def buildProtocol(self, addr): """ - Function call from twisted and build rdp protocol stack + @summary: Function call from twisted @param addr: destination address """ rawLayer = self.buildRawLayer(addr) @@ -153,23 +151,24 @@ class RawLayerServerFactory(protocol.ClientFactory): def buildRawLayer(self, addr): """ - Override this function to build raw layer + @summary: Override this function to build raw layer @param addr: destination address """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) def connectionLost(self, rawlayer): """ - Overirde this method to handle connection lost + @summary: Override this method to handle connection lost + @param rawlayer: rawLayer that cause connectionLost event """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender): """ - Inherit from protocol twisted class - allow this protocol to wait until expected size of packet - and use Layer automata to call next automata state + @summary: Wait event from twisted engine + And format correct size packet + And send correct packet to next automata callback """ def __init__(self, presentation = None): """ @@ -185,15 +184,15 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender): def setFactory(self, factory): """ - Call by RawLayer Factory + @summary: Call by RawLayer Factory @param param: RawLayerClientFactory or RawLayerFactory """ self._factory = factory def dataReceived(self, data): """ - Inherit from protocol class - main event of received data + @summary: Inherit from twisted.protocol class + main event of received data @param data: string data receive from twisted """ #add in buffer @@ -209,24 +208,31 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender): def connectionMade(self): """ - inherit from twisted protocol + @summary: inherit from twisted protocol """ #join two scheme self.connect() def connectionLost(self, reason): + """ + @summary: Call from twisted engine when protocol is closed + @param reason: str represent reason of close connection + """ self._factory.connectionLost(self) def close(self): """ - Close raw layer + @summary: Close raw layer + Use File descriptor directly to not use TLS close + Because is bugged """ FileDescriptor.loseConnection(self.transport) def expect(self, expectedLen, callback = None): """ - Configure layer to change next state with callback only - when expectLen bytes is received from transport layer + @summary: Set next automata callback, + But this callback will be only called when + data have expectedLen @param expectedLen: in bytes length use to call next state @param callback: callback call when expected length bytes is received """ @@ -236,8 +242,9 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender): def send(self, message): """ - Send Stream on TCP layer - format message into raw stream understood by transport layer + @summary: Send Stream on TCP layer + write rdpy Stream message to str + And send it to transport layer @param message: (tuple | Type) """ s = Stream() diff --git a/rdpy/network/type.py b/rdpy/network/type.py index 7251094..19057e7 100644 --- a/rdpy/network/type.py +++ b/rdpy/network/type.py @@ -18,10 +18,10 @@ # """ -All type use RDPY +Raw type use un RDPY -It's a basic implementation that seems to protobuf but dynamically -We are in python we can use that! +It's a basic implementation looks like protobuf but dynamically +We are in python! """ import struct @@ -32,10 +32,10 @@ import rdpy.base.log as log def sizeof(element): """ - Byte size of type sum sizeof of tuple element - And count only element that condition is true at sizeof call + @summary: Size in Byte of element. + Ignore element which conditional is False @param element: Type or Tuple(Type | Tuple,) - @return: size of element in byte + @return: size of element in byte or zero for unknown element """ if isinstance(element, tuple) or isinstance(element, list): size = 0 @@ -44,19 +44,20 @@ def sizeof(element): return size elif isinstance(element, Type) and element._conditional(): return element.__sizeof__() - return 0 - + return 0 class Type(object): """ - Root type object inheritance - Record conditional optional of constant mechanism + @summary: Root type object inheritance + Record conditional optional of constant mechanism """ def __init__(self, conditional = lambda:True, optional = False, constant = False): """ - @param conditional : function call before read or write type - @param optional: boolean check before read if there is still data in stream - @param constant: if true check any modification of object during reading + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation """ self._conditional = conditional self._optional = optional @@ -70,9 +71,9 @@ class Type(object): def write(self, s): """ - Write type into stream if conditional is true - Call virtual __write__ method - @param s: Stream which will be written + @summary: Check conditional callback + before call __write__ function + @param s: Stream that will be written """ self._is_writed = self._conditional() if not self._is_writed: @@ -81,9 +82,11 @@ class Type(object): def read(self, s): """ - Read type from stream s if conditional is true - Check constantness + @summary: Check conditional callback + Call __read__ function + And check constness state after @param s: Stream + @raise InvalidExpectedDataException: if constness is not respected """ self._is_readed = self._conditional() if not self._is_readed: @@ -105,51 +108,54 @@ class Type(object): def __read__(self, s): """ - Interface definition of private read function + @summary: Interface definition of private read function @param s: Stream """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "__read__", "Type")) def __write__(self, s): """ - Interface definition of private write function + @summary: Interface definition of private write function @param s: Stream """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "__write__", "Type")) def __sizeof__(self): """ - Return size of type use for sizeof function + @summary: Return size of type use for sizeof function @return: size in byte of type """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "__sizeof__", "Type")) class CallableValue(object): """ - Wrap access of callable value. - When use getter value is call. - Constant value can also be wrap and will be transformed into callable value(lambda function) + @summary: Expression evaluate when is get or set + Ex: Type contain lenght of array and array + To know the size of array you need to read + length field before. A ctor time no length was readed. + You need a callable object that will be evaluate when it will be used """ def __init__(self, value): """ - @param value: value will be wrapped (constant | lambda | function) + @param value: value will be wrapped (raw python type | lambda | function) """ self._value = None self.value = value def __getValue__(self): """ - Can be overwritten to add specific check before - self.value is call - @return: result of callable value + @summary: Call when value is get -> Evaluate inner expression + Can be overwritten to add specific check before + self.value is call + @return: value expression evaluated """ return self._value() def __setValue__(self, value): """ - Can be overwritten to add specific check before - self.value = value is call - Check if value is callable and if not transform it + @summary: Call when value is set + Can be overwritten to add specific check before + self.value = value is call @param value: new value wrapped if constant -> lambda function """ value_callable = lambda:value @@ -161,7 +167,7 @@ class CallableValue(object): @property def value(self): """ - Shortcut to access inner value main getter of value + @summary: Evaluate callable expression @return: result of callable value """ return self.__getValue__() @@ -169,24 +175,28 @@ class CallableValue(object): @value.setter def value(self, value): """ - Setter of value after check it main setter of value + @summary: Setter of value @param value: new value encompass in value type object """ self.__setValue__(value) class SimpleType(Type, CallableValue): """ - Simple type + @summary: Non composite type + leaf in type tree + And is a callable value """ def __init__(self, structFormat, typeSize, signed, value, conditional = lambda:True, optional = False, constant = False): """ @param structFormat: letter that represent type in struct package @param typeSize: size in byte of type @param signed: true if type represent a signed type - @param value: value recorded in this object (can be callable value which be call when is access useful with closure) - @param conditional : function call before read or write type - @param optional: boolean check before read if there is still data in stream - @param constant: if true check any modification of object during reading + @param value: value recorded in this object (raw python type | lambda | function) + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation """ self._signed = signed self._typeSize = typeSize @@ -196,15 +206,20 @@ class SimpleType(Type, CallableValue): def __getValue__(self): """ - CallableValue overwrite check mask type of value - use CallableValue access + @summary: Check value if match range of type + And apply sign + Ex: UInt8 can be > 255 @return: Python value wrap into type @raise InvalidValue: if value doesn't respect type range + @see: CallableValue.__getValue__ """ value = CallableValue.__getValue__(self) + + #check value now because it can be an callable value + #and evaluate a this time + if not self.isInRange(value): raise InvalidValue("value is out of range for %s"%self.__class__) - if self._signed: return value else: @@ -212,52 +227,44 @@ class SimpleType(Type, CallableValue): def __setValue__(self, value): """ - CallableValue overwrite - Check mask type of value - @param value: new value encompass in object (respect Python type | lambda | function) + @summary: Check if new value respect type declaration + Ex: UInt8 can be > 256 + @param value: new value (raw python type | lambda | function) @raise InvalidValue: if value doesn't respect type range + @see: CallableValue.__setValue__ """ #check static value range if not callable(value) and not self.isInRange(value): raise InvalidValue("value is out of range for %s"%self.__class__) CallableValue.__setValue__(self, value) - - - def __cmp__(self, other): - """ - Compare inner value - Magic function of Python use for any compare operators - @param other: SimpleType value which will be compared with self value - or try to construct same type as self around other value - @return: Python value compare - """ - if not isinstance(other, SimpleType): - other = self.__class__(other) - return self.value.__cmp__(other.value) def __write__(self, s): """ - Write value in stream s - Use Struct package to pack value - @param s: Stream which will be written + @summary: Write value in stream + Use struct package to pack value + In accordance of structFormat field + @param s: Stream that will be written """ s.write(struct.pack(self._structFormat, self.value)) def __read__(self, s): """ - Read inner value from stream - Use struct package - @param s: Stream + @summary: Read inner value from stream + Use struct package to unpack + In accordance of structFormat and typeSize fields + @param s: Stream that will be read + @raise InvalidSize: if there is not enough data in stream """ if s.dataLen() < self._typeSize: - raise InvalidSize("Stream is too small to read expected data") + raise InvalidSize("Stream is too small to read expected Simple") self.value = struct.unpack(self._structFormat, s.read(self._typeSize))[0] def mask(self): """ - Compute bit mask for type - Because in Python all numbers are Int long or float + @summary: Compute bit mask for type + Because in Python all numbers are Int long or float + Cache result in self._mask field """ if not self.__dict__.has_key("_mask"): mask = 0xff @@ -268,7 +275,7 @@ class SimpleType(Type, CallableValue): def isInRange(self, value): """ - Check if value is in mask range + @summary: Check if value is in range represented by mask @param value: Python value @return: true if value is in type range """ @@ -279,15 +286,27 @@ class SimpleType(Type, CallableValue): def __sizeof__(self): """ - Return size of type + @summary: Return size of type in bytes @return: typeSize pass in constructor """ return self._typeSize + def __cmp__(self, other): + """ + @summary: Compare two simple type + Call inner value compare operator + @param other: SimpleType value or try to build same type as self + around value + @return: python value compare + """ + if not isinstance(other, SimpleType): + other = self.__class__(other) + return self.value.__cmp__(other.value) + def __invert__(self): """ - Implement not operator - @return: __class__ value + @summary: Implement not operator + @return: not inner value """ invert = ~self.value if not self._signed: @@ -296,10 +315,10 @@ class SimpleType(Type, CallableValue): def __add__(self, other): """ - Implement addition operator - @param other: SimpleType value or try to construct same type as self - around other value - @return: self.__class__ object with add result + @summary: Implement addition operator + @param other: SimpleType value or try to construct same type as self + around other value + @return: add operator of inner values @raise InvalidValue: if new value is out of bound """ if not isinstance(other, SimpleType): @@ -308,10 +327,10 @@ class SimpleType(Type, CallableValue): def __sub__(self, other): """ - Implement sub operator - @param other: SimpleType value or try to construct same type as self - around other value - @return: self.__class__ object with sub result + @summary: Implement sub operator + @param other: SimpleType value or try to construct same type as self + around other value + @return: sub operator of inner values @raise InvalidValue: if new value is out of bound """ if not isinstance(other, SimpleType): @@ -320,10 +339,11 @@ class SimpleType(Type, CallableValue): def __and__(self, other): """ - Implement bitwise and operator - @param other: SimpleType value or try to construct same type as self - around other value - @return: self.__class__ object with and result + @summary: Implement bitwise and operator + @param other: SimpleType value or try to construct same type as self + around other value + @return: and operator of inner values + @raise InvalidValue: if new value is out of bound """ if not isinstance(other, SimpleType): other = self.__class__(other) @@ -331,10 +351,11 @@ class SimpleType(Type, CallableValue): def __or__(self, other): """ - implement bitwise or operator - @param other: SimpleType value or try to construct same type as self - around other value - @return: self.__class__ object with or result + @summary: Implement bitwise or operator + @param other: SimpleType value or try to construct same type as self + around other value + @return: or operator of inner values + @raise InvalidValue: if new value is out of bound """ if not isinstance(other, SimpleType): other = self.__class__(other) @@ -342,10 +363,11 @@ class SimpleType(Type, CallableValue): def __xor__(self, other): """ - Implement bitwise xor operator - @param other: SimpleType value or try to construct same type as self - around other value - @return: self.__class__ object with or result + @summary: Implement bitwise xor operator + @param other: SimpleType value or try to construct same type as self + around other value + @return: xor operator of inner values + @raise InvalidValue: if new value is out of bound """ if not isinstance(other, SimpleType): other = self.__class__(other) @@ -353,9 +375,11 @@ class SimpleType(Type, CallableValue): def __lshift__(self, other): """ - Left shift operator - @param other: Python Int - @return: self.__class__ object with or result + @summary: Left shift operator + @param other: SimpleType value or try to construct same type as self + around other value + @return: lshift operator of inner values + @raise InvalidValue: if new value is out of bound """ if not isinstance(other, SimpleType): other = self.__class__(other) @@ -363,9 +387,11 @@ class SimpleType(Type, CallableValue): def __rshift__(self, other): """ - Left shift operator - @param other: python int - @return: self.__class__ object with or result + @summary: Right shift operator + @param other: SimpleType value or try to construct same type as self + around other value + @return: rshift operator of inner values + @raise InvalidValue: if new value is out of bound """ if not isinstance(other, SimpleType): other = self.__class__(other) @@ -373,14 +399,14 @@ class SimpleType(Type, CallableValue): def __hash__(self): """ - Hash function to treat simple type in hash collection + @summary: Hash function to handle simple type in hash collection @return: hash of inner value """ return hash(self.value) def __nonzero__(self): """ - Boolean conversion + @summary: Boolean conversion @return: bool of inner value """ return bool(self.value) @@ -388,16 +414,19 @@ class SimpleType(Type, CallableValue): class CompositeType(Type): """ - Type node of other sub type + @summary: Type node in Type tree + Track type field declared in __init__ function + Ex: self.lengthOfPacket = UInt16Le() -> record lengthOfPacket as sub type of node """ def __init__(self, conditional = lambda:True, optional = False, constant = False, readLen = None): """ - Keep ordering declaration of simple type - in list and transparent for other type - @param conditional : function call before read or write type - @param optional: boolean check before read if there is still data in stream - @param constant: if true check any changing of object during reading - @param readLen: max length to read + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + @param readLen: Max length in bytes can be readed from stream + Use to check length information """ Type.__init__(self, conditional = conditional, optional = optional, constant = constant) #list of ordoned type @@ -406,7 +435,9 @@ class CompositeType(Type): def __setattr__(self, name, value): """ - Magic function to update type list + @summary: Track Type field + For Type field record it in same order as declared + Keep other but bot handle in read or write functino @param name: name of new attribute @param value: value of new attribute """ @@ -416,8 +447,12 @@ class CompositeType(Type): def __read__(self, s): """ - Call read on each ordered sub-type + @summary: Read composite type + Call read on each ordered sub-type + And check read length parameter + If an error occurred rollback type already read @param s: Stream + @raise InvalidSize: if stream is greatter than readLen parameter """ readLen = 0 for name in self._typeName: @@ -440,12 +475,13 @@ class CompositeType(Type): s.pos -= sizeof(self.__dict__[tmpName]) raise e if not self._readLen is None and readLen < self._readLen.value: - log.debug("still have correct data in packet %s, read it as padding"%self.__class__) + log.debug("Still have correct data in packet %s, read it as padding"%self.__class__) s.read(self._readLen.value - readLen) def __write__(self, s): """ - Call write on each ordered sub type + @summary: Write all sub-type handle by __setattr__ function + Call write on each ordered sub type @param s: Stream """ for name in self._typeName: @@ -457,8 +493,8 @@ class CompositeType(Type): def __sizeof__(self): """ - Call sizeof on each sub type - @return: sum of sizeof of each public type attributes + @summary: Call sizeof on each sub type + @return: sum of sizeof of each Type attributes """ size = 0 for name in self._typeName: @@ -466,12 +502,12 @@ class CompositeType(Type): return size def __eq__(self, other): - ''' - compare each properties which are Type inheritance - if one is different then not equal + """ + @summary: Compare each properties which are Type inheritance + if one is different then not equal @param other: CompositeType - @return: True if each subtype are equals - ''' + @return: True if each sub-type are equals + """ if self._typeName != other._typeName: return False for name in self._typeName: @@ -480,201 +516,219 @@ class CompositeType(Type): return True def __ne__(self, other): - ''' - return not equal result operator + """ + @summary: return not equal result operator @param other: CompositeType @return: False if each subtype are equals - ''' + """ return not self.__eq__(other) +""" +All simple Raw type use in RDPY +""" + class UInt8(SimpleType): - ''' - unsigned byte - ''' + """ + @summary: unsigned byte + """ def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): - ''' - constructor + """ @param value: python value wrap - @param conditional : function call before read or write type - @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 conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + """ SimpleType.__init__(self, "B", 1, False, value, conditional = conditional, optional = optional, constant = constant) class SInt8(SimpleType): - ''' - signed byte - ''' + """ + @summary: signed byte + """ def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): - ''' - constructor - @param conditional : function call before read or write type - @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 value: python value wrap + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + """ SimpleType.__init__(self, "b", 1, True, value, conditional = conditional, optional = optional, constant = constant) class UInt16Be(SimpleType): - ''' - unsigned short with big endian representation - @attention: inner value is in machine representation - Big endian is just for read or write in stream - ''' + """ + @summary: unsigned short + with Big endian representation in stream + """ def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): - ''' - constructor - @param conditional : function call before read or write type - @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 value: python value wrap + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + """ SimpleType.__init__(self, ">H", 2, False, value, conditional = conditional, optional = optional, constant = constant) class UInt16Le(SimpleType): - ''' - unsigned short with little endian representation - @attention: inner value is in machine representation - Big endian is just for read or write in stream - ''' + """ + @summary: unsigned short + with Little endian representation in stream + """ def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): - ''' - constructor - @param conditional : function call before read or write type - @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 value: python value wrap + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + """ SimpleType.__init__(self, "I", 4, False, value, conditional = conditional, optional = optional, constant = constant) class UInt32Le(SimpleType): - ''' - unsigned int with little endian representation - @attention: inner value is in machine representation - Big endian is just for read or write in stream - ''' + """ + @summary: unsigned int + with Little endian representation in stream + """ def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): - ''' - constructor - @param conditional : function call before read or write type - @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 value: python value wrap + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + """ SimpleType.__init__(self, "I", 4, True, value, conditional = conditional, optional = optional, constant = constant) class UInt24Be(SimpleType): - ''' - unsigned 24 bit int with big endian representation - @attention: inner value is in machine representation - Big endian is just for read or write in stream - ''' + """ + @summary: unsigned 24 bit integer + with Big endian representation in stream + """ def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): - ''' - constructor - @param conditional : function call before read or write type - @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 value: python value wrap + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + """ SimpleType.__init__(self, ">I", 3, False, value, conditional = conditional, optional = optional, constant = constant) def __write__(self, s): - ''' - special write for a special type + """ + @summary: special write for a special type @param s: Stream - ''' + """ s.write(struct.pack(">I", self.value)[1:]) def __read__(self, s): - ''' - special read for a special type + """ + @summary: special read for a special type @param s: Stream - ''' + """ self.value = struct.unpack(self._structFormat, '\x00' + s.read(self._typeSize))[0] class UInt24Le(SimpleType): - ''' - unsigned int with little endian representation - @attention: inner value is in machine representation - Big endian is just for read or write in stream - ''' + """ + @summary: unsigned 24 bit integer + with Little endian representation in stream + """ def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): - ''' - constructor - @param conditional : function call before read or write type - @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 value: python value wrap + @param conditional : Callable object + Read and Write operation depend on return of this function + @param optional: If there is no enough byte in current stream + And optional is True, read type is ignored + @param constant: Check if object value doesn't change after read operation + """ SimpleType.__init__(self, "