
    ,heh                     >   d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZ ddlmZ ddlmZmZmZmZmZmZ  ej.                  e      Zej5                   ej6                                 ej8                  dd       e	j:                  d   dk\  r
eZd*d	Z d
 Z!n	e"Zd*dZ d Z!d Z#d Z$d Z%d Z&d Z'd Z(d Z)d Z*d Z+d Z,d Z-d Z.d Z/d Z0d Z1d Z2d Z3d Z4d Z5d ddd!dejl                  d"fd#Z7d$ Z8d% Z9dejt                  dd!d&fd'Z;d( Z<d) Z=y)+z#
Utility functions of extract_msg.
    N)	constants)os_)ConversionErrorIncompatibleOptionsErrorInvaildPropertyIdErrorUnknownCodepageErrorUnknownTypeErrorUnrecognizedMSGTypeError   	DEVELOPER   c                    d}t        | t              rZdj                  t        t	        |             D cg c],  }t        t        | |               dd j                  dd      . c}      }n?t        | t              r| j                         }nt        | t              rt        |       dd }t	        |      dz  dk7  rd|z   }|j                  |d      j                         S c c}w )zh
        Taken (with permission) from https://github.com/TheElementalOfDestruction/creatorUtils
            N0r   )
isinstancestrjoinrangelenhexordrjustbytesintupperinplengthaxs       S/var/www/html/Resume-Scraper/venv/lib/python3.12/site-packages/extract_msg/utils.py	properHexr#      s     c3U3s8_USQ[)!"-33As;UVAU#	AS!CAq6A:?aAwwvs#))++ Vs   1C"c                 "    | t        | d      S d S N	utf_16_le)r   strings    r"   windowsUnicoder)   +   s    +1+=s6;'G4G    c                    d}t        | t        t        f      rZdj                  t	        t        |             D cg c],  }t        t        | |               dd j                  dd      . c}      }n=t        | t              rt        |       dd }nt        | t              rt        |       dd }t        |      dz  dk7  rd|z   }|j                  |d      j                         S c c}w )a+  
        Converts the input into a hexadecimal string without the beginning "0x". The string
        will also always have a length that is a multiple of 2 (unless :param length: has
        been specified). :param length: only specifies the MINIMUM length that the string
        will use.
        r   r   Nr   r   )r   r   unicoder   r   r   r   r   r   r   longr   r   s       r"   r#   r#   1   s     cC>*U3s8_USQ[)!"-33As;UVAS!CAT"C2Aq6A:?aAwwvs#))++ Vs   1C&c                 "    | t        | d      S d S r%   )r-   r'   s    r"   r)   r)   C   s    /5/Awv{+KtKr*   c                     t        dd      D ]/  }	 | dz   t        |      z   dz   }t        j                  |       |c S  y# t        $ r
}Y d}~Ad}~ww xY w)z@
    Attempt to create the directory with a '(n)' appended.
    r   d   z ()N)r   r   osmakedirs	Exception)dirNamei
newDirNamees       r"   addNumToDirr:   F   sa     1c] 	 4#a&036JKK
#	   		s   *A  	AAc                 h    |dk  rt        d      | t        |      ddd   j                  d      z	  S )a  
    Uses a given mask to adjust the location of bits after an operation like
    bitwise AND. This is useful for things like flags where you are trying to
    get a small portion of a larger number. Say for example, you had the number
    0xED (0b11101101) and you needed the adjusted result of the AND operation
    with 0x70 (0b01110000). The result of the and operation (0b01100000) and the
    mask used to get it (0x70) are give and the output gets adjusted to be 0x6
    (0b110).

    :param mask: MUST be greater than 0.
       Mask MUST be greater than 0Nr,   1
ValueErrorbinindexr   masks     r"   bitwiseAdjustrE   S   s9     ax677#d)DbD/'',,,r*   c                 n    |dk  rt        d      | |z  t        |      ddd   j                  d      z	  S )z
    Preforms the bitwise AND operation between :param inp: and :param mask: and
    adjusts the results based on the rules of the bitwiseAdjust function.
    r<   r=   Nr,   r>   r?   rC   s     r"   bitwiseAdjustedAndrG   c   s=    
 ax677$J3t9TrT?00555r*   c           	         | D cg c]  }t        |       }}|d   |d   z   |d   z   |d   z   |d   |d   z   |d   |d   z   |d	   |d
   z   dj                  |dd       g} dj                  | j                         S c c}w )Nr   r   r<   r   r               	   r   
      z{{{}-{}-{}-{}-{}}})r#   r   formatr   )bytes_inputbytehexinputhexss       r"   bytesToGuidrU   l   s    ,78D	$8H8QK(1+%3hqkA8A;QYZ[Q\C\^fgh^iltuvlw^w  zB  CD  zE  HP  QR  HS  zS  UW  U\  U\  ]e  fh  ik  ]l  Um  nD&&&-3355 9s   A?c                     | | z   S )z
    Returns the int from the ceil division of n / d.
    ONLY use ints as inputs to this function.

    For ints, this is faster and more accurate for numbers
    outside the precision range of float.
     )nds     r"   ceilDivrZ   q   s     1"W:r*   c           	          t        t        t        t        |       |                  D cg c]  }| ||z  ||dz   z    c}S c c}w )a  
    Taken (with permission) from https://github.com/TheElementalOfDestruction/creatorUtils

    Divides a string into multiple substrings of equal length.
    If there is not enough for the last substring to be equal,
    it will simply use the rest of the string.
    Can also be used for things like lists and tuples.

    :param string: string to be divided.
    :param length: length of each division.
    :returns: list containing the divided strings.

    Example:
    >>>> a = divide('Hello World!', 2)
    >>>> print(a)
    ['He', 'll', 'o ', 'Wo', 'rl', 'd!']
    >>>> a = divide('Hello World!', 5)
    >>>> print(a)
    ['Hello', ' Worl', 'd!']
    r<   )r   r   rZ   r   )r(   r   r!   s      r"   divider\   {   sB    * :?s73v;X^C_?`9abAF6A:fA./bbbs   Ac                 2    dj                  d | D              S )za
    Adjusts :param filename: so that it can succesfully be used as an actual
    file name.
    r   c              3   *   K   | ]  }|d vs|  yw)z
\/:*?"<>| NrW   ).0r7   s     r"   	<genexpr>z"prepareFilename.<locals>.<genexpr>   s     I!3H*H1Is   	)r   )filenames    r"   prepareFilenamerb      s     77IhIIIr*   c                 f    t         j                   j                  | t        j                               S N)datetimefromtimestamptzlocalget_localzone)stamps    r"   fromTimeStamprj      s$    **5'2G2G2IJJr*   c                    t        j                  t        j                  d      }|j	                  ddddd       |j	                  d	d
dd       |j	                  dddd       |j	                  dddd       |j	                  dddd       |j	                  dddd       |j	                  ddd       |j	                  ddd       |j	                  dd d!       |j	                  d"d#dd$       |j	                  d%d&dd'       |j	                  d(d)d*d+,       |j                  |       }|j                  s|j                  rd-|_        |j                  rd.|_
        d/|_        d/|_        d/|_        |j                  }g }g }d-}|D ]e  }|r|j                  |       d/}|t        j                   v r'|j                  |       |t        j"                  v sOd-}R|j                  |       |g}g |j                  |       ||_        |S )0z&
    Parse command-line arguments
    extract_msg)descriptionprogz--use-content-idz--cidcid
store_truez_Save attachments by their Content ID, if they have one. Useful when working with the HTML body.)destactionhelpz--devdevzsChanges to use developer mode. Automatically enables the --verbose flag. Takes precedence over the --validate flag.z
--validatevalidatez=Turns on file validation mode. Turns off regular file output.z--jsonjsonz&Changes to write output files as json.z--file-loggingfile_loggingz(Enables file logging. Implies --verbose.z	--verboseverbosezTurns on console logging.z--loglogz&Set the path to write the file log to.)rq   rs   z--configconfig_pathz-Set the path to load the logging config from.z--outout_pathzJSet the folder to use for the program output. (Default: Current directory)z--use-filenameuse_filenamezBSets whether the name of each output is based on the msg filename.z--dump-stdoutdump_stdoutz^Tells the program to dump the message body (plain text) to stdout. Overrides saving arguments.msgsmsg+zAn msg file to be parsed)metavarnargsrs   TNF)argparseArgumentParserr   MAINDOCadd_argument
parse_argsrt   rw   rx   r}   r{   rv   r|   ro   r~   appendKNOWN_FILE_FLAGS	NEEDS_ARG)argsparseroptions	file_argsfile_tables
temp_tableneed_argr!   s           r"   get_command_argsr      s    $$1B1BWF
*G%~  @ eL S  T :l\  ^ vlE  G (~lG  I )L8  : eE  G 
L  N ji  k (~la  c mL}   S7  9 %G {{g**
   %IKJH  
a H),,,a I'''z*J
 z"GLNr*   c                 h    dj                  | j                  dd      j                  d      dd       S )zP
    Takes in the path to a file and tries to return the containing folder.
    /\Nr,   )r   replacesplit)_file_s    r"   getContFileDirr     s0     88FNN4-33C8"=>>r*   c                 4   | t         j                  vrt        t        |             	 t	        j
                  t         j                  |           t         j                  |    S # t        $ r- t        dj                  | t         j                  |                w xY w)zG
    Returns the name of the encoding with the specified codepage.
    zJThe codepage {} ({}) is not currently supported by your version of Python.)	r   
CODE_PAGESr   r   codecslookupLookupErrorUnsupportedEncodingErrorrP   )codepages    r"   getEncodingNamer   	  s     y+++"3x=11fi**845##H-- f&'s'z'z  |D  FO  FZ  FZ  [c  Fd  (e  f  	ffs   8A! !6Bc                 b    | j                   j                  dz   | j                   j                  z   S )N.)	__class__
__module____name__r   s    r"   get_full_class_namer     s&    ==##c)CMM,B,BBBr*   c                 <    	 | j                    y# t        $ r Y yw xY w)z8
    Checks if :param obj: has a __len__ attribute.
    TF)__len__AttributeError)objs    r"   has_lenr     s#     s    	c                     t        | t        j                        r| S t        | t        j                        r| j	                  |      S | yt        d      )Nr*   zCannot convert to BYTES type)r   r   BYTESSTRINGencoder   )string_input_varencodings     r"   inputToBytesr   "  sN    "IOO4	$i&6&6	7&&x00		!<==r*   c                     t        | t        t        f      rdj                  |       } t	        | d      j                  dd      j                  d      }|d   dk7  r|S g S )z.
    Converts the input into an msg path.
    r   zutf-8r   r   r   )r   listtupler   inputToStringr   r   )r   rets     r"   inputToMsgpathr   ,  sX     #e}%hhsm
W
%
-
-dC
8
>
>s
CCa&B,3&B&r*   c                     t        | t        j                        r| S t        | t        j                        r| j	                  |      S | yt        d      )Nr   zCannot convert to STRING type)r   r   r   r   decoder   )bytes_input_varr   s     r"   r   r   5  sL    /9#3#34	OY__	5%%h//		 =>>r*   c                     | dk(  xs | du S )zB
    Returns true if the input is None or is an Empty string.
    r   NrW   r   s    r"   isEmptyStringr   ?  s     2I$%r*   c                     | dz
  dz  S )z`
    Taken (with permission) from https://github.com/TheElementalOfDestruction/creatorUtils
    l     }*{Gg    cArW   r   s    r"   msgEpochr   E  s     $$
22r*   c                 ~    | yt        | t        t        f      rdj                  |       } | j	                  dd       | S )z[
    Converts an msgpath (one of the internal paths inside an msg file) into a string.
    Nr   r   )r   r   r   r   r   r   s    r"   msgpathToStringr   K  s:     {#e}%hhsmKKcJr*   r   FTc           	         ddl m} ddlm}	 ddlm}
 ddlm} ddlm	} ||	n|} || |||||      }|j                  }|j                  d      s|j                  d      r|j                           |
| |||||      S |j                  d	      s|j                  d
      r|j                           || ||||||      S |j                  d      s|j                  d      r|j                           || ||||||      S |r4|j                          t        dj                  |j                              t        j!                  dj                  |j                               |S )a   
    Function to automatically open an MSG file and detect what type it is.

    :param path: path to the msg file in the system or is the raw msg file.
    :param prefix: used for extracting embeded msg files
        inside the main one. Do not set manually unless
        you know what you are doing.
    :param attachmentClass: optional, the class the Message object
        will use for attachments. You probably should
        not change this value unless you know what you
        are doing.
    :param filename: optional, the filename to be used by default when saving.
    :param delayAttachments: optional, delays the initialization of attachments
        until the user attempts to retrieve them. Allows MSG files with bad
        attachments to be initialized so the other data can be retrieved.

    If :param strict: is set to `True`, this function will raise an exception
    when it cannot identify what MSGFile derivitive to use. Otherwise, it will
    log the error and return a basic MSGFile instance.
    r   )Appointment)
Attachment)Contact)Message)MSGFilezIPM.ContactzIPM.DistListzIPM.NoteREPORTzIPM.AppointmentzIPM.Schedulez]Could not recognize msg class type "{}". It is recommended you report this to the developers.)extract_msg.appointmentr   extract_msg.attachmentr   extract_msg.contactr   extract_msg.messager   extract_msg.msgr   	classType
startswithcloser
   rP   loggererror)pathprefixattachmentClassra   delayAttachmentsoverrideEncodingattachmentErrorBehaviorstrictr   r   r   r   r   r   	classtypes                  r"   openMsgr   V  sv   * 41++'$3$;jO
$;KMd
eCIM*i.B.B>.R		tV_h@PRijj			j	)Y-A-A(-K		tV_h@PRbd{||			/	0I4H4H4X		4(DTVfh  A  	A			&  (G  (N  (N  OR  O\  O\  (]  ^  	^t{{|  }J  }J  K  	L
r*   c           	      >   }t        |      }| dk(  r	 |S | dk(  r|dk7  rt        j                  d       y| dk(  r"t        j                  j                  |      d   S | dk(  r"t        j                  j                  |      d   S | dk(  r"t        j                  j                  |      d   S | d	k(  r"t        j                  j                  |      d   S | d
k(  r%t        j                  j                  |      d   dz  S | dk(  rIt        j                  j                  |      d   }t        j                  t        j                  |      z   S | dk(  r-t        j                  j                  |      d   }t        d      | dk(  r+t        t        j                   j                  |      d         S | dk(  rt        d      | dk(  r"t        j                  j                  |      d   S | dk(  r|j#                  |      S | dk(  r|j#                  d      S | dk(  rCt%        t'        t        j                   j                  |      d               j)                  d      S | dk(  rt+        |      S | dk(  rt        d      | dk(  rt        d      | dk(  rt        d      | d k(  r|S | d!z  d!k(  r| d"v r|D cg c]  }|j#                  |       }}t-        j
                  d#j/                  t        |                  }t        |      }	|	|kD  r2t        j                  d$j/                  |	|	dkD  s|	dk(  rd%nd&|             t1        |      D ]H  \  }}
||   t        |
      k7  st        j                  d'j/                  ||   t        |
                   J |S | d(k(  rt3        j4                  |      }t7        fd)t9        t              d*z        D              }t        |      }	|	|kD  r2t        j                  d$j/                  |	|	dkD  s|	dk(  rd%nd&|             t1        |      D ]H  \  }}
||   t        |
      k7  st        j                  d'j/                  ||   t        |
                   J |S | d+v r	t        |      k7  r6t        j                  d,j/                  dk(  rd-nd.t        |                   | d/k(  rt7        d0 |D              S | d1k(  rt7        d2 |D              S | d3k(  rt7        d4 |D              S | d5k(  rt7        d6 |D              S | d7k(  rt7        d8 |D              }t        d9      | d:k(  rt7        d; |D              S | d<k(  rt7        d= |D              S | d>k(  rt7        d? |D              S |S t        d@j/                  | |             |S c c}w )Aa  
    Converts the data in :param stream: to a
    much more accurate type, specified by
    :param _type: the data's type.
    :param stream: is the data to be converted.
    :param encoding: is the encoding to be used for regular strings.
    :param extras: is used in the case of types like PtypMultipleString.
    For that example, extras should be a list of the bytes from rest of the streams.

    WARNING: Not done. Do not try to implement anywhere where it is not already implemented
    r   r<   s           z1Property type is PtypNull, but is not equal to 0.Nr   r   rI   r   rK   g     @rJ   )daysrN   zParsing for type 0x000A has not yet been implmented. If you need this type, please create a new issue labeled "NotImplementedError: parseType 0x000A"      zmCurrent version of extract-msg does not support the parsing of PtypObject/PtypEmbeddedTable in this function.         r&   @   z%a, %d %b %Y %H:%M:%S %zH      zParsing for type 0x00FB has not yet been implmented. If you need this type, please create a new issue labeled "NotImplementedError: parseType 0x00FB"   zParsing for type 0x00FD has not yet been implmented. If you need this type, please create a new issue labeled "NotImplementedError: parseType 0x00FD"   zParsing for type 0x00FE has not yet been implmented. If you need this type, please create a new issue labeled "NotImplementedError: parseType 0x00FE"i  i   )i  i  z<{}izJError while parsing multiple type. Expected {} stream{}, got {}. Ignoring.sr   zHError while parsing multiple type. Expected length {}, got {}. Ignoring.i  c              3   z   K   | ]2  }t         j                  j                  |d z  |dz   d z         d    4 yw)rL   r<   r   N)r   STUI32unpack)r_   posstreams     r"   r`   zparseType.<locals>.<genexpr>  s;     sTWI,,33F3q5#a%4KLQOss   8;rL   )            @  H  zHError while parsing multiple type. Expected {} entr{}, got {}. Ignoring.yiesr   c              3   `   K   | ]&  }t         j                  j                  |      d     ( ywr   N)r   STMI16r   r_   r!   s     r"   r`   zparseType.<locals>.<genexpr>  &     KqY--44Q7:K   ,.r   c              3   `   K   | ]&  }t         j                  j                  |      d     ( ywr   )r   STMI32r   r   s     r"   r`   zparseType.<locals>.<genexpr>  r   r   r   c              3   `   K   | ]&  }t         j                  j                  |      d     ( ywr   )r   STMF32r   r   s     r"   r`   zparseType.<locals>.<genexpr>  r   r   r   c              3   `   K   | ]&  }t         j                  j                  |      d     ( ywr   r   STMF64r   r   s     r"   r`   zparseType.<locals>.<genexpr>  r   r   r   c              3   `   K   | ]&  }t         j                  j                  |      d     ( ywr   r  r   s     r"   r`   zparseType.<locals>.<genexpr>  s&     My//66q9!<Mr   zParsing for type 0x1007 has not yet been implmented. If you need this type, please create a new issue labeled "NotImplementedError: parseType 0x1007"r   c              3   `   K   | ]&  }t         j                  j                  |      d     ( ywr   )r   STMI64r   r   s     r"   r`   zparseType.<locals>.<genexpr>  r   r   r   c              3   r   K   | ]/  }t        t        j                  j                  |      d           1 ywr   )r   r   ST3r   r   s     r"   r`   zparseType.<locals>.<genexpr>  s)     RaXimm&:&:1&=a&@ARs   57r   c              3   2   K   | ]  }t        |        y wrd   )rU   r   s     r"   r`   zparseType.<locals>.<genexpr>  s     <[^<s   zParsing for type {} has not yet been implmented. If you need this type, please create a new issue labeled "NotImplementedError: parseType {}")r   r   warningr   STI16r   STI32STF32STF64STI64PYTPFLOATINGTIME_STARTre   	timedeltar   NotImplementedErrorboolr  r   rj   r   
__format__rU   structrP   	enumeratecopydeepcopyr   r   )_typer   r   extrasvaluelength_extrasr!   r   lengthslength_lengthsr   valuess    `          r"   	parseTyper#    sh    EKM~ L} 
&77NNNO	&%%e,Q//	&%%e,Q//	&%%e,Q//	&%%e,Q//	&&&u-a0G;;	&&&u-a0//(2D2DE2RRR	&  ''.q1 "  #z  {  	{	&IMM((/233	& "  #R  S  	S	&%%e,Q//	&||H%%	&||K((	&Ximm&:&:5&A!&DEFQQRlmm	&5!!	&!  #z  {  	{	&!  #z  {  	{	&!  #z  {  	{	&	6	!$$/56!188H%6C6mmFMM#c($;VDG \N-krr  tB  KY  \]  K]  ao  st  at  DG  z|  ~K   L  M!&) J11:Q'NN#m#t#tu|}~u  BE  FG  BH  $I  JJ Jf_--'Cs[`adekalpqaq[rssG \N-krr  tB  KY  \]  K]  ao  st  at  DG  z|  ~K   L  M!&) J11:Q'NN#m#t#tu|}~u  BE  FG  BH  $I  JJ JVVV$ippqw  BH  LM  BMz}  SX  [^  _e  [f   g  hKFKKKKFKKKKFKKKKFKKKMfMM)  +B  C  CKFKKKR6RRR<V<<< L &  'v  '}  '}  ~C  EJ  'K  L  LLS 7s   0Vc                     | || z
  |z  z   S )zH
    Rounds :param inp: up to the nearest multiple of :param mult:.
    rW   )r   mults     r"   roundUpr&    s     $*$$$r*   EXTRACT_MSG_LOG_CFGc                    t        t              dz   }t        j                  dk(  rd}|dz  }nt        j                  dk(  rd}|dz  }| s|} | dd	d
|g}d}|D ]%  }	t        j                  j                  |	      s#|	} n t        j                  |d      }
|
r!t        j                  j                  |
      r|
}|vt        d       t        d       t        t        |dd              t        j                  |       t        j                  dj                  t        j                               yt        |d      5 }t        j                  |      }ddd       d   D ]  }d|d   |   v s|rt        j                  j!                  t        j                  j#                  |r|n
|d   |   d               x|d   |   d<   }t        |      }t        j                  j                  |      rt        j$                  |       |d   |   d<    	 t        j&                  j)                  |       t        j,                         j/                  |       y# 1 sw Y   xY w# t*        $ r }t        d       t        |       Y d}~Ud}~ww xY w)aq  
    Setup logging configuration

    Args:
        default_path (str): Default path to use for the logging configuration file
        default_level (int): Default logging level
        env_key (str): Environment variable name to search for, for setting logfile path

    Returns:
        bool: True if the configuration file was found and applied, False otherwise
    z/logging-config/ntNULzlogging-nt.jsonposixz	/dev/nullzlogging-posix.jsonzlogging.jsonz../logging.jsonz../../logging.jsonNz.Unable to find logging.json configuration filezMake sure a valid logging configuration file is referenced in the default_path argument, is inside the extract_msg install location, or is available at one of the following file-paths:r<   )levelzThe extract_msg logging configuration was not found - using a basic configuration.Please check the extract_msg installation directory for "logging-{}.json".Frthandlersra   zDFailed to configure the logger. Did your installation get messed up?T)r   __file__r3   namer   existsgetenvprintr   loggingbasicConfigr  rP   openrv   load
expanduser
expandvarsr4   config
dictConfigr@   	getLoggersetLevel)default_pathdefault_levellogfileenable_file_loggingenv_keyshipped_confignullpathsr   rz   r  fr:  r!   tmpr9   s                   r"   setup_loggingrH    sc    $H-0BBN	ww$++	G	..% 	E D 77>>+&D
 IIgt$E&|>? - 	. 	c%)n-0 eekeklnlslset	v	dD	 Q1 J 	9
+A..":<'':L:LGG&&'wvj?QRS?TU_?`a;c cz"1%j1C$S)ww~~c*KK$48z"1%j1	9!!&)
   /+    TUas$   <II" I"	J+JJc                     t        | t              st        d      t        |       dk7  rt        d      	 t	        | d       y# t
        $ r t        d      w xY w)z
    Determines whether a property ID is valid for the functions that this function
    is called from. Property IDs MUST be a 4 digit hexadecimal string.
    z'ID was not a 4 digit hexadecimal stringrI   rO   N)r   r   r   r   r   r@   )ids    r"   verifyPropertyIdrK  I  s]    
 b#$%NOO	RA$%NOO	TBK 	T()RSS	Ts   A Ac                     | @| t         j                  vr-| t         j                  vrt        dj	                  |             y y y )NzUnknown type {})r   VARIABLE_LENGTH_PROPS_STRINGFIXED_LENGTH_PROPS_STRINGr	   rP   )r  s    r"   
verifyTyperO  X  sJ    ???eS\SvSvFv"#4#;#;E#BCC Gw? r*   )r   )>__doc__r   r   r  re   rv   r4  logging.configr  sysrg   rl   r   extract_msg.compatr   r3   extract_msg.exceptionsr   r   r   r   r	   r
   r<  r   r   
addHandlerNullHandleraddLevelNameversion_infoinput	get_inputr#   r)   	raw_inputr:   rE   rG   rU   rZ   r\   rb   rj   r   r   r   r   r   r   r   r   r   r   r   ATTACHMENT_ERROR_THROWr   r#  r&  WARNrH  rK  rO  rW   r*   r"   <module>r^     sx           
  ! ( g  g			8	$   %'%%' (   Q $A!I,H I,$L- 66
c.JKdL?
fC>'?&3	 $[`uy  V_  Vv  Vv  AE -^ob%  $7<<ch/KZTDr*   