From 2927582db1867ae4f89bfef43d8e1fff729f224a Mon Sep 17 00:00:00 2001 From: sylvain Date: Sat, 12 Oct 2013 20:30:44 +0200 Subject: [PATCH] add sources --- rdpy/__init__.py | 0 rdpy/__init__.pyc | Bin 0 -> 166 bytes rdpy/main.py | 36 +++ rdpy/protocols/__init__.py | 0 rdpy/protocols/__init__.pyc | Bin 0 -> 156 bytes rdpy/protocols/common/__init__.py | 0 rdpy/protocols/common/__init__.pyc | Bin 0 -> 148 bytes rdpy/protocols/common/layer.py | 38 +++ rdpy/protocols/common/layer.pyc | Bin 0 -> 1605 bytes rdpy/protocols/common/protocolbuffer.py | 49 ++++ rdpy/protocols/common/protocolbuffer.pyc | Bin 0 -> 2056 bytes rdpy/protocols/common/stream.py | 70 +++++ rdpy/protocols/common/stream.pyc | Bin 0 -> 5341 bytes rdpy/protocols/rdp/__init__.py | 0 rdpy/protocols/rdp/__init__.pyc | Bin 0 -> 150 bytes rdpy/protocols/rdp/tpdu.py | 42 +++ rdpy/protocols/rdp/tpdu.pyc | Bin 0 -> 1909 bytes rdpy/protocols/rdp/tpkt.py | 69 +++++ rdpy/protocols/rdp/tpkt.pyc | Bin 0 -> 3052 bytes rdpy/protocols/rfb/__init__.py | 0 rdpy/protocols/rfb/__init__.pyc | Bin 0 -> 156 bytes rdpy/protocols/rfb/factory.py | 25 ++ rdpy/protocols/rfb/factory.pyc | Bin 0 -> 1795 bytes rdpy/protocols/rfb/observer.py | 23 ++ rdpy/protocols/rfb/observer.pyc | Bin 0 -> 1144 bytes rdpy/protocols/rfb/rfb.py | 325 +++++++++++++++++++++++ rdpy/protocols/rfb/rfb.pyc | Bin 0 -> 11065 bytes rdpy/protocols/rfb/types.py | 80 ++++++ rdpy/protocols/rfb/types.pyc | Bin 0 -> 3679 bytes rdpy/qt/__init__.py | 0 rdpy/qt/__init__.pyc | Bin 0 -> 169 bytes rdpy/qt/adaptor.py | 63 +++++ rdpy/qt/adaptor.pyc | Bin 0 -> 3123 bytes rdpy/qt/observer.py | 12 + rdpy/qt/observer.pyc | Bin 0 -> 724 bytes rdpy/qt/widget.py | 38 +++ rdpy/qt/widget.pyc | Bin 0 -> 2142 bytes 37 files changed, 870 insertions(+) create mode 100644 rdpy/__init__.py create mode 100644 rdpy/__init__.pyc create mode 100644 rdpy/main.py create mode 100644 rdpy/protocols/__init__.py create mode 100644 rdpy/protocols/__init__.pyc create mode 100644 rdpy/protocols/common/__init__.py create mode 100644 rdpy/protocols/common/__init__.pyc create mode 100644 rdpy/protocols/common/layer.py create mode 100644 rdpy/protocols/common/layer.pyc create mode 100644 rdpy/protocols/common/protocolbuffer.py create mode 100644 rdpy/protocols/common/protocolbuffer.pyc create mode 100644 rdpy/protocols/common/stream.py create mode 100644 rdpy/protocols/common/stream.pyc create mode 100644 rdpy/protocols/rdp/__init__.py create mode 100644 rdpy/protocols/rdp/__init__.pyc create mode 100644 rdpy/protocols/rdp/tpdu.py create mode 100644 rdpy/protocols/rdp/tpdu.pyc create mode 100644 rdpy/protocols/rdp/tpkt.py create mode 100644 rdpy/protocols/rdp/tpkt.pyc create mode 100644 rdpy/protocols/rfb/__init__.py create mode 100644 rdpy/protocols/rfb/__init__.pyc create mode 100644 rdpy/protocols/rfb/factory.py create mode 100644 rdpy/protocols/rfb/factory.pyc create mode 100644 rdpy/protocols/rfb/observer.py create mode 100644 rdpy/protocols/rfb/observer.pyc create mode 100644 rdpy/protocols/rfb/rfb.py create mode 100644 rdpy/protocols/rfb/rfb.pyc create mode 100644 rdpy/protocols/rfb/types.py create mode 100644 rdpy/protocols/rfb/types.pyc create mode 100644 rdpy/qt/__init__.py create mode 100644 rdpy/qt/__init__.pyc create mode 100644 rdpy/qt/adaptor.py create mode 100644 rdpy/qt/adaptor.pyc create mode 100644 rdpy/qt/observer.py create mode 100644 rdpy/qt/observer.pyc create mode 100644 rdpy/qt/widget.py create mode 100644 rdpy/qt/widget.pyc diff --git a/rdpy/__init__.py b/rdpy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdpy/__init__.pyc b/rdpy/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8443e7df066ad7ee846a68cf624eb028b525ff9 GIT binary patch literal 166 zcmZSn%*(Z;LOm#%0SXv_v;zlIX%Z~#rR R$<0qG%}KQb*;fq23;=lJB@_Sv literal 0 HcmV?d00001 diff --git a/rdpy/protocols/common/__init__.py b/rdpy/protocols/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdpy/protocols/common/__init__.pyc b/rdpy/protocols/common/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7bf028da91c751979395b6369b6c3c94d3ea9ce5 GIT binary patch literal 148 zcmZSn%**xg3P(^f0~9af5mAA&9969$9OD5k^@YT1|S2N29S5)8sG}x z=cEI97shdqEMFsuhDHh}bEXwLWL8#gpS?SH`#y?}#nR2R`NZt1JQszE+z#4*7b|Jn zq#cxerBH`RBqqgNNHNI{Wxoz!(rdC$P|~F`CVj|z{xX1c9QVon5t4}EFDr$$xz23O zQT&@TB*PC#E(5p(;J!b)bcmgM_ACT*Kx{2RmGbg&Oi}DpBt9^u8fs;oS!Pa~RyK+W zMz@EAS4LW?oN$F!j_?|3C0#@+XN0nqHqM9jN-N8$+ePJVw-|Zw#D2kBGSds0G;k)l zJWnpPIkT0>WMcAaRg=nSr!!sJB-4w9R!K>a?^i2#4`Xm%sKRkhC0Y_dFAT!qVUPuQ zr$e23tX|+{1DR(HJMYje1RKzNMB^3)TbwFUGB*RI3#l;Wh6Y^n_cgCdVd>+}~AR2=SC?&l;W&JPO(+%g_C3N+sTz~eEhYdq$-K{Wul zq|I);L2cW}g(;k5OIxVZyC1)`8P&rdqF*~7{)zC;>yA^h*mAe`G2}};mOeK0MlrSD z?1vHy$$7n~e4+DYNi59^=lHU4?uRs`cgIgrO1>#YTo>tY+t}F@!vf{D^lZvD<50WDj| literal 0 HcmV?d00001 diff --git a/rdpy/protocols/common/protocolbuffer.py b/rdpy/protocols/common/protocolbuffer.py new file mode 100644 index 0000000..93997a2 --- /dev/null +++ b/rdpy/protocols/common/protocolbuffer.py @@ -0,0 +1,49 @@ +''' +Created on 17 aout 2013 + +@author: sylvain +''' +from twisted.internet import protocol +from stream import Stream + +class ProtocolBuffer(protocol.Protocol): + ''' + classdocs + ''' + def __init__(self): + ''' + Constructor + ''' + #data buffer received from twisted network layer + self._buffer = "" + + #len of next packet pass to next state function + self._expectedLen = 0 + + def dataReceived(self, data): + ''' + inherit from protocol class + main event of received data + ''' + self._buffer += data + while len(self._buffer) >= self._expectedLen: + expectedData = Stream(self._buffer[0:self._expectedLen]) + self._buffer = self._buffer[self._expectedLen:] + self.recv(expectedData) + + def expect(self, expectedLen, callback = None): + ''' + newt expected len + ''' + self._expectedLen = expectedLen + + if callback is None: + callback = self.__class__.recv + + self.recv = callback + + def recv(self, data): + ''' + call when expected data is receive + ''' + pass \ No newline at end of file diff --git a/rdpy/protocols/common/protocolbuffer.pyc b/rdpy/protocols/common/protocolbuffer.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb90fd4ba403d6bb007ed22d4cd822d966c869b0 GIT binary patch literal 2056 zcmc&#+iuf95FK9bI#1#`+1@NEwxS|M9MuK6Q7P<_ThZJLZ1Nq zc)%YwA0S$6vvN;WnGL3uE`n7qHKpyI0+ko2r7CTpr$MC>l`T=NX>8+mPeV@!zC%tA zu@dE~48SbBshoQU{9eKn|_xX3^KDS%699h&XYY$rG?Dn8RRb1Jm$PO6!J#{ z&c3-K%D7NbWI41bDs;NYc`BD%g!>f4vL13>hJ2BPmstqeP|hSB&VmDwzUX>gr`PYT zt*?#bXBJ+=irrvwkQv;fPqNag3gsHYMpk|s@_!2DLSQIXKdF#C{~<`U|9fS>t{Hg4 O7nb2ryUvEQ;r<5e#_b*e literal 0 HcmV?d00001 diff --git a/rdpy/protocols/common/stream.py b/rdpy/protocols/common/stream.py new file mode 100644 index 0000000..340dc23 --- /dev/null +++ b/rdpy/protocols/common/stream.py @@ -0,0 +1,70 @@ +''' +Created on 12 aout 2013 + +@author: sylvain +''' +import struct +from StringIO import StringIO + +class Stream(StringIO): + + def dataLen(self): + return self.len - self.pos + + def read_uint8(self): + return struct.unpack("B",self.read(1))[0] + + def read_beuint16(self): + return struct.unpack(">H",self.read(2))[0] + + def read_leuint16(self): + return struct.unpack("I",'\x00'+self.read(3))[0] + + def read_leuint24(self): + return struct.unpack("I",self.read(4))[0] + + def read_leuint32(self): + return struct.unpack("i",self.read(4))[0] + + def read_lesint32(self): + return struct.unpack("H", value)) + + def write_leuint16(self, value): + self.write(struct.pack("I", value)[1:]) + + def write_beuint32(self, value): + self.write(struct.pack(">I", value)) + + def write_leuint32(self, value): + self.write(struct.pack("i", value)) + + def write_lesint32(self, value): + self.write(struct.pack("4g7*^?+^m)swlQEJV7ZL&j~9?hEb)_G%^##t+q)qimG#;)?ab!fxyOtQ{u z2E)jiBY`!7vpLdS|#57FQwkRh7lkan^ZKEO7AB0O|f0 zJKV4ksD5Fg##5-*E`hqjZ4%6PS(yhjhw(I?ONi$i2tFBS z2Ag-$zgGtBcNGGtsF-s{dk61dg8AR>T2m)S7*|;Lwms~@7u|a$sD8JoP;;-gy~d)T zcA}y}eQ7e@l-u6Ni-PJG7HT|&dhc&gkt$Kn1#r%lc(Vn)aOkbj3*M9+&5Th%#xS|D3n-~Q;ScyI{RR+iZ|bzo(f`xtd_Hi z>#UstRqvt7WeJQCd@08`qZOZV%R*rKg@iey6s>o`1f{q%dYN9_nMW}A!;Df0NiMEi zy#w!Ec#(@P#XHA}fS+M4F3h&~`J!O@-6F!AWzQAzgiSbzmPCznY42O zCN`FNCVj@0pC2H5e*67B`XxW#@QH^LYJOzrgqnZ5oMZ95{s3qf}2~t}4(3g*Jr*uJBHUG^e&8wjpqIR=W_m=@IzqCbU}+IE1JVAU=e^=BPe` zKw3Ck3DN$~>m23qd{r(Ps8+?Tmj3xqE^g!=C LWUu0EINR=DwRnj= literal 0 HcmV?d00001 diff --git a/rdpy/protocols/rdp/__init__.py b/rdpy/protocols/rdp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdpy/protocols/rdp/__init__.pyc b/rdpy/protocols/rdp/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a76ec4a951fac1231fb05c6cd6a49ce06e2f30b8 GIT binary patch literal 150 zcmZSn%**xuv_?=e0~9ag zrAL`hg-0`=CO&!411bWVwP?~}y+zummu%gj_e2Y#-zOWCwP_q~;-v3cbceN0oX&ct z?ETcUx^n&A-VeL`-R{F=?p~YvZqJ?;CrMs*^FR3sJ>KLtSUgEib=|0KR>zJHnz|~R&E`Yx0H66+}0e`}lEty=R ztRs_cQseLv4!_Ex+v6uK5^FP)TG8^4Y|lSHyo2}zQL#t?3j?$cdDv?#9MROHGomw( z77d}}5UcEWEaXJoF*K!h^*nW^UfCj9r+68;Ef#T{m${2$F$*hJ7cbp5zp83&b?Fk9 zn=-@(5%wO>2CvOb50-=vGJP_5W9mO_m85!5XVqyVs@gb{n!-XIxGJ0Xt5XF}zU5&7 zm|7_Ug#mTtA^SFEgdRL2_=XvSwG*o@V z#x+$3kB%QnvB)|}j91#7B*k32F0YJTos_mRwG;YyA`8ju zcst$|9#_9Y>T_Q3U!qHg7n7?(k0tL+gtto?RF>VY(*iiRnkO}VrW+VRiEmoW;QcrzrZJ>>5*tDUth$n el|Lj+GT`0|_C?}%zlqobe8()1x49Cocz*%=t%~me literal 0 HcmV?d00001 diff --git a/rdpy/protocols/rdp/tpkt.py b/rdpy/protocols/rdp/tpkt.py new file mode 100644 index 0000000..b164c8e --- /dev/null +++ b/rdpy/protocols/rdp/tpkt.py @@ -0,0 +1,69 @@ +''' +Created on 5 sept. 2013 + +@author: sylvain +''' + +from rdpy.protocols.common.protocolbuffer import ProtocolBuffer +from rdpy.protocols.common.layer import Layer +from rdpy.protocols.common.stream import Stream +class TPKT(ProtocolBuffer, Layer): + ''' + classdocs + ''' + def __init__(self, presentation = None): + ''' + Constructor + ''' + ProtocolBuffer.__init__(self) + Layer.__init__(self, presentation) + #last packet version read from header + self._lastPacketVersion = 0 + #length may be coded on more than 1 bytes + self._lastShortLength = 0 + + def connectionMade(self): + ''' + call when transport layer connection + is made + ''' + self.expect(2, self.readHeader) + self.connect() + + def readHeader(self, data): + #first read packet version + self._lastPacketVersion = data.read_uint8() + + if self._lastPacketVersion == 3: + data.read_uint8() + self.expect(2, self.readExtendedHeader) + else: + self._lastShortLength = data.read_uint8() + if self._lastShortLength & 0x80: + self.expect(1, self.readExtendedFastPathHeader) + return + self.expect(self._lastShortLength - 2, self.readFastPath) + + + def readExtendedHeader(self, data): + self.expect(data.read_beuint16() - 4, self.readData) + + def readExtendedFastPathHeader(self, data): + self._lastShortLength &= ~0x80 + self._lastShortLength = (self._lastShortLength << 8) + data.read_uint8() + self.expect(self._lastShortLength - 3, self.readFastPath) + + def readFastPath(self, data): + pass + + def readData(self, data): + self._protocol.dataReceived(data) + self.expect(2, self.readHeader) + + def write(self, data): + s = Stream() + s.write_uint8(3) + s.write_uint8(0) + s.write_beuint16(data.len + 4) + s.write(data.getvalue()) + self.transport.write(s.getvalue()) \ No newline at end of file diff --git a/rdpy/protocols/rdp/tpkt.pyc b/rdpy/protocols/rdp/tpkt.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72ad5552d10f4cd7ae653ef7fe2c4a31785c21fd GIT binary patch literal 3052 zcmcImZExH}5FYzplDjk{5JaJXxJpQzgp{;YpbAik8YoCKr^;6$^}blHy=k2I?8CcB z8mS1R^gF+k&-?&*p4p2JLGy{^l-=yizIf)DXJ)tc=W74(k71O`{MW+&9W*;emx)Z! z2a<(S1Tqa}5=ub1B}Gf7ZJD&a+?Jvv)2>XqkUL^RF>TooCF{y7k#mu!lO@TPWISBv z;h&=G@75-9CL2`c;PZhsGq*YT?9SbL{r*mJ><+5>%Yi*BPLjOr=YQch46w<)f$ni# zxhk!S2gm#SruO;{^pBFW3$Y7v?C^o9Yh(E2@sCd$7ego>pxLMB3|xdi=wWP+zP<|R z6+DLFlMY?wpetEh2XLdKgXOt%6YN@2O<%HQ9jr(+9`^Z(4Rrkherb_dn^mdx5^Z23 z+`DM@9y%v-7|2WD6-eO1x$rWObB{V^;ucNhKusJEd4T;8ou11t?N+6A^>ON|y3yrW zAfq8cia2zPLLBF1?&3I7VBew!M!LsIdSu*BrnY%iy7de77!h%gO!?d$3~9OIYE7~4 zZa|vV#+uS44ts`>MLYWyMq3Bf)NIW|xs{odt<$PLva=*LTXi-&^WAJ7Q%iBn&5qpW z>?~sJ7;#H`rM()ghrKYRy=)D5w;=$NL**(59x{$Xk}p6s=TJSw(gv0^DT=}Afhh;B zPD(q2hl7G78l+WOnv~u&i*q}eCYf2tL}8=>aHN}gF~bhWu!DEm4``-#OAzOWDN!RT zt_~s1hlR0w_~v`~fF*F!*$C4A5bbwNfKtE;z>r8vCm;r19feYFFJ=P|Ai(3Gr8F$} z7W3TQ);W-|B|G)cDj<=(w&RJ5O!Nn_SK7YIQMZ{@UXF5xGpD`zZ|oT1A<=3ejgRxv z-H(*TJx-9b#QM7z&Xk#%V;(UY?$*EN9xAon!8|S`Ew^5%mD3s=C3cp$C87t6Psjawy(JTi*Rc#*SjJ47*QczYQ(zE!PAzpXC z&}66PTRKI6)z9cn?nAqxTxFi#L9_gVyvC>Rp+?}_Ow+dPg>jR#p=>v9-o%pmjOQIz zw?sYn3{=m_fsyA4OM~amue>FQhB6=`5DH1eB6fX_uiu z$;}HL>KAmVU;Zv8G+ov%u5Yv&PR4a!x-b%-jI3G_adRu^PV3wmU%Rwid%4L@+Fao7 zB&Bsq^YyuLCrNQ^6z=AB@@cNeGkYD$WUnxXd3SunGg%%b=%GHX25+NAjfT|BpABU) zHF2zEIgY1Qc3e=TRdI|b4#tW~RT{m(BW~3a7_mk~%ok0`h=Gjw&4mj(`rq#cVoDBb zz3sNT!Ht#Pa&N8I>UGfCBYlNNA7O?27#*J>n-`R}n`t$jR%N4jrq4|OZhHUH)%tGm mPpJ2KX>&2 literal 0 HcmV?d00001 diff --git a/rdpy/protocols/rfb/__init__.py b/rdpy/protocols/rfb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdpy/protocols/rfb/__init__.pyc b/rdpy/protocols/rfb/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51c7837276d183d0e06d161c9e69b95d214e3ff2 GIT binary patch literal 156 zcmZSn%*!Qnl_Myb0SXv_v;ztH3KAY literal 0 HcmV?d00001 diff --git a/rdpy/protocols/rfb/factory.py b/rdpy/protocols/rfb/factory.py new file mode 100644 index 0000000..ab911fb --- /dev/null +++ b/rdpy/protocols/rfb/factory.py @@ -0,0 +1,25 @@ +''' +Created on 22 aout 2013 + +@author: sylvain +''' +from twisted.internet import protocol + +class RfbFactory(protocol.Factory): + ''' + classdocs + ''' + def __init__(self, protocol): + self._protocol = protocol + + def buildProtocol(self, addr): + return self._protocol; + + def startedConnecting(self, connector): + print 'Started to connect.' + + def clientConnectionLost(self, connector, reason): + print 'Lost connection. Reason:', reason + + def clientConnectionFailed(self, connector, reason): + print 'Connection failed. Reason:', reason \ No newline at end of file diff --git a/rdpy/protocols/rfb/factory.pyc b/rdpy/protocols/rfb/factory.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8c33fe21365dc38323bc493aad520a76ea667e4 GIT binary patch literal 1795 zcmcgsO>fjN5S=9Lwha`@r+`y#tc0|I?X4hGQK@i16>?AP#Y$r@O`Ek>vA5MqdxPJ> zpX3L?8zS2;JJ_Cc3~9J2sR=WQytMHrcq3h zN=V0ame3dzYxIriE79lC8mVjuFY-&U=tfI;PPR3-z=+_jYipc`=a69=WSM6V|3{UEd~ChrN*h< zoLwQmd-$zu(H*qND)Z$&2N@bF#k%vjsuU~292fELJf9-|(K^3mSXnd3GNB7+&0fov z*9mL|m)rB{#IeNu>gxFr|19fD8^5G$4WoX9_&-yAhNSCdvT;$>S`GfjS#X(G$M*3J zY!3sFWO4l`>4N;h`0meci3E34o7cL8Lu03sn7reqpp*OrB;hRi2bIO_btP9 KtZFaXiGKla^uNOZ literal 0 HcmV?d00001 diff --git a/rdpy/protocols/rfb/observer.py b/rdpy/protocols/rfb/observer.py new file mode 100644 index 0000000..59ef141 --- /dev/null +++ b/rdpy/protocols/rfb/observer.py @@ -0,0 +1,23 @@ +''' +Created on 4 sept. 2013 + +@author: sylvain +''' + +class RfbObserver(object): + ''' + Rfb protocol obserser + ''' + def notifyFramebufferUpdate(self, width, height, x, y, pixelFormat, encoding, data): + ''' + recv framebuffer update + width : width of image + height : height of image + x : x position + y : y position + pixelFormat : pixel format struct from rfb.types + encoding : encoding struct from rfb.types + data : in respect of dataFormat and pixelFormat + ''' + pass + \ No newline at end of file diff --git a/rdpy/protocols/rfb/observer.pyc b/rdpy/protocols/rfb/observer.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26d7cf243b173781021b7ce0f0926b6045f451d9 GIT binary patch literal 1144 zcmcgrOOMkq5Oy0VkYy!q9Py1xNDZ%3h0tnO+T$W+wFk-}O5;gmwuvL#edG*3gMY~% z;2Wo;D=p#%Q9SWH{LOggOZ4+Je)i*KT8P6F;d_SVB!m)T0XY&h66Hv!vG^{;Hz7VR z#zIBndisDPUqQqdmU2%@nnu2tj#@vHZ(qN87sqG$##hG9rR&z)TsN^lL3~;+KP?^E zE!iRU2=;RZd=5cKjf{{;HR8OBNTk=(F$cVWh$SAt$krNf3bU4m0r3V;@fb0@81(|n zajif)K+Itefed4**viu8H7z$~Nmg!Jh1(4NomRe*bJ>4QDRrH%j_!)|s`Bt3HunPd z@a<)5oc7u@18fI#ci-IVJ*_W|t#if2A3DgValQ;0ANv;2zLhPeM+Tj<+|zEdBWAfc&~ F@h=7=F)jcA literal 0 HcmV?d00001 diff --git a/rdpy/protocols/rfb/rfb.py b/rdpy/protocols/rfb/rfb.py new file mode 100644 index 0000000..71fdffc --- /dev/null +++ b/rdpy/protocols/rfb/rfb.py @@ -0,0 +1,325 @@ +''' +Created on 12 aout 2013 + +@author: sylvain +''' + +from rdpy.protocols.common.stream import Stream +from rdpy.protocols.common.protocolbuffer import ProtocolBuffer +from types import PixelFormat,ProtocolVersion,SecurityType, Rectangle, Encoding + +class Rfb(ProtocolBuffer): + ''' + implements rfb protocol message + ''' + CLIENT = 0 + SERVER = 1 + + def __init__(self, mode): + ''' + constructor + mode can be only client or server mode + ''' + ProtocolBuffer.__init__(self) + #usefull for rfb protocol + self._callbackBody = None + #mode of automata + self._mode = mode + #protocol version negociated + self._version = ProtocolVersion.RFB003008 + #nb security launch by server + self._securityLevel = SecurityType.INVALID + #shared framebuffer + self._sharedFlag = 0 + #framebuffer width + self._width = 0 + #framebuffer height + self._height = 0 + #pixel format structure + self._pixelFormat = PixelFormat() + #server name + self._serverName = None + #nb rectangle + self._nbRect = 0 + #current rectangle header + self._currentRect = Rectangle() + #client or server adaptor + self._observer = [] + + def addObserver(self, observer): + self._observer.append(observer) + + def expectWithHeader(self, expectedHeaderLen, callbackBody): + ''' + 2nd level of waiting event + read expectedHeaderLen that contain body size + ''' + self._callbackBody = callbackBody + self.expect(expectedHeaderLen, self.expectedBody) + + def expectedBody(self, data): + ''' + read header and expect body + ''' + bodyLen = 0 + if data.len == 1: + bodyLen = data.read_uint8() + elif data.len == 2: + bodyLen = data.read_beuint16() + elif data.len == 4: + bodyLen = data.read_beuint32() + else: + print "invalid header length" + return + self.expect(bodyLen, self._callbackBody) + + def readProtocolVersionFormat(self, data): + if data.getvalue() == "RFB 003.003\n": + self._version = ProtocolVersion.RFB003003 + return + if data.getvalue() == "RFB 003.007\n": + self._version = ProtocolVersion.RFB003007 + return + if data.getvalue() == "RFB 003.008\n": + self._version = ProtocolVersion.RFB003008 + return + self._version = ProtocolVersion.UNKNOWN + + def writeProtocolVersionFormat(self): + s = Stream() + if self._version == ProtocolVersion.RFB003003: + s.write("RFB 003.003\n") + if self._version == ProtocolVersion.RFB003007: + s.write("RFB 003.007\n") + if self._version == ProtocolVersion.RFB003008: + s.write("RFB 003.008\n") + self.transport.write(s.getvalue()) + + def connectionMade(self): + ''' + call when transport layer connection + is made + ''' + if self._mode == Rfb.CLIENT: + self.expect(12, self.readProtocolVersion) + else: + self.writeProtocolVersionFormat() + + def readProtocolVersion(self, data): + ''' + read handshake packet + protocol version nego + ''' + self.readProtocolVersionFormat(data) + if self._version == ProtocolVersion.UNKNOWN: + print "Unknown protocol version %s send 003.008"%data.getvalue() + #protocol version is unknow try best version we can handle + self._version = ProtocolVersion.RFB003008 + #send same version of + self.writeProtocolVersionFormat() + + #next state read security + if self._version == ProtocolVersion.RFB003003: + self.expect(4, self.readSecurityServer) + else: + self.expectWithHeader(1, self.readSecurityList) + + def readSecurityServer(self, data): + ''' + security handshake for 33 rfb version + server imposed security level + ''' + self._version = data.read_beuint32() + + + def readSecurityList(self, data): + ''' + read all security list + ''' + securityList = [] + while data.dataLen() > 0: + securityList.append(data.read_uint8()) + #select high security level + for s in securityList: + if s in [SecurityType.NONE, SecurityType.VNC] and s > self._securityLevel: + self._securityLevel = s + break + #send back security level choosen + s = Stream() + s.write_uint8(self._securityLevel) + self.transport.write(s.getvalue()) + self.expect(4, self.readSecurityResult) + + def readSecurityResult(self, data): + ''' + Read security result packet + ''' + result = data.read_beuint32() + if result == 1: + print "Authentification failed" + if self._version == ProtocolVersion.RFB003008: + self.expectWithHeader(4, self.readSecurityFailed) + else: + print "Authentification OK" + self.writeClientInit() + + def readSecurityFailed(self, data): + print "Security failed cause to %s"%data.getvalue() + + def readServerInit(self, data): + ''' + read server init packet + ''' + self._width = data.read_beuint16() + self._height = data.read_beuint16() + serverPixelFomat = PixelFormat() + serverPixelFomat.read(data) + self.expectWithHeader(4, self.readServerName) + + def readServerName(self, data): + ''' + read server name from server init packet + ''' + self._serverName = data.getvalue() + print "Server name %s"%self._serverName + #end of handshake + #send pixel format + self.writeSetPixelFormat(self._pixelFormat) + #write encoding + self.writeSetEncoding() + #request entire zone + self.writeFramebufferUpdateRequest(False, 0, 0, self._width, self._height) + self.expect(1, self.readServerOrder) + + def readServerOrder(self, data): + ''' + read order receive from server + ''' + packet_type = data.read_uint8() + if packet_type == 0: + self.expect(3, self.readFrameBufferUpdateHeader) + + def readFrameBufferUpdateHeader(self, data): + ''' + read frame buffer update packet header + ''' + #padding + data.read_uint8() + self._nbRect = data.read_beuint16(); + self.expect(12, self.readRectHeader) + + def readRectHeader(self, data): + ''' + read rectangle header + ''' + self._currentRect.X = data.read_beuint16() + self._currentRect.Y = data.read_beuint16() + self._currentRect.Width = data.read_beuint16() + self._currentRect.Height = data.read_beuint16() + self._currentRect.Encoding = data.read_besint32() + + if self._currentRect.Encoding == Encoding.RAW: + self.expect(self._currentRect.Width * self._currentRect.Height * (self._pixelFormat.BitsPerPixel / 8), self.readRectBody) + + def readRectBody(self, data): + ''' + read body of rect + ''' + for observer in self._observer: + observer.notifyFramebufferUpdate(self._currentRect.Width, self._currentRect.Height, self._currentRect.X, self._currentRect.Y, self._pixelFormat, self._currentRect.Encoding, data.getvalue()) + self._nbRect = self._nbRect - 1 + #if there is another rect to read + if self._nbRect == 0: + #job is finish send a request + self.writeFramebufferUpdateRequest(True, 0, 0, self._width, self._height) + self.expect(1, self.readServerOrder) + else: + self.expect(12, self.readRectHeader) + + def writeClientInit(self): + ''' + write client init packet + ''' + s = Stream() + s.write_uint8(self._sharedFlag) + self.transport.write(s.getvalue()) + self.expect(20, self.readServerInit) + + def writeSetPixelFormat(self, pixelFormat): + ''' + write set pixel format packet + ''' + s = Stream() + #message type + s.write_uint8(0) + #padding + s.write_uint8(0) + s.write_uint8(0) + s.write_uint8(0) + pixelFormat.write(s) + self.transport.write(s.getvalue()) + + def writeSetEncoding(self): + ''' + write set encoding packet + ''' + s = Stream() + #message type + s.write_uint8(2) + #padding + s.write_uint8(0) + #nb encoding + s.write_beuint16(1) + #raw encoding + s.write_besint32(0) + self.transport.write(s.getvalue()) + + def writeFramebufferUpdateRequest(self, incremental, x, y, width, height): + ''' + request server the specified zone + incremental means request only change before last update + ''' + s = Stream() + s.write_uint8(3) + s.write_uint8(incremental) + s.write_beuint16(x) + s.write_beuint16(y) + s.write_beuint16(width) + s.write_beuint16(height) + self.transport.write(s.getvalue()) + + def writeKeyEvent(self, downFlag, key): + ''' + write key event packet + ''' + s = Stream() + s.write_uint8(4) + s.write_uint8(downFlag) + s.write_beuint16(0) + s.write_beuint32(key) + self.transport.write(s.getvalue()) + + def writePointerEvent(self, mask, x, y): + ''' + write pointer event packet + ''' + s= Stream() + s.write_uint8(5) + s.write_uint8(mask) + s.write_beuint16(x) + s.write_beuint16(y) + self.transport.write(s.getvalue()) + + def writeClientCutText(self, text): + ''' + write client cut text event packet + ''' + s = Stream() + s.write_uint8(6) + #padding + s.write_uint8(0) + s.write_uint8(0) + s.write_uint8(0) + s.write_beuint32(len(text)) + s.write(text) + self.transport.write(s.getvalue()) \ No newline at end of file diff --git a/rdpy/protocols/rfb/rfb.pyc b/rdpy/protocols/rfb/rfb.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77d8d5a338654bd79978aaf2c90eacbe366b6a86 GIT binary patch literal 11065 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 UhU&u)c?ZIt%lT=%cBtk51J0erz5oCK literal 0 HcmV?d00001 diff --git a/rdpy/protocols/rfb/types.py b/rdpy/protocols/rfb/types.py new file mode 100644 index 0000000..04322e6 --- /dev/null +++ b/rdpy/protocols/rfb/types.py @@ -0,0 +1,80 @@ +''' +Created on 22 aout 2013 + +@author: sylvain +''' +class ProtocolVersion(object): + ''' + different ptotocol version + ''' + UNKNOWN = 0 + RFB003003 = 1 + RFB003007 = 2 + RFB003008 = 3 + +class SecurityType: + ''' + security type supported by twisted remote desktop + ''' + INVALID = 0 + NONE = 1 + VNC = 2 + +class PixelFormat(object): + def __init__(self): + self.BitsPerPixel = 32 + self.Depth = 24 + self.BigEndianFlag = False + self.TrueColorFlag = True + self.RedMax = 255 + self.GreenMax = 255 + self.BlueMax = 255 + self.RedShift = 16 + self.GreenShift = 8 + self.BlueShift = 0 + + def read(self, data): + self.BitsPerPixel = data.read_uint8() + self.Depth = data.read_uint8() + self.BigEndianFlag = data.read_uint8() + self.TrueColorFlag = data.read_uint8() + self.RedMax = data.read_beuint16() + self.GreenMax = data.read_beuint16() + self.BlueMax = data.read_beuint16() + self.RedShift = data.read_uint8() + self.GreenShift = data.read_uint8() + self.BlueShift = data.read_uint8() + #padding + data.read(3) + + def write(self, data): + data.write_uint8(self.BitsPerPixel) + data.write_uint8(self.Depth) + data.write_uint8(self.BigEndianFlag) + data.write_uint8(self.TrueColorFlag) + data.write_beuint16(self.RedMax) + data.write_beuint16(self.GreenMax) + data.write_beuint16(self.BlueMax) + data.write_uint8(self.RedShift) + data.write_uint8(self.GreenShift) + data.write_uint8(self.BlueShift) + #padding + data.write_uint8(0) + data.write_uint8(0) + data.write_uint8(0) + +class Pointer: + BUTTON1 = 0x1 + BUTTON2 = 0x2 + BUTTON3 = 0x4 + +class Rectangle: + def __init__(self): + self.X = 0 + self.Y = 0 + self.Width = 0 + self.Height = 0 + self.Encoding = 0 + +class Encoding: + RAW = 0 \ No newline at end of file diff --git a/rdpy/protocols/rfb/types.pyc b/rdpy/protocols/rfb/types.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4054b1ef67c5aaf8893008180f0ebdc3e6449dd2 GIT binary patch literal 3679 zcmcgv>rNX-6rS}Bd$}gG^ddFdwozm$O3X!VqqeFN6G)=A1`Pxn|ERTi#<0ZfTDxNi zQhpWQr4Li}Y5D-|`Oetu8;bhJ*v#R4GiP_sea6{;=H0))xhI>V_sP=#r!-|W(Q<^!h-)FPgm_cW2$dCTL8x3hML6FVETpqJp=N|y6snj`<%KE46rRSE4(&v?nD1MuC z{HCs$iy$F)K8p2OWb`AYleZ@7^r!7?e==?zRm^2aCyzUq68jWrB(R{6EjadT<_PZT zW;YJZ5=A`%f{hrg;FJp1LUmJ&zD8o56PN<>HE^m64(vi>GpOM zpZR9ekzbQQ>d5PU_JZLA29rbH!ki}T#(?RYG?L$mP_nH7em%waB?I`9Yj1Yuwr4Y)4(2%9>k ziEk6}><^CXpEU8_5vSau5gv;1Fr)wvLyF}vq=*vCiz|8wJxk#nhBKqEIQnd96uP5u zmf;+7?#dAkCkPa;+17~YauBG$t422n%@c_^xd4KskvIf63s~exLul<=xr{aVM}7>l z0`dt69LG_Ed>MPm&-zo!H~uXIQhq@j-lvg3E#WLqelwFH{EI|wR44|g)?WjhVgC(q zb`0lI%vE_>7{fDTxR_#&`DtkkyJL8E2={_%mAMHFg&7X8<&MsFK?j8XK%5bFEA#&X zJ&3QohmB}VyGYTbSjBZ>lFzZDvo!h5%scZAUmk`IA4qd@$S;W-Xv^Hx`iexiMQtSL zj|HbRS6bkfxEoO|>9|R={6WL3(qOC7GiL01)H_;+E;%`RWXw|AabNH6@6^_Y`1K)v z2S6)biu?aX`3KDYF%gW?%$Zc;nVLhP;?GLrGk!fkEf#Ob9E zvi2nnzVr=_BfepbCHFub=BB691JdUObb=oXj>NBo$vj3oNyA8a+>4>TgJfxOpgv#! z$Dvj7_J(CArE8{62zB6NhJoC(pYuuqD!4!XNB}vDZv30-TRO+Mh5# Vp(iF#rr@lUmWwM7m-EZT{{S9U{wDwc literal 0 HcmV?d00001 diff --git a/rdpy/qt/__init__.py b/rdpy/qt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdpy/qt/__init__.pyc b/rdpy/qt/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65985a2570b8c6631d8d68bb680c5f11ca33173e GIT binary patch literal 169 zcmZSn%**w>LOm#%0SXv_v;zqn?Sf;3L%?!524XYM^S!5`OJcYkk3eOdeheD9&yClIMf7t)i| zmysu9U%I|{YzHz5WL%SO4egqkx(v`;m)}IrMV@pUk~YL(pdkZa(v8)?hNPR)X*a3j zBZ$_6(j?BLVUdS-LTe^&JKVkf@m{NSFPXYwQGOcQ+2}OM@>cdIer*qnTpi-Edo<0e z=1XWsgWeB4Ysynox=jv$nI;oglq=7;@IH$D48p)O7z_#PJX+Tc=p`#Py2nag(wbJ_ zjk?y|kf_scaFcf+S|NT5HQ}Hr!(=oHPn>r5sgA`*d4OWCK^%(AMPfy^qqgr?9<(qR z7YqAs)mP&xGa9t<6%}?5wVh!xHaiPYc2aY?b5@kcc9QhXPMJ<-l{j%bNd>_6WTt?` zah7K;j%kObRpRyeSr#?&20CEj5&k`ydvfmKd<6$aSN7YS8wC)KAS9EC${qh-bqV-`A2`K zD=%C&n0-4=4o#aIN9q(kf9fb_>h(#+3DE{RUfjosiSRm#r7rQd)@MSE>KaEb*vxqs zFy}P$;)b|au#d%YeqT(j`TEr4?oIYQ7bySDsE9Fk1vb6Bu<1K9Q;Tk{EoyI~zKH8Q z8JjqEnnUrpNT(wzVM8#Dp4Sq&SBeTgvNDK7( zaul%gT&^?h)Gfm>RFb$p_13oWQRi+=`bnJQB0_%+Cz1jykDeQeyAp;iA!{rTE3XYO||{v z$F4L}G4I0?iQJnG2B!RJ0=?z~sIg2|sW-g$yz745tNCxODUva%QNb))U0VnV94^B8 z9Kj9R$Iz--W0MyF0;SUquuPu=t5fS2IUf+X#lta)Ff?Ss&=nzK{9@N?MTR|aGZK-A zx2Jm*;;hV^IVdzLl(`Ugz`3w%e7ZUwllm#xDZ4p&RJgVq53OzoO&(+@zWN9LAmB)gH->f&UHyitUgQ<3X z&K}GjyE|2OlP~y^+n$t#EBeLAZX^1(moKY`m#}7Y(K|AISCKT;tg}&Tmt|rlfxFd3 S`Z#OibyBAL}`4brAeH|rjQwa2LF;jz@1aU z!U~rBa?ifpdv~(`xhHQw4wD?#%cpOjwh0Loz?e_~3YZE|9=rp*0X&R7P(F;J4bD6w zkb?#@iz?Jrcpe(8Z7)1MJ~@-JpFP`|ZZ1O87OQMtNxMV&_jeP6%?g`uVL_+YT<(AX zNtpBqNl38j*oR~kd7Q9MAVXStkr|_OZaT(hcypxzZNo$X+2oU9L;%jQj1g$pb>Dq6LIdL$awKEhr5X1inVMR_L zjo3&gMy8RRL}F;iGKpoH$Vmb_5f{mMOI#wSk=T~}E^;mM{G=_mEyw*Wp7#Yr_fhTg z;B2p)^&a%RtHRyhSNFcW-|ark=i#iZzv=nqWRVxMZt)j>eS?=m3*spJG%p&nL-W@c z-qj0Nhqq~^ZdwNS*!giNtFn4VEd=n|Ed*hH1Kq{n?; zJ&YhYNX;V*r6vHFkt;X~*CV=XuGG$A&Ys6|p6Id>GZJc9OBdP_w&WVKRzM9g9`xUW zR5$&+a&^kf0_U=h78MHpNbcI3j{YMJuR+ZetJA!gTf%nrto^RI_;dmj_z4}T*CxJ1y5>|Y^d5grIw zEc>L7n;PCNiR2*%h=|Bbl}qKbnC2tbXW&vg8`=SOrau<&b9vz~918S4i^TLf_ux4$YU#f?J@n8tZb~edSpDiek#<@T6QgMe!-XdBT(?-KcW>H}AB#;oUE8`G#-lhd6B0Zn0*^ zZh43v==J_ROsu@p99{L(Sw3~>r;K)%O-nnUz|`)PzKgl^1B#C*K7qJFy}{N{|>b&}4lPJGb7X4}d#TMmJmX>_