PNG  IHDRxsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<,tEXtComment File Manager

File Manager

Path: /opt/cloudlinux/venv/lib/python3.11/site-packages/clcommon/

Viewing File: clconfpars.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 configparser
import locale
import os
import re
import syslog
from collections import namedtuple


class WebConfigParsingError(Exception):
    def __init__(self, message):
        self.message = message


class WebConfigMissing(Exception):
    def __init__(self, message):
        self.message = message


SECHEAD = 'asection'


def load(path, case_sensitive=False, ignore_bad_encoding=False):
    config = configparser.ConfigParser(allow_no_value=True,
                                       interpolation=None,
                                       strict=False)
    if case_sensitive:
        config.optionxform = str
    if ignore_bad_encoding:
        with open(path, 'rb') as f:
            raw = f.read().decode(locale.getpreferredencoding(), 'replace')
    else:
        with open(path, 'r', encoding='utf-8') as f:
            raw = f.read()
    config.read_string(f'[{SECHEAD}]\n' + raw, source=path)
    return dict(config.items(section=SECHEAD))


_QUOTES = "'", '"'

def _strip_escape_quotes_of_config_value(val: str) -> str:
    """
    Strips single or double quote char only if the quote present from both sides.
    """
    if val.startswith(_QUOTES) and val.endswith(_QUOTES):
        return val[1:-1]
    return val


def load_fast(path, delimiter="=", strip_quotes=False):
    data = {}
    with open(path, "r", encoding="utf-8", errors="surrogateescape") as f:
        for line in f.readlines():
            parts = line.split(delimiter, 1)
            try:
                key, value = parts
            except ValueError:
                # Skip broken lines
                continue

            value = value.strip()
            value = (
                _strip_escape_quotes_of_config_value(value)
                if strip_quotes
                else value
            )
            data[key.strip()] = value
    return data


cache = {}

def load_once(path, ignore_errors=False):
    """
    Read ini file once (cached) and return its content as dict
    """
    try:
        res = cache[path]
    except KeyError:
        try:
            res =  cache[path] = load(path)
        except (IOError, configparser.Error):
            if not ignore_errors:
                raise
            res = cache[path] = {}
    return res


def change_settings(settings_dict, path, tmp_path=None):
    if not tmp_path:
        tmp_path = path + ".tmp"

    used_keys = []

    with (open(path, 'r', encoding='utf-8') as fin,
          open(tmp_path, 'w', encoding='utf-8') as fout):
        for line in fin:
            stripped_line = line.strip()
            if stripped_line and not stripped_line.startswith('#'):
                key, _ = stripped_line.split('=', 1)
                key = key.strip()
                if key in settings_dict:
                    fout.write(f'{key}={settings_dict[key]}\n')
                    used_keys.append(key)
                    continue
            fout.write(line)

    with open(tmp_path, 'a', encoding='utf-8') as fout:
        for key in settings_dict:
            if key not in used_keys:
                fout.write(f'{key}={settings_dict[key]}\n')

    os.rename(tmp_path, path)


_NGINX_TOKENS_RE = re.compile(
    r"""
    (
      # Comments
      (:? \# .* $ )

      # Single-, double-quoted strings and bare strings without whitespaces
      | (:? "[^"\n]*?" )
      | (:? '[^'\n]*?' )
      | (:? [^"';\s\{\}]+ )

      # Structural characters
      | ;
      | \{
      | \}
      | \n
    )
    """,
    re.IGNORECASE | re.MULTILINE | re.VERBOSE,
)


def _ngx_tokenize(data):
    tokens = (
        match.group(0)
        for match in _NGINX_TOKENS_RE.finditer(data)
        if match and match.group(0)
    )
    # Explicitly ignore comments
    return (tok for tok in tokens if not tok.startswith('#'))

def _ngx_take_until(it, val):
    for tok in it:
        if tok in val:
            return

        yield tok

def _ngx_take_until_block_end(it):
    lvl = 1
    for t in it:
        if t == "{":
            lvl += 1
        elif t == "}":
            lvl -= 1
        if lvl < 1:
            return
        yield t

def _ngx_scan_block_info(block_tokens, need_fields):
    """Scan a block for required fields, skips nested blocks"""
    info = {}
    for tok in block_tokens:
        # We need to skip until the end of inner block if it occurs
        if tok == "{":
            for _ in _ngx_take_until_block_end(block_tokens):
                pass
        # Now gather the value, the last occurrence is in priority
        if tok in need_fields:
            value_tokens = _ngx_take_until(block_tokens, ";\n")
            info[tok] = list(value_tokens)

    return info


def nginx_conf_loose_parser(data):
    """
    Parse content of NGINX configuration in a manner tolerant to minor mistakes
    and extract relevant fields from all `server` directives.

    Relevant fields are:
    - `server_name`
    - `root` - returned as `document_root`
    - `ssl` - if `listen` field contains "ssl" word

    Doesn't handle interpolated values (ex. `${val}`) outside of quoted strings
    """
    tokens = _ngx_tokenize(data)
    for tok in tokens:
        if tok != "server":
            continue

        # Nothing seems to be allowed between "server" directive and
        #  the opening of his block, so we just discard everything
        #  until first block opening seen
        for _ in _ngx_take_until(tokens, "{"):
            pass

        # Limit further scan by the inside of block
        block_tokens = _ngx_take_until_block_end(tokens)

        # By using only `block_tokens` we ensure all blocks are properly delimited
        info = _ngx_scan_block_info(block_tokens, ("server_name", "root", "listen"))
        try:
            server_name = info["server_name"]
            root = info["root"]
        except KeyError:
            continue
        if not server_name and not root:
            continue

        yield {
            "server_name": _strip_escape_quotes_of_config_value(server_name[0]),
            "document_root": _strip_escape_quotes_of_config_value(root[0]),
            "ssl": "ssl" in info.get("listen", []),
        }


def nginx_conf_parser(conf_file):
    """Parse NGINX config file, see `nginx_conf_loose_parser` for more details"""
    if not os.path.isfile(conf_file):
        raise WebConfigMissing(f'File does not exists {conf_file}')

    dirty_data = read_unicode_file_with_decode_fallback(conf_file)

    return list(nginx_conf_loose_parser(dirty_data))


def apache_conf_parser(conf_file):
    if not os.path.isfile(conf_file):
        raise WebConfigMissing(f'File does not exists {conf_file}')

    conf_data = []

    data_all = read_unicode_file_with_decode_fallback(conf_file).splitlines()

    data = [i for i in data_all if re.search('^((?!#).)*$', i)]

    ID = 0
    enable = False

    result = {}
    vhost = []
    while len(data) > 0:
        out = data.pop(0)
        if "<VirtualHost" in out:
            ip_port = out.split()[1]
            port = '0'
            try:
                ip, port = ip_port.split(':')
                port = port.replace('>', '')
            except ValueError:
                ip = ip_port
            vhost.append(ip)
            vhost.append(port)
            enable = True
            continue

        if "</VirtualHost>" in out:
            result[ID] = vhost
            ID+=1
            enable = False
            vhost = []
            continue

        if enable:
            vhost.append(out)
            continue

    for value in result.values():
        # result[i][0] is an IP
        # result[i][1] is a port
        data = {
                'user' : None,
                'server_name' : '',
                'document_root' : '',
                'server_alias' : None,
                'port' : int(value[1]),
                'ssl' : False,
               }
        for line in value:
            if "ServerName" in line:
                data['server_name'] = line.split()[1].strip().replace('www.', '')
                continue
            if "DocumentRoot" in line:
                # remove all whitespaces (first strip) and also quotes (second one)
                data['document_root'] = line.split()[1].strip().strip('"')
                continue
            if "ServerAlias" in line:
                data['server_alias'] = ','.join(str(n) for n in line.split()[1:])
                continue
            if "SuexecUserGroup" in line:
                data['user'] = line.split()[1].strip()
            if "SSLEngine" in line:
                data['ssl'] = line.split()[1].strip().lower() == 'on'

        conf_data.append(data)

    return conf_data


def openlitespeed_conf_parser(conf_file):
    """
    Parsing Open Litespeed config
    Retrieves data from user's openlitespeed.conf
    :param: full httpd.conf path (/usr/local/directadmin/data/users/<username>/openlitespeed.conf)
    :return: list of virtual host configurations.
    """
    if not os.path.isfile(conf_file):
        raise WebConfigMissing(f'File does not exist {conf_file}')

    conf_data = []
    data_all = read_unicode_file_with_decode_fallback(conf_file).splitlines()
    vhost_dict = None

    for line in data_all:
        line = line.strip()
        if line.startswith('virtualHost'):
            # Add the previous virtual host to the list
            if vhost_dict:
                conf_data.append(vhost_dict)
            # Start a new virtual host
            vhost_dict = {'user': None, 'server_name': None, 'document_root': None,
                          'server_alias': None, 'port': None, 'ssl': False}

            # Extract port information
            match_port = re.search(r'-(\d+)', line)
            vhost_dict['port'] = int(match_port.group(1)) if match_port else None

        if vhost_dict is None:
            continue

        if line.startswith('user'):
            parts = line.split()
            if len(parts) == 2:
                vhost_dict['user'] = parts[1]
        elif line.startswith('docRoot'):
            parts = line.split()
            if len(parts) == 2:
                vhost_dict['document_root'] = parts[1]
        elif line.startswith('vhDomain'):
            parts = line.split()
            if len(parts) == 2:
                vhost_dict['server_name'] = parts[1]
        elif line.startswith('vhAliases'):
            parts = line.split()
            if len(parts) == 2:
                vhost_dict['server_alias'] = parts[1]
        elif line.startswith('vhssl'):
            vhost_dict['ssl'] = True

    # Add the last virtual host to the list
    if vhost_dict:
        conf_data.append(vhost_dict)

    return conf_data


PamLVECfg = namedtuple('PamLVECfg', ['min_uid', 'cagefs_enabled', 'groups'])

def parse_pam_lve_config(configfile):
    """
    Parse string like:
    "session      required      pam_lve.so      500      1     group1,group2"
    :param configfile: path to config file to parse
    :type configfile: str
    :return: PamLVECfg instance when pam_lve configuratiom is found, None otherwise
    :rtype: namedtuple
    :raises: IOError, ValueError
    """
    with open(configfile, 'r', encoding='utf-8') as f:
        for line in f:
            if line.startswith('#'):
                continue
            s = line.split()
            if len(s) >= 3 and s[2] == 'pam_lve.so':
                # parse config string taking pam_lve defaults into account
                min_uid = int(s[3]) if len(s) >= 4 else 500
                cagefs_enabled = bool(int(s[4])) if len(s) >= 5 else False
                groups = s[5].split(',') if len(s) >= 6 else ['wheel']
                return PamLVECfg(min_uid, cagefs_enabled, groups)
    # pam_lve line is not found in config file
    return None


def read_unicode_file_with_decode_fallback(file_path: str) -> str:
    with open(file_path, 'rb') as f:
        raw_data = f.read()
    try:
        return raw_data.decode()
    except UnicodeDecodeError:
        syslog.syslog(
            syslog.LOG_WARNING,
            f'Failed to decode "{file_path}" content as utf-8 - loading with placeholders for invalid unicode sequences'
        )
        return raw_data.decode(errors='replace')
b IDATxytVսϓ22 A@IR :hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-EIENT ;@xT.i%-X}SvS5.r/UHz^_$-W"w)Ɗ/@Z &IoX P$K}JzX:;` &, ŋui,e6mX ԵrKb1ԗ)DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA݀!I*]R;I2$eZ#ORZSrr6mteffu*((Pu'v{DIߔ4^pIm'77WEEE;vƎ4-$]'RI{\I&G :IHJ DWBB=\WR޽m o$K(V9ABB.}jѢv`^?IOȅ} ڶmG}T#FJ`56$-ھ}FI&v;0(h;Б38CӧOWf!;A i:F_m9s&|q%=#wZprrrla A &P\\СC[A#! {olF} `E2}MK/vV)i{4BffV\|ۭX`b@kɶ@%i$K z5zhmX[IXZ` 'b%$r5M4º/l ԃߖxhʔ)[@=} K6IM}^5k㏷݆z ΗÿO:gdGBmyT/@+Vɶ纽z񕏵l.y޴it뭷zV0[Y^>Wsqs}\/@$(T7f.InݺiR$푔n.~?H))\ZRW'Mo~v Ov6oԃxz! S,&xm/yɞԟ?'uaSѽb,8GלKboi&3t7Y,)JJ c[nzӳdE&KsZLӄ I?@&%ӟ۶mSMMњ0iؐSZ,|J+N ~,0A0!5%Q-YQQa3}$_vVrf9f?S8`zDADADADADADADADADAdqP,تmMmg1V?rSI꒟]u|l RCyEf٢9 jURbztѰ!m5~tGj2DhG*{H9)꒟ר3:(+3\?/;TUݭʴ~S6lڧUJ*i$d(#=Yݺd{,p|3B))q:vN0Y.jkק6;SɶVzHJJЀ-utѹսk>QUU\޲~]fFnK?&ߡ5b=z9)^|u_k-[y%ZNU6 7Mi:]ۦtk[n X(e6Bb."8cۭ|~teuuw|ήI-5"~Uk;ZicEmN/:]M> cQ^uiƞ??Ңpc#TUU3UakNwA`:Y_V-8.KKfRitv޲* 9S6ֿj,ՃNOMߤ]z^fOh|<>@Å5 _/Iu?{SY4hK/2]4%it5q]GGe2%iR| W&f*^]??vq[LgE_3f}Fxu~}qd-ږFxu~I N>\;͗O֊:̗WJ@BhW=y|GgwܷH_NY?)Tdi'?խwhlmQi !SUUsw4kӺe4rfxu-[nHtMFj}H_u~w>)oV}(T'ebʒv3_[+vn@Ȭ\S}ot}w=kHFnxg S 0eޢm~l}uqZfFoZuuEg `zt~? b;t%>WTkķh[2eG8LIWx,^\thrl^Ϊ{=dž<}qV@ ⠨Wy^LF_>0UkDuʫuCs$)Iv:IK;6ֲ4{^6եm+l3>݆uM 9u?>Zc }g~qhKwڭeFMM~pМuqǿz6Tb@8@Y|jx](^]gf}M"tG -w.@vOqh~/HII`S[l.6nØXL9vUcOoB\xoǤ'T&IǍQw_wpv[kmO{w~>#=P1Pɞa-we:iǏlHo׈꒟f9SzH?+shk%Fs:qVhqY`jvO'ρ?PyX3lх]˾uV{ݞ]1,MzYNW~̈́ joYn}ȚF߾׮mS]F z+EDxm/d{F{-W-4wY듏:??_gPf ^3ecg ҵs8R2מz@TANGj)}CNi/R~}c:5{!ZHӋӾ6}T]G]7W6^n 9*,YqOZj:P?Q DFL|?-^.Ɵ7}fFh׶xe2Pscz1&5\cn[=Vn[ĶE鎀uˌd3GII k;lNmشOuuRVfBE]ۣeӶu :X-[(er4~LHi6:Ѻ@ԅrST0trk%$Č0ez" *z"T/X9|8.C5Feg}CQ%͞ˣJvL/?j^h&9xF`њZ(&yF&Iݻfg#W;3^{Wo^4'vV[[K';+mӍִ]AC@W?1^{එyh +^]fm~iԵ]AB@WTk̏t uR?l.OIHiYyԶ]Aˀ7c:q}ힽaf6Z~қm(+sK4{^6}T*UUu]n.:kx{:2 _m=sAߤU@?Z-Vކеz왍Nэ{|5 pڶn b p-@sPg]0G7fy-M{GCF'%{4`=$-Ge\ eU:m+Zt'WjO!OAF@ik&t݆ϥ_ e}=]"Wz_.͜E3leWFih|t-wZۍ-uw=6YN{6|} |*={Ѽn.S.z1zjۻTH]흾 DuDvmvK.`V]yY~sI@t?/ϓ. m&["+P?MzovVЫG3-GRR[(!!\_,^%?v@ҵő m`Y)tem8GMx.))A]Y i`ViW`?^~!S#^+ѽGZj?Vģ0.))A꨷lzL*]OXrY`DBBLOj{-MH'ii-ϰ ok7^ )쭡b]UXSְmռY|5*cֽk0B7镹%ڽP#8nȎq}mJr23_>lE5$iwui+ H~F`IjƵ@q \ @#qG0".0" l`„.0! ,AQHN6qzkKJ#o;`Xv2>,tێJJ7Z/*A .@fفjMzkg @TvZH3Zxu6Ra'%O?/dQ5xYkU]Rֽkق@DaS^RSּ5|BeHNN͘p HvcYcC5:y #`οb;z2.!kr}gUWkyZn=f Pvsn3p~;4p˚=ē~NmI] ¾ 0lH[_L hsh_ғߤc_њec)g7VIZ5yrgk̞W#IjӪv>՞y睝M8[|]\շ8M6%|@PZڨI-m>=k='aiRo-x?>Q.}`Ȏ:Wsmu u > .@,&;+!!˱tﭧDQwRW\vF\~Q7>spYw$%A~;~}6¾ g&if_=j,v+UL1(tWake:@Ș>j$Gq2t7S?vL|]u/ .(0E6Mk6hiۺzښOrifޱxm/Gx> Lal%%~{lBsR4*}{0Z/tNIɚpV^#Lf:u@k#RSu =S^ZyuR/.@n&΃z~B=0eg뺆#,Þ[B/?H uUf7y Wy}Bwegל`Wh(||`l`.;Ws?V@"c:iɍL֯PGv6zctM̠':wuW;d=;EveD}9J@B(0iհ bvP1{\P&G7D޴Iy_$-Qjm~Yrr&]CDv%bh|Yzni_ˆR;kg}nJOIIwyuL}{ЌNj}:+3Y?:WJ/N+Rzd=hb;dj͒suݔ@NKMԄ jqzC5@y°hL m;*5ezᕏ=ep XL n?מ:r`۵tŤZ|1v`V뽧_csج'ߤ%oTuumk%%%h)uy]Nk[n 'b2 l.=͜E%gf$[c;s:V-͞WߤWh-j7]4=F-X]>ZLSi[Y*We;Zan(ӇW|e(HNNP5[= r4tP &0<pc#`vTNV GFqvTi*Tyam$ߏWyE*VJKMTfFw>'$-ؽ.Ho.8c"@DADADADADADADADADA~j*֘,N;Pi3599h=goضLgiJ5փy~}&Zd9p֚ e:|hL``b/d9p? fgg+%%hMgXosج, ΩOl0Zh=xdjLmhݻoO[g_l,8a]٭+ӧ0$I]c]:粹:Teꢢ"5a^Kgh,&= =՟^߶“ߢE ܹS J}I%:8 IDAT~,9/ʃPW'Mo}zNƍ쨓zPbNZ~^z=4mswg;5 Y~SVMRXUյڱRf?s:w ;6H:ºi5-maM&O3;1IKeamZh͛7+##v+c ~u~ca]GnF'ټL~PPPbn voC4R,ӟgg %hq}@#M4IÇ Oy^xMZx ) yOw@HkN˖-Sǎmb]X@n+i͖!++K3gd\$mt$^YfJ\8PRF)77Wא!Cl$i:@@_oG I{$# 8磌ŋ91A (Im7֭>}ߴJq7ޗt^ -[ԩSj*}%]&' -ɓ'ꫯVzzvB#;a 7@GxI{j޼ƌ.LÇWBB7`O"I$/@R @eee@۷>}0,ɒ2$53Xs|cS~rpTYYY} kHc %&k.], @ADADADADADADADADA@lT<%''*Lo^={رc5h %$+CnܸQ3fҥK}vUVVs9G R,_{xˇ3o߾;TTTd}馛]uuuG~iԩ@4bnvmvfϞ /Peeeq}}za I~,誫{UWW뮻}_~YƍSMMMYχ֝waw\ďcxꩧtEƍկ_?۷5@u?1kNׯWzz/wy>}zj3 k(ٺuq_Zvf̘:~ ABQ&r|!%KҥKgԞ={<_X-z !CyFUUz~ ABQIIIjݺW$UXXDٳZ~ ABQƍecW$<(~<RSSvZujjjԧOZQu@4 8m&&&jԩg$ď1h ͟?_{768@g =@`)))5o6m3)ѣƌJ;wҿUTT /KZR{~a=@0o<*狔iFɶ[ˎ;T]]OX@?K.ۈxN pppppppppppppppppPfl߾] ,{ァk۶mڿo5BTӦMӴiӴ|r DB2e|An!Dy'tkΝ[A $***t5' "!駟oaDnΝ:t֭[gDШQ06qD;@ x M6v(PiizmZ4ew"@̴ixf [~-Fٱc&IZ2|n!?$@{[HTɏ#@hȎI# _m(F /6Z3z'\r,r!;w2Z3j=~GY7"I$iI.p_"?pN`y DD?: _  Gÿab7J !Bx@0 Bo cG@`1C[@0G @`0C_u V1 aCX>W ` | `!<S `"<. `#c`?cAC4 ?c p#~@0?:08&_MQ1J h#?/`7;I  q 7a wQ A 1 Hp !#<8/#@1Ul7=S=K.4Z?E_$i@!1!E4?`P_  @Bă10#: "aU,xbFY1 [n|n #'vEH:`xb #vD4Y hi.i&EΖv#O H4IŶ}:Ikh @tZRF#(tXҙzZ ?I3l7q@õ|ۍ1,GpuY Ꮿ@hJv#xxk$ v#9 5 }_$c S#=+"K{F*m7`#%H:NRSp6I?sIՖ{Ap$I$I:QRv2$Z @UJ*$]<FO4IENDB`