MpjZdZdZddlZddlZddlZddlZddlZddlZejjejjejje Z ejjejje dZeejvrejj!deddlZej$dzfdZedZdZd Zd Zd ezZGd d eZdZdZdZdZGddeZ GddeZ!Gdde!Z"Gdde!Z#Gdde!Z$Gdde!Z%GddeZ&Gd d!e!Z'Gd"d#e!Z(Gd$d%e!Z)Gd&d'e!Z*Gd(d)e!Z+Gd*d+e!Z,Gd,d-e,Z-Gd.d/e,Z.d0Z/d1Z0Gd2d3eZ1d7d4Z2e3d5k(r e4d6yy)8a( Parse a ppt (MS PowerPoint 97-2003) file Based on olefile, parse the ppt-specific info Code much influenced by olevba._extract_vba but much more object-oriented (possibly slightly excessively so) Currently quite narrowly focused on extracting VBA from ppt files, no slides or stuff, but built to be extended to parsing more/all of the file. For better "understanding" of ppt files, see module ppt_record_parser, which will probably replace this module some time soon. References: * https://msdn.microsoft.com/en-us/library/dd921564%28v=office.12%29.aspx and links there-in WARNING! Before thinking about understanding or even extending this module, please keep in mind that module ppt_record_parser has a better "understanding" of the ppt file structure and will replace this module some time soon! z0.54Nz..c4|tjjjvr(tj|}|j ||Stj|}|j tj|j ||S)an Create a suitable logger object for this module. The goal is not to change settings of the root logger, to avoid getting other modules' logs on the screen. If a logger exists with same name, reuse it. (Else it would have duplicate handlers and messages would be doubled.) The level is set to CRITICAL+1 by default, to avoid any logging. )loggingLoggermanager loggerDict getLoggersetLevel addHandler NullHandler)namelevelloggers a/mnt/ssd/data/Dropbox/adrian/scripts/msg_venv/lib/python3.12/site-packages/oletools/ppt_parser.py get_loggerrJsy w~~%%000""4(   t $F g))+, OOE MpptcJtjtjy)z Enable logging for this module (disabled by default). This will set the module-specific logger level to NOTSET, which means the main application controls the actual logging level. N)logr rNOTSETrrenable_loggingrks LL rPowerPoint Documentz.https://bitbucket.org/decalage/oletools/issueszPlease report this issue on %sc"eZdZdZfdZxZS)PptUnexpectedDataz= raise by PptParser if some field's value is not as expected crdj|||||_tt||jy)NzCIn stream "{0}" for field "{1}" found value "{2}" but expected {3}!)formatmsgsuperr__init__)selfstream field_name found_valueexpected_value __class__s rr zPptUnexpectedData.__init__s3 Q VFJ ^ D  /9r)__name__ __module__ __qualname____doc__r __classcell__r&s@rrrsG::rrcRtjd|jddS)z read 1 byte from stream K|]}dj|yw{0:04X}Nr.0vals r zcheck_value..s#(=,/)2(8(8(=(=] Current UserrCN) isinstancelisttuplejoinrr)r valueexpectedexp_strs r check_valuerSs(T5M*  FKK(=3;(===?BCG#  '2 2 ! U  D   U #Y%5%5h%?A A rc8eZdZdZdZedZeddZy) RecordHeaderz a record header, at start of many types found in ppt files https://msdn.microsoft.com/en-us/library/dd926377%28v=office.12%29.aspx https://msdn.microsoft.com/en-us/library/dd948895%28v=office.12%29.aspx c<d|_d|_d|_d|_yN)rec_ver rec_instancerec_typerec_lenr!s rr zRecordHeader.__init__s    rcD|}tjd|jd\}t|d\|_|_tjd|jd\|_tjd|jd\|_|S)z reads 8 byte from stream r7r8r;r<)r0r1r2divmodrYrXrZr[)clzr"objversion_instances r extract_fromzRecordHeader.extract_froms}e"MM$ A?(./?(F%#+ dFKKN;  }}T6;;q>:   rNc| td|d|zz}|tjd||Stjd|||S)zv generate a record header string given values length of result depends on rec_len being given or not zRECORD_TYPE not set!r^zK|]}dj|ywrBrDrEs rrHz&PptType.check_value..s%,A03-6,<,>22 ,A7?,A!AACFG)$$hnt&;$$U+W699  ~~..H%  (S.4"7  ')9)9()CEH H rcLd}| | td|||krd}|||k\rd}|r~|jj}|dj|}n&|dj|}ndj||}t |j |dz|zdj||gSgS) aI simplify verification of values: check value is in given range expect_lower or expected_upper can be given as None to check only one boundary. If value equals one of the boundaries, that is also an error (boundaries form an open interval) :returns: list of PptUnexpectedData exceptions Fz$need at least one non-None boundary!Tz < {0:04X}z > {0:04X}zwithin ({0:04X}, {1:04X})ryrC)rer&r'rrrp)r!r rP expect_lower expect_upperis_errrz expect_strs r check_rangezPptType.check_range s  L$8CD D  # $  # $ ~~..H#(// = %(// = 8?? @LN %d&6&638M&/&6&6u&=zKN NIrc(g}|j|jd|jj|j|j|jd|jj |j |j td|j|jd|jj|j|6|j|jd|jj||S)z to be called by check_validity to check the self.rec_head uses self.RECORD_... constants, (not quite that constant for DummyType) zrec_head.recVerzrec_head.recInstancezRECORD_TYPE not specified!zrec_head.recTypezrec_head.recLen) extendrSrqrXRECORD_VERSIONrYRECORD_INSTANCE RECORD_TYPErnrZr[)r!lengtherrss rcheck_rec_headzPptType.check_rec_head.s  D$$%6 8M8M%)%8%8: ; D$$%;%)]]%?%?%)%9%9; <    #%&BC C D$$%7%)]]%;%;%)%5%57 8   KK(():)-)>)>H I rcntj|j||j|jS)z6 call RecordHeader.generate with values for this type )rUrgrrr)r`r[s rgenerate_patternzPptType.generate_patternDs2$$S__g%(%8%8#:L:LN NrrW)r'r(r)r*rrrrhrcMAIN_STREAM_NAMEr rtrvrSrrrrrrrjrjsd KON==$4: =( D,NNrrjcbeZdZdZdZdZdZdZdZdZ dZ d Z d Z fd Z d Zed ZdZxZS)CurrentUserAtoma An atom record that specifies information about the last user to modify the file and where the most recent user edit is located. This is the only record in the Current User Stream (section 2.1.1). https://msdn.microsoft.com/en-us/library/dd948895%28v=office.12%29.aspx lDgl_@#G iirctt| dd|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ y)NrK)rp)rrr rqsize header_tokenoffset_to_current_edit len_user_namedoc_file_version major_version minor_versionansi_user_nameunicode_user_name rel_versionr!r&s rr zCurrentUserAtom.__init__bsk ot-.-I   &*#! $!!"!%rc4|j|jk(SrW)rHEADER_TOKEN_ENCRYPTr\s r is_encryptedzCurrentUserAtom.is_encryptedps  D$=$===rc|}tj||_tjd|j d\|_tjd|j d\|_tjd|j d\|_tjd|j d\|_ tjd|j d\|_ tjd|j d\|_ tjd|j d\|_ |j d|j |j|_ tjd|j d\|_|j d|jz|_|S)z' create instance with info from stream r;r<r7r8r.r)rUrcrqr0r1r2rrrrrrrrrrr`r"ras rrczCurrentUserAtom.extract_fromssOe$008 MM$ A7 "MM$ A?&,mmD&++a.&I#"#]]4Q@ & dFKKN C#]]4Q@#]]4Q@ A#[[):):;!==v{{1~> & A0A0A,A B rc8|j}|j|jd|j|j|j|jd|j |j |jg|j|jd|jdd|j|jd|j|j|j|jd|j|j|j|jd|j|j|j|jd|j |j"|j$g|S) Nr headerToken lenUserNamedocFileVersion majorVersion minorVersion relVersion)rrrSrSIZErrHEADER_TOKEN_NOCRYPTrrrDOC_FILE_VERSIONr MAJOR_VERSIONr MINOR_VERSIONrREL_VERSION_CAN_USEREL_VERSION_NO_USEr!rs rrvzCurrentUserAtom.check_validitysb""$ D$$VTYY BC D$$]D4E4E&*&?&?&*&?&?&AB C D$$]D4F4F%(* + D$$%5t7L7L%)%:%:< = D$$^T5G5G%)%7%79 : D$$^T5G5G%)%7%79 : D$$\43C3C&*&>&>&*&=&=&?@ A r)r'r(r)r*rrrrrrrrrr rrhrcrvr+r,s@rrrKsc&%%#K DMM  >,rrcFeZdZdZdZdZdZfdZedZ ddZ xZ S) UserEditAtomz An atom record that specifies information about a user edit https://msdn.microsoft.com/en-us/library/dd945746%28v=office.12%29.aspx irrctt| d|_d|_d|_d|_d|_d|_d|_ d|_ d|_ d|_ d|_ yrW)rrr rqlast_slide_id_refversionrroffset_last_editoffset_persist_directorydoc_persist_id_refpersist_id_seed last_viewencrypt_session_persist_id_refrs rr zUserEditAtom.__init__se lD*, !% !! $(,%"&#.2+rctjd|}tj||_t j d|jd\|_t j d|jd\|_ t j d|jd\|_ t j d|jd\|_ t j d|jd\|_ t j d|jd\|_ t j d|jd\|_t j d|jd\|_t j d|jd\|_|jd|r.t j d|jd\|_|Sd|_|S) z@ extract info from given stream (already positioned correctly!) z extract UserEditAtom from streamr;r<r7r8r.rN)rdebugrUrcrqr0r1r2rrrrrrrrrr)r`r"rras rrczUserEditAtom.extract_fromsz 45e$008 !'tV[[^!D}}T6;;q>:  #]]4Q@#]]4Q@ & dFKKN C(. dFKKN(K%$"(--fkk!n"E%}}T6;;q>BtV[[^<  A  dFKKN3 0C .  26C . rc |j}|j|jd|j|j|j|jd|j |j ||j|k\r6|jtdd|jdj||j|k\s|j|jkrA|jtdd|jdj|j||j|jd|jd |S) NrrrzUserEditAtom.offsetLastEditz< {0}z#UserEditAtom.offsetPersistDirectoryz in ({0}, {1})docPersistIdRefr) rrrSrrrrrappendrrrr)r!offsetrs rrvzUserEditAtom.check_validitys;""$ D$$^T5G5G%)%7%79 : D$$^T5G5G%)%7%79 :  $$. -)+H))7>>&+ACD,,611T5J5JJ -)9))#**4+@+@&I KL D$$%6%)%<%eZdZdZdZfdZedZddZxZ S)PersistDirectoryAtomz one part of a persist object directory with unique persist object id contains PersistDirectoryEntry objects https://msdn.microsoft.com/en-us/library/dd952680%28v=office.12%29.aspx ircFtt| d|_d|_yrW)rrr rg_persist_dir_entry stream_offsetrs rr zPersistDirectoryAtom.__init__s! "D24$(!!rctjd|}|j|_|j ||j}||j j z}tjdj||g|_||krjtj|}|jj||j}tjdj|||krj|S)z6 create and return object with data from given stream z-Extracting a PersistDirectoryAtom from streamz(start reading at pos {0}, read until {1}z at pos {0}) rrrrrtrqr[rrPersistDirectoryEntryrcr)r`r"racurr_posstop_pos new_entrys rrcz!PersistDirectoryAtom.extract_from$s ABe#KKM &!;;=cll222 <6(H- /#% !-::6BI  $ $ + +I 6{{}H IIl))(3 4 !  rc|j}|jD]-}|j|j||j/|SrW)rrrrvr)r!user_edit_last_offsetrentrys rrvz#PersistDirectoryAtom.check_validity?sU""$.. BE KK,,-B-1-?-?A B B rrW r'r(r)r*rr rhrcrvr+r,s@rrrs-K" 4rrc2eZdZdZdZedZ ddZy)raa an entry contained in a PersistDirectoryAtom.rg_persist_dir_entry A structure that specifies a compressed table of sequential persist object identifiers and stream offsets to associated persist objects. NOT a subclass of PptType because has no RecordHeader https://msdn.microsoft.com/en-us/library/dd947347%28v=office.12%29.aspx c.d|_d|_d|_yrW) persist_id c_persistrg_persist_offsetr\s rr zPersistDirectoryEntry.__init__Rs!%rc 4tjd|}tjd|j d\}t |d\|_|_tjdj||j|j t|j Dcgc]*}tjd|j dd,c}|_ tjdj|j|Scc}w)Nz.Extracting a PersistDirectoryEntry from streamr;r<iz4temp is 0x{0:04X} --> id is {1}, reading {2} offsetsrzoffsets are: {0}) rrr0r1r2r_rrrranger)r`r"ratemp_s rrcz"PersistDirectoryEntry.extract_fromWs BCe dFKKN3(.tU(;% s~ H6$ > @+0 *>!@%&"(tV[[^!DQ!G!@ $++C,A,ABC !@s./DNc g}|jdkDr:|jttd|jdj d|j dk(r+|jttd|j d|[t |j|krC|jttdt |jdj ||[t|j|kDrC|jttdt|jd j ||S) Niz PersistDirectoryEntry.persist_idz< 0xFFFFE (dec: {0})rzPersistDirectoryEntry.c_persistz> 0z'PersistDirectoryEntry.rg_persist_offsetz> UserEdit.offsetLastEdit ({0})z%> PersistObjectDirectory offset ({0})) rrrrrrminrmax)r!rpersist_obj_dir_offsetrs rrvz$PersistDirectoryEntry.check_validityzs  ??W $ KK) "D!7!>!>w!GI J >>Q  KK) "C' ( ! ,../2GG KK) "KD**+1-. 0 1 " -../2HH KK) "KD**+7./ 1 2  r)NN)r'r(r)r*r rhrcrvrrrrrGs.&   D48.2rrc@eZdZdZgdZfdZedZdZxZ S)DocInfoListSubContainerOrAtoma; one of various types found in a DocInfoListContainer https://msdn.microsoft.com/en-us/library/dd921705%28v=office.12%29.aspx actual type of this object is defined by the recVersion field in its Record Head Similar to DummyType, RECORD_TYPE varies from instance to instance for this type )iiiiiic*tt| yrW)rrr rs rr z&DocInfoListSubContainerOrAtom.__init__s +T;=rctjd|}|j||jjt j k(r"t j||j}|Stjdj|jjtjdj|j|j|jjtjtjdj|j|S)+ build instance with info read from stream z1Parsing DocInfoListSubContainerOrAtom from streamz7skipping over {0} Byte in DocInfoListSubContainerOrAtomrr)rrrtrqrZVBAInfoContainerrrcrr[rrrrrs rrcz*DocInfoListSubContainerOrAtom.extract_froms EFe &! << $4$@$@ @"// EC IIOvcll223 5 II(// > ? KK ,,bkk : II&--fkkm< = rcf|jd|jj|jy)z can be any of multiple types z rh.recTypeN)rSrqrZVALID_RECORD_TYPESr\s rrvz,DocInfoListSubContainerOrAtom.check_validitys' t}}'='=00 2r) r'r(r)r*rr rhrcrvr+r,s@rrrs. ">"2rrc@eZdZdZdZdZfdZedZdZ xZ S)DocInfoListContainerz information about the document and document display settings https://msdn.microsoft.com/en-us/library/dd926767%28v=office.12%29.aspx ic8tt| d|_yrW)rrr rg_child_recrs rr zDocInfoListContainer.__init__s "D24 rc8tjd|}|j||j}||jj z}tjdj ||g|_||krntj|}|jj||j}tjdj |||krntjdj ||y)rz(Parsing DocInfoListContainer from streamz-start reading at pos {0}, will read until {1}rz? A "++F36"88D V8L  %&S33BBDEF H ))CV145 J , V 4  %&S11@@BCD F &&=v.133?<3G  %&S..==?@A C $$=v,/11=f1E  %&S__;;=>? A 1==fEp rcd|j}|j|jj|j|jj|j|j j|j|j j|j|jj|j|jj|j|jj|S)z- check all values in object for valid values ) rrrrvrrrrrrrs rrvz DocumentContainer.check_validitys""$ D&&5578 D$$3356 D++::<= D))88:; D&&5578 D$$3356 D&&5578 rrr,s@rrrs0 K-,xxv rrcJeZdZdZdZdZdZdZfdZe d dZ dZ xZ S) rz A container record that specifies VBA information for the document. https://msdn.microsoft.com/en-us/library/dd952168%28v=office.12%29.aspx irrrc8tt| d|_yrW)rrr vba_info_atomrs rr zVBAInfoContainer.__init__s .0!rctjd|}||j|ntjd||_tj ||_|S)zL since can determine this type only after reading header, it is arg zparsing VBAInfoContainerzskip parsing of RecordHeader)rrrtrq VBAInfoAtomrcr")r`r"rqras rrczVBAInfoContainer.extract_fromsX ,-e     f % II4 5#CL'44V< rc|j|j}|j|jj |S)Nr)r RECORD_LENGTHrr"rvrs rrvzVBAInfoContainer.check_validitys;""$*<*<"= D&&5578 rrW) r'r(r)r*rrrr'r rhrcrvr+r,s@rrrs< KNOM"  rrcDeZdZdZdZdZdZfdZedZ dZ xZ S)r$z An atom record that specifies a reference to the VBA project storage. https://msdn.microsoft.com/en-us/library/dd948874%28v=office.12%29.aspx r8 cTtt| d|_d|_d|_yrW)rr$r persist_id_ref f_has_macrosrrs rr zVBAInfoAtom.__init__s' k4)+"  rctjd|}|j|t||_t||_t||_|S)Nzparsing VBAInfoAtom)rrrtr=r,r-rrs rrczVBAInfoAtom.extract_fromsR '(e &! $F^ "&> Vn  rc|j|j}|j|jd|jdd|j|j d|j d|S)Nr& fHasMacrosr8r)rr'rrr-rSrrs rrvzVBAInfoAtom.check_validitysd""$*<*<"= D$$\43D3DdANO D$$Y a@A r) r'r(r)r*rrr'r rhrcrvr+r,s@rr$r$s7 KNM ,rr$c<eZdZdZdZdZdZdfd ZdZdZ xZ S) ExternalObjectStorageaI storage for compressed/uncompressed OLE/VBA/ActiveX control data Matches types ExOleObjStgCompressedAtom, ExOleObjStgUncompressedAtom, VbaProjectStgCompressedAtom, VbaProjectStgUncompressedAtom, ExControlStgUncompressedAtom, ExControlStgCompressedAtom Difference between compressed and uncompressed: RecordHeader.rec_instance is 0 or 1, first variable after RecordHeader is decompressed_size Data is not read at first, only its offset in the stream and size is saved e.g. https://msdn.microsoft.com/en-us/library/dd952169%28v=office.12%29.aspx irrctt| |d|_n3|r|j|_d|_n|j |_d|_d|_d|_d|_ y)NTF) rr2r rRECORD_INSTANCE_COMPRESSED is_compressedRECORD_INSTANCE_UNCOMPRESSEDuncompressed_size data_offset data_size)r!r5r&s rr zExternalObjectStorage.__init__se #T35  #'D #'#B#BD !%D #'#D#DD !&D !%rcDtjdj|j|j ||j j |_|jr%t||_ |xjdzc_|j|_ y)zX not a classmethod because of is_compressed attrib see also: DummyType z:Parsing ExternalObjectStorage (compressed={0}) from streamr<N) rrrr5rtrqr[r9r=r7rr8rss rrcz"ExternalObjectStorage.extract_fromss N6$,,- / 6"..   %+F^D " NNa N!;;=rc"|jSrW)rr\s rrvz$ExternalObjectStorage.check_validitys""$$rrW) r'r(r)r*rr4r6r rcrvr+r,s@rr2r2s* K!"#$   )%rr2cNeZdZdZej ZfdZefdZ xZ S)!ExternalObjectStorageUncompressedz< subclass of ExternalObjectStorage for uncompressed objects c,tt| dy)NF)rr=r rs rr z*ExternalObjectStorageUncompressed.__init__'s /?Frc<|}tt| ||SzL note the usage of super here: call instance method of super class! )rr=rcr`r"rar&s rrcz.ExternalObjectStorageUncompressed.extract_from*s!e /B6J r) r'r(r)r*r2r6rr rhrcr+r,s@rr=r=#s+F+HHOGrr=cNeZdZdZej ZfdZefdZ xZ S)ExternalObjectStorageCompressedz: subclass of ExternalObjectStorage for compressed objects c,tt| dy)NT)rrCr rs rr z(ExternalObjectStorageCompressed.__init__7s -t=dCrc<|}tt| ||Sr@)rrCrcrAs rrcz,ExternalObjectStorageCompressed.extract_from:s!e -s@H r) r'r(r)r*r2r4rr rhrcr+r,s@rrCrC3s+D+FFODrrCcfd}|S)z a decorator that can open and close the default stream for func to be applied only to functions in PptParser that read from default stream (:py:data:`MAIN_STREAM_NAME`) Decorated functions need to accept args (self, stream, ...) crd} |jYtjdjtj |j jt|_d}||jg|i||rUtjdjtj |jjd|_SS#t$r |jr|jYnwxYw |rUtjdjtj |jjd|_yy#|rUtjdjtj |jjd|_wwxYwNFzopening stream {0!r} for {1}Tzclosing stream {0!r} after {1}) _open_main_streamrrrrr'ole openstreamclose Exception fast_fail_log_exception)r!argskwargsstream_opened_by_mefuncs rwrappedz(with_opened_main_stream..wrappedNsp# .%%- 8!6"2DMMBD)-)<)<=M)N&&*#d44FtFvF# :!6"2DMMBD&&,,.)-& # &~~##%  &&# :!6"2DMMBD&&,,.)-& #" :!6"2DMMBD&&,,.)-& #s%A;C&D>EDEAF6rrSrTs` rwith_opened_main_streamrVEs.6 Nrcfd}|S)zB same as with_opened_main_stream but with yield instead of return c?Kd} |jYtjdjtj |j jt|_d}||jg|i|D]}| |rUtjdjtj |jjd|_yy#t$r |jr|jYwxYw#|rUtjdjtj |jjd|_wwxYwwrH) rIrrrrr'rJrKrMrNrOrL)r!rPrQrRresultrSs rrTz2generator_with_opened_main_stream..wrappedos;# .%%- 8!6"2DMMBD)-)<)<=M)N&&*#tT%;%;MdMfM   # :!6"2DMMBD&&,,.)-& # &~~##%  &# :!6"2DMMBD&&,,.)-& #s=E-BC% D AE-%&D D DDAE**E-rrUs` r!generator_with_opened_main_streamrZls.8 NrceZdZdZddZddZdZdZedZ edZ e d Z e d Z e d Zed Zed Ze dZy) PptParserzW Parser for PowerPoint 97-2003 specific data structures requires an OleFileIO ct|tjrtj d||_n>tj dj |tj||_||_d|_d|_ d|_ d|_ |j j}td|Dr|jdd|d|Dcgc]}|dj}}d |vr|jdd|d t j|vr|jdd|t d|_ycc}w) z constructor :param ole: OleFileIO or anything that OleFileIO constructor accepts :param bool fast_fail: if True, all unexpected data will raise a PptUnexpectedData; if False will only log error zusing open OleFileIOzOpening file {0}Nc38K|]}t|dk7yw)rN)r)rFr"s rrHz%PptParser.__init__..s;Fs6{a;srootlistdirzlen = 1rz current userrK)rLolefile OleFileIOrrrJrrNcurrent_user_atomnewest_user_editdocument_persist_objpersist_object_directoryr`any_faillowerrrI)r!rJrN root_streamsr"s rr zPptParser.__init__s c7,, - II, -DH II(//4 5((-DH"!% $$(!(,%xx'')  ;l; ; JJvy, B8DEfq )E E- JJvy, G%%'<7 JJvy,8H I!% FsENc|tjdd}n8tj\}}}tj|}t |}t j|ttj|D]?\}}|jD]'}t jdj||)Ay)a log an exception instead of raising it call in one of 2 ways: try: if fail(): self._log_exception('this is the message') except: self._log_exception() # only possible in except clause Nztrace {0}: {1}) traceback extract_stacksysexc_info extract_tbstrrerror enumerate format_list splitlinesrr) r!rstackrexctracei_entryrlines rrOzPptParser._log_exceptions ?++-cr2ELLNMAsE((/Ec(C #' (=(=e(DE BNGU((* B *11'4@A B Brcn|jrt||jt|jy)zt depending on self.fast_fail raise PptUnexpectedData or just log err args as for PptUnexpectedData N)rNrrOr)r!rPs rrhzPptParser._fails0 >>#T* *    14 8 < < =rc |jtjdtjdd} tjd|jj d}t j||_|&tjd|jyy#t$r |jr|jYQwxYw#|&tjd|jwwxYw)z parse the CurrentUserAtom record from stream 'Current User' Structure described in https://msdn.microsoft.com/en-us/library/dd948895%28v=office.12%29.aspx Nz>--::< (*% $k KK ,%11&,GI$$,(1% II) *++-D =#VCI.0 L >EEcJK L1g II*vi@@A C KK ::BKK H 4@@H  II) *#226:D =#VCI.0 * F#VC[* *1g **>> !**3u/F/F+GGI E!6%"2"2G"'"9"9;<&/u/F/F%G!ME6 11%2B2B52HI!  !//FYkrc|jtjd|j|j |j j }|j|}tjdj||tjdj||j|tjtj||_tjd|jj}|r-tjdjt||D]&}tjdj|(|r|j r|dyy) z. Part 2: Identify the document persist object NzBre-reading and overwriting previously read document_persist_objectz)newest user edit ID is {0}, offset is {1}z seek to {0}rrz)check_validity(document_persist_obj): {0}r)rerr~rfrrdrrrrrrrrcrvrrN)r!r" newest_refrrrs rparse_document_persist_objectz'PptParser.parse_document_persist_objectYs=  $ $ 0 KKB C  ( ( 0  / / 1 **== ..z: =6*f- / -&&v./ FBKK(%6$B$B6$J! %&((779  KK9@@TK L &C KKC & & DNNq'M#4rc#hKd}t|}tjdj|||kDr t dd} |j }|dz }|j |}|j|}|dk7rItjdj||z||z|j||dz}|dk7rIt||k(r'|j||z|z tjn0tjdjt|||y w) z. search for pattern in stream, return indices r)zpattern length is {0}zneed buf > pattern to search!rrrlzfound pattern at index {0}z1reached end of buf (read {0}<{1}) after {2} readsN) rrrrrerr2findrrr) r!r"patternBUF_SIZE pattern_lenn_reads start_posbufidxs rsearch_patternzPptParser.search_patterns'l  )00=>  !<= = I qLG++h'C((7#C) 6==imLM#o%hhwA.) 3x8# I0;> L M!6#c(Hg>@%s C D2 A&D2c#Ktjdtjtjt jt jz}|j |D]7}|j|tjdj| tj|}|j}|r.tjdjt|nitjd|j}tjdj|j |j"|j$||D]&}tjdj|(|s'|j&s5|dy #t$r|jYXwxYww) aY search through stream for VBAInfoContainer, alternative to parse... quick-and-dirty: do not parse everything, just look for right bytes "quick" here means quick to program. Runtime now is linear is document size (--> for big documents the other method might be faster) .. seealso:: search_vba_storage zlooking for VBA info containers)r[extracting at idx {0}rzcontainer is okz2persist id ref is {0}, has_macros {1}, version {2}z%check_validity(VBAInfoContainer): {0}rN)rrrrr'r$rrrrcrMrOrvr~rr"r,r-rrN)r!r"rr containerratomrs rsearch_vba_infozPptParser.search_vba_infos 34"33(8(F(F4H..(3(A(A/CC &&w/ C KK  II-44S9 : ,99&A ++-D =#VCI.0 +, .. N!6$"5"5t7H7H"&,,01  * C#VC[* *1g 3   ##% s7B)G,F.CG G'G.G G G  Gc #tKtjdttfD]o}|j dt j |j}|j|D]'}|j |tjdj| |j|}|j}|r.tjdjt|nRtjdj|j |j"j$|j&||D]1}tjdj|j(|3|s|j*s%|dry#t$r|jYKwxYww)a search through stream for VBAProjectStg, alternative to parse... quick-and-dirty: do not parse everything, just look for right bytes "quick" here means quick to program. Runtime now is linear is document size (--> for big documents the other method might be faster) The storages found could also contain (instead of VBA data): ActiveX data or general OLE data yields results as it finds them .. seealso:: :py:meth:`search_vba_info` zlooking for VBA storage objectsrrrz8storage is ok; compressed={0}, size={1}, size_decomp={2}zcheck_validity({0}): {1}N)rrr=rCrrrrrrrcrMrOrvr~rr5rqr[r7r'rN)r!r"obj_typerrstoragerrs rsearch_vba_storagezPptParser.search_vba_storages" 34:8:! "H KK2;; '//1G**73 " C  188=>&33F;G --/KK A!'D !24II0%vg&;&;&-&6&6&>&>&-&?&? AB "MACKK :!'(9(93!?AADNNq'M3 "! "!'')s7B F8#F4C F8 F8 F8F51F84F55F8c6tjd|j|jtj t ||j\}}}tjdj|t|||r|jr||S)z2 return decompressed data from search_vba_storage z.decompressing storage for VBA OLE data stream z-decompressed {0} to {1} bytes; found err: {2}) rrrr8rriterative_decompressr9rrrN)r!r"rdecompn_readrs rdecompress_vba_storagez PptParser.decompress_vba_storage s| BC  G''5 ):): ;  A6&#f+s3 5 4>>I rctjdj|j|j|j |jt j|j|j}|S)z0 return data pointed to by uncompressed storage zCreading uncompressed VBA OLE data stream: {0} bytes starting at {1}) rrrr9r8rrrr2)r!r"rdatas rread_vba_storage_datazPptParser.read_vba_storage_data'sa .6'++W-@-@A C  G''5{{7,,- rc#Kd}d}|jD]&}|dz }|jjdkDs"|dz }(d}d}|jD]?}|dz }|jr|dz }|j |-|j |Atjdj||||yw)z@ search vba infos and storages, yield uncompressed storage data rrzCfound {0} infos ({1} with macros) and {2} storages ({3} compressed)N) rr"r-rr5rrrrr)r!r"n_infosn_macrosr n_storages n_compressedrs r iter_vba_datazPptParser.iter_vba_data2s((* D qLG!!..2A     ..0 :G !OJ$$! 11'::0099  : $&(J E Gs 6B>BB>)FrW)r'r(r)r*r rOrhrrVrrrZrrrrrrrrrr\r\s %&NB,>8_0_0B$$V''>',',\'2"'2"h:'G'Grr\c tj}d}d}d} ||kr=t||z |}||j|j |z }||z }||kr=|||fS#tj $r }|}Yd}~d}~wwxYw)z( decompress data from stream chunk-wise rrN)zlib decompressobjr decompressr2rs) r"r chunk_size decompressorrr return_errn_newrs rrrMs%%'L F FJtmV Z0E l--fkk%.@A AF eOFtm 6: %% :: sAA##B6A==B__main__znothing here to run!)i)5r* __version__rorr0rmrrpathnormpathabspathdirname__file___thismodule_dirrO _parent_dirinsertraCRITICALrrrrURL_OLEVBA_ISSUESMSG_OLEVBA_ISSUESrMrr4r9r=rSobjectrUrjrrrrrrrrrr$r2r=rCrVrZr\rr'printrrrrs Z    ''""277??277??83L#MNggrww||OTBC chhHHOOA{##++A-<!)E47HH : :2 2 2 A"(L6(LVqNfqNhPgPfJ7J^%7%7P/7/dLFL^,2G,2^,7,^ddN w F.'.b1%G1%h (=  &; $$ND|G|G~ && z !r