z:WebsiteCollector._hash_and_sort_docroots..s 1Q4r1)keyzYHash-based selection: %d already sent, %d remaining limit, selected %d from %d candidates)rlenr+getmaxrr#itemshashlibsha256encode hexdigestappendsort) r.r2already_sent_countremaining_limithashed_docrootsdocrootdata docroot_hashselected_docroots_s r/_hash_and_sort_docrootsz(WebsiteCollector._hash_and_sort_docroots\s > ! !!3!7!7 B!G!GHHa2D!DEE a   K  U0$. B B BI }   0 0 *0022 B BMGT">'..*:*:;;EEGGL  " "L'4#@ A A A A 000 /0@0@ A . . Aw)- g & & ;,o/00#m2D2D F F F ! r1cdi}d} t}|D])} t|}i}|D]\}}t|} | rut jt | tjrI||r|dz }o||vr ||gd||<||d |d} | D]&\}} t|tkrd} n| ||<'| rn5#t$r'} |jd|| Yd} ~ #d} ~ wwxYwn2#t$r%} |jd | Yd} ~ nd} ~ wwxYw|jd t||||S) z Get docroots for sending, with optional filtering and limit. Returns: Dict of docroots ready for processing (limited and filtered by sent status) r)rGusernamedomainsrPFTz%Failed to get domains for user %s: %sNz([WEBSITE-COLLECTOR] Panel API failed: %sz>>sAEC0D  E D>D93E9D>>E E1 E,,E1cj |jret|jdd5}tj||_|jdddddS#1swxYwYdSdS#t$r&}|jd|Yd}~dSd}~wwxYw)z+Load sending statistics from tracking file.rutf-8encodingzLoaded sending statsNz Failed to load sending stats: %s) r(rQopenjsonloadr+rr#rVr.fr`s r/r,z$WebsiteCollector._load_sending_statss E%%'' >$/3AAA>Q)-1D&K%%&<===>>>>>>>>>>>>>>>>>> > > E E E K  @! D D D D D D D D D Es:0B4A3& B3A77B:A7;B B2 B--B2c|jdd}|tkrdS|jdt t t |t|j|_dS)z Sync sending stats cache within the same version to remove entries for docroots that no longer exist in the system. r"rNzj[WEBSITE-COLLECTOR] New version of collector, current version=%s, previously collected=%s re-setting cache) r+r;rrinfor$rr r*)r.prev_collected_vers r/r-z+WebsiteCollector.reset_cache_within_versions "/334GKK  * * F DEH\\SVWiSjSj l l l&d&@AAr1 file_pathc t|}|}|r|dn|}|r@|r,t jt|t js|j d|dS| j tkr5|j d|| j dS|ddS#t$r'}|j d ||Yd}~dSd}~wwxYw) zx Read any file content on-demand for sending. Supports both Apache configs and .htaccess files. F)strictz([WEBSITE-COLLECTOR] Cannot read file: %sNz5[WEBSITE-COLLECTOR] File too big: %s (size: %d bytes)rdreplace)rferrorsz-[WEBSITE-COLLECTOR] Error reading file %s: %s)r is_symlinkresolverQis_filerRrSr$rTrrWstatst_sizer read_textrV)r.roprtreal_pr`s r/read_file_contentz"WebsiteCollector.read_file_contentsB  YAJ0:AQYYeY,,,FMMOO (8(8 RYs6{{TVT[=\=\  !!"LiXXXt{{}}$66 !!"Y[d"(++--"7999t##WY#GG G    K  MyZ[ \ \ \44444 s%B,D0ADD E(E  Ec tjtj|jd<t |jd<t|jdd5}tj |j|ddd d d n #1swxYwY|j d d S#t$r&}|j d |Yd }~d Sd }~wwxYw) z)Save sending statistics to tracking file. last_updatedr"wrdreF)indent ensure_asciiNzSaved sending statsz4[WEBSITE-COLLECTOR] Failed to save sending stats: %s)rnowrutc isoformatr+rrgr(rhdumprr#rVrWrjs r/_save_sending_statsz$WebsiteCollector._save_sending_statss< Y19hl1K1K1U1U1W1WD ~ .6?D 2 3dosW=== O $,aNNNN O O O O O O O O O O O O O O O K  3 4 4 4 4 4 Y Y Y K  TVW X X X X X X X X X Ys<AB1 B > B1 BB1BB11 C!;CC!rGc~|jdi|d}t|S)zMCheck if docroot data was fully sent (both htaccess and user apache configs).r!F)r+r;bool)r.rG docroot_statss r/rUz WebsiteCollector.is_docroot_sents8*..z2>>BB7ERR M"""r1rOc||jdvrd|i|jd|<tjtj|jd|d<dS)zMark docroot data as sent.r!rOlast_processedN)r+rrrrr)r.rGrOs r/mark_docroot_sentz"WebsiteCollector.mark_docroot_sent sj $,Z8 8 8H7D z *7 3EMLQYQ]D^D^DhDhDjDj:&w/0@AAAr1cd|jd<dS)z#Mark global apache configs as sent.Tr N)r+)r.s r/mark_global_apache_sentz(WebsiteCollector.mark_global_apache_sents;?7888r1c(tjS)z Check if website collection is disabled via opt-out file. Returns: True if collection is disabled, False otherwise )rrQr6r1r/is_collection_disabledz'WebsiteCollector.is_collection_disableds"$$$r1c6|ddddS)z Create necessary directory structure. Args: storage_dir: Directory for storing website collector state iT)modeparentsexist_okN)mkdir)r.rs r/r%z$WebsiteCollector._ensure_directories$s% udTBBBBBr1 client_id system_idplatformpanelserverc lttjtt|||||iid S)z&Create base record with common fields.) collection_timestampcollection_versioncollected_package_versionrrrrrrH meta_data)inttimerr )r.rrrrrs r/_create_base_recordz$WebsiteCollector._create_base_record-s?%( $4$4"))4""    r1recordc0tj|ddS)z` Serialize record to JSON. Returns: Serialized JSON string F),:)r separators)rhdumps)r.rs r/_serialize_recordz"WebsiteCollector._serialize_record=sz&uLLLLr1 base_recordcB|}i|d<i|d<|S)z Create new record part with base data but empty data/meta_data sections. Args: base_record: Base record to copy from Returns: New record with empty data sections rHr)copy)r.r new_records r/_create_new_record_partz(WebsiteCollector._create_new_record_partFs/!%%''  6"$ ;r1 file_contentrtcurrent_recordrecords_to_sendc"|}|d|d<|d|d<||d|<|r ddi|d|<||}t|d} | tkrb|drZ||} || ||}||d|<|r ddi|d|<n|}|S)a Add file to current record, splitting if size limit exceeded. Args: file_path: Path of the file to add file_content: Content of the file is_symlink: Whether the file is a symlink current_record: Current record being built base_record: Base record template for new parts records_to_send: List to append completed records to Returns: Updated current record (may be new if split occurred) rHrrtTrd)rrr:r@rrBr) r.rorrtrrr temp_recordtemp_serialized temp_sizecurrent_serializeds r/_add_file_to_recordsz%WebsiteCollector._add_file_to_recordsUs8$%))++ ,V499;; F#1+#>#C#C#E#E K *6 FI&  G3?2FK $Y /00==..w7788  & &>&+A &"&!7!7!G!G   " "#5 6 6 6!99+FFN1=N6 "9 - N:F9M{+I6)Nr1api_urldry_runserialized_dataremote_conf_verc|r} t|jdd5}||dzdddn #1swxYwYdS#t$r&}|jd|Yd}~dSd}~wwxYwd t d |d }|} tr|d} tj | d } t|d} t| } d| | z z dz} |j d| d| d| tj| d} t| }|j d| d|dd}d}d}t!|D]} ||| |d}|jdkrdS|j}||dz kr=|j d|j|dz|t'jd|zn |j d|j#t$rv}t+|}||dz kr8|j d|dz||t'jd|zn|jd|Yd}~d}~wwxYw|rd|}n |rd|}nd}|jd ||dS)!zJSend record or save to JSONL file for dry run. Returns True if successful.ardre NTz3[WEBSITE-COLLECTOR] Error saving dry-run record: %sFz text/plainrK)z Content-TypezX-Website-Collector-Version) compresslevelrNdz;Applied gzip compression: %s -> %s bytes (%.1f%% reduction)rasciizBase64 encoded: %s -> %s bytes)rHheaderstimeoutz"Non-200 response %s, attempt %d/%drz)Final attempt failed: Non-200 response %szHTTP error on attempt %d/%d: %sz=[WEBSITE-COLLECTOR] Final HTTP error while sending record: %szHTTP z Exception: z Unknown errorzF[WEBSITE-COLLECTOR] Failed to send record after %d retries. Reason: %s)rgr)writerVrrWrrr@gzipcompressr:r#base64 b64encodedecoderangepost status_codersleepr$)r.sessionrrrrrkr`r data_to_sendcompressed_dataoriginal_bytescompressed_bytes reduction base64_bytes max_retries last_errorlast_status_codeattemptresponsereasons r/_send_or_collect_recordz(WebsiteCollector._send_or_collect_records I  $+S7CCC4qGGOd2333444444444444444t    !!"WYZ[[[uuuuu  !-29/M/MO/M/MG+L" P.55g>> "&- A"N"N"N!$_%;%;G%D%D!E!E#&#7#7 !1N!BBcI  !!"_%3"7"7! [ZZZ 1<0000 ))*U*2*>@@@!nnn!$QJq00 ))*K*1A+{AGGG 1<0000 ))*iklmmmn  )3!133 )3z33( K  f)6 3 3 35sYA A A AA AA A=A88A=$H$6A-H$$ J$.A+JJ$remote_config_versionc ||||||} | dgdd|| } g} |j} | dg}|jdt|t|dD]y\}}|jd|d| |d}|d }| |d||d d | | | } z|j }|d g| d d<|dd| d d<| | }| |t| D]M\}}|jd|dzt| |||||| sd SNdS)z Send global Apache configs and system info, splitting into multiple records if needed. Returns: True if all records sent successfully, False otherwise rOrP document_root config_pathsz#Found %d global Apache config filesrNz!Processing global config file: %sroN# ERROR: File unreadablertFloaded_modulesrHmodulesapache_versionversionz"Sending global config record %d/%dT)rupdaterr'collect_global_config_pathsr;rr#r: enumerater|rget_apache_system_inforrBr)r.rrrrrrrrrrrrglobal_configsri path_infor system_inforrs r/_send_global_configsz%WebsiteCollector._send_global_configss<"..y)XuV\]]      55kBB.JJLL%))."==  ?\ARARSSS%lA66  LAy K  A9[CY Z Z Z11)K2HIIL#9 !66+& immLRW6X6X _NN +BBDD ,7OO%>*;7BTBXBXYeglBmBm& _&&NN                ,OOQYPZ[[  L,,^<<  **  !-n!=I#'#9#9)K:P#Q#QL#+'A %)%>%>!+. immLZ_>`>`& _&&NN                 & ! 7 7!%!7!7!G!G   " "#5 6 6 6#,O"<"<   A K  ?!a%QTUdQeQe f f f//'?\qrr uu ts&AC??DD AF77F;>F;Fc tj} |stjnd} |jdds|jd|rdnd|| |||||||| rD|sB|| |jd| } | D]\} } tj| z tkr|j dn|jd | | d || | d | d | |||||||| rM|sK|| | d | |jd | tj| z }|r"|jd |j|n|jd|n7#t$$r*}|j d|rdnd|Yd}~nd}~wwxYw| r| dSdS#| r| wwxYw)a Send data directly or save to JSONL file in dry-run mode. Read htaccess and apache configs on-demand and track sending status. Args: dry_run: If True, saves data to dry-run.jsonl file instead of sending to API Nr Fz%s global Apache configs CollectingSendingz'Global Apache configs sent successfullyz=[WEBSITE-COLLECTOR] Timeout reached during docroot processingz!Processing docroot %s for user %srOrPzDocroot %s sent successfullyz.DRY RUN completed: saved to %s in %.1f secondsz*Sequential sending completed: %.1f secondsz([WEBSITE-COLLECTOR] Failed during %s: %szdry runzsequential sending)rrequestsSessionr+r;rr#rrrrar=rrWrrrmr)rVclose)r.rrrrrrrr start_timerunprocessed_docrootsrG domain_dataexecution_timer`s r/ send_datazWebsiteCollector.send_dataSs "Y[[ ,3=("$$$; %))*FNN U !!"dll[deee,,W-4-4-6-6-5-2-3-BDD U#U4466600222 ))*STTT$(#A#A#C#C (<(B(B(D(D S S$9;;+o==K%%&efffE !!"EwP[\fPghhh**7+6y+A+6z+B+2+2+2+4+4+3+0+1+@ B BS#S..w J8OPPP00222 ))*H'RRR!Y[[:5N `   !QSWSdftuuuu !!"NP^___ ! ! ! K  H+2Lii8L ! ! ! ! ! ! ! ! !     w   s0G4H! I2! I+ I I2II22J )NN)r3N)F)#__name__ __module__ __qualname____doc__loggingLoggerrrrr0rrLrar,r-r$r|rrrUrr staticmethodrr%rrrrlistrrrrr r6r1r/rr*s%*%*w~%*HTN%*^fgj^k%*%*%*%*N0!T0!d0!0!0!0!d:?4:?:?:?:?xEEEE B B B B38C=2 Y Y Y Y#s#t#### kkkkkkk@@@@%D%%%\%CtCCCCC S S C X[ eh mq Hn    MS#XM3MMMM 4S> d3PS8n    .c..RV.-1#s(^.JNsTWx...2.7;CH~....`P),P*.P25 P 25 P :> PPPPd;&);'+;), ; ), ; (+ ;%(;&);58;=A;;;;z=#===_b=$(=58=EH=%(=14=>A=Z]=bf====N#(P P  P  P  P  P  P  P *-P  P -1P P P P P P r1r))rhrrRrrrr>rrpathlibrtypingrrrrr r lve_utilsr clcommon.cpapir r clcommon.clpwdr %cl_website_collector.apache_processorr&cl_website_collector.docroot_processorrcl_website_collector.constantsrrrrrrrrrrrr6r1r/rs  ''''''''&&&&&&&&&&!!!!!!////////******AAAAAACCCCCC                        y  y  y  y  y  y  y  y  y  y  r1