
    ZiO                     <   S SK JrJrJrJrJr  S SKJr  S SKJ	r	  S SK
Jr  S SKJr  S SKJr  S SKJrJr  S SKJr  S S	KJr  S S
KJr  S SKJr  S SKJrJrJr  S SKJrJrJr  S SKJr  S SK J!r!  S SK J"r"  S SK#J$r$J%r%J&r&  S SK'r'\'RP                  " S5      r) " S S5      r*g)    )UnionAnyStrTupleDictAny)BufferedReader)Lark)Tree)Token)UnexpectedInput)
RTFCleanerStripControlWords)StripNonVisibleRTFGroups)StripUnusedSpecialCharacters)transform_based_on_content_type)encode_escaped_control_chars)log_validatorslog_transformationsis_logger_on)get_stripped_HTMLRTF_valuesDeleteTokensFromTreestrip_binary_objects)make_concise_grammar)TextDecoder)validate_ansi_cpg)NotEncapsulatedRtfMalformedEncapsulatedRtfMalformedRtfNRTFDEc                      \ rS rSrSrSS\S\\S4   4S jjrS r	SS\
S	\4S
 jjrS rS rS rS rS	\\S4   4S jrS\4S jrS	\4S jrS	\4S jrS r\S\S\S	\\\S\4   4   4S j5       r\S\4S j5       r\S\4S j5       rSr g)DeEncapsulator(   a  De-Encapsulating RTF converter of HTML/TEXT found in .msg files.

De-encapsulation enables previously encapsulated HTML and plain text content to be extracted and rendered as HTML and plain text instead of the encapsulating RTF content. After de-encapsulation, the HTML and plain text should differ only minimally from the original HTML or plain text content.


Parameters:
    raw_rtf: (bytes): It's the raw RTF file as bytes.
    grammar: (str): OPTIONAL - Lark parsing grammar which defines the RTF language. https://github.com/lark-parser/lark If you think my grammar is shoddy this is your chance to test out a better one and make a pull request.

Attributes:
    content: (bytes) The deencapsulated content no matter what format it is in. Populated by the `deencapsulate` function.
    html: (bytes) The deencapsulated content IF it is HTML content. Populated by the `set_content` function.
    text: (bytes) The deencapsulated content IF it is plain text content. Populated by the `set_content` function.
    found_binary: List of dictionaries containing binary data extracted from the rtf file.
    content_type: The type of content encapsulated in .rtf data (html or text). Populated by the `get_content_type` function.
    full_tree: The full .rtf object parsed into an object Tree using the grammar. Populated by the `parse_rtf` function.
    doc_tree: The `document` portion of the .rtf full_tree object.
    raw_rtf: The raw encapsulated .rtf data in byte format.
    grammar: The Lark parsing grammer used to parse the .rtf data.
    content_type_token: The .rtf header token identifying the content type. (\fromhtml1 OR \fromtext)
    parser: The lark parser. Should not need to be manipulated directly. But, useful for debugging and saving the parsed object.
    Nraw_rtfgrammarc                 8   U   U   U   U   U   U   U   U   U   U R                  U5        [        U[        5      (       a  UnO[        S5      eUR	                  S5      nUR                  SS5      nUR                  SS5      nX0l        Ub  X l        g[        5       U l        g)a[  Load in the Encapsulated test and setup the grammar used to parse the encapsulated RTF.

NOTE: This does not do the parsing in the init so that you can initiate the object and do the parsing step by step.

Parameters:
        raw_rtf: (bytes): It's the raw RTF string.
        grammar: (str): OPTIONAL - Lark parsing grammar which defines the RTF language. https://github.com/lark-parser/lark If you think my grammar is shoddy this is your chance to test out a better one and make a pull request.

Raises:
        TypeError: The raw_rtf data passed is not the correct type of data (string/byte string).
zGDeEncapssulator only accepts RTF files in string or byte-string formats    s   
   
   N)	catch_common_validation_issues
isinstancebytes	TypeErrorrstripreplacer#   r$   r   )selfr#   r$   raw_rtf_bytess       m/var/www/eduai.edurigo.com/question_generate/ques_gen_env/lib/python3.13/site-packages/RTFDE/deencapsulate.py__init__DeEncapsulator.__init__@   s     	++G4gu%%#Meff%,,W5%--ge<%--eE:+ 'L/1DL    c                    [        U R                  5      nUS   nUS   n[        U5      S:  a  X0l        [        R                  S5        [        U5      n[        S5      SL a  [        S5        [        U5         U R                  U5        [        5       nUR                  U R                  5        U R                  5         U R!                  5         U R#                  5       n[%        5       R'                  U5      n[)        5       n	U	R'                  U5      n
[+        5       nUR'                  U
5      n[-        SS	9nUR'                  U5      nXl        U R1                  5         g! [         a  n[        S5      UeSnAff = f)
a  De-encapsulate the RTF content loaded into the De-Encapsulator.

Once you have loaded in the raw rtf this function will set the properties containing the encapsulated content. The `content` property will store the content no matter what format it is in. The `html` and `text` properties will be populated based on the type of content that is extracted. (self.html will be populated if it is html and self.text if it is plain text.)
        r      z.Binary data found and extracted from rtf file.RTFDE.transform_loggerTzEncoding Escaped Rtfz&Malformed encapsulated RTF discovered:N)visit_tokens)r   r#   lenfound_binaryloginfor   r   r   	parse_rtfr   r   r   update_children	full_treeget_doc_treevalidate_encapsulationstrip_htmlrtf_tokensr   	transformr   r   r   contentset_content)r/   stripped_datanon_binary_rtfr:   escaped_rtf_eDecoderhtmlrtf_strippedcontrol_strippedspecial_strippernon_special_treestripperstripped_treecleanercleaned_texts                  r1   deencapsulateDeEncapsulator.deencapsulated   s\   
 -T\\:&q)$Q'|q  ,HHEF2>B01T9 67,	^NN;' -/##%  446,.889IJ79+556FG+- **+;<$/((7#-  	^*-SU[]]	^s   :E   
E;*E66E;fallback_to_defaultreturnc                 &   U R                  5       nU H  nUR                  S;   d  M  Us  $    [        R                  S5        USL a  [	        S5      e[        R                  S5        [        R                  S5        [        R                  S5        g)	a\  Validate and return the RTF charset keyword from the RTF streams header.

Args:
        fallback_to_default (bool): Allows you to force the use of the default charset "\ansi" if one is not found.

Raises:
        MalformedRtf: RTF stream does not include charset control word.

Returns:
        The RTF charset keyword from the RTF streams header.
)   \ansis   \macs   \pcs   \pcazAcceptable charset not found as the second token in the RTF stream. The control word for the character set must precede any plain text or any table control words. So, if this stream doesn't have one it is malformed or corrupted.Fz1RTF stream does not include charset control word.zThe fallback_to_default option on _get_charset is considered DANGEROUS if used on possibly malicious samples. Make sure you know what you are doing before using it.zAttempting to decode RTF using the default charset ansi. This is not recommended and could have unforeseen consequences for the resulting file and your systems security.zYou have a malformed RTF stream. Are you sure you really want to be parsing it? It might not just be corrupted. It could be maliciously constructed.rX   )+get_header_control_words_before_first_groupvaluer;   debugr   warningr<   )r/   rU   main_headerstokens       r1   validate_charsetDeEncapsulator.validate_charset   s     GGI!E{{FF " 			  y  	z%'RSS  {  	|  }  	~		  i  	jr4   c                     U R                  5       U l        U R                  S:X  a&  [        U R                  U R                  5      U l        gU R                  U l        g)zlPopulate the html or text content based on the content type. Populates self.html and/or self.text variables.htmlN)get_content_typecontent_typer   rD   rb   textr/   s    r1   rE   DeEncapsulator.set_content   sD     113&7dFWFWXDIDIr4   c                     U R                   R                  S   R                  S:X  a  U R                   R                  S   U l        g[	        S5      e)zExtract the document portion of the .rtf full_tree object. Populates the classes doc_tree attribute.

Raises:
        ValueError: The .rtf document object is missing or mis-located in the .rtf's full_tree object.
r6   documentz1Document object in the wrong place after parsing.N)r?   childrendatadoc_tree
ValueErrorrf   s    r1   r@   DeEncapsulator.get_doc_tree   sC     >>""1%**j8 NN33A6DMPQQr4   c                     U R                   c  U R                  5         O"U R                   S:X  a  gU R                   S:X  a  g[        S5      e)ah  Provide the type of content encapsulated in RTF.

NOTE: This function will only work after the header validation has completed. Header validation also extracts the content type of the encapsulated data.

Raises:
        NotEncapsulatedRtf: The .rtf object is missing an encapsulated content type header. Which means that it is likely just a regular .rtf file.

   \fromhtml1rb   	   \fromtextre   zCData is missing encapsulated content type header (the FROM header).)content_type_tokenvalidate_FROM_in_doc_headerr   rf   s    r1   rc   DeEncapsulator.get_content_type   sJ     ""*,,.$$6$$5 !fggr4   c                     U R                  U R                  5        U R                  5         U R                  5         U R	                  5       nUb  [        UR                  5        gg)zhRuns simple tests to validate that the file in question is an rtf document which contains encapsulation.N)validate_rtf_doc_headerrl   r_   rs   get_ansicpg_headerr   rZ   )r/   ansicpgs     r1   rA   %DeEncapsulator.validate_encapsulation   sQ    $$T]]3((*))+gmm, r4   c                 ~    U R                  5       nU H'  nUR                  R                  S5      (       d  M%  Us  $    g)a  Extract the ansicpg control word from the .rtf header.

Returns:
        A lark CONTROLWORD Token with the `\ansicpg` value. Returns None if the `\ansicpg` control word is not included as this is only required if there is Unicode which needs to be converted to ANSI within a .rtf file.
s   \ansicpgN)rY   rZ   
startswith)r/   headersitems      r1   rw   !DeEncapsulator.get_ansicpg_header   s;     BBDDzz$$\22  r4   rtfc                     [        U R                  SSSSS9U l        U R                  R                  U5      U l        [        S5      SL a!  [        S5        [        U R                  5        gg)zParse RTF file's header and document and extract the objects within the RTF into a Tree. Populates the self.full_tree attribute.

Args:
        rtf: The .rtf string to parse with the projects lark grammar.
lalrT)parserkeep_all_tokens	use_bytespropagate_positionsr7   z
Parsed RTFN)r	   r$   r   parser?   r   r   )r/   r   s     r1   r=   DeEncapsulator.parse_rtf   sa     4<<"(+/%)/35 **3/01T9-/ :r4   c                     [        U R                  5      n[        U5      n[        U5      nUR	                  U R                  5      nU$ )zStrip tokens from with htmlrtf regions of the doc_tree as they were not part of the original HTML content.

Returns:
        .rtf doc_tree stripped of all non-original tokens.
)r   rl   listr   rC   )r/   delete_generatortokens_to_deletedeleterhtmlrtf_cleaned_trees        r1   rB   #DeEncapsulator.strip_htmlrtf_tokens   sE     7t}}E 01&'78&00?##r4   c                     / nU R                   R                  SS  H-  n[        U[        5      (       a  UR	                  U5        M+  Us  $    U$ )a-  Extracts all the control words in the first 20 tokens of the document or all the tokens which occur before the first group (whichever comes first.)

This is used to extract initial header values for validation functions.

Returns:
        A list containing the header tokens in the .rtf data.
        N   )rl   rj   r*   r   append)r/   initial_control_wordsr^   s      r1   rY   :DeEncapsulator.get_header_control_words_before_first_group  sN     !#]]++CR0E%''%,,U3,,	 1
 %$r4   c                 $   SSSSS.n[        5       R                  U R                  5      nUR                  SS n/ nSnU HI  n[	        U[
        5      (       a  UR                  U5        M+  U[        UR                  S 5      5      -  nMK     [        S5      SL a  [        SU 35        U H:  nU R                  XaS	9u  pUS
   SL a  US   SL a  [        S5      eUc  M4  XPl        M<     US
   SL a   [        R                  S5        [!        S5      eg)a  Inspect the header to identify what type of content (html/plain text) is encapsulated within the document.

NOTE: The de-encapsulating RTF reader inspects no more than the first 10 RTF tokens (that is, begin group marks and control words) in the input RTF document, in sequence, starting from the beginning of the RTF document. If one of the control words is the FROMHTML control word, the de-encapsulating RTF reader will conclude that the RTF document contains an encapsulated HTML document and stop further inspection. If one of the control words is the FROMTEXT control word, the de-encapsulating RTF reader concludes that the RTF document was produced from a plain text document and stops further inspection. - MS-OXRTFEX

Raises:
        MalformedEncapsulatedRtf: The .rtf headers are malformed.
        NotEncapsulatedRtf: The .rtf object is missing an encapsulated content type header. Which means that it is likely just a regular .rtf file.
        F)rtf1fromfonttbl	malformedN
   c                      U R                   S:H  $ )NCONTROLWORD)type)ts    r1   <lambda><DeEncapsulator.validate_FROM_in_doc_header.<locals>.<lambda>+  s    QVV}E\r4   RTFDE.validation_loggerTzHeader tokens being evaluated: )r^   cw_foundr   r   zRTF file looks like is was supposed to be encapsulated HTML/TEXT but the headers are malformed. Turn on debugging to see specific informationzpFROMHTML/TEXT control word not found in first 10 RTF tokens. This is not an HTML/TEXT encapsulated RTF document.z%FROMHTML/TEXT control word not found.)r   rC   rl   rj   r*   r   r   r   scan_valuesr   r   check_from_tokenr   rr   r;   r[   r   )r/   r   decoded_treefirst_ten_tokensoperating_tokensfound_tokenr^   s          r1   rs   *DeEncapsulator.validate_FROM_in_doc_header  s>    ! # %'
 )*44T]]C'00"5%E%'' ''. D):):;\)]$^^ 	 &
 12d:<=M<NOP%E#'#8#8u#8#X H4'H[,AT,I.  0  @  @&*5' & Fu$II  I  J$%LMM %r4   r^   r   c                    SS/nSnSnSnU R                   S:X  a  U R                  R                  5       U;   aa  US   SL a  SUS	'   [        R	                  S
5        US   SL a  SUS'   U R                  nX4$ [        R	                  S5        SUS'   SUS	'    X4$ U R                  R                  5       U:X  a  SUS'   X4$ U R                  R                  5       U:X  a'  SUS'   US   SLa  [        R	                  S5        SUS	'   X4$ )a4  Checks if fromhtml1 or fromtext tokens are in the proper place in the header based on the state passed to it by the validate_FROM_in_doc_header function.

Args:
        token: The token to check for in the cw_found state dictionary.
        cw_found: The state dictionary which is used to track the position of the from token within the header.

        `cw_found = {"rtf1":<BOOL>, "from":<BOOL>, "fonttbl":<BOOL>, "malformed":<BOOL>}`


Returns:
        cw_found: Updated state dictionary
        found_token: The content_type_token found in the header.

        rp   rq      \rtf1Ns   \fonttblr   r   Tr   zVMultiple FROM HTML/TXT tokens found in the header. This encapsulated RTF is malformed.r   z^FROMHTML/TEXT control word found before rtf1 control word. That's not allowed in the RTF spec.r   z\fonttbl code word found before FROMTML/TEXT was defined. This is not allowed for encapsulated HTML/TEXT. So... this is not encapsulated HTML/TEXT or it was badly encapsulated.)r   rZ   stripr;   r[   )r^   r   from_cwsrtf1_cwr   
fonttbl_cws         r1   r   DeEncapsulator.check_from_token;  s;     #M2!
::&{{  "h.F#t+,0H[)IIvwF#t+'+HV$"'++K $$ II~'+HV$,0H[) $$ ""$/#'  $$ ""$
2&*#F#4/II  R  S,0H[)$$r4   rl   c                     U R                   S   R                  nUS:w  a=  [        R                  S5        [	        S5      SL a  [        SU< 35        [        S5      eg)	a  Check if doc starts with a valid RTF header `\rtf1`.

        "Before the de-encapsulating RTF reader tries to recognize the encapsulation, the reader SHOULD ensure that the document has a valid RTF document heading according to [MSFT-RTF] (that is, it starts with the character sequence "{\rtf1")." - MS-OXRTFEX

Raises:
        MalformedRtf: The .rtf headers do not include \rtf1.
r   r   z_RTF stream does not contain valid valid RTF document heading. The file must start with "{\rtf1"r   Tz(First child object in document tree is: z%RTF stream does not start with {\rtf1N)rj   rZ   r;   r[   r   r   r   )rl   first_tokens     r1   rv   &DeEncapsulator.validate_rtf_doc_headerf  s^     ''*00)#IIz{56$>!I+YZGHH	 $r4   c                     [        U [        5      (       a  [        S5      eU c  [        S5      eU SS S:X  a  [        S5      eU S;   a  [        S5      eg)	af  Checks for likely common valid input mistakes that may occur when folks try to use this library and raises exceptions to try and help identify them.

Args:
        raw_rtf: A raw .rtf string or byte-string.

Raises:
        TypeError: The data passed is the wrong type of data.
        MalformedRtf: The data passed is not a correctly formatted .rtf string.
zFData passed as file pointer. DeEncapsulator only accepts byte objects.Nz<Data passed as raw RTF file is a null object `None` keyword.   s   ࡱzSData passed is a full MSG object. You must extract the encapsulated RTF body first.)r4    z/Data passed as raw RTF file is an empty string.)r*   r   r,   r   )r#   s    r1   r)   -DeEncapsulator.catch_common_validation_issuesv  sb     g~..dee?Z[[2A;==qrriPQQ  r4   )rD   rd   rr   rl   r:   r?   r$   rb   r   r#   re   )N)F)!__name__
__module____qualname____firstlineno____doc__r+   r   strr2   rS   boolr_   rE   r@   rc   rA   r   rw   r=   r
   rB   r   rY   rs   staticmethoddictr   r   r   rv   r   r)   __static_attributes__ r4   r1   r!   r!   (   s
   ."2u "2uSX "2H'R4 5 6%	Rh"-
E%*$5 
0S 0*$d $%T %"$NL '%u '%t '%d5c?>R8S '% '%T I$ I I R R Rr4   r!   )+typingr   r   r   r   r   ior   larkr	   	lark.treer
   
lark.lexerr   lark.exceptionsr   RTFDE.transformersr   r   r   r   r   RTFDE.utilsr   r   r   r   r   r   r   RTFDE.grammarr   RTFDE.text_extractionr   r   RTFDE.exceptionsr   r   r   logging	getLoggerr;   r!   r   r4   r1   <module>r      sj    3 2     + < 7 ; > 4 I I f f . - 3 X W  `R `Rr4   