?U6 PUdZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlZddlZddlZddlmZmZddlmZmZmZddlmZmZddlmZddl m!Z!ddl"m#Z#dd l$m%Z%dd l&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/dd l0m1Z1dd l2m3Z3dd l4m5Z5ddl6m7Z7ddl8m9Z9m:Z:m;Z;ddlZ>ddl?m@Z@ddlAmBZBmCZCddlDmEZFddlGmHZHe#eIZJe jKdZLdZMdZNdeOfdZPdZQdZRdZSdZTdZUe jKd ZVd!ZWd"ZXd#ZYd$ZZ e j[ Z\e/e]e^e_eOde)e]e'fe+e'fZ`daae,eCebd%<dace,eBebd&<deOfd'ZddYd(ZedeCfd)ZfdeBfd*ZgGd+d,ehZiGd-d.ehZjd/Zkd0e jld1e^de(fd2Zmde`fd3Znd4e]d5eoddfd6Zpe:ejekeY7d4e]de`fd8Zqid9d4e]d:e_fd;Zre:ejekeY7d4e]dd?d4e]d:e_fd@ZtdAe(fdBZud5eodCe^deOfdDZve:ejekeYevEd>ddFd4e]dGe jldHe^de]fdIZwedJd4dKgZxdLe'de-exfdMZydNZzeVfdOe jKdPe jKddfdQZ{GdRdSZ|dYdTZ}e|j~Ze|jZ dZdVe,e]ddfdWZdXZdS)[aPUtilities for managing local file storage synchronised with a remote server. Files are divided into types: signatures, modsecurity bundles, ip white lists, etc. Each type is represented by an Index instance. Index has a local subdirectory and a description that contains its files' metadata used to decide if the update is necessary. N) defaultdict namedtuple) ExitStacksuppresscontextmanager) formatdateparsedate_to_datetime)GzipFile)chain) getLogger)Version) AnyBinaryIODictIterableListOptionalSetTupleUnion)urlparse)config) LicenseCLN)PanelException) file_hashretry_onrun_with_umask) rate_limitHOUR) to_thread) UrlTransportRandomIpChooserWithIPv6Toggle)safe_extractall) default_hookz/var/imunify360/.ipv6_disabledz(/proc/sys/net/ipv6/conf/all/disable_ipv6z(/sys/module/{mod}/parameters/{parameter}returnctdd} t|5}|dkr ddddS dddn #1swxYwYn#t $rYdSwxYw tt 5}|dkr ddddS dddn #1swxYwYn#t $rYnwxYwdS) zCheck whether IPv6 is disabled at the kernel level. Reads the module parameter and the runtime sysctl without depending on the im360 package. ipv6disable)mod parameter0NT1F) _MOD_PAR_PATHformatopenreadstripOSError_SYSCTL_DISABLE_IPV6) param_filefs S/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/files/__init__.py_is_kernel_ipv6_disabledr8Ds %%&I%FFJ *   vvxx~~3&&        &                tt & ' ' 1vvxx~~3&&        &                      5svA?,A3 A?' A?3A77A?:A7;A?? B  B C7%,C+ C7 C7+C//C72C/3C77 DDeulasigszrealtime-av-confzwp-rulesgeoz/var/imunify360/filesz$https://files.imunify360.com/static/i g? _IP_CHOOSER _TRANSPORTcPtptS)zCheck if IPv6 should be disabled at startup. True when either the kernel has disabled IPv6 or a previous agent run persisted the disabled state after a runtime network failure. )r8_IPV6_DISABLED_STATEexistsr7_should_disable_ipv6rD~s" $ % % F)=)D)D)F)FFrCc tdS#t$r tddYdSwxYw)z:Persist IPv6 disabled state so it survives agent restarts.z%Could not persist IPv6 disabled stateT)exc_infoN)r@touchr3loggerdebugrBrCr7_persist_ipv6_disabledrJs_M""$$$$$ MMM r!rOrBrCr7_get_transportrRs$!_->->??? rCceZdZdZdS)IntegrityErrorzERaised when on disk content does not match hashes in description.jsonN__name__ __module__ __qualname____doc__rBrCr7rTrTsOOOOrCrTceZdZdZdS) UpdateErrora Raised on other errors during files update. Possible reasons are: * server returns non 200 status; * hash mismatched between downloaded content and description.json; * urllib errors; * JSON decoding errors; * errors while writing to disk. NrUrBrCr7r[r[s    rCr[cKtd||tjt jd|ztzd{VdS)Nz2Files update failed with error: {err}, try: {try_})errtry_r$)rHwarningr/asynciosleeprandom randrange_TIMEOUT_MULTIPLICATORexcis r7_log_failed_updaterhsy NN<CC! D   -(a003II J JJJJJJJJJJrCpathmodectd5tj|tjtjztjz|}dddn #1swxYwYtj|dS)zbOpen file at `path` using permission `mode` for writing in binary mode and return file object.rNwb)rosr0O_WRONLYO_CREATO_TRUNCfdopen)rirjfds r7_open_with_moderss   HH WT2;3bj@$ G GHHHHHHHHHHHHHHH 9R  s;AAAc t||5}tjtj|d|ddcdddS#1swxYwYdS)Ntimeoutfileheaderszutf-8)encoding) _fetch_urljsonloadio TextIOWrapperget_content_charset)urlrvresponses r7_fetch_json_syncrs C ) ) ) Xy   !),@@II                       sAA&&A*-A*rrfct}|rg|rUtd||||tdSdSdS)Nz>Network error for %s via IPv6 IP %r, disabling IPv6. Error: %r)rOis_ipv6_enabledlast_ip_was_ipv6rHr_last_ip disable_ipv6rJ)rrfchoosers r7_disable_ipv6_on_network_errorrsG  !W%=%=%?%?! L  OO            !!!!rC)on_error max_triescZKtj} |dt||d{VS#tt jf$r(}td||d}~wtj $r7}t||td|d}~wt$r7}t||td|d}~wt$r}td|d|d}~wtjjt"jjf$r8}t||td||d}~wt($r*}t||td|d |d}~wwxYw) zDownload and decode JSON from *url*. Return decoded JSON. Raise UpdateError: * HTTP response status code is not 200; * Unicode or JSON decoding fails; * on time outs during HTTP request; * on other HTTP errors. Nz!json decode error [{}] for url {}request to {} timed outrequest to {} reset%eof error while updating files, url: , err: 8urllib/http error while updating files, url: {}, err: {} Can't fetch , reason: )r`get_event_looprun_in_executorrUnicodeDecodeErrorr{JSONDecodeErrorr[r/socketrvrConnectionResetErrorEOFErrorhttpclient HTTPExceptionurlliberrorURLErrorr3)rrvloopes r7 _fetch_jsonrs  ! # #D=))$0@#wOOOOOOOOO  4 5NNN=DDQLLMMM >AAA&sA...3::3??@@@ ===&sA.../66s;;<<<     CC C C C C    K %v|'< =   &sA... F M MQ     ===&sA...;;;;;<<<=sQ":F*#A33F*2B77 F*2C66 F*D(F*3E33 F*%F%%F*rxrvc tj|dtjpdi|d}t ||5}|j|jfcdddS#1swxYwYdS)z>Perform HEAD http request to *url* with *timeout* & *headers*.Imunify-Server-IdHEAD)rxmethodruN) rrequestRequestr get_server_idrRr0coderx)rrvrxreqrs r7_perform_http_head_syncrs .  !9!;!;!Ar   !  C     sG  4 4!vqy !!!!!!!!!!!!!!!!!!sA88A<?A< current_mtimecK|turdSt|d} tt||d|id{V\}}|dkrt d|d|t t 5t|d }||kr$t d ||d ||dddn #1swxYwYdS#tj $r7}t||t d |d}~wt$r7}t||t d |d}~wt jjt&jjf$rY}t-|d r|jdkrYd}~dSt||t d||d}~wwxYw)zCheck if we need to download description.json file: - perform HEAD request if local file exists and older return True otherwise return False T)usegmtzIf-Modified-SincerNzUnexpected http code z for z Last-ModifiedaGot code %r, but last modification date %s is earlier than or equal to the date provided in the If-Modified-Since header, the origin server SHOULD generate a 304 (Not Modified) response [rfc7232]. Here's curl cmd: curl -s -I -w '%%{http_code}' -H 'If-Modified-Since: %s' '%s'rrri0Fr)_NEVERrr rr[r Exceptionr timestamprHr_rrvrr/rrrrrrrhasattrr)rrrvformatted_mtimerrx last_mtimers r7_need_to_downloadrst t<<AAA&sA...3::3??@@@ ===&sA.../66s;;<<< K %v|'< =   1f   !&C--55555&sA... F M MQ      sH#C2ACCCG,2D G+2E(GG&3GGT)compressc#Ki}|||d<dtjpdi}|r|dditj||}t j|fi|5}t5}|j ddk}|rl|sj|j d d krLt d |j d|j ||r#|t| n||j d Vdddn #1swxYwYddddS#1swxYwYdS)zs Fetch *url* as binary file. If *compress* is true, ungzipping is done automatically if necessary. NrvrrzAccept-EncodinggziprContent-Encodingz Content-Typezapplication/zipzRequested gzip but got Content-Encoding=%r. Read response as is [identity]. Headers: %s, as curl cmd: curl -Is -H 'Accept-Encoding: gzip' '%s')fileobj)rwrx)rrupdaterrrrRr0rrxgetrHrNitems enter_contextr ) rrvr parameters req_headersrrstackgzippeds r7rzrzQs7J ' 9& (@(B(B(HbIK8-v6777 . k : :C          9;; "'"&&'9::fD    $$^448III KKJ $$%788 &&((    ##HX$>$>$>???'      %                                 s77E(B>E E(E E(E E((E,/E, dest_filec0tj}|}t|||5}|dt x}rL|||||dt x}Ldddn #1swxYwY|dddks||z } |ddd} | Nt| } | | kr9tj d | | | z d |} || |krtd |d |d | | S)z Fetch *url* to *dest_file* and return its md5sum. Raise *urllib.error.ContentTooShortError* if the downloaded file has unexpected length. )rvrrwNrxrrzContent-Lengthz&{got} bytes read, {diff} more expected)gotdiff)messagecontentzcontent fetched from z does not match hash: expected=z, got=)hashlibmd5tellrzr1_BUFSIZErwriterintrrContentTooShortErrorr/ hexdigestr[) rrrvrmd5sumrinitial_file_offsetrchunk file_lengthcontent_length_headerexpected_file_length got_md5sums r7_fetch_n_md5sum_urlr~s +--C#..** C8 < < <#',,X666e # JJu    OOE " " " ',,X666e ################ I  " "#5 6 6& @ @ nn&&)<< !) 3 7 78H$ O O ,#&'<#=#= #{22l77DKK'1K?L! 8J jF22 4C 4 4 4 4'1 4 4    sA/B55B9<B9rgc(Kdt|vS)NzHTTP Error 404)strres r7$_fetch_and_save_should_retry_handlerrs 3s88 ++rC)rr should_retryrr dest_path dest_modec 0K t||5}tt|||||d{VcdddS#1swxYwYdS#tj$r7}t ||t d|d}~wt$r7}t ||t d|d}~wt$r*}t ||t d|d|d}~wtj j tjjf$r8}t ||t d||d}~wt $r-}t ||t d|d |d |d}~wwxYw) zFetch bytes from `url`, save them to `dest_path`, and return md5 checksum of downloaded content. Raise UpdateError: * HTTP response status code is not 200; * on time outs during HTTP request; * on other HTTP errors. rNrrrrrrz to r)rsr rrrvrr[r/rrrrrrrrr3)rrrvrrrrrs r7_fetch_and_saversT0L Y 2 2 i"#!                    >AAA&sA...3::3??@@@ ===&sA.../66s;;<<<    &sA... CC C C C C    K %v|'< =   &sA... F M MQ     LLL&sA...JJJ)JJqJJKKKLsiA A AAAA AF2B F2C F%D(F(3E F((FF_Itemrdatac&d|dDS)z,Return a set of _Item for easy manipulation.cFh|]}t|d|dS)rr)r).0items r7 z_items..s* I I I4E$u+tH~ . . I I IrCrrB)rs r7_itemsrs I I4= I I IIrCcd}|||tj|D]d\}}}|D],}|tj|||-|D],}|tj|||-edS)zCheck and change file/dir modes recursively. Starting at dirname, change all inner directory permissions to dir_perm, file permissions to file_perm c tj|jdz}||krmtj|sPt d|t|t|tj||dSdSdS#t$rt d|YdSwxYw)NizGFixing wrong permission to file/dir %s [%s] expected [%s] (not symlink)z&Failed to change permission to file %s) rmlstatst_moderiislinkrHr_octchmodPermissionErrorr) file_dir_path permission current_modes r7 _os_chmodz"check_mode_dirs.._os_chmods 8M22:UBLz))"'..33);! %% OO  33333*)))    LL8-       sB B%B>=B>N)rmwalkrijoin) dirnamedir_perm file_permrridirsfiles directorynames r7check_mode_dirsrs&Igx   WW--;;dE ? ?I Ibgll433X > > > > ; ;D Ibgll4.. : : : : ;;;rCdescription_path files_pathc||jvsJ|j}|}||krR|r/|d|dddS|j}||kPdSdS)aP Try to fix the structure of /var/imunify360/files/ when NotADirectoryError happens. It indicates that some part in the path is a file: /var/imunify360/files/sigs <- is a file => open("/var/imunify360/files/sigs/v1/description.json") will fail. We try to rectify it by deleting the file but up to FILES_DIR. T) missing_ok)parentsexist_okN)r parentis_fileunlinkmkdir)rr_dir topmost_dirs r7_fix_directory_structurers )1 1 1 1 1  "DK *   <<>>  KK4K ( ( (   dT  : : : E{ *      rCceZdZeejZeeZiZ iZ eZ eZ iZ dZedezejZdDdZdZdZdejd d fd Zdejfd Zd Zeddddedededededed d fdZ ed efdZ!ed e"efdZ#eded efdZ$dEd efdZ%d efdZ&d e"efdZ'edZ(d e)efdZ*dZ+e,fd e-fd Z.d efd!Z/d"e-d efd#Z0dEd$Z1de2j3d%e"e4d d fd&Z5d'e"e4d e6e"e4e"effd(Z7eded efd)Z8eded efd*Z9e:dEd+Z;e:d,e2j3d e2j3fd-Zd e?e@e?eeezfffd2ZAd eBefd3ZCdFd4ZDd5e2j3d,e2j3d eEe2j3fd6ZFd efd7ZGe:d8ejd9ejd:e"ed d fd;ZHdZJdFd?ZKdEdFd@ZLe dGdAeEed d fdBZMeded d fdCZNd S)HIndexz/static)periodTcX||jvrtd|d|j||_d|_dgi|_|} t |5}tj||_dddn #1swxYwYn#t$rEt d|ttj|tYnTt t"tjf$r6}|r#t'd||d|_Yd}~nd}~wwxYw|rX|}t-|r5t'd d ||js|dSdS) z :param bool integrity_check: check if last update did not break anything (by interrupting it in the middle or another programmatic error) :raise IntegrityError: z*Trying to initiate unregistered file type z. Allowed types FrNzPath %s has a file in parentszcannot read description file {}Tz'some files are missing or corrupted: {}z, )_TYPES ValueErrortype _is_blank_json_descriptionfile_pathr0r{r|NotADirectoryErrorr_throttled_log_errorrpathlibPath FILES_DIRFileNotFoundErrorrrrTr/_corrupted_fileslenrr)selftype_integrity_checkrir6r bad_filess r7__init__zIndex.__init__1s)  # #(U((+((  r] ))++ "d *q!Yq\\  * * * * * * * * * * * * * * *! D D D  & &'F M M M $W\$%7%7 C C C C C     " " "  $5<}>)r-rVrrr%rr&s r7__repr__zIndex.__repr__ds\ '       4::<<((    rCrr&Nctd|j|||}||jddS)zjWhether *files_path* dir may be used for this type's file group. :raises: IntegrityError zValidating [%s]: %sTr(N)rHrNr_make_file_groupr&r FileGroups r7validatezIndex.validatelsT  )49jAAA))     $)T222222rCc6Gfddj}|S)zV Return FileGroup class: Index class with local path == *files_path*. c6eZdZededeffd ZdS))Index._make_file_group..FileGroupr'r&cF|jksJtjSz+Return local base path for given file type.)rrmfspath)clsr'rr&s r7rz4Index._make_file_group..FileGroup.files_path~s( ))))y,,,rCN)rVrWrX classmethodrr)rr&sr7r8r<}sP  -s -s - - - - - -[ - - -rCr8)r-r7s`` r7r6zIndex._make_file_groupxsG  - - - - - - - - - - -rCc .tj|j}ttjtjttj |jtj |d|ddS)Ndirrw) r_PERMSrrrmrinormpathrr"_PATHSpardir)r&permss r7rzIndex.check_mode_dirssq TY' G   Y TY(?KK   %L &M      rCFall_zip essentialr' relative_pathrrrJrKc|j||r|j|||j|<||d|j|<||j|<dS)aAdd a type to known file types. * relative_path is a relative path to all files for that type. * dir_perm is permission mask used to create directories. * file_perm is permission mask used to create files. * all_zip is a flag which shows whether that type of files can be downloaded in all.zip archive. all.zip is expected to be on the server. * essential is whether the agent can start if there are errors updating that type. )rCrwN)radd_ESSENTIAL_TYPESrFrD_ALL_ZIP_SUPPORT)r@r'rLrrrJrKs r7add_typezIndex.add_typesj, u  ,  $ $U + + +) 5$,i@@ 5&-U###rCcBKtd|jDS)zuWhether essential files exist. Note: the files may be corrupted (integrity check is not performed). c3DK|]}t|dj VdS)Fr5N)rr)rr's r7 z.Index.essential_files_exist..sI  eU333= =      rC)allrOr@s r7essential_files_existzIndex.essential_files_exists9  -      rCc4|jS)z&Return a set of all known files types.)rcopyrVs r7typesz Index.typessz   rCcbtjt|j|Sr>)rmrirr"rFr@r's r7rzIndex.files_paths!w||Isz%'8999rCc |rJ|jtjjvr2tj|dStj||jdS)z9Return local path for description.json for current index.description.json) rr FilesUpdateDISABLEDrmrir_descriptionfile_path_latestr)r&latests r7rzIndex._descriptionfile_pathsm  di6#5#>>>7<<11335G w||DOODI668JKKKrCcrd|Dd}|S)Nc.g|]}|d |dS)rbrCrB)rxs r7 z6Index._descriptionfile_path_latest..s%JJJAakJqxJJJrCr) _get_listvalues)r&ltss r7raz"Index._descriptionfile_path_latests5JJ!1!1!8!8!:!:JJJ1M rCcRt}t|jD]}||j} t |t jt}n%#t$r| |Y_wxYw||j kr| ||S)z9Return a set of file paths that are missing or corrupted.) setrr localfilepathrrrrrr#rNr)r&r)rriactuals r7r$zIndex._corrupted_filessEE 4:&& $ $D%%dh//D "4h??$    d### $$ d###s A!!BBc|j|S)z` usage example: >> async with Index.locked(WHITELISTS): ... )_lockr\s r7lockedz Index.lockedsyrCcDfdtjDS)z(Return iterable over all files in index.c3LK|]}|jVdSr,rlrrrr&s r7rTzIndex.files..s3LL""48,,LLLLLLrC)rrr2s`r7rz Index.filess'LLLL 9K9KLLLLrCc|jdS)z+Return 'items' field from JSON description.r)rr2s r7rz Index.itemssz'""rCc tj|djS#t$r|cYSwxYw)zBReturn mtime of description file if it exists, otherwise -math.infTrb)rmstatrst_mtimer3)r&defaults r7_descriptionfile_mtimezIndex._descriptionfile_mtimesO 7455T5BBCCL L   NNN s ,/ >>c|}|sdS|tjjzt jkS)z4Return True if last update was too late in the past.T)r{rr_PERIODtime)r& _desc_mtimes r7 _is_outdatedzIndex._is_outdateds<1133  4V/66DDrCrvcK|jpyt|dkpT|o@t ||j||d{VS)z>Return True if update from server is needed for current index.rN)rr%r$rr_descriptionfile_urlrr{)r&rvs r7is_update_neededzIndex.is_update_neededs N 4((**++a/ !!##+--di88//11 rCc td5tj|||ddddS#1swxYwYdS#t$r"}t t ||d}~wwxYw)z)Create local directory for current index.r)rjr N)rrmmakedirsr3r[r)r&rdir_moder rs r7 _makedirszIndex._makedirs s -"" G G G(XFFFF G G G G G G G G G G G G G G G G G G - - -c!ff%%1 , -s2A6 A:A:A A/ A**A/ to_updatecK||}||jd}|j|jd}|j|jd}|D]}||j} t j| } t j| s| | |dt|j| |||j d{VdS)zX Fetch files from *to_update* set, verify hashes, save to *files_path*. Fr5rCrwr )rrN) r6rrDrlrrmririsdirrrr) r&rrrvr8fgr file_moderfilenamers r7 _update_fileszIndex._update_filess ))   Yty% 8 8 89RW%e,Ibg&v.   D''11Hgooh//G7==)) Bw5AAA!#{           rC remote_itemsctj}fd|D}fd|D}||z }fd|D}||z }||fS)zFigure out what should be updated based on current items, file system state and remote items. Return tuple of files to fetch and files to delete. Files to fetch is a set of _Item. Files to delete is a set of file paths.cDh|]}|jSrBrsrts r7rz+Index._calculate_changes..7s)LLLt))$(33LLLrCcDh|]}|jSrBrsrts r7rz+Index._calculate_changes..8s)NNN**4844NNNrCcLh|] }|jv|!SrBrs)rrr)r&s r7rz+Index._calculate_changes..<s>   !!$(++9<< <<J#>???rCc Ktj||j}t|}||jdz}|j|jd}|j|jd}| |j}t5}| ||d| tj ||dt||||dd {V} tj|d 5} t#| |d d d n #1swxYwYt%j|D]v\} } } | D]5}t%jt$j| ||6| D]5}t%jt$j| ||6w|||||}nX#t2t4t6t8tjtjf$r"}t?tA||d }~wwxYw|!d d d n #1swxYwYt ||tE||jtFj$j%vS) a Update current type of files using all.zip archive. Directory with current type of files will be cleared and replaced with all.zip contents. all.zip is expected to be on the server Return whether updated. :param timeout: :raise UpdateError: if OSError or http error or integrity check error (got wrong data from the server) all.ziprwrCFrT)r)rrNr)&r r!rrrrrrrDrrrcallbackrrzipfileZipFile_safe_extractallrmrrrirr9_replace_live_with_new_dirrrTr3r BadZipfile LargeZipFiler[rpop_allboolrr_r`)r&rvrnew_path archive_pathrr all_zip_urlrollback_stack_archiveroot directories filenamesrrold_pathrs r7_run_update_all_zipzIndex._run_update_all_zipesTL!;!;<< ++I66))(-)*CDD K *62 ;ty)%0'' 22 [[1 %N NN8XN > > >  # #&! $   &# A 1_\37787$Wh777888888888888888 57GH4E4EJJ0D+y%0JJ dI!>!>IIII$-JJdH!=!=yIIIIJ h''' ::8YOO"$  1 1 1"#a&&))q0 1  " " $ $ $c1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %h  lh    y 2 ;;;s\>AJH *E; H E H E B9H J 3I<IIJJJversionforcecKtj||j}|st d|jd||dkrY|}d|Dd}t d|j|| d} t|d z t|kr|st d |d |jn(#t$rt d|jd |wxYw|jD]}|s|rt|d z t|krh||jd z}||d|||dd{VdSt d |d|j)a Update to the version specified in *version*. :param version: version to update to :raise UpdateError: if OSError or http error or integrity check error (got wrong data from the server) zCannot update z, because it is not a symlink: rbc(g|]\}}|d |SrwrB)rverprops r7rfz#Index.update_to..s5!ThrCrz%Try to update to latest version %s %sFstrictVERSIONzVersion z is already set for z5, because current version doesn't have VERSION file: liveTtarget_is_directory is_updatedNz not found in )r r!rr is_symlinkr[rgrrHrNresolver read_textr2r#r iterdiris_dirrr symlink_torename _run_hooks)r&rrrversions current_pathri new_live_paths r7 update_tozIndex.update_tosL!;!;<< ##%% +999ii)  h  ~~''H%-^^%5%5G KK7G   !(((66   1<<>>DDFFGG7##$$$"GwGGDIGG!   +'+yyy,,@   $,,..  D??$$  D9,7799??AABBgGG%)NN49v3E$F$FM!,,Tt,LLL!((333//T/:::::::::FFk'''499MNNNs A#D77%Ectj||j}|siS|d}i}t d}|jD]}|r||krd}nd}|dz x} rqd} | }t |} |dt|d|| <| |kr| }#t$rtd||YwxYw|t dkr d||d<|S) NFrr,Tr)currentrbrCz Version file %s is not valid: %srb)r r!rrrrr r rrArrrrHr) r&rrresult max_versionrir version_filer_vers r7rgzIndex._get_listsL!;!;<< ##%% I (((66 cll $,,..  D   ||~~-- $y 00 88:: *4466G"7++D#*"'"4yy$$F4L k))&* !LL:$ H  &  % %,0F;  ) sAD&D>=D>cg}t|dD]4\}}|drdn |drdnd}|||5|S)z/Return list of versions available in the index.c|dS)NrrB)res r7z Index.get_list..s AaDrC)keyrz (current)rbz (latest)r)sortedrgrappend)r&rrrmarkers r7get_listzIndex.get_lists# NN   " " $ $..   0 0MGT  ? >[[  MMW.f.. / / / / rCctj||j}|sdS|d}|jD]}tj tj j tj |jtj j z j}|rf|sR||krL|t$jjkr7t*d|j|t/j|ddS)z~Remove old versions of files. This is done by removing old directories in the path, that older than 30 days. NFrzRemoving old version of %s: %sTr)r r!rrrrr rrrnowtimezoneutc fromtimestamprxrydaysrrr_ DAYS_TO_KEEPrHrNrr)r&rrridays_olds r7_clean_old_versionszIndex._clean_old_versions s6 L!;!;<< ##%%  F (((66 $,,.. 8 8D  00+++IIKK("+/   8)) 8L(( 2 ??? &&  9%&) "))*555'// 0BINNN-O0% j====  " " $ $ $[- %- %- %- %- %- %- %- %- %- %- %- %- %- %- %^s8=A/G -DG B FG F/G  GGc Kj}t||d{V}t |\}}|p|}|s6t djdStj j}| r| dnd}t|} t5} | jjdd| t&j| d |r|r|n|} | rAt| | |fd |Dd{V| ||d{V t3| d z jjd 5} | t7j|dddn #1swxYwY| | |}n6#t@tBf$r"} tEtG| | d} ~ wwxYw| $dddn #1swxYwY|rE|r1t d |t'j|d jtJj&j'vS)z Run update, return whether updated. :raise UpdateError: if OSError or http error or integrity check error (got wrong data from the server) ruNzupdating %s: nothing to update.FrrCrTrc3LK|]}|jVdSr,rsrts r7rTz$Index._run_update..sD$$9=**4844$$$$$$rCr^rwz,Removing old path on file by file update: %s)(rrrrrrHrN_touchr r!rrrrrrrrDrrrr _copytreeunionrrsrr{dumpsencoder9rrTr3r[rrrr_r`)r&rvras_jsonrr need_updaterrrr from_pathrwrs` r7 _run_updatezIndex._run_updates'' 22#C999999999#66vgGG 9,9   KK949 E E E KKMMM5L!;!;<< 09/C/C/E/E OI  U  + + +4 ++I66[[* %N NN$+di07%      # # xt $   %I):):I  !! ooOO$$$$AJ$$$$$Xy'$JJ J J J J J J J 1$11K *62=JJtz'2299;;<<< =============== h''' ::8YOO"G, 1 1 1!#a&&))q0 1  " " $ $ $U* %* %* %* %* %* %* %* %* %* %* %* %* %* %* %Z  8)) 8 KK>    M($ 7 7 7 7y 2 ;;;s[CK15)J:I$ J$I( (J+I( ,.JK1K,K  KK11K58K5from_dirto_dir ignored_pathsc`Kfd}ttj||d|dd{VdS)z7Copy *from_dir* to *to_dir* except for *ignored_paths*.cttjtsJt fd|DS)z(Return names that should not be copied.c3`K|](}tj|v$|V)dSr,)rmrir)rrr ris r7rTz8Index._copytree..ignore_names..sJ7<<d++}<<<<<<rC) isinstancermr?r frozenset)rinamesr s` r7 ignore_namesz%Index._copytree..ignore_namess_bioos33 3 33! rCT)symlinksignore dirs_exist_okN)r rcopytree)r r r rs ` r7rzIndex._copytreesu       O               rCrctjt|j|j}|j|j}tj|tj|j vsJd ||tj||}tj | |j|S)z.Return a local file path corresponding to URL.z$url ({}) does not fit file path ({})) rmrirelpathr_URL_PATH_PREFIXrFrr r!r r/rr)r&r url_relpath type_pathrLs r7rlzIndex.localfilepathsgoo SMM  5  K * L # #w|K'@'@'H H H H 1 8 8i H H I H H Y?? w||DOODI66 FFFrCc |d}tj|rtj|dSdS#t $r2}t t|Yd}~dSd}~wwxYw)z5Update mtime of description.json file so it is fresh.TrwN) rrmriisfileutimer3rHr_r)r&rirs r7rz Index._touchs #--T-::Dw~~d##    # # # NN3q66 " " " " " " " " " #sA A B 'BB cKt|j|jtgD]}} |||d{V#tt f$r&}t d||Yd}~Hd}~wt$r&}t d||Yd}~vd}~wwxYwt d|jd| zdS)Nzhook %s error: %sz%s files update finished%sz (not updated)) r _HOOKSrr%rTrrHrr exceptionrN)r&rhookrs r7rzIndex._run_hookss$+di0<.AA ? ?D ?d4,,,,,,,,,,"N3 ; ; ; 0$:::::::: ? ? ?  !4dA>>>>>>>> ? ( I J /     s!?B$A11 B$>BB$c$Ktjj}|sy||d{Vs^td|jttjjdz| dd{VdS|j o|j |j}| }|rd}td|j| tj |tjj|d{V}|r!td|j|nG#tjt"f$r.}td |j||d }Yd}~nd}~wwxYw|rd }td|j| tj |tjj|d{V}|r!td|j|nr#tjt"f$rY}td |j||| dd{V|j|jvr|Yd}~dSd}~wwxYw| |p|d{VdS) aRun update for the current `type` of files. Normally update is performed when either is true: * index is never been fetched (description.json missing or broken); * last update was performed longer than configured period of time ago; * some local files are missing or have wrong content (md5 hash differs from description.json). If force is True then update is performed unconditionally. Raises asyncio.TimeoutError, UpdateError. Nz(%s was updated less than %s minutes ago.<FrrzUpdating %s files via %szUpdated %s using %sz%s update error via %s: %sTzfile by file download)rr_TIMEOUTrrHrNrrr}rrrPr`wait_forrSOCKET_TIMEOUT TimeoutErrorr[rr rO)r&rrvrJ file_by_filelog_strupdatedrs r7rz Index.update s$, 4#8#8#A#AAAAAAA  KK: F&-344    //U/33 3 3 3 3 3 3 3 F.ET%:49%E"{  $G KK2DIw G G G $ ' 0,,*9 !! KKK 5ty'JJJ(+6 $ $ $ 0$)Wa $  $  -G KK2DIw G G G  ' 0$$V%7%FGG!!KKK 5ty'JJJ(+6    0$)Waooo7777777779 555GFFFFF oo)9Eo:::::::::::s3A D22E6$E11E6A HI/AI**I/ only_typecK|rj||d}||4d{V||d{Vdddd{VdS#1d{VswxYwYdS|rtd|jD]i}||d}||4d{V||d{Vdddd{Vn#1d{VswxYwYjdStd|jD]i}||d}||4d{V||d{Vdddd{Vn#1d{VswxYwYjdS)zkRun update for all registered `types` of files. Raises asyncio.TimeoutError, UpdateError. Fr5NzUpdating essential fileszUpdating all files)rprrHrNrOr)r@r-ronly_essentialindexr's r7 update_allzIndex.update_allOss  .C 5999Ezz),, * * * * * * * *ll5))))))))) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  . KK2 3 3 3- . .E5999::e,,........,,u---------........................... . . KK, - - - . .E5999::e,,........,,u---------........................... . .s5A A&)A&<C** C4 7C4 E77 F F cF|j||dS)z:Add a hook for type_ to be called after successful update.N)r!rN)r@r'r#s r7add_hookzIndex.add_hookhs% 5d#####rC)T)Fr&N)NFF)OrVrWrXrr`Lockrorkr!rFrDrrOrPrrrrHrrr*r/r3rmPathLiker9r6rrArrrrQrWrrZrrrar$rprrrrfloatr{rrrr r!rrrrrr staticmethodrrrrrr rgrrrrrr rrlrrrr1r3rBrCr7rr$s K % %E [  F F F SUUFsuu 6::QX666v|DD)#)#)#)#V       32; 34 3 3 3 3 2;       ..... .  ... ...[.8  D    [  !c#h!!![!:s:s:::[:LLSLLLLc #c(      [ Mx}MMMM###.4EdEEEE  e       ----!,36u: 2$J$ s5z3s8# $$$$$.KKKKK[KBBBBB[B : : :\ :@gl@w|@@@\@O >?%%%%N$s) 88884C C18C ', CCCCJNr"rr_MAX_TRIES_FOR_DOWNLOADrdinfrrrr7JSONTyper=__annotations__r>rDrJrOrR RuntimeErrorrTr[rhr6rsrrrrrrrzrrrrrrrrr?r1rrWrArCrBrCr7r^s  ////////::::::::::99999999%%%%%%                      "!!!!!-,,,,,888888======EEEEEEEEEE99999999333333NMMMMM 8  #w|$DEEA: $4 %   GL0 1 1 1  ( c5$d38nd3iG H8< X3 4;;;%) H\ ")))GdGGGGMMMM 6     PPPPP\PPP     ,   KKK"+SX h     ! !) ! ! ! ! ! ,8O"=3"=H"="="="=L*, ! ! !  ! ! ! ! !  ,8O7 7"7-27 7777t59) ) ) C) U) ) ) ) X&&&&&R,I,#,$,,,,  %5  0L0L0L 0L{0L  0L 0L0L0L  0Lf  7UH-..JJUJJJJ ;;;F@Il07  ,G $G $G $G $G $G $G $G $TOOOO,  3,1   }             rC