|imdZdZddlZddlmZddlmZddlmZm Z m Z ddl m Z ddl Z ddlZ ddlmZn#e$r ddlmcmZYnwxYwejdZd Zd Zd Zd Zd ZdZdZdZdZdZ dZ!dZ"dZ#dZ$dZ%dZ&dZ'dZ(dZ)dZ*dZ+dZ,Gdd e-Z.Gd!d"e/Z0Gd#d$e-Z1d%Z2e3d&krej4e2dSdS)'a Common operations for OpenXML files (docx, xlsx, pptx, ...) This is mostly based on ECMA-376 (5th edition, Part 1) http://www.ecma-international.org/publications/standards/Ecma-376.htm See also: Notes on Microsoft's implementation of ECMA-376: [MS-0E376] .. codeauthor:: Intra2net AG License: BSD, see source code or documentation ooxml is part of the python-oletools package: http://www.decalage.info/python/oletools z0.54.2N) log_helper)uopen)ZipFile BadZipfile is_zipfile)splitextooxmlz[Content_Types].xmlz _rels/.rels)zz Word.Documentz Excel.Sheetwordexcel powerpointnonemixedzword-xmlz excel-xmlz word-xml2003z excel-xml2003c|dS|jddkr]|jddkr?d|j|jddzdg}nd|jg}|jrA|d |jd d |jrA|d |jd d |j D]\}}|d |||dkr| tr|dxxdz cc<_| tr|dxxdz cc<| tr|dxxdz cc<|tvr|dxxdz cc<|dxxdz cc<d|}t!|dkr |dddzS|dzS)z! for debugging: print an element NNoner{}z[tag={{...}}{0}z[tag={0}z text="{0}" z\nz tail="{0}"z {0}="{1}" ContentTypez-->xlsz-->docz-->pptz-->_z!!!z, z...]])tagcountformatindextextappendreplacetailattribitems startswithCONTENT_TYPES_EXCELCONTENT_TYPES_WORDCONTENT_TYPES_PPTCONTENT_TYPES_NEUTRALjoinlen)elempartskeyvaluers >#3F3Fq3H3I3I*JKKN""48,,/ yK ]))$)*;*;D%*H*HIIJJJ yK ]))$)*;*;D%*H*HIIJJJk'')) $ $ U \((e44555 -   344 $b Y& !!"455 $b Y& !!"344 $b Y& ///b W$ b V# ::e  D 4yy3DSDzG## $;ctjjdkrt|tSt|t S)zC version-independent test for isinstance(some_var, (str, unicode)) )sys version_infomajor isinstance basestringstr)some_vars r/isstrr;s3 ""(J/// h $ $$r1c\t|}|rd}t|d5}tjt |d}dddn #1swxYwY|stS|d}|tkrtS|tkrtStSd}d}d} | tD]\}} }tdt#| z | jd} n#t&$rYKwxYw|| t*z}|| t,z}|| t.z}nG#t0$r:} | jdrt| jvr tcYd} ~ Sd} ~ wwxYw|r |s |st4S|s |r |st6S|s |s |rt8S|s |s |stStd t<S) z6 return one of the DOCTYPE_* constants or raise error NrrF rinvalid subfilez'Encountered contradictory content types) XmlParser is_single_xmlrresearchOFFICE_XML_PROGID_REGEXread DOCTYPE_NONEgroupsWORD_XML_PROG_IDDOCTYPE_WORD_XMLEXCEL_XML_PROG_IDDOCTYPE_EXCEL_XMLiter_xmlFILE_CONTENT_TYPESloggerdebugr0r"KeyErrorr$r%r&r'BadOOXML more_info DOCTYPE_WORD DOCTYPE_EXCELDOCTYPE_POWERPOINTwarning DOCTYPE_MIXED) filenameparsermatchhandleprog_idis_docis_xlsis_ppt_r+ content_typeoo_errs r/get_typerds x F   8S ! ! JVI5v{{47H7HIIE J J J J J J J J J J J J J J J  ,,..# & & &# # ' ' '$ $ F F F //*<== A AJAtQ LL40 1 1 1 #{=9      l--.ABB BF l--.@AA AF l--.?@@ @FF A    & &'8 9 9 "f&666       fV fV "&"V"!! & NN<=== s[.A00A47A4A F D)(F) D63F5D66AF G.G GGGc| t|}n#t$rYdSt$rYdSwxYw|tkrdSdS)z? Determine whether given file is an ooxml file; tries get_type FT)rdrRIOErrorrG)rYdoctypes r/is_ooxmlrhsf8$$ uu uu,u 4s , ,,ceZdZdZdZddZdZdZdZd Z d Z dd Z e j fd ZdZdZdZdZdZdZdZdS) ZipSubFilea A file-like object like ZipFile.open returns them, with size and seek() ZipFile.open() gives file handles that can be read but not seek()ed since the file is being decompressed in the background. This class implements a reset() function (close and re-open stream) and a seek() that uses it. --> can be used as argument to olefile.OleFileIO and olefile.isOleFile() Can be used as a context manager:: with zipfile.ZipFile('file.zip') as zipper: # replaces with zipper.open(subfile) as handle: with ZipSubFile(zipper, 'subfile') as handle: print('subfile in file.zip has size {0}, starts with {1}' .format(handle.size, handle.read(20))) handle.reset() Attributes always present: container: the containing zip file name: name of file within zip file mode: open-mode, 'r' per default size: size of the stream (constructor arg or taken from ZipFile.getinfo) closed: True if there was an open() but no close() since then Attributes only not-None after open() and before close(): handle: direct handle to subfile stream, created by ZipFile.open() pos: current position within stream (can deviate from actual position in self.handle if we fake jump to end) See also (and maybe could some day merge with): ppt_record_parser.IterStream; also: oleobj.FakeFile ir=Nc||_||_|R||j|_t d|jn||_d|vr"td|||_ d|_ d|_ d|_ dS)z1 remember all necessary vars but do not open yet Nzzip stream has size {0}wz%Can only read, mode "{0}" not allowedT) containernamegetinfo file_sizesizerOrPrlower ValueErrormoder\posclosed)selfrmrYrtrqs r/__init__zZipSubFile.__init__s" <!))(33=DI LL299$)DD E E E EDI $**,,  D$fTll,, ,   r1cdSNTrws r/readablezZipSubFile.readable!tr1cdSNFr{r|s r/writablezZipSubFile.writable$sur1cdSrzr{r|s r/seekablezZipSubFile.seekable'r~r1c|jtd|j|j|j|_d|_d|_|S)zA open subfile for reading; open mode given to constructor before Nzre-opening file not supported!rF)r\rfrmopenrnrtrurvr|s r/rzZipSubFile.open*sI ; ":;; ;n))$)TY??   r1c td)z write is not allowed zwriting not implemented)rfrwargskwargss r/writezZipSubFile.write4s/000r1rc|jtd|j|jkrdS|j|}|xjt |z c_|S)z read given number of bytes (or all data) from stream returns bytes (i.e. str in python2, bytes in python3) Nzread on closed handler1)r\rfrurqrFr*)rwrqdatas r/rFzZipSubFile.read8s_ ; 122 2 8ty 3{%% CII r1c&|tjkr|}nX|tjkr |j|z}n=|tjkr |j|z}n"t d||dkrtd||jkrdS|dkr| dS||jkr+| | |dS||jkr| ||jz dS||_dS)z5 re-position point so read() will continue elsewhere z(invalid offset {0}, need SEEK_* constantrz%Seek beyond start of file not allowedN) ioSEEK_SETSEEK_CURruSEEK_ENDrqrsrrfreset _seek_skip)rwruoffsetnew_poss r/seekzZipSubFile.seekHs R[ GG r{ " "hnGG r{ " "i#oGGG$fVnn.. . Q;;ABB B  D \\ JJLLLLL tx   JJLLL OOG $ $ $ $ $ ty OOGdh. / / / / /DHHHr1ct||j\}}t|D]}||j||dS)z< helper for seek: skip forward by given amount using read() N)divmod CHUNK_SIZErangerF)rwto_skipn_chunksleftoverras r/rzZipSubFile._seek_skipjs^$GT_==(x ' 'A IIdo & & & & (r1c|jS)z$ inform about position of next read )rur|s r/tellzZipSubFile.tellus xr1cV||dS)z close and re-open N)closerr|s r/rzZipSubFile.resetzs"  r1cp|j|jd|_d|_d|_dS)z close file NT)r\rrurvr|s r/rzZipSubFile.closes9 ; " K       r1c.||S)z* start of context manager; opens the file )rr|s r/ __enter__zZipSubFile.__enter__s  r1c.|dS)z) end of context manager; closes the file N)rrs r/__exit__zZipSubFile.__exit__s r1c|jd}n;|jdkrd}n-|j|jkrd}nd|j}d|j|j|j|S)z7 creates a nice textual representation for this object Nrvrzopen, at startz open, at endzopen, at pos {0}z*[ZipSubFile {0} (size {1}, mode {2}, {3})])r\rurqrrnrt)rwstatuss r/__str__zZipSubFile.__str__sp ; FF X]]%FF X " "#FF'..tx88F;ty$)TY?? @r1)r=N)r)__name__ __module__ __qualname____doc__rrxr}rrrrrFrrrrrrrrrrr{r1r/rjrjs">J"111 "{    D    @ @ @ @ @r1rjc$eZdZdZdfd ZxZS)rRz4 exception thrown if file is not an office XML file Nctt|d||rd|znd||_||_dS)z3 create exception, remember filename and more_info z {0} is not an Office XML file{1}z: N)superrRrxrrYrS)rwrYrS __class__s r/rxzBadOOXML.__init__s] h&& . VH)CdY.. D D F F F! "r1N)rrrrrx __classcell__)rs@r/rRrRsC>>##########r1rRc:eZdZdZdZdZd dZd dZdZd Z dS) rAa parser for OOXML files handles two different types of files: "regular" OOXML files are zip archives that contain xml data and possibly other files in binary format. In Office 2003, Microsoft introduced another xml-based format, which uses a single xml file as data source. The content of these types is also different. Method :py:meth:`is_single_xml` tells them apart. cV||_d|_t|_d|_dSr)rY did_iter_allsetsubfiles_no_xml_is_single_xml)rwrYs r/rxzXmlParser.__init__s+  !"uu"r1cP|j|jSt|jr d|_dSd}t|jd5}t jt |d}dddn #1swxYwY|r d|_dSt|jd)z determine whether this is "regular" ooxml or a single xml file Raises a BadOOXML if this is neither one or the other NFr=r>Tzis no zip and has no prog_id) rrrYrrCrDrErFrR)rwr[r\s r/rBzXmlParser.is_single_xmls   *& & dm $ $ "'D 5 4=# & & J&I5v{{47H7HIIE J J J J J J J J J J J J J J J  "&D 4t}&DEEEs.A==BBNc#(K|rS|rt|jdt|jd5}d|fVdddn #1swxYwYd|_dSd}d} t |j}|s|}n,t|r|g}ntd|D}|D]6}||d5}||fVdddn #1swxYwY7|sd|_nU#t$r*}t|jdt|zd}~wt$rt|jdwxYw|r| dSdS#|r| wwxYw) a! Find files in zip or just give single xml file yields pairs (subfile-name, file-handle) where file-handle is an open file-like object. (Do not care too much about encoding here, the xml parser reads the encoding from the first lines in the file.) zxml has no subfilesrbNTc3K|]}|VdSrr{).0args r/ z'XmlParser.iter_files..s"$9$9SS$9$9$9$9$9$9r1r=zinvalid subfile: znot in zip format) rBrRrYrrrnamelistr;tuplerQr9rr)rwrr\zippersubfilessubfileorig_errs r/ iter_fileszXmlParser.iter_filessu     # # Et}.CDDDdmT** #fFl""" # # # # # # # # # # # # # # # $D   FH # //:%00HH4[[: $xHH %$9$9D$9$9$999H'..GWc22.f%vo---...............-(,D% D D Dt}2S]]BDDD C C Ct}.ABBB C#LLNNNNN##6#LLNNNN#saAAA/A1D C3' D 3C7 7D :C7 ; D E8 E%D88#EE88FFc #K|g}not|r1|g}td|n/|}td|||D]s\}}d}d}g} t j||D]\} } |  | dkr^| j|vrOtd| j|| | j|f|dz }m| dksJ|dz}|dksJ| j|v} | r| j|f} | d | kr| nt d | | d | rm| d d|kr[td | d | | r| d d|k[n*#t$rt d YnwxYw| s|s|| |fV|s| s| n#t j $r}|j||t!|jd |dr tj}n tj}|d||dzYd}~nd}~wwxYw|dksJudS)ai Iterate xml contents of document If given subfile name[s] as optional arg[s], will only parse that subfile[s] yields 3-tuples (subfilename, element, depth) where depth indicates how deep in the hierarchy the element is located. Containers of element will come *after* the elements they contain (since they are only finished then). Subfiles that are not xml (e.g. OLE or image files) are remembered internally and can be retrieved using iter_non_xml(). The argument need_children is set to False per default. If you need to access an element's children, set it to True. Note, however, that leaving it at False should save a lot of memory. Otherwise, the parser has to keep every single element in memory since the last element returned is the root which has the rest of the document as children. c.f. http://www.ibm.com/developerworks/xml/library/x-hiperfparse/ Argument tags restricts output to tags with names from that list (or equal to that string). Children are preserved for these. Nzlooking for tags: {0})startendrrz remember start of tag {0} at {1}rrrzBfound end for wanted tag {0} but last start tag {1} does not matchzrecover: pop {0}z0found end of {0} at depth {1} but no start eventzcontent is not valid XMLz.xmlz$ xml-parsing for {0} failed ({1}). z Run iter_non_xml to investigate.)r;rOrPrrET iterparserrpoperror IndexErrorclear ParseErrorraddrRrYendswithrW)rwr need_childrentags want_tagsrr\eventsdepth inside_tagseventr+ is_wantedcurr_tagerrlogs r/rMzXmlParser.iter_xmls0 <II 4[[ ?I LL077== > > > >I LL077== > > >#x88< < OGV%FEK7 8#%<#?#?(%(%KE4| ''8y00"LL)K*0&5*A*ACCC'..%/@AAA   E>>>>QJE A:::: $I 5I ;$(He#4;*2(:: + 1 1 1 1 & .66\}}}|jdr|jd}|dr |dd}|||jdft d |d|jd r|jd }|d r |dd}|||jdft d |d@nA#t$r4}|j d rt|j vrnYd}~nd}~wwxYwt|t|fS)a& retrieve subfile infos from [Content_Types].xml subfile returns (files, defaults) where - files is a dict that maps file-name --> content-type - defaults is a dict that maps extension --> content-type No guarantees on accuracy of these content types! Default Extension.rNrz/found content type for extension {0[0]}: {0[1]}rOverridePartName/z-found content type for subfile {0[0]}: {0[1]}r@)rBrMrNrrr"r$rrOrPrrRrSdict)rwdefaultsfilesrar+ extensionrrcs r/get_content_typeszXmlParser.get_content_typeses      r6M "mm,>?? = = 48$$Y// = $ K 8I ++C002$-abbM OOY M0J$KLLLLL"**0&"*>*>@@@@X&&z22="k*5G))#...")!""+LL'4;}+E!FGGGLL"**0&r*;*;=== =   **+<== &&*:::  E{{DNN**sEE99 F7*F22F7c#bK|jstd|jsdS|rdS|\}}t |j5}|jD]}|dr |dd}d}||vr ||}n@t|d}|dr |dd}||vr||}t||5}|||fVdddn #1swxYwY ddddS#1swxYwYdS)a retrieve subfiles that were found by iter_xml to be non-xml also looks for content type info in the [Content_Types].xml subfile. yields 3-tuples (filename, content_type, file_handle) where content_type is based on filename or default for extension or is None, and file_handle is a ZipSubFile. Caller does not have to care about closing handle, will be closed even in error condition. To handle binary parts of an xlsb file, use xls_parser.parse_xlsb_part zQDid not iterate through complete file. Should run iter_xml() without args, first.Nrrr) rrOrWrrBrrrYr$rrj)rw content_typescontent_defaultsrrrbrr\s r/ iter_non_xmlzXmlParser.iter_non_xmls  I NNH I I I#  F       F*.*@*@*B*B' ' T] # # 8v/ 8 8%%c***%abbkG# m++#0#9LL ( 1 1! 4I ++C002$-abbM  $444'7 'B 008F!<7777888888888888888 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8s7.BD$6D > D$ D D$D D$$D(+D(r)NFN) rrrrrxrBrrMrrr{r1r/rArAs### FFF*+#+#+#+#Z]]]]~&+&+&+P&8&8&8&8&8r1rAc tjddttjdkrt ddSt dt tjdzttjd}|D]@\}}}|dkr4t d |d |zt|At| D]E\}\}}}t d ||pd |d krt d nFtj dS)z Test xml parsing; called when running this file as a script. Prints every element found in input file (to be given as command line arg). FrPr3z/To test this code, give me a single file as argzDetected type: rz {0} {1}{2}r?z Non-XML subfile: {0} of type {1}unknowndz...r)renable_loggingr*r4argvprintrdrArMrr0 enumerater end_logging)rZrr+rrrbras r/testrsY eW--- 38}} @AAAq hsx{33 3444sx{ # #F & 1 1PPu 199 -&&wu iooNN O O O-6v7J7J7L7L-M-M))q 1vg|9z:: < < < 3;; &MMM E  1r1__main__)5r __version__r4oletools.common.log_helperroletools.common.io_encodingrzipfilerrros.pathrrrC lxml.etreeetreer ImportErrorxml.etree.cElementTree cElementTreeget_or_create_silent_loggerrOrNFILE_RELATIONSHIPSr%r&r'r(rErIrKrTrUrVrGrXrJrLDOCTYPE_WORD_XML2003DOCTYPE_EXCEL_XML2003r0r;rdrhobjectrjrsrRrArrexitr{r1r/r s  X  111111------3333333333 (((('''''''''''( 0 / 8 8+" A"!  !  %'@%%%---`   $r@r@r@r@r@r@r@r@j # # # # #z # # #"88888888D   > z CHTTVVs5AA