,Q ddlZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlZddlmZddlmZmZddlmZmZddlmZmZmZddlmZddl m!Z!ddl"m#Z#m$Z$m%Z%m&Z&dd lm'Z'dd lm(Z(dd l)m*Z*dd l+m,Z,dd l-m.Z.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5ddl6Z6ddl7Z7ddl8Z8ddl9m:Z:ddl;me?Z@dZAdZBdaCdZDe*dZEdZFe*dZGe*dZHe*dZIeJejKLddZMGdde!ZNejOd d!ZPedd"ZQe@ddfd#ZRGd$d%ZSdejTejTd&dfd'e4eJeUeUffd(ZVddd)d*ZWGd+d,ejXZYeYfd'eUfd-ZZeYfdd.Z[dd'e\fd0Z]d1Z^Gd2d3Z_d4Z`Gd5d6eaZbd7Zcd8Zd dd:Zed/ddd/ddd;dZhejOdd?Zidd'e\fd@ZjGdAdBZkejldCfdDe\dEeJd'e\fdFZm ddDe\dEeJd'e4e\eJffdGZndHZodIZpddKZq ddMZrdNZsejOdOeJfdPZtejue6jvdQd/RZwdSZxdDejgdTeUd'dfdUZydVZzdDejgdWeUd'effdXZ{dYZ|GdZd[Z}eBfd\Z~d]Zed^eJfd_Zddae\d'e*fdbZdce\dae\d'effddZdeZddgedheJd'efdiZdjZGdkdleZejOd9 dmZdnZdd'effdoZGdpdqZeZdrdsdtZduZdvZejOd d'effdwZdxZdyZddze\d'e\fd{Zdd|Zdd}d~Zdd}dZdd}dZGddejZdZ ddZdZeadfdZefdZdZdZd&ddZeddZdZdZdce*d'eUfdZdeUd'e*fdZdZdZddZde0de/ffdZdZdrZdrZdZdQddze3e\fdZddrddZddZGddZdWe\d'ee\e\fdzfdZdWe\d'ee\e\fdzfdZejOd d'eee\e0e\gee\e\fdzfffdZGddeaZdZehdZdee\d'ee\e\dzffdZde\dee\de0e\gee\e\fdzfd'ee\e\dzffdZe@jfd'e\fdZdeJfdZde1e.e.fdeJfdZejOd dZde*d'eJfdZde*fdZdZdS)N)Future) OrderedDictdeque) GeneratorIterable) ExitStackcontextmanagersuppress) timedelta)Enum)LOCK_EXLOCK_NBLOCK_UNflockwraps)islice)Path)NamedTemporaryFile)Any AwaitableCallableDict FrozenSetListTupleTypeVar)rmtree)atomic_rewrite_fdF)bounduser_id)z User-AgentzAccept-LanguagezAccept-Encoding ConnectionDNTz.i360bakz/run/systemd/systemz/etc/cloudlinux-edition-soloz/var/run/imunify-antivirus.pidz/var/run/imunify360-agent.pidz/var/run/imunify360.pid%IMUNIFY360_HTTP_REQUEST_RETRY_TIMEOUT<ceZdZdZdZdZdZdS)ScopezAV onlyz AV and IM360z IM360 onlyzIM360 resident onlyN)__name__ __module__ __qualname__AVAV_IM360IM360IM360_RESIDENTS/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/utils/__init__.pyr*r*Gs" BH E*NNNr3r*)maxsizecto2tot S)zReturn True if /run/systemd/system folder exists: [sd_booted] (https://www.freedesktop.org/software/systemd/man/sd_booted.html) )_SYSTEMD_BOOTED_DIRexistsis_dir is_symlinkr2r3r4is_systemd_bootr;NsC ""$$ 1  & & ( ( 1#..00 0r3c#K|s|sJtj}|p|jd|dVtj}|p|jd|||z dS)z :param str: action name to log :param logging.Logger: logger you want action name and timing to be logged with :param func: log function to use (`log` has preference over `logger_`) z %s startedNz%s took %.2f second(s))time monotonicdebug)actionlogger_logstartstops r4timeitrE[sz c> N  ESGM<000 EEE >  DSGM3VTE\JJJJJr3cfd}|S)NcNtjfd}|S)NcKtpj5|i|d{VcdddS#1swxYwYdSN)rArBrEr+argskwargsr@funrBrAs r4wrapperz+timefun..decorator..wrapperms.#,SIII 2 2 S$1&11111111 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2s 8<< functoolsrrNrOr@rBrAs` r4 decoratorztimefun..decoratorlsH    2 2 2 2 2 2 2   2r3r2rAr@rBrSs``` r4timefunrUks0 r3c0eZdZdZeeddfdZdS)synczF the same timefun decorator variation but without async/await Ncfd}|S)z :param logging.Logger: logger you want action name and timing to be logged with :param str: action name to log cNtjfd}|S)Ncztpj5|i|cdddS#1swxYwYdSrIrJrKs r4rOz0sync.timefun..decorator..wrappersF2clGMMM003///000000000000000000s 044rPrRs` r4rSzsync.timefun..decoratorsH _S ! ! 0 0 0 0 0 0 0" ! 0Nr3r2rTs``` r4rUz sync.timefun|s0       r3)r+r,r-__doc__ staticmethodloggerrUr2r3r4rWrWwsEt\r3rWFreturnc $K||tdtj}|r't|tsJ|g}t j}n*t|ttfsJt j }ttdtd|||||dd|d{V}| |d{V\} } |d{V} td |||| | | f| | | fS) zYAsynchronous command executor. Returns a tuple (exit_code, stdout_data, stderr_data).Nz/stdin and input arguments may not both be used.r)seconds) max_trieson_errorTstdinstdoutstderrstart_new_sessionz run(%s, stdin=%s, shell=%s) = %s) ValueError _subprocessPIPE isinstancestrasynciocreate_subprocess_shelllisttuplecreate_subprocess_execretry_onBlockingIOError await_for communicatewaitr]r?) commandrerfrgshellinputrMcreate_subprocessprocouterr exit_codes r4runrs   NOO O  ;'3''''')#;'D%=11111#:1y/C/C/C             D%%e,,,,,,,,HCiikk!!!!!!I LL*  C  c3 r3)looptimeoutc|rtdD]b} tj}|sn7n#t$rYnwxYwtjtjc|tjt|tj r|ntj ||S)zjRun coroutine from a blocking code (outside the event loop). Coroutine will be wrapped in Task. Nr`r) rangernget_event_loop is_closed RuntimeErrorset_event_loopnew_event_looprun_until_completewait_forrlrTask)cororr_s r4run_corors  |q = =A -//~~''E       "7#9#;#; < < < <  " "tW^44 LDD',t:L:L     s? A  A ceZdZdZdS) CheckRunErrorcd}||j|j|jpd|jpdS)Nz[Command {cmd!r} returned non-zero code {returncode}, Stdout: {output}, Stderr: {error} )cmd returncodeoutputerror)formatrrrdecoderg)self_MESSAGEs r4__str__zCheckRunError.__str__s_ $  ;%%''/4+$$&&.$    r3N)r+r,r-rr2r3r4rrs#      r3rc`Kt|fi|d{V\}}}|dkr||||||S)zJ Asynchronous command executor. Returns output as bytestring. Nr)r)rx raise_excrMrr}r~s r4 check_runrsZ "%W!7!7!7!7777777JSQi GS#666 Jr3cKt|tjtjtjd{V\}}}|dkr |||dS)z Asynchronous command executor. Raises raise_exc if exit code is nonzero. Stdin, stdout and stderr of command are connected to /dev/null. )rerfrgNr)rrjDEVNULL)rxrcoders r4check_exit_codersy !"" JD!Q qyyig&&&yr3TcK t|fi|d{V\}}}n,#t$rtd|YdSwxYw|r%|dkrtd|||dS |}n,#t $rtd|YdSwxYw|S)zGSafe run command. Returns stdout as string or empty string on errorNzCommand %s failed with OSErrorrz'Command %s failed with exit code %s: %sz#Command %s returned non-utf8 output)rOSErrorr]warningstriprUnicodeDecodeError)rxcheck_returncoderMrcr}r~results r4safe_runrs 33F33333333 C 7AAArrB!GG 5wC   r##%% .wrapper s  %+--Kr3r2)rrOrs` @r4plainold_lazy_initrs.K Nr3ceZdZdZdZdZdS) PeriodicCheckz Invoke a callback with a certain period and return cached result in between. Raising an exception from the callback does not affect the next check schedule. c||_||_tj|z |_d|_t tj|_ dSr) _cb_coro_check_every_n_secondsr=r>_last_check_timestamp_last_check_resultrrnLock_lock)rcb_corocheck_every_n_secondss r4__init__zPeriodicCheck.__init__2sD &;#%)^%5%58M%M""&' 55 r3cK|4d{Vtj|jz }||jkrVt d|j|jtj|_|j|i|d{V|_|jcdddd{VS#1d{VswxYwYdS)Nz3Timeout %d seconds has expired, doing the check: %s) rr=r>rrr]r?rr)rrLrMdeltas r4__call__zPeriodicCheck.__call__;s\::<< + + + + + + + +N$$t'AAE333 I/M .2^-=-=*0= t0Nv0N0N*N*N*N*N*N*N'* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +sBB33 B=B=N)r+r,r-r[rrr2r3r4rr)s<666 + + + + +r3rcfd}|S)Nc$t|Sr)r)rnsecs r4decoratezcache_result..decorateJsT4(((r3r2)rrs` r4 cache_resultrIs#))))) Or3ceZdZdZdS)RecurringCheckStopz: raised by coroutine to stop recurring_check loop Nr+r,r-r[r2r3r4rrPs Dr3rcK t|r!tj|di|d{Vntj|d{VdS#tj$rYdSwxYw)NFTr2)callablernsleepCancelledError)period period_kwargss r4wait_for_periodrXs F   (- 7 7 7 788 8 8 8 8 8 8 8 8-'' ' ' ' ' ' ' 'u  !ttsA AA#"A#c8K|r|rt|fi|d{VndSNF)r)checkrrs r4should_stop_after_period_passedrcsG   of66 666666666 r3 c fd}|S)z run decorated corotine in a loop every :period: seconds. If more then consecutive_err_limit error occured, exit loop. :param period: :param consecutive_err_limit: :param check_period_first: default false :return: cFtfd}|S)NcKd} tfid{VrdS |i|d{Vd}n#t$rOd|vrG t|dtr|dn#t $rYnwxYwYdSt j$rYdSt$r}|dz }|kr t dYd}~dSt|tj r3t d|j |j|j|jnt dYd}~nd}~wwxYwt fid{VrdST)NrT lock_filerz-Error count exceeded limit,exiting check loopz+Failed to run %s (%s). stdout=%s, stderr=%szError executing %s)rrrlrunlinkFileNotFoundErrorrnr Exceptionr] exceptionrjCalledProcessErrorrrrrg) rLrMconsecutive_err_cntexccheck_period_firstconsecutive_err_limitrNrrs r4wrappedz3recurring_check..decorator..wrappedxsH"# ' 8&2?E,#t.v.........:+,''9*"f,,!)&*=tDD= &{ 3 : : < < <0!!! D!EE-EE DDD'1,'*-BBB((K!#{'EFF D((IGNJJ (()=sCCC!D&9**F6CEO' sK0D?5A54D?5 B?D?BD?D? D?"%D: A(D::D?r)rNrrrrrs` r4rSz"recurring_check..decoratorwsI s) ) ) ) ) ) ) )  ) Vr3r2)rrrrrSs```` r4recurring_checkrks7--------^ r3)backupuidgidallow_empty_content permissionsdir_fdrrc t|tr|}|'|rtdt |||||||St t 5t|d5}|t|dz} dddn #1swxYwY| |kr ddddS dddn #1swxYwY|s |st d||dS|rt|ttj fr|} ntj|tz} t t 5t!j|| dddn #1swxYwY|k t%jtj|j}n>#t $r1tjd} tj| d | z}YnwxYwtj|\} } t1| st d | t55}t7d | d | d zdd5fd}||||*|(tj ||tj! |tj" dddn #1swxYwYtj#j$||%dddn #1swxYwYdS)aAtomically rewrites *filename* with given *data*. If *filename*'s content is *data* already, do nothing. If both *uid* and *gid* are given then resulting file is chowned to given user id and group id. Skip rewrite with empty content if *allow_empty_content* is False. Chmod to given access *permissions* else preserve *filename* 's permissions. Return True if *filename* file was updated, False otherwise When *dir_fd* is provided it must be an O_NOFOLLOW-opened file descriptor for the parent directory of *filename*. All file I/O is then performed relative to that descriptor, closing the TOCTOU symlink-attack window. *backup* is not supported with *dir_fd*. Nz/backup is not supported when dir_fd is provided)rrrrrrbrFzempty content: %r for file: %srizParent dir is missing: wbz .i360editr)modedirsuffixprefix bufferingdeletectt5tjjddddS#1swxYwYdSr)r rosremovename)tfsr4cleanupzatomic_rewrite..cleanups/00''Ibg&&&''''''''''''''''''s=AAT)&rlrmencoderir r ropenreadlenr]rrPathLikefspathBACKUP_EXTENSIONshutilcopystatS_IMODEst_modeumaskpathsplitrr8rrcallbackwriteflushchownfilenochmodfsyncrenamerpop_all)filenamedatarrrrrrfile old_contentbackup_filename current_umaskdirpathbasenamestackrrs @r4atomic_rewriters6${{}}   PNOO O   3#     # $ $ (D ! ! 3T))CIIM22K 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 $     t 5tXFFFu 3 fsBK0 1 1 E$OO i114DDO ' ( ( 3 3 K/ 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1,rwx'8'8'@AAKK  1 1 1HQKKM H] # # #=.0KKK  1  h//GX ==   ! !G E' E EFFF  c>     " ' ' ' ' ' NN7 # # # HHTNNN HHJJJ3?c3/// HRYY[[+ . . . HRYY[[ ! ! !- " " " " " " " " " " " " " " ". "'8$$$ 38 4s(C9&B+ C+B/ /C2B/ 3 CCCE<<FF +F668G10G1M>0B=L9- M>9L= =M>L= 1M>>NNc tdS#t$rYdSwxYw)Nz/etc/system-release)r read_textrstriprr2r3r4os_release_and_versionr sP)**4466==??? tts25 AAc|p t}|r-tjd|}|r|dSntt d|z)z3Return os version, if can't get it raise ValueErrorz\s*(\d+\.\d+\S*)(\s|$)rz!Can't discover os version from %r)r researchgroup cache_clearri)release_and_versionrvmatchs r4 os_versionr)st  8 6 8 8B - 3R88  ";;q>> ! " **,,, 82= > >>r3ceZdZdZedZedZedZdZe dZ e de e e ffdZe dee fd Ze de fd Ze de fd Ze d Ze d Ze dZe dZe dZe dZe dZe dZe dZdS) OsReleaseInfoz/etc/os-release)debian)rhelfedoracentos)unknownNct|j5}|D]U} |d\}}|d||<F#t $rYRwxYw dddn #1swxYwYd|vr,t |d|d<dSt |ddf|d<dS)N="ID_LIKEIDlinux)rETC_OS_RELEASErr rri frozensetget)clsdict_flinekvs r4dict_from_filezOsReleaseInfo.dict_from_file-s3 #$ % %   ;;==..s33DAq wws||E!HH!D                    (y)9)?)?)A)ABBE)    )%))D'*B*B)DEEE)   s5A;AAA; A+(A;*A++A;;A?A?r^c\|jt}tj|jr||ntj}|r|dr|d d}|dkr d|dvrd}||d<d |d|d|d|d <|d vr |j |d <n.|d vr |j |d <n|j|d <nd |d<|j|d <d |d <||_|jS)NrredzRed Hat Enterprise Linuxr-r5z {} {} ({})rr` PRETTY_NAME) cloudlinuxr/r-r4)ubuntur,r0)r;dictrrr8r7r@distrolinux_distributionlowerr rRHEL_FEDORA_CENTOSDEBIANUNKNOWN)r:r;dosids r4to_dictzOsReleaseInfo.to_dict<sQ 9 $(FFEw~~c011 5""5))))-//515Q4::<<--//2Du}})Cqt)K)K%"&E$K+7+>+>!adAaD,,E-(???+.+Ai((!555+.:i((+.;i(("+E$K'*{E)$+4E-(CIyr3c6|dS)Nr4rOr:s r4id_likezOsReleaseInfo.id_like[s{{}}Y''r3c6|dS)NrCrQrRs r4 pretty_namezOsReleaseInfo.pretty_name_s{{}}]++r3cR|ddS)zi :return: OS name, like centos, ubuntu, debian, cloudlinux, redhat in lower case r5r0)rOr9rRs r4get_oszOsReleaseInfo.get_oscs" {{}}  y111r3c2|dkS)Nr-rWrRs r4is_rhelzOsReleaseInfo.is_rhelkszz||v%%r3c2|dkS)Nr/rYrRs r4 is_centoszOsReleaseInfo.is_centosozz||x''r3c2|dkS)NrErYrRs r4 is_ubuntuzOsReleaseInfo.is_ubuntusr]r3c.|dvS)N)rDcloudlinuxserverrYrRs r4 is_cloudlinuxzOsReleaseInfo.is_cloudlinuxwszz||AAAr3cJtjtSr)rrr8_CL_SOLO_EDITION_FILErRs r4is_cloudlinux_soloz OsReleaseInfo.is_cloudlinux_solo{sw~~3444r3c2|dkS)Nr,rYrRs r4 is_debianzOsReleaseInfo.is_debianr]r3c2|dkS)NolrYrRs r4is_oracle_linuxzOsReleaseInfo.is_oracle_linuxszz||t##r3c2|dkS)N almalinuxrYrRs r4 is_almalinuxzOsReleaseInfo.is_almalinuxszz||{**r3c2|dkS)NrockyrYrRs r4 is_rockylinuxzOsReleaseInfo.is_rockylinuxszz||w&&r3)r+r,r-r7r8rKrJrLr; classmethodr@rrmrrOrrSrUrWrZr\r_rbrergrjrmrpr2r3r4r+r+%s&N Y{ # #F"#?@@i %%G E F F[ FS#X[<( #((([(,C,,,[,2s222[2&&[&(([((([(BB[B55[5(([($$[$++[+''['''r3r+r chunksizec0t|||dS)zReturn hash of the file `filename`, reading it in chunks. * filename is a path to a file; * hash_func is a function that returns hash object (one of hashlib.md5 etc); * chunksize is a size of chunks to read, in bytes. r)file_hash_and_size)r hash_funcrss r4 file_hashrws h 9 = =a @@r3c|}d}t|d5} ||}|sn(|||t|z }@ dddn #1swxYwY||fS)aCalculate hash and size of the file `filename`, reading it in chunks. * filename is a path to a file; * hash_func is a function that returns hash object (one of hashlib.md5 etc); * chunksize is a size of chunks to read, in bytes. Return tuple(hash, file size).rrTN)rrupdater hexdigest)rrvrshash_sizer<chunks r4rurus IKKE D h   FF9%%E  LL    CJJ D    ??  d ""sAA,,A03A0c|\}}||kr&tdjdit|S)z0Given login.defs line, return *varname*'s value.z"Expected {varname!r}, got {name!r}r2)r rirvars)varname defs_linervalues r4_parse_name_valuersJ//##KD%$D=DNNtvvNNOOO Lr3cHtdkrt\a}tS)Nr&)_MIN_UID_get_max_min_uid)rs r4 get_min_uidrs2~~&(( ! Or3/etc/login.defschd\}} t|5}|D]f}|drttd|}|drttd|}g dddn #1swxYwYn#tt f$rYnwxYw||fS)zGet UID_MIN, UID_MAX from the login.defs file specified as *path*. On error, return default for the current OS values. )ii`UID_MINUID_MAXN)r startswithintrrri)ruid_minuid_maxrr=s r4rrs( #GW  $ZZ F4 F F??9--F!"3It"D"DEEG??9--F!"3It"D"DEEG  F F F F F F F F F F F F F F F F Z       G s5BA*B  B BBBBB-,B-zimunify360-captchazimunify360-webshieldclt\fdtjDS)z~ :param excludes: users to exclude in results :return: list: list of pwd.struct_passwd objects representing users cPg|]"}|jcxkrknn |jv |#Sr2pw_uidpw_name).0entryexcludesrrs r4 z(get_non_system_users..sS     el - - - -g - - - - -%-x2O2O 2O2O2Or3rpwdgetpwall)rrrs`@@r4get_non_system_usersrsR())GW      \^^   r3cdt\}fdtjDS)z; :return: list: list of str with system user names c4g|]}|jk |jSr2r)rrrs r4rz)get_system_user_names..s.   W 5L5L 5L5L5Lr3r)rrs @r4get_system_user_namesrsE"##JGQ    #&<>>   r3rc0t\}}||kSr)r)rrrs r4is_system_userrs'))GW =r3d)r5typedct|d5}|dd}|dkrF||dz |ddkr|d|||ds|dddddS#1swxYwYdS)Nzr+rr`r rseekrr endswithrrr< last_char_poss r4append_with_newliners h   q! A   FF=1$ % % %vvayyD     }}T""  GGDMMM                  B"CCCrct|d5}|dd}|dkrF||dz |ddkr|d|||ds|dddddS#1swxYwYdS)z>Append *data* to *filename* making sure there is at the end.zr+brr`r Nrrs r4append_with_newline_bytesrs h   !q! A   FF=1$ % % %vvayyE!!  }}U##  GGENNN                  rcd}t|d5}tfd|Dsd}dddn #1swxYwY|rt||S)zAdd *line* to *filename* if it is not present in the file Returns: True if the file was changed, False otherwise. Frc3HK|]}|kVdSrrr_liner=s r4 z&ensure_line_in_file..(088U5;;==D(888888r3TN)ranyrrr=changedr<s ` r4ensure_line_in_filer s G h  8888a88888 G,Hd+++ N>AAr=cd}t|d5}tfd|Dsd}dddn #1swxYwY|rt||S)zAdd *line* to *filename* if it is not present in the file. Returns: True if the file was changed, False otherwise. Frc3HK|]}|kVdSrrrs r4rz,ensure_line_in_file_bytes..7rr3TN)rrrrs ` r4ensure_line_in_file_bytesr/s G h  8888a88888 G2!(D111 Nrctj|}t|d5}t d|d5}|D]/}||kr||0tj|j|dddn #1swxYwYddddS#1swxYwYdS)NrwF)rrr) rrdirnamerrrr rr)rr=basedirsfrrs r4remove_line_from_filer>s5gooh''G Xs%!???%CE  E{{}}$$ "'8$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%s6B4A B B4B B4#B $B44B8;B8c,eZdZdZdZefdZdZdZdS)FileLockz` Simple context manager to enable UNIX-specific file locking with flock system call rcZ||_d|_t|d|_||_dS)NFr)rlockedrrr)rrrs r4rzFileLock.__init__Rs*  sOO  r3cKtj} t|jttzd|_|S#t tf$r}|jtj kr|j tj|z kr&t d|j Yd}~dStjdd{VYd}~nd}~wwxYw)NTz)Failed to lock file %s. Timeout exceeded.r)r=rrr rrrIOErrorerrnoEAGAINrr]rrrnr)rrCexs r4 __aenter__zFileLock.__aenter__Xs  ' 'di7!2333"  W% ' ' '8u|++\DIKK%$777NNCTYEEEEEmA&&&&&&&&&&&&&& ' 's*ACAC/CCcK|jrt|jtd|_|jdSr)rrrrclose)rexc_typeexc_valexc_tbs r4 __aexit__zFileLock.__aexit__psC ; & $)W % % %  r3N)r+r,r-r[_TIMEOUTrrrr2r3r4rrJsZ H%- '''0r3rc |g}|fdt|Dtj}|d|dd|S#ttf$r%}t d|Yd}~nd}~wwxYwdS)Nc3DK|]\}}|v t|VdSr)rm)rfieldrfieldss r4rz user_identity..s?  u JJ  r3rutf8surrogateescapez9Generation of user identity hash failed, invalid data: %s) extendsorteditemshashlibsha1ryjoinrrzriUnicodeEncodeErrorr]r) attackers_ipsourceruid_datahash_alges ` r4 user_identityrzs  !>    &v||~~ 6 6      <>>))009JKKLLL!!### * +    G         4sB%B))C:CCc0tjdkS)Nr)rgetuidr2r3r4 is_root_userrs 9;;! r3maskc#Ktj|} dVtj|dS#tj|wxYwr)rr)r current_masks r4run_with_umaskrsM8D>>L  s 2Arusernamec~t|tstd|ztj|vrtd t j|}n0#t$r#td|wxYwtj |j |}t|S)z Returns user's home dir if `relpath` is not specified. Otherwise, returns absolute path of `relpath` build from `username`'s home dir :raise ValueError: when user home dir is not exists z#Invalid type for %s, should be str!zInvalid usernamezUser {!r} doesn't exist) rlrmrirseprgetpwnamKeyErrorrrrpw_dirr)rrelpathpwabs_paths r4get_abspath_from_user_dirrs h $ $K>IJJJ v+,,,E \( # # EEE299(CCDDDEw||BIw//H >>s A-Brcd} t|}t||d}n>#t$r1}tt |Yd}~nd}~wwxYw|S)NFT)rr relative_torir]rrm)rrstatus user_homers r4does_path_belong_to_userrs F-h77  T y))) s1vv Ms38 A3'A..A3ctj|std|z tj|rg t jtj|jj S#t$r)ttj|jcYSwxYwtj |})NzPath %s should be absolute!) rrabspathrir8rgetpwuidrst_uidrrrmrrs r4get_path_ownerrs 7??4 ?6=>>>% 7>>$   1 1|BGDMM$899AA 1 1 1274==/00000 1wt$$ %s/B0B65B6iterable chunk_sizec#Kt|}tt||}|r%|Vtt||}|#dSdS)a Generator that splits iterable on N-parts by chunk_size items in each chunk >>> list(split_for_chunk([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], chunk_size=2)) [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]] :param iterable: :param int chunk_size: :return: generator: N)iterrpr)rr ipieces r4split_for_chunkrsp XA :&& ' 'E , VAz**++ ,,,,,r3ct|tr+td|DSt|trt d|DS|S)Nc3>K|]\}}|t|fVdSrfreeze)rkeyrs r4rzfreeze..s1JJ*#u#ve}}-JJJJJJr3c34K|]}t|VdSrr)rrs r4rzfreeze..s(22uVE]]222222r3)rlrFr8rrprq)rMs r4rrsm!T3JJ JJJJJJ At  322222222 Hr3c&eZdZdZiZfdZxZS) Singletonzc Metaclass for creating only one instance of class, when providing the same arguments. c|t|t|f}|j|s(tt|j|i||j|<|j|Sr)r _instancesr9superrr)r:rLrMr __class__s r4rzSingleton.__call__srF4LL&..1~!!#&& "@% 3"7"7"@###CN3 ~c""r3)r+r,r-r[rr __classcell__)rs@r4rrsI J#########r3rctjdd5}|cdddS#1swxYwYdS)z3 :return str: server's external IP address zhttps://api.ipify.orgr`rN)urllibrequesturlopenrr)rs r4get_external_ipr s    7  C C!qvvxx  !!!!!!!!!!!!!!!!!!s&AAAc<d}|||}tj|st d|d|t |d5}|}dddn #1swxYwY|S)z Reads parameter of kernel module from /sys/module/{module_name}/parameters/{parameter} :return str: value of the parameter z(/sys/module/{mod}/parameters/{parameter})mod parameterzCannot find parameter z for module rN)rrrr8rirrr) module_namer# _MOD_PAR_PATH param_fileprs r4get_kernel_module_parameterr(s ?M%%+%KKJ 7>>* % % j8A ;; O    j#  !!  !!!!!!!!!!!!!!! Ls'BBBcd}|D][\}}t|tr%||vs|s|||<d}(t|||}?||vs|sJ|d||||<d}\|S)zPerforms deep update of dict dst with values from src. Does not overwrite subdicts in dst blindly with new dicts in src, but does a deep update of (sub)dict content recursivelyFTz already exists in )rrlrFdict_deep_update)dstsrcallow_overwriteupdatedr>r?s r4r*r*s G  1 a   ||1|A*3q6155  ---- /CFGG Nr3c8eZdZd dZdZdZdZdedefdZd S) TimedCacherct|tsJ||_||_t |_i|_dSr)rlr expirationr5rcache_locks)rr2r5s r4rzTimedCache.__init__)s<*i00000$  ]]  r3ct}|jD]J}|j|\}}tj|z |jkr||f||<K||_dS)zClear cache from expired valuesN)rr3r=r2 total_seconds)r tmp_cacherradded_ats r4_collectzTimedCache._collect0shMM : 1 1C"joOE8 h&$/*G*G*I*III!& # r3c:t|_i|_dSr)rr3r4rs r4r%zTimedCache.cache_clear9s ]]  r3c|}|r3t|}|t|z }t|S)z Generate key from call arguments :param args: call positional args :param kwargs: call keyword args :return: )rrrqhash)rrLrMseedkws r4 _make_keyzTimedCache._make_key=sB   ''B E"II DDzzr3funcr^ctfd}tfd}tjr|n|}j|_|S)a  Use it to cache calls to decorated function @TimedCache(expiration=timedelta(minutes=10)) async def func(*args, **kwargs): pass :param func: decorated function :return: NOTE: is not thread safe. chK||}j|}|tjx}j|< tj|jd{VnP#tj $r=|j|urtjx}j|<n j|}YnwxYw   j |\}}ns#t$rftj jkrj d|i|d{V}|t!jfj |<YnwxYw|n#|wxYw|S)NTFlast)r@r4r9rnrracquirer2r6 TimeoutErrorr9r3rrr5popitemr=release)rLrMrlockrrrArs r4 wrapper_asyncz*TimedCache.__call__..wrapper_asyncWs..v..C;??3''D|*1,..8t{3' 00!* (E(E(G(G+ 0 0 0 t{3///29,..@t{3//#{3/ 0  0  : $ 3IFAA:::4:$,66 ***666#'4#8#8#8888888F&,dikk&9DJsOOO :   MsEABA C&%C&+FDFA-F>FFFF/cZ||} j|\}}nm#t$r`t jjkrjd|i|}|tjfj|<YnwxYw|S)NFrD)r9r@r3rrr5rHr=)rLrMrrrrArs r4 wrapper_syncz)TimedCache.__call__..wrapper_synczs MMOOO..v..C 6 JsO  6 6 6tz??dl22J&&E&222t.v.."($)++"5 3  6 Ms>A'B('B()rrniscoroutinefunctionr%)rrArKrMrOs`` r4rzTimedCache.__call__Js t       D t       *400 MM  #.r3N)r) r+r,r-rr9r%r@r!rr2r3r4r0r0(s   CQC1CCCCCCr3r0rcK|r<|s& |n#t$rYnwxYwdS|t j|h|d{V\}}|rL|s|nd}|rt d||dSdS|st d||dSdS)uCancel *task* and wait up to *timeout* seconds for it to finish. Unlike the common ``task.cancel(); suppress(CancelledError); await task`` pattern, this function **always returns** within *timeout* seconds — even if the task catches ``CancelledError`` and continues running (see DEF-40570 / CPython #103486). Uses ``asyncio.wait`` (not ``wait_for``) because ``wait_for`` also hangs when the inner task survives cancellation. Nrz&Task %r raised during cancellation: %sz.Task %r did not finish within %ds after cancel) done cancelledrrcancelrnrwrr]r)taskrrQrrs r4safe_cancel_taskrUs: yy{{~~       KKMMML$999999999GD!  &*nn&6&6@dnnD  P NNCT3 O O O O O P P YY[[  =A>)z systemd-runz--quietz--waitz--pipez --collectz--property=NoNewPrivileges=noz--property=ProtectSystem=noc`|sdStd|DS)Nr2c3,K|]\}}d|d|VdS)z --setenv=r2Nr2)rr>r?s r4rz+_systemd_run_setenv_args.. s7==A$Q$$$$======r3)rqrenvs r4_systemd_run_setenv_argsrrs4 r ===== = ==r3rcts|Stt|zdd|fz}dd|DS)zWrap a shell command so it runs as a transient systemd unit outside the agent's NoNewPrivileges= sandbox. Returns the original command unchanged when this process is not under NNP.z/bin/shz-c c3>K|]}tj|VdSr)shlexquote)rr's r4rz._wrap_outside_sandbox_shell..s*22qEKNN222222r3)rm_SYSTEMD_RUN_BASErrr)rrqpartss r4_wrap_outside_sandbox_shellrzsa     "3 ' ' ( dC  ! 8822E222 2 22r3ctst|Sttt|zdzt |zS)z5Argv-form counterpart of _wrap_outside_sandbox_shell.)z--)rmrprxrrrq)argvrqs r4_wrap_outside_sandbox_argvr}sY   Dzz  "3 ' ' (   ++   r3rpcLKtt|||fi|d{VS)urun_cmd_and_log variant that escapes the agent's systemd sandbox. Use for shell commands whose RPM/apt scriptlets perform LSM domain transitions on exec (e.g. kernelcare install, hardened-php groupinstall) — see the module-level NNP note above. rpN)rjrz)rrerqrfs r4run_cmd_and_log_outside_sandboxr(sZ!#CS111       r3cJKtt||fi|d{VS)z7run() variant that escapes the agent's systemd sandbox.rpN)rr}r|rqrMs r4run_outside_sandboxr8s</#>>>II&II I I I I I IIr3cJKtt||fi|d{VS)z=check_run() variant that escapes the agent's systemd sandbox.rpN)rr}rs r4check_run_outside_sandboxr=s<5dDDDOOOO O O O O O OOr3ceZdZdZdS)rc `|jdkr7|jr0|dd}|jr |j|d<|j| t j|dS#t$rt|j ddpt|j dd}t|j ddpt|j dd}t|j d dpt|j d d}|j }|r|j p|j }td ||j||YdSwxYw) NPENDINGz%Task was destroyed but it is pending!)rTmessagesource_tracebackr-r+gi_codecr_codegi_framecr_framez+!> Finalizer error in {}() {} at {} line {})_state_log_destroy_pending_source_traceback_loopcall_exception_handlerr__del__AttributeErrorgetattr_coro co_filenamef_linenoco_firstlinenoprintr)rcontextrrframerlinenos r4rz Task.__del__Es{ ;) # #(A #BG% E.2.D*+ J - -g 6 6 6  N4    4:~t<< JAAD4:y$777 It<<DDJ D99W J>>E'H.F43FF =DD$+x       sACD-,D-N)r+r,r-rr2r3r4rrDs#r3rcfd}|S)zReturn async callback which waits for *seconds*. Usage: @retry_on(Error, on_error=await_for(seconds=PAUSE_INTERVAL), timeout=T) async def coro(): 'here's something that may raise Error.' c<Ktjd{VSr)rnr)rLras r4pausezawait_for..pauseos)]7+++++++++r3r2)rars` r4rurues#,,,,, Lr3cjtgstdfd}|S)a Retry the function call on exception (or exceptions, if given in tuple) at most *max_tries*. Await *on_error* (if set) for each exception. If *timeout* is set, stop all attempts in *timeout* seconds. If *silent* is set to True - don't raise exceptions after max If *should_retry* is set - await it and on False, stop auto-retry cycle tries or timeout. zSet any of max_tries, timeoutc tj fd}tj fd}tjr|S|S)Nc~Krtjz} stjdnt d dzD]} rg|tjz }|dkr$t j|i||d{VcS s t j r dn|i|d{VcS}#t jt j f$r$rX}  ||d{V}|s }| kr s r d|  ||d{VYd}~d}~wwxYwdS)Nrrr Timeout exceeded when calling %s0Max tries exceeded when calling %s with error %s) r=r> itertoolscountrrnrrGrrrLrMend_timer remaining_timershould_retry_retrrArBrbrc should_retrysilentrs r4rKz2retry_on..decorator..wrapper_asyncs, 6>++g5!- """1i!m,,( /( / #/;)1DN4D4D)D)A--)0)9 $d 5f 5 5~***$$$$$$$*"&-&: :!$" # $F!"!"!"&*T4%:6%:%::::::::::,g.DE ///#/1=c11E1E+E+E+E+E+E+E(/* )AI~~%! II!, $ #   +&hsA.........#//( /( /s?C 4C D:"AD55D:crtjz} stjdnt d dzD]} rH|tjz }|dkr |i|cS st  r dn |i|cSX#$rL}  ||}|s }| kr s r d|  ||Yd}~d}~wwxYwdS)Nrrrr)r=r>rrrrGrrs r4rMz1retry_on..decorator..wrapper_syncs 6>++g5!- """1i!m,,$ )$ ) ) 5)1DN4D4D)D)A--#'4#8#8#8888#)"&2 2!$" # $F!"!"!" $tT4V44444 )))#/+7<Q+?+?(/* )AI~~%! II!, $ #   + a(((#)'$ )$ )s%B.)BC, AC''C,rQrrnrN) rArKrMrrBrbrcrrrs ` r4rSzretry_on..decorators   + /+ /+ /+ /+ /+ /+ /+ /+ /+ /+ /  + /Z   ' )' )' )' )' )' )' )' )' )' )' )  ' )R  &t , ,   r3)rri)rrcrbrrrBrrSs``````` r4rsrsusz$  7# $ $:8999\ \ \ \ \ \ \ \ \ \ \ | r3ctjfd}tjfd}tjr|n|S)zhIf func throws an exception it is catched, converted to a string and returned as a result of a call.crK |i|d{VS#t$r}t|cYd}~Sd}~wwxYwrrreprrLrMrrAs r4rKz,stub_unexpected_error..wrapper_asyncsg t.v........ .   77NNNNNN s  6166cb |i|S#t$r}t|cYd}~Sd}~wwxYwrrrs r4rMz+stub_unexpected_error..wrapper_syncsQ 4((( (   77NNNNNN s .)..r)rArKrMs` r4stub_unexpected_errorrs_T _T $7== O==<Or3c2 tjfd}|S)zkA decorator that logs uncaught exceptions ignoring them otherwise. CancelledError is not handled. Nctjfd}tjfd}tjr|S|S)Nc K |i|d{VS#tj$r$r'}dtdd|Yd}~dSd}~wwxYwNzIgnoring exception from %s: %sr-r)rnrrrLrMrrr log_handlers r4rKz>log_error_and_ignore..decorator..wrapper_async s !T426222222222)       4D.&99 s AA  Ac t |i|S#$r'}dtdd|Yd}~dSd}~wwxYwr)rrs r4rMz=log_error_and_ignore..decorator..wrapper_syncs tT,V,,,    4D.&99 s 727r)rrKrMrrs` r4rSz'log_error_and_ignore..decorators                          &t , ,   r3)r]r)rrrSs`` r4log_error_and_ignorers9 l       < r3cfd}|S)z'Abort the agent service on *exception*.cLtjfd}|S)NcK |i|d{VS#$r/}t|Yd}~dSd}~wwxYwr)r]r)rLrMrabortrrs r4rOz2abort_agent_on..decorator..wrapper-s !T426222222222     ###  s A$AArP)rrOrrs` r4rSz!abort_agent_on..decorator,sC            r3r2)rrrSs`` r4abort_agent_onr)s*       r3cRtjdd|S)zPascalCase to snake_casez([a-z])([A-Z])z\1_\2)r"subrI)strings r4 snake_caser<s# 6"Hf 5 5 ; ; = ==r3)exec_expr_with_empty_iterc'K|s|rdg}nt|t}ddlm}|j5|D]}||g|REd{V ddddS#1swxYwYdS)a] Get iterator over results of sql expression expr. Given iterable will be split for chunks and we will return iterator containing results of all split queries. Useful for sql selects with in_() in order to avoid too many sql variables error. If exec_expr_with_empty_iter is True and iterable is None(empty) we will process expression once, passing here chunk=None expr(None, *args) :param expr: :param iterable: :param exec_expr_with_empty_iter: if iterable is None(empty) process given expression once, passing here chunk=None expr(None, *args) :return: Nr rinstance)rCHUNK_SIZE_SQL_QUERYdefence360agent.modelrdb transaction)exprrrrLchunksrr}s r4get_results_iterable_expressionrDs& L1L 6JKKK......  " "** * *EtE)D))) ) ) ) ) ) ) ) ) *******************sA##A'*A'rcd}ddlm}|j5t ||D] }|||g|Rz }! dddn #1swxYwY|S)z Get number of results of sql expression expr. Given iterable will be split for chunks and we will return number of results of all split queries. Useful for sql delete with in_() in order to avoid too many sql variables error rrrN)rrrrrexecute)rrr rLrrr}s r4execute_iterable_expressionrcsG......  " "44$X*EEE 4 4E ttE)D)))1133 3GG 4444444444444444 Ns3A""A&)A&cXtj|dddzS)Nr\nr)rfsencoder^rs r4encode_filenamervs% ;t||D%00 1 1E 99r3cbtj|ddddS)Nr&rr)rfsdecoder^rs r4decode_filenamerzs+ ;t  SbS ! ) )% 6 66r3cNtjtj|Sr)base64 b64encoderrrs r4base64_encode_filenamer~s  BK-- . ..r3b64namechttjtj|Sr)rrrr b64decode)rs r4base64_decode_filenamers%  F,W5566 7 77r3cV tj|}n#t$rd}YnwxYw|S)zS Like pwd.getpwnam(username) but returns None instead of raising KeyError. N)rrr)rrs r4rrsAh''  Ms  &&c>tt|||S)zH Put the specified `value` inside the [`low`, `high`] interval. )maxmin)rlowhighs r4cliprs s5$ % %%r3Background task failedc| tj} |dS#tj$rYdSt $r}|d||Yd}~dSd}~wwxYw)a[ Callback for asyncio.Future that logs exceptions and ignores CancelledError. Use this as a done_callback for asyncio tasks/futures: future.add_done_callback(log_future_errors) Or with custom logging: future.add_done_callback( lambda f: log_future_errors(f, logger.warning, "Upload failed") ) Nz%s: %s)r]rrrnrr)futrrrs r4log_future_errorsrsn *  !     *** Hgq)))))))))*s&A A AAr.crfd}||i|}|||S)z Use this function in plugin initialization instead of loop.create_task to be able to see the exceptions from the specified coroutine. c|sA|/d||ddSdSdS)Nz1Unhandled exception during plugin initialization!)rrrT)rRrr)rTrs r4_log_exceptionz6create_task_and_log_exceptions.._log_exceptionsv~~ DNN$4$4$@  ' 'L!%!1!1        $@$@r3) create_taskadd_done_callback)rrrLrMrnew_tasks` r4create_task_and_log_exceptionsrsY     d 5f 5 566H ~... Or3cfd}|S)a5 Create coroutine from regular function Useful to pass functions to APIs requiring coroutines Note: coroutine will still block event loop in main thread. For most blocking functions, run_in_executor should be considered instead :param function: :return: coroutine running function cK|i|Srr2)rLrMfunctions r4rzmake_coro..corosx((((r3r2)rrs` r4 make_corors#))))) Kr3cK|tkr tj}n tj}|dt |ddx}rd|dnd||t jtd{VdS)Nz7Failed to copy data%s to modsec ruleset dir %r, try: %srz ()r)COPY_TO_MODSEC_MAXTRIESr]rrrrnr_MODSEC_COPY_FAILURE_TIMEOUT)rr rBfns r4log_failed_to_copy_to_modsecrs ###lnCA$S*d;;;rD R "   -4 5 5555555555r3) err_buf_sizec 4Kd}t|}tj|dtjjtjjd|d{V} tj||j||j23d{V}|WV 6 |d{V}|dkr%t||dd |dS#|d{V}|dkr%t||dd |wxYw)z Start *cmd*, yield its stdout line by line [b' '] If *cmd* return nonzero exit status, raise CheckRunError with the last *err_buf_size* lines from stderr. cJK|23d{V}||6dSr)append)pipebufr=s r4read_pipe_intoz1readlines_from_cmd_output..read_pipe_intosL       $ JJt    $$s")maxlenT)rhrfrgNrr3) rrnrrr_rkrrgrfrwrr)rrrfr err_bufr|r=rs r4readlines_from_cmd_outputr s<(((G/ !&!&         D I NN4;@@AAA+       $JJJJJ&+ 99;;&&&&&& ?? Cchhw6G6GHH H ? 99;;&&&&&& ?? Cchhw6G6GHH H H H H Hs*C:BCADr`)rbdelaycKtd|dzD]3}||d{V}|s!||krtj|d{V0|cSdS)z Retry *predicate_coro(*args)* until it becomes true, but no more than *max_tries* attempts. Sleep for *delay* seconds before the next *predicate_coro()* call. Return whether the predicate became true. rN)rrnr)predicate_cororbrrLattemptrs r4finally_happenedr sIM**%~t,,,,,,, 'I---&& & & & & & & &  r3'cKt|dD]-\}}|WV||zdkrtjdd{V.dS)z6Yield to the event loop every *chunk_size* iterations.r)rCrN) enumeraternr)rr r items r4 nice_iteratorrsoXQ///##4 Nq -"" " " " " " " "##r3ceZdZdZdZdZdS)LazyLocka Descriptor object to share async Lock between client objects. Used in order to achieve lazy evaluation of the lock and share state between it's clients. Using asyncio.Lock in client code directly: >>> class Foo: >>> lock = asyncio.Lock() leads to an unclear error ([Errno 9] Bad file descriptor), when trying to move this Lock during demonization process. cd|_dSr)rr;s r4rzLazyLock.__init__6s  r3cN|jstj|_|jSr)rrnr)rrowners r4__get__zLazyLock.__get__9s!z ( DJzr3N)r+r,r-r[rrr2r3r4rr's<  r3rc|}|rd|vsd|vrdS|dd\}}||fS)zOParse RPM output line, return (package_name, version) or None if not installed.z not installed: Nr)rrIr )r=pkg_nameversions r4_parse_rpm_liner"?s[ ::<.wrappervs\ 7>>= > > +4 T4*6*********r3r)rArOs` r4check_disabled_firewallr-us3 4[[++++[+ Nr3> imunify-ui imunify-coreimunify-antivirusimunify360-firewallpackagescKt\}}t|}t||zddd{V}t|||S)a Retrieves the version of the specified system packages using a command and regex specific to the current system. Parameters: packages (Iterable[str]): A set of package names to retrieve version for. Returns: A dictionary mapping package names to their corresponding version strings, or None if the package is not installed or version information cannot be retrieved. F)rrN)r(rpsafe_run_with_timeout_parse_package_info_output)r2r parse_line packages_listrs r4system_packages_infor9st-..OCNNM( mR%F &fmZ H HHr3rr7cnfd|Dfd|DS)NcXi|]&}|xdxdx#'S)rrr2)rr=r7pkgrvers r4 z._parse_package_info_output..sf  j&& &F1I S  1I S  Sr3c<i|]}||Sr2)r9)rr<parseds r4r>z._parse_package_info_output..s% 5 5 5SCC 5 5 5r3) splitlines)rr2r7r@r<rr=s `@@@@r4r6r6sf %%''F 6 5 5 5H 5 5 55r3cK tjt|fi||d{VS#tj$r|d|YdSwxYw)Nrz#Command %s failed: Timeout occurredr)rnrrrG)rxrrBrMs r4r5r5s% W ' ' ' '              17;;;rrs&+A  A nc#K|dkrtdt|}tt||x}r%|Vtt||x}#dSdS)Nrzn must be at least one)rir rpr)rrCitbatchs r4batchedrGs  1uu1222 hBr1 && &% r1 && &%r3rMc#RKt|D]}fd|DVdS)Nc"i|] }|| Sr2r2)rr>rMs r4r>z batched_dict..s&&&1q!A$&&&r3)rG)rMrCrFs` r4 batched_dictrJsKA''&&&&&&&&&&&''r3cn tjddgd}|d}|s?t drtjddgd}d|vrd}|S#t $r&}td |Yd}~d Sd}~wwxYw) Nhostnamez-fT)text)z.cloudwaysapps.comz.cloudwaysstagingapps.comz/usr/local/sbin/apminfo Cloudwaysz$Error while checking environment: %sF) rj check_outputrrrr8rr]r)rL _is_cloudwaysrrs r4 is_cloudwaysrRs+  T   %'' !)) ?   %&;!r{s  ********////////::::::::::222222222222''''''                     %%%%%% GCx     8 $ $ d0116d344 "d#BCC$899 SJNN:B?? +++++D+++Q     K K K K4T    4        00 3u 0000f 0      K2    (5  5    .; ' ' ' ' ' ,   ++++++++@        :?;;;;F(,  ddd 3J $ d $Jd ddddNQ ? ?C ? ? ? ?h'h'h'h'h'h'h'h'X%[4 A A A58 A A A A A #### 38_ ####2.<     $)#         5 T         5 T     % % %--------`0E6D&3#$ % % % , ,h ,C ,) , , , ,    ########"R   !!! !"2eeeeeeeeP -.     >)))ZQ4 $>>> 3 3S 3s 3 3 3 3     $      ,0JJJJJ 26PPPPP7<B   $   sssslPPP*$-$&&&&R%7&>>> 6;*****@';&:::777//%////8E8d8888&&&****.i(4      6 6 6%(III cIIIID=>Q      ####0#%S/D"835c?T#9"Q $s)XseU38_t%;;< <= .FFFFF FFF   " IsmI #sTz/IIII, 6  63i 6#c3h$ 667 6 #sTz/ 6 6 6 6 !,           'DcN's'''' Q ( T c     t    &&&&&r3