PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/imunify360/venv/lib/python3.11/site-packages/clcommon
Viewing File: /opt/imunify360/venv/lib/python3.11/site-packages/clcommon/clwpos_lib.py
# -*- coding: utf-8 -*- # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved # # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENSE.TXT # import json import re import os import subprocess import logging from pathlib import Path from typing import Union, Iterable, Optional, Tuple, List from enum import Enum from clcommon.clpwd import ClPwd from clcommon.utils import get_rhn_systemid_value class PluginType(Enum): """ Plugin types that are currently detected """ OBJECT_CACHE = 'object-cache' ADVANCED_CACHE = 'advanced-cache' class WpPlugins(Enum): """ Static WP plugin names, that are not detected dynamically from drop-in files, dir names, etc """ UNKNOWN = 'Unknown' WP_ROCKET = 'WP Rocket' ACCELERATE_WP = 'AccelerateWP' def clean_comment(line: str, is_multiline_comment: bool) -> Tuple[str, bool]: """ Yep, this bicycle is needed to handle different comment types in .php file https://www.php.net/manual/en/language.basic-syntax.comments.php and ensure that needed line is not under comment """ if is_multiline_comment: if '*/' not in line: return '', True else: pos = line.find('*/') part1, _ = clean_comment(line[:pos], True) part2, is_multiline_comment = clean_comment(line[pos + 2:], False) return part1 + part2, is_multiline_comment if '//' in line: pos = line.find('//') return line[:pos], False if '#' in line: pos = line.find('#') return line[:pos], False if '/*' in line: pos = line.find('/*') part1, _ = clean_comment(line[:pos], False) part2, is_multiline_comment = clean_comment(line[pos + 2:], True) return part1 + part2, is_multiline_comment return line, False def _is_real_file(file: str) -> bool: realpath_file = os.path.realpath(file) return os.path.isfile(realpath_file) def _check_wp_config_php(abs_path: Union[str, Path]) -> bool: """ WordPress looks for wp-config.php file in the (1) WordPress root and (2) one directory above the root. Check that there is no wp-settings.php file in the second case. This check helps when there is a nested installation, e.g / is WordPress and /wp_path/ is WordPress. """ try: wp_config_php = os.path.join(abs_path, 'wp-config.php') if os.path.exists(wp_config_php) and _is_real_file(wp_config_php): return True except OSError: pass abs_path_level_up = os.path.join(abs_path, os.pardir) wp_config_php_level_up = os.path.join(abs_path_level_up, 'wp-config.php') wp_settings_php = os.path.join(abs_path_level_up, 'wp-settings.php') wp_settings_php_exists = os.path.exists(wp_settings_php) and _is_real_file(wp_settings_php) return os.path.exists(wp_config_php_level_up) and \ not wp_settings_php_exists and \ _is_real_file(wp_config_php_level_up) def _is_real_dir(dir: str) -> bool: realpath_dir = os.path.realpath(dir) return os.path.isdir(realpath_dir) def _check_wp_includes(abs_path: Union[str, Path]) -> bool: """ Check wp-includes exists and is dir. """ wp_includes = os.path.join(abs_path, 'wp-includes') return 'wp-includes' in os.listdir(abs_path) and _is_real_dir(wp_includes) def is_wp_path(abs_path: Union[str, Path]) -> bool: """ Checks whether passed directory is a wordpress directory by checking presence of wp-includes folder and wp-config.php file. """ try: if not os.path.exists(abs_path): return False # skip paths that can't be read (wrong permissions etc) except OSError: return False try: return _check_wp_config_php(abs_path) and _check_wp_includes(abs_path) except OSError: pass return False def find_wp_paths(doc_root: str, excludes: Optional[List[str]] = None) -> Iterable[str]: """ Returns folder with wordpress Empty string is wp is in docroot dir :param doc_root: root path to start search from :param excludes: list of paths that must be excluded from search, e.g. subdomains """ if not os.path.exists(doc_root): return if is_wp_path(doc_root): yield '' for path in Path(doc_root).iterdir(): if not path.is_dir(): continue if excludes and str(path) in excludes: continue if is_wp_path(path): yield path.name def _is_php_define_var_found(var, path): """ Looks for defined php variable with true value """ r = re.compile(fr'^\s*define\s*\(\s*((\'{var}\')|(\"{var}\"))\s*,\s*true\s*\)\s*;') # let`s find needed setting by reading line by line with open(path, encoding='utf-8', errors='ignore') as f: is_multiline_comment = False while True: line = f.readline() if not line: break cleaned_line, is_multiline_comment = clean_comment(line, is_multiline_comment) if r.match(cleaned_line): return True return False def is_advanced_cache_enabled(wordpress_path: Path): """ Detects whether plugin is really enabled, cause not all plugins are enabled 'on load' # https://kevdees.com/what-are-wordpress-drop-in-plugins/ """ wp_config = wordpress_path.joinpath('wp-config.php') # really strange when main wordpress config is absent if not os.path.exists(wp_config): return False return _is_php_define_var_found('WP_CACHE', wp_config) def wp_rocket_plugin(drop_in_path): """ They are advising to check whether WP_ROCKET_ADVANCED_CACHE is defined to ensure plugin is working https://docs.wp-rocket.me/article/134-advanced-cache-error-message """ if accelerate_wp_plugin(drop_in_path) is None and \ _is_php_define_var_found('WP_ROCKET_ADVANCED_CACHE', drop_in_path): return WpPlugins.WP_ROCKET.value return None def accelerate_wp_plugin(drop_in_path): """ Checking if the plugin folder name exists in the drop-in """ with open(drop_in_path, encoding='utf-8', errors='ignore') as f: if '/clsop' in f.read(): return WpPlugins.ACCELERATE_WP.value return None def get_wp_cache_plugin(wordpress_path: Path, plugin_type: str): """ Looking for object-cache.php or advanced-cache.php in wordpress folder If found - tries to find 'plugin-owner' of <-cache>.php by content comparison If cannot be found -> tries to read <-cache>.php headers looking for Plugin name: <Plugin> """ wp_content_dir = wordpress_path.joinpath("wp-content") activated_cache = wp_content_dir.joinpath(f'{plugin_type}.php') if not os.path.exists(activated_cache): return None if plugin_type == PluginType.ADVANCED_CACHE.value and not is_advanced_cache_enabled(wordpress_path): return None plugins_dir = wp_content_dir.joinpath("plugins") plugin_name = get_wp_cache_plugin_by_scanning_dirs(activated_cache, plugins_dir) \ or get_wp_cache_plugin_by_header(activated_cache) \ or accelerate_wp_plugin(activated_cache) \ or wp_rocket_plugin(activated_cache) \ or WpPlugins.UNKNOWN.value return plugin_name def get_wp_cache_plugin_by_scanning_dirs(activated_plugin: Path, plugins_dir: Path) -> Optional[str]: """ Scanning plugins/* dir and looking for similar <object/advanced_cache>.php """ activated_plugin_bytes = activated_plugin.read_bytes() if not os.path.exists(plugins_dir): return None for plugin in plugins_dir.iterdir(): for root, dirs, files in os.walk(plugin): if activated_plugin.name in files: plugin_object_cache_path = Path(root) / activated_plugin.name if plugin_object_cache_path.read_bytes() == activated_plugin_bytes: return plugin.name return None def get_wp_cache_plugin_by_header(activated_plugin: Path) -> Optional[str]: """ Looking for Plugin name: <Some name> in <object/advanced.php> headers """ if not os.path.exists(activated_plugin): return None # must be enough to loop through headers max_top_lines_count = 30 r = re.compile(r'^.*plugin name:\s*(?P<plugin_name>[\w ]+)\s*$', re.IGNORECASE) with open(activated_plugin, encoding='utf-8', errors='ignore') as f: for _ in range(max_top_lines_count): line = f.readline() match = r.search(line) if match is not None: return match.group('plugin_name') return None def get_wp_paths_with_enabled_module(user, user_wp_paths, plugin='object_cache'): """ Filter user`s wp paths with paths with enabled module """ paths_with_enabled_module = [] try: home = ClPwd().get_homedir(user) except ClPwd.NoSuchUserException: return [] config = os.path.join(home, '.clwpos', 'clwpos_config.json') if not os.path.exists(config): return [] try: with open(config, encoding='utf-8', errors='ignore') as f: conf = f.read() data = json.loads(conf) except Exception: return [] modules_data = data.get('docroots', {}).get('public_html', {}) for wp_path in user_wp_paths: if wp_path in modules_data and plugin in modules_data[wp_path]: paths_with_enabled_module.append(wp_path) return paths_with_enabled_module def install_accelerate_wp(): packages = [] if not os.path.exists('/usr/bin/cloudlinux-awp-admin'): packages.append('accelerate-wp') if not os.path.exists('/usr/sbin/cloudlinux-ssa-manager'): packages.append('alt-php-ssa') if not os.path.exists('/usr/sbin/cloudlinux-xray-agent'): packages.append('alt-php-xray') if packages: install_command = ["yum", "install", "-y"] + packages proc = subprocess.run(install_command, capture_output=True, text=True, check=False) logging.debug('Installing AccelerateWP packages captured out: %s, err: %s', proc.stdout, proc.stderr) if not os.path.exists('/usr/share/clos_ssa/ssa_enabled'): proc = subprocess.run(['/usr/sbin/cloudlinux-ssa-manager', 'enable-ssa'], capture_output=True, text=True, check=False) logging.debug('Activation SSA captured out: %s, err: %s', proc.stdout, proc.stderr) proc = subprocess.run( ['/usr/sbin/cloudlinux-autotracing', 'enable', '--all'], capture_output=True, text=True, check=False, ) logging.debug('Activation autotracing captured out: %s, err: %s', proc.stdout, proc.stderr) system_id = get_rhn_systemid_value('system_id') if system_id: proc = subprocess.run( ['/usr/sbin/cloudlinux-xray-manager', 'enable-user-agent', '--system_id', system_id.replace('ID-', '')], capture_output=True, text=True, check=False, ) logging.debug('Activation xray manager captured out: %s, err: %s', proc.stdout, proc.stderr) def configure_accelerate_wp(async_set_suite: bool = False, source: str = None): """ 1. Installs needed packages 2. Enables autotracing 3. Allows AccelerateWP Free for all """ install_accelerate_wp() if os.path.exists('/var/clwpos/admin/allowed_for_all_site_optimization.flag'): return command = ['/usr/bin/cloudlinux-awp-admin', 'set-suite', '--allowed-for-all', '--suites', 'accelerate_wp'] if source == 'BILLING_OVERRIDE': command.extend(['--source', 'BILLING_OVERRIDE']) # For WHMCS command if async_set_suite: subprocess.Popen( # pylint: disable=consider-using-with command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) else: proc = subprocess.run(command, check=True, capture_output=True, text=True) logging.debug('Activation AccelerateWP Free captured out: %s, err: %s', proc.stdout, proc.stderr) def configure_accelerate_wp_premium(source: str = None): install_accelerate_wp() # just in case server already allowed for all if os.path.exists('/var/clwpos/admin/allowed_for_all_object_cache.flag'): return command = ['/usr/bin/cloudlinux-awp-admin', 'set-suite', '--visible-for-all', '--suites', 'accelerate_wp_premium'] # If this is a request from WHMCS, we must pass --source to correctly save the suite status # and not overwrite the settings of users if source == 'BILLING_OVERRIDE': command.extend(['--source', 'BILLING_OVERRIDE', '--preserve-user-settings']) # set AccelerateWP visible for all users proc = subprocess.run(command, check=True, capture_output=True, text=True) logging.debug('Activation AccelerateWP Premium captured out: %s, err: %s', proc.stdout, proc.stderr) def configure_accelerate_wp_cdn(source: str = None): install_accelerate_wp() # just in case server already allowed for all if os.path.exists('/var/clwpos/admin/allowed_for_all_cdn.flag'): return # set AccelerateWP visible for all users command_cdn = ['/usr/bin/cloudlinux-awp-admin', 'set-suite', '--allowed-for-all', '--suites', 'accelerate_wp_cdn'] # If this is a request from WHMCS, we must pass --source to correctly save the suite status if source == 'BILLING_OVERRIDE': command_cdn.extend(['--source', 'BILLING_OVERRIDE']) proc = subprocess.run(command_cdn, check=True, capture_output=True, text=True) logging.debug('Activation AccelerateWP CDN captured out: %s, err: %s', proc.stdout, proc.stderr) # set AccelerateWP CDN PRO visible for all users command_cdn_pro = ['/usr/bin/cloudlinux-awp-admin', 'set-suite', '--visible-for-all', '--suites', 'accelerate_wp_cdn_pro'] # If this is a request from WHMCS, we must pass --source to correctly save the suite status # and not overwrite the settings of users if source == 'BILLING_OVERRIDE': command_cdn_pro.extend(['--source', 'BILLING_OVERRIDE', '--preserve-user-settings']) proc = subprocess.run(command_cdn_pro, check=True, capture_output=True, text=True) logging.debug('Activation AccelerateWP CDN PRO captured out: %s, err: %s', proc.stdout, proc.stderr) def configure_upgrade_url(upgrade_url): if not upgrade_url: return options_json = subprocess.run(['/usr/bin/cloudlinux-awp-admin', 'get-options'], check=True, capture_output=True, text=True).stdout options = json.loads(options_json) # nothing to do, upgrade url already set to same value if upgrade_url == options.get('upgrade_url'): return proc = subprocess.run(['/usr/bin/cloudlinux-awp-admin', 'set-options', '--upgrade-url', upgrade_url, '--suite', 'accelerate_wp_premium'], check=True, capture_output=True, text=True) logging.debug('Setting AccelerateWP Premium upgrade url captured out: %s, err: %s', proc.stdout, proc.stderr)