PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/opt/imunify360/venv/lib/python3.11/site-packages/clcommon/
Upload File :
Current 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)