Sindbad~EG File Manager

Current Path : /opt/dedrads/
Upload File :
Current File : //opt/dedrads/cms_counter.py

#!/usr/lib/rads/venv/bin/python3
"""
CMS Counter 2.3
06/03/2024
Corey S
-------
All Server Version
- Should be acceptable for any server environment
- Server Type determined by Fabric wrapper ckx.run_cms_counter() and passed to
  script via '-s'
- 3rd Party/Additional Panels added to determine_panel_type()
- Refactored NodeJS detection split into its own module
- '--new' switch added to only check newly provisioned accounts
- Overall code refactored to be more efficient
"""
import os
import subprocess
import sys
from pathlib import Path
import argparse
import json
import re
import socket

import rads


def run(command=None, check_flag=True, error_flag=True):
    """
    check_flag ignores if the command exits w/ anything other than 0 if True
    error_flag sends errors to /dev/null if set to True
    """
    output = None
    if command and str(command) and error_flag is True:
        try:
            output = (
                subprocess.run(
                    command,
                    shell=True,
                    check=check_flag,
                    stdout=subprocess.PIPE,
                )
                .stdout.decode("utf-8")
                .strip()
            )
        except Exception as e:
            print(e)
    elif command and str(command) and error_flag is False:
        try:
            output = (
                subprocess.run(
                    command,
                    shell=True,
                    check=check_flag,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.DEVNULL,
                )
                .stdout.decode("utf-8")
                .strip()
            )
        except Exception:
            return None
    return output


def determine_panel_type():
    """
    Determines type of Panel in use on server, i.e. CWP, Platform i, Plesk, etc

    Panels Currently Supported:
        - cPanel
        - CWP
        - Platform i
        - Webmin
        - Plesk
        - ISPConfig
        - DirectAdmin
        - CloudPanel
    """
    if os.path.exists('/var/cpanel'):  # cPanel
        return 'cPanel'
    if os.path.exists('/usr/local/cwp'):  # CWP
        return 'CWP'
    if os.path.exists('/etc/profile.d/wp3.sh'):  # Platform i
        return 'Platform_i'
    if os.path.exists('/etc/webmin'):  # Webmin
        return 'Webmin'
    if os.path.exists(
        '/etc/httpd/conf.d/zz010_psa_httpd.conf'
    ) or os.path.exists(
        '/etc/apache2/conf.d/zz010_psa_httpd.conf'
    ):  # Plesk
        return 'Plesk'
    if os.path.exists('/usr/local/directadmin'):  # DirectAdmin
        return 'DirectAdmin'
    if os.path.exists('/usr/local/bin/dploy'):  # CloudPanel
        return 'CloudPanel'
    if os.path.exists(
        '/home/admispconfig/ispconfig/lib/config.inc.php'
    ):  # ISPConfig 2
        return 'ISPConfig-2'
    if os.path.exists(
        '/usr/local/ispconfig/interface/lib/config.inc.php'
    ):  # ISPConfig 3
        return 'ISPConfig-3'

    return 'cVPS'


def identify_apache_config():
    """Function to determine Apache config file to use to check domains as
    different installs/versions can store the config in different files,
    starting w/config for 2.2 and checking different 2.4 config locations
    to end"""
    if os.path.exists('/usr/local/apache/conf/httpd.conf'):
        return '/usr/local/apache/conf/httpd.conf'
    if os.path.exists('/etc/httpd/conf/httpd.conf'):
        return '/etc/httpd/conf/httpd.conf'
    if os.path.exists('/etc/apache2/httpd.conf'):
        return '/etc/apache2/httpd.conf'
    if os.path.exists('/etc/apache2/conf/httpd.conf'):
        return '/etc/apache2/conf/httpd.conf'
    sys.exit('Unable to determine Apache config!\nQuitting...')


def identify_nginx_config():
    """
    Function to find NGINX config file
    """
    if os.path.exists('/etc/nginx/vhosts'):
        return '/etc/nginx/vhosts'
    if os.path.exists('/etc/nginx/conf.d/vhosts'):
        return '/etc/nginx/conf.d/vhosts'
    if os.path.exists('/etc/nginx/nginx.conf'):
        return '/etc/nginx/nginx.conf'
    sys.exit("Unable to locate NGINX config file! Quitting...")


def find_nodejs(docroot=None, domain=None):
    """
    Find possible Node.JS installs per doc root
    """
    install_list = []
    if docroot and '\n' not in str(docroot):
        user = docroot.split('/')[2]
        if os.path.exists(f"""{docroot}/.htaccess"""):
            try:
                with open(
                    f"""{docroot}/.htaccess""", encoding='utf-8'
                ) as htaccess:
                    for line in htaccess.readlines():
                        if 'PassengerAppRoot' in line:
                            install_dir = line.split()[1].strip().strip('"')
                            if (
                                f"""{domain}:{install_dir}"""
                                not in install_list
                            ):
                                # Dont want a dictionary as a single domain
                                # could have multiple subdir installs
                                install_list.append(
                                    f"""{domain}:{install_dir}"""
                                )
            except Exception:
                return None
        if (
            len(install_list) == 0
        ):  # If not found in htaccess, check via procs instead
            user_id = run(
                f"""id -u {user}""", check_flag=False, error_flag=False
            )
            if user_id and user_id.isdigit():
                # Only return procs whose true owner is the user ID of the
                # currently checked user
                node_procs = run(
                    f"""pgrep -U {user_id} node""",
                    check_flag=False,
                    error_flag=False,
                ).split('\n')
                if len(node_procs) > 0:
                    for pid in node_procs:
                        try:
                            cwd = run(
                                f"""pwdx {pid} | cut -d ':' -f 2""",
                                check_flag=False,
                                error_flag=False,
                            )
                            command = run(
                                f"""ps --no-headers -o command {pid} | """
                                + """awk '{print $2}'""",
                                check_flag=False,
                                error_flag=False,
                            ).split('.')[1]
                            install_dir = cwd + command
                        except Exception:
                            return None
                        if (
                            install_dir
                            and os.path.exists(install_dir)
                            and f"""{domain}:{install_dir}"""
                            not in install_list
                        ):
                            install_list.append(f"""{domain}:{install_dir}""")
    return install_list


def determine_cms(docroot=None):
    """Determine CMS manually with provided document root by matching expected
    config files for known CMS"""
    docroot = str(docroot)
    cms_dictionary = {
        f"""{docroot}/concrete.php""": 'Concrete',
        f"""{docroot}/Mage.php""": 'Magento',
        f"""{docroot}/clientarea.php""": 'WHMCS',
        f"""{docroot}/configuration.php""": 'Joomla',
        f"""{docroot}/ut.php""": 'PHPList',
        f"""{docroot}/passenger_wsgi.py""": 'Django',
        f"""{docroot}/wp-content/plugins/boldgrid-inspirations""": 'Boldgrid',
        f"""{docroot}/wp-config.php""": 'Wordpress',
        f"""{docroot}/sites/default/settings.php""": 'Drupal',
        f"""{docroot}/includes/configure.php""": 'ZenCart',
        f"""{docroot}/config/config.inc.php""": 'Prestashop',
        f"""{docroot}/config/settings.inc.php""": 'Prestashop',
        f"""{docroot}/app/etc/env.php""": 'Magento',
        f"""{docroot}/app/etc/local.xml""": 'Magento',
        f"""{docroot}/vendor/laravel""": 'Laravel',
    }
    for config_file, content in cms_dictionary.items():
        if os.path.exists(config_file):
            return content
    if os.path.exists(f"""{docroot}/index.php"""):
        try:
            with open(f"""{docroot}/index.php""", encoding='utf-8') as index:
                for line in index.readlines():
                    if re.search('CodeIgniter', line, re.IGNORECASE):
                        return 'CodeIgniter'
        except Exception as e:
            print(e)
    if os.path.exists(f"""{docroot}/main.ts"""):
        return 'Angular'
    if os.path.exists(f"""{docroot}/main.js"""):
        if os.path.exists(f"""{docroot}/index.php"""):
            try:
                with open(
                    f"""{docroot}/index.php""", encoding='utf-8'
                ) as index:
                    for line in index.readlines():
                        if re.search('bootstrap', line, re.IGNORECASE):
                            return 'Bootstrap'
                return 'Javascript'
            except Exception as e:
                print(e)
        else:
            return 'Javascript'
    if os.path.exists(f"""{docroot}/config.php"""):
        if os.path.exists(f"""{docroot}/admin/config.php"""):
            return 'OpenCart'
        try:
            with open(f"""{docroot}/config.php""", encoding='utf-8') as config:
                for line in config.readlines():
                    if 'Moodle' in line:
                        return 'Moodle'
                if os.path.exists(f"""{docroot}/cron.php"""):
                    with open(
                        f"""{docroot}/cron.php""", encoding='utf-8'
                    ) as cron:
                        for line in cron.readlines():
                            if 'phpBB' in line:
                                return 'phpBB'
                return 'Undetermined'
        except Exception as e:
            print(e)

    if os.path.exists(f"""{docroot}/index.html"""):
        return 'HTML'

    return None


def double_check_wordpress(directory=None):
    if directory and os.path.exists(directory):
        if os.path.exists(
            f"""{directory}/wp-content/plugins/boldgrid-inspirations"""
        ):
            return 'Boldgrid'
    return 'Wordpress'


def manual_find_docroot(domain=None, web_server_config=None, web_server=None):
    docroot_cmd = None
    if web_server == 'apache':
        docroot_cmd = (
            f"grep 'ServerName {domain}' {web_server_config} -A3 | "
            "grep DocumentRoot | " + r"""awk '{print $2}' | uniq"""
        )
        domain_name = domain
    elif web_server == 'nginx':
        domain_name = domain.removesuffix('.conf')
        if r'*' in domain_name:
            return None  # Skip wildcard subdomain configs
        if domain_name.count('_') > 0:
            new_domain = ''
            if domain_name.split('_')[1] == '':  # user__domain_tld.conf
                domain_name = domain_name.split('_')
                limit = len(domain_name) - 1
                start = 2
                while start <= limit:
                    new_domain += domain_name[start]
                    if start != limit:
                        new_domain += '.'
                    start += 1
                domain_name = new_domain
            else:  # domain_tld.conf
                limit = len(domain_name) - 1
                start = 0
                while start <= limit:
                    new_domain += domain_name[start]
                    if start != limit:
                        new_domain += '.'
                    start += 1
                domain_name = new_domain
        # This is the file name, above we extracted the actual domain
        # for use later
        nginx_config = f"{web_server_config}/{domain}"
        if os.path.exists(nginx_config):
            docroot_cmd = (
                f"""grep root {nginx_config} | """
                + r"""awk '{print $2}' | uniq | tr -d ';'"""
            )
        else:
            docroot_cmd = None
    if docroot_cmd:
        docroot = run(docroot_cmd)
    else:
        print(f"""Cannot determine docroot for: {domain_name}""")
        return None

    return docroot


def cms_counter_no_cpanel(
    verbose=False,
    server=None,
    user_list=None,
    unique_wordpress_users=None,
    unique_boldgrid_users=None,
):
    """
    Function to get counts of CMS from all servers without cPanel
    """
    if user_list is None:
        user_list = []
    if unique_wordpress_users is None:
        unique_wordpress_users = []
    if unique_boldgrid_users is None:
        unique_boldgrid_users = []
    # Set Variables
    web_server_config = None
    domains_cmd = None
    domains_list = None
    domains = {}
    docroot_list = []
    users = []
    # List of system users not to run counter against - parsed from /etc/passwd
    sys_users = [
        'root',
        'bin',
        'daemon',
        'adm',
        'sync',
        'shutdown',
        'halt',
        'mail',
        'games',
        'ftp',
        'nobody',
        'systemd-network',
        'dbus',
        'polkitd',
        'rpc',
        'tss',
        'ntp',
        'sshd',
        'chrony',
        'nscd',
        'named',
        'mailman',
        'cpanel',
        'cpanelcabcache',
        'cpanellogin',
        'cpaneleximfilter',
        'cpaneleximscanner',
        'cpanelroundcube',
        'cpanelconnecttrack',
        'cpanelanalytics',
        'cpses',
        'mysql',
        'dovecot',
        'dovenull',
        'mailnull',
        'cpanelphppgadmin',
        'cpanelphpmyadmin',
        'rpcuser',
        'nfsnobody',
        '_imunify',
        'wp-toolkit',
        'redis',
        'nginx',
        'telegraf',
        'sssd',
        'scops',
        'clamav',
        'tier1adv',
        'inmotion',
        'hubhost',
        'tier2s',
        'lldpd',
        'patchman',
        'moveuser',
        'postgres',
        'cpanelsolr',
        'saslauth',
        'nagios',
        'wazuh',
        'systemd-bus-proxy',
    ]
    nginx_status = run(
        """systemctl status nginx 1>/dev/null 2>/dev/null;echo $?"""
    )
    apache_status = run(
        """systemctl status httpd 1>/dev/null 2>/dev/null;echo $?"""
    )
    # Determine Domain List
    # Even if NGiNX server generally Apache is still in use, and is much less
    # convoluted than the NGiNX config
    web_server = None
    if str(apache_status) == '0':
        web_server = 'apache'
        web_server_config = identify_apache_config()
        if web_server_config:
            domains_cmd = (
                f"""grep ServerName {web_server_config} | """
                + r"""awk '{print $2}' | sort -g | uniq"""
            )
    # If Apache is not in use and NGiNX is, use NGiNX
    elif str(nginx_status) == '0':
        web_server = 'nginx'
        web_server_config = identify_nginx_config()
        if web_server_config:
            # THIS MAY NEED REFINED - is this compatible with all NGiNX configs
            # we're checking for? I think one doesn't end in .conf at least
            domains_cmd = (
                f"find {web_server_config} -type f -name '*.conf' -print | "
                "grep -Ev '\\.ssl\\.conf' | xargs -d '\n' -l basename"
            )
    if domains_cmd:
        domains_list = run(domains_cmd)  # Get list of domains
    if domains_list:
        for domain_name in domains_list.split():
            docroot = manual_find_docroot(
                domain=domain_name,
                web_server_config=web_server_config,
                web_server=web_server,
            )
            if docroot and os.path.exists(docroot):
                node_installs = []
                docroot_list.append(docroot)
                domains.update({f"""{docroot}""": f"""{domain_name}"""})
                try:
                    # Try and find NodeJS installs and add them to list of
                    # user_installs
                    node_installs += find_nodejs(
                        docroot=docroot, domain=domain_name
                    )
                except Exception:
                    pass
                # Check sub-directories
                bad_dirs = [
                    f"""{docroot}/wp-admin""",
                    f"""{docroot}/wp-includes""",
                    f"""{docroot}/wp-content""",
                    f"""{docroot}/wp-json""",
                    f"""{docroot}/admin""",
                    f"""{docroot}/cache""",
                    f"""{docroot}/temp""",
                    f"""{docroot}/tmp""",
                    f"""{docroot}/__wildcard__""",
                    f"""{docroot}/cgi-bin""",
                    f"""{docroot}/.well-known""",
                    f"""{docroot}/config""",
                    f"""{docroot}/language""",
                    f"""{docroot}/plugins""",
                    f"""{docroot}/media""",
                    f"""{docroot}/libraries""",
                    f"""{docroot}/images""",
                    f"""{docroot}/includes""",
                    f"""{docroot}/include""",
                    f"""{docroot}/modules""",
                    f"""{docroot}/components""",
                    f"""{docroot}/templates""",
                    f"""{docroot}/cli""",
                    f"""{docroot}/layouts""",
                    f"""{docroot}/storage""",
                    f"""{docroot}/logs""",
                    f"""{docroot}/bin""",
                    f"""{docroot}/uploads""",
                    f"""{docroot}/docs""",
                    f"""{docroot}/style""",
                    f"""{docroot}/files""",
                    f"""{docroot}/sounds""",
                    f"""{docroot}/cart""",
                    f"""{docroot}/shop""",
                    f"""{docroot}/assets""",
                    f"""{docroot}/system""",
                    f"""{docroot}/documentation""",
                    f"""{docroot}/application""",
                    f"""{docroot}/script""",
                    f"""{docroot}/scripts""",
                    f"""{docroot}/backups""",
                    f"""{docroot}/backup""",
                ]
                docroot_dir = Path(docroot)
                try:
                    for d in docroot_dir.iterdir():
                        if (
                            os.path.exists(d)
                            and d.is_dir()
                            and str(d) not in bad_dirs
                        ):
                            try:
                                node_installs += find_nodejs(
                                    docroot=str(d), domain=domain_name
                                )
                            except Exception:
                                pass
                            dirname = str(os.path.basename(d))
                            d = str(d)  # Convert from Path object to String
                            docroot_list.append(d)
                            domains.update(
                                {f"""{d}""": f"""{domain_name}/{dirname}"""}
                            )
                            bad_dirs.append(d)
                except NotADirectoryError:
                    continue
                except Exception as e:
                    print(e)
                    continue

    # Determine User List
    if len(user_list) >= 1:
        users = user_list
    # Get users if they weren't passed already - if cPanel was detected but not
    # Softaculous for instance
    if os.path.exists('/home') and len(user_list) == 0:
        users_dir = Path('/home')
        try:
            with open('/etc/passwd', encoding='utf-8') as passwd:
                passwd_file = passwd.readlines()
        except Exception:
            sys.exit('Unable to read /etc/passwd!\nExiting...')
        for u in users_dir.iterdir():
            if u.is_dir():
                limit = len(u.parts) - 1
                for line in passwd_file:
                    if (
                        u.parts[limit] == line.split(':')[0]
                        and u.parts[limit] not in sys_users
                    ):
                        users.append(u.parts[limit])
    if len(users) >= 1 and len(docroot_list) >= 1:
        for docroot in docroot_list:
            get_cms = None
            docroot_user = None
            for user in users:
                if user in sys_users:
                    continue  # Go to next user if current user is System user
                if user in docroot.split('/'):
                    docroot_user = user
                    break
            get_cms = determine_cms(docroot=docroot)
            if (
                str(get_cms).lower() == 'wordpress'
                and docroot_user
                and docroot_user not in unique_wordpress_users
            ):
                unique_wordpress_users.append(docroot_user)
            elif (
                str(get_cms).lower() == 'boldgrid'
                and docroot_user
                and docroot_user not in unique_boldgrid_users
            ):
                unique_boldgrid_users.append(docroot_user)

            if verbose:
                pt = determine_panel_type()  # Determine Panel Type if needed
            if get_cms and docroot_user and docroot_user not in sys_users:
                domain = domains.get(f"""{docroot}""", None)
                hostname = socket.gethostname().split('.')[0]
                if os.path.exists(f"/var/cpanel/users/{docroot_user}"):
                    plan_type = run(
                        f"""grep PLAN /var/cpanel/users/{docroot_user}"""
                    ).split('=')[1]
                else:
                    plan_type = 'N/A'
                if verbose:
                    print(
                        f"{hostname} {server} {pt} {docroot_user} "
                        f"{plan_type} {domain} {get_cms}"
                    )
                else:
                    print(f"""{docroot_user} {domain} {get_cms}""")
            for install in node_installs:
                if verbose:
                    print(
                        f"{hostname} {server} {pt} {docroot_user} "
                        f"{plan_type} {install} NodeJS"
                    )
                else:
                    print(f"""{docroot_user} {install} NodeJS""")

    print(f"""Unique Wordpress Users: {len(unique_wordpress_users)}""")
    print(f"""Unique Boldgrid Users: {len(unique_boldgrid_users)}""")


def cms_counter_cpanel(verbose=False, new=False, server=None):
    """
    Function to determine CMS counter on servers with cPanel
    Attempting to use Softaculous first
    Checking manually if that returns no results
    Including users list as passed variable so we can run against specific users
    only as well, future-proofing/anticipating possible request
    """
    # Establish Variables
    softaculous = 0
    unique_wordpress_users = []
    unique_boldgrid_users = []
    # Softaculous omits the Name of some CMS in table view for some awesome
    # reason; this is a fallback dictionary to use to manually retrieve Name
    # if it isnt included.
    # Moodle and Magento only ones so far I've determined with this issue
    softaculous_ids = {
        '503': 'Moodle 3.5',
        '542': 'Moodle',
        '98': 'Moodle 2.6',
        '343': 'Moodle 2.0',
        '517': 'Moodle 3.7',
        '485': 'Moodle 3.6',
        '670': 'Moodle 3.9',
        '668': 'Moodle 3.8',
        '699': 'Moodle 3.11',
        '681': 'Moodle 3.10',
        '706': 'Moodle 4.0',
        '713': 'Moodle 4.1',
        '67': 'Magento 1.9',
        '545': 'Magento 2.3',
        '465': 'Magento 1.7',
        '486': 'Magento 2.2',
        '665': 'Magento',
        '688': 'Magento 2.4.2',
        '709': 'Magnto 2.4.1',
    }
    cms_info = {}
    site_owners = {}
    user_list = []
    no_install_users = []
    # List of system users not to run counter against - parsed from /etc/passwd
    sys_users = [
        'root',
        'bin',
        'daemon',
        'adm',
        'sync',
        'shutdown',
        'halt',
        'mail',
        'games',
        'ftp',
        'nobody',
        'systemd-network',
        'dbus',
        'polkitd',
        'rpc',
        'tss',
        'ntp',
        'sshd',
        'chrony',
        'nscd',
        'named',
        'mailman',
        'cpanel',
        'cpanelcabcache',
        'cpanellogin',
        'cpaneleximfilter',
        'cpaneleximscanner',
        'cpanelroundcube',
        'cpanelconnecttrack',
        'cpanelanalytics',
        'cpses',
        'mysql',
        'dovecot',
        'dovenull',
        'mailnull',
        'cpanelphppgadmin',
        'cpanelphpmyadmin',
        'rpcuser',
        'nfsnobody',
        '_imunify',
        'wp-toolkit',
        'redis',
        'nginx',
        'telegraf',
        'sssd',
        'scops',
        'clamav',
        'tier1adv',
        'inmotion',
        'hubhost',
        'tier2s',
        'lldpd',
        'patchman',
        'moveuser',
        'postgres',
        'cpanelsolr',
        'saslauth',
        'nagios',
        'wazuh',
        'systemd-bus-proxy',
    ]
    if secure_user := rads.SECURE_USER:
        sys_users.append(secure_user)
    if os.path.exists('/var/cpanel/users'):
        if new:
            user_list_cmd = """tail -250 /etc/passwd | cut  -d ':' -f 1"""
            user_list = run(user_list_cmd).split('\n')
        else:
            users_dir = Path('/var/cpanel/users')
            for u in users_dir.iterdir():
                limit = len(u.parts) - 1
                if (
                    not u.is_dir()
                    and u.parts[limit] != 'system'
                    and '.bak' not in u.parts[limit]
                ):
                    user_list.append(u.parts[limit])
    else:
        sys.exit('Unable To Determine cPanel Users!\nQuitting...')

    if len(user_list) == 0:  # Confirm Users
        sys.exit('No Users Detected. Quitting...')
    if not os.path.exists('/var/cpanel/users'):  # Confirm cPanel
        sys.exit('cPanel not detected. Quitting...')
    else:
        if os.path.exists('/var/softaculous'):  # Confirm Softaculous
            softaculous = 1

    if softaculous == 1:
        for user in user_list:
            if user in sys_users:
                continue  # Go to next user if system user detected
            plan_type = run(f"""grep PLAN /var/cpanel/users/{user}""").split(
                '='
            )[1]
            user_installs = None
            user_installs_json = None
            node_installs = []
            cmd = (
                "/usr/local/cpanel/3rdparty/bin/php "
                "/usr/local/cpanel/whostmgr/docroot/cgi/softaculous/cli.php "
                f"--list_ins --user={user} --resp=json"
            )
            user_installs = run(cmd, check_flag=False)
            try:
                user_installs_json = json.loads(user_installs)
            except Exception:
                user_installs_json = None
            if user_installs_json and 'error' in user_installs_json.keys():
                no_install_users.append(user)
                continue  # Jump to next user
            if user_installs_json:
                install_list = [
                    install
                    for install in user_installs_json
                    if install != 'error'
                ]
                for install in install_list:
                    try:
                        user_cms = user_installs_json[install]['script_name']
                        url = user_installs_json[install]['softurl']
                        if (
                            not user_cms
                        ):  # some scripts will give 'null' as the script_name
                            try:
                                sid = user_installs_json[install][
                                    'sid'
                                ]  # Get script id
                                user_cms = softaculous_ids[
                                    sid
                                ]  # determine CMS from script id
                            except Exception:
                                print(f"""{user}: Error determining CMS""")
                                continue
                        if user_cms and url:
                            # Softaculous only reports Wordpress, check if its
                            # really Boldgrid before recording it
                            if user_cms == 'WordPress':
                                docroot = user_installs_json[install][
                                    'softpath'
                                ]
                                user_cms = double_check_wordpress(
                                    directory=docroot
                                )
                                if (
                                    str(user_cms).lower() == 'wordpress'
                                    and user
                                    and user not in unique_wordpress_users
                                ):
                                    unique_wordpress_users.append(user)
                                elif (
                                    str(user_cms).lower() == 'boldgrid'
                                    and user
                                    and user not in unique_wordpress_users
                                ):
                                    unique_boldgrid_users.append(user)
                            cms_info.update({url: user_cms})
                            site_owners.update({url: user})
                    except Exception as e:
                        print(e)
                        continue
            else:
                no_install_users.append(user)
                continue

            # Check for NodeJS installs
            apache_status = run(
                """systemctl status httpd &>/dev/null;echo $?""",
                check_flag=False,
            )
            nginx_status = run(
                """systemctl status nginx &>/dev/null;echo $?""",
                check_flag=False,
            )
            get_domains_cmd = f"""grep -E '{user}$' /etc/userdomains"""
            domain_list = run(get_domains_cmd, check_flag=False).split(
                '\n'
            )  # .split(':')[0].strip()
            # This ensures the actual correct user is used and not one
            # containing the same string
            for domain_string in domain_list:
                if (
                    len(domain_string.split(':')) > 1
                    and str(domain_string.split(':')[1]).strip()
                    == str(user).strip()
                ):
                    domain = domain_string.split(':')[0]
                    if str(apache_status) == '0':
                        web_server_config = identify_apache_config()
                        web_server = 'apache'
                    elif str(nginx_status) == '0':
                        web_server_config = identify_nginx_config()
                        web_server = 'nginx'
                    else:
                        print('Unable to identify web server config')
                        return
                    docroot = manual_find_docroot(
                        domain=domain,
                        web_server_config=web_server_config,
                        web_server=web_server,
                    )
                    if web_server_config and docroot:
                        try:
                            node_installs += find_nodejs(
                                docroot=docroot,
                                domain=domain_string.split(':')[0].strip(),
                            )
                        except Exception:
                            pass
            if len(node_installs) > 0:
                for install in node_installs:
                    # Add found NodeJS installs to dictionaries for each user,
                    # allowing reporting to print them at the end
                    cms_info.update({install.split(':')[1]: 'NodeJS'})
                    site_owners.update({install.split(':')[1]: user})
        if verbose:
            hostname = socket.gethostname().split('.')[0]
            for url, cms in cms_info.items():
                site_owner = site_owners.get(url, None)
                print(
                    f"{hostname} {server} cPanel {site_owner} "
                    f"{plan_type} {url} {cms}"
                )
        else:
            for url, cms in cms_info.items():
                site_owner = site_owners.get(url, None)
                print(f"""{site_owner} {url} {cms}""")

    else:  # cPanel present but no Softaculous
        try:
            cms_counter_no_cpanel(
                verbose=verbose,
                server=server,
                unique_wordpress_users=unique_wordpress_users,
                unique_boldgrid_users=unique_boldgrid_users,
            )
        except Exception as e:
            print(e)

    if (
        len(no_install_users) >= 1
    ):  # Run no softaculous cms_counter for users w/o installs found
        cms_counter_no_cpanel(
            user_list=no_install_users,
            verbose=verbose,
            server=server,
            unique_wordpress_users=unique_wordpress_users,
            unique_boldgrid_users=unique_boldgrid_users,
        )
    else:
        print(f"""Unique Wordpress Users: {len(unique_wordpress_users)}""")
        print(f"""Unique Boldgrid Users: {len(unique_boldgrid_users)}""")

    return


def main():
    """Main function, initializes global variables as globals, gets hostname and
    call relevant checks after determining enviroment"""
    parser = argparse.ArgumentParser(description='CMS Counter 2.1')
    parser.add_argument(
        '-v',
        '--verbose',
        dest='verbose',
        help='Include Panel Type and Server Type in output',
        action='store_const',
        const=True,
        default=False,
    )
    parser.add_argument(
        '-n',
        '--new',
        dest='new',
        help='Check the 250 most recently provisioned users only',
        action='store_const',
        const=True,
        default=False,
    )
    parser.add_argument(
        '-s',
        '--server-type',
        dest='server',
        help='Server Type - passed by Fabric generally',
        action='store',
    )
    args = vars(parser.parse_args())

    server_types = ['Shared', 'Reseller', 'Hub', 'VPS', 'Dedicated']
    server_type = None
    for st in server_types:
        if (
            args['server'] == st.lower()
            or args['server'] == st
            or args['server'] == st.upper()
        ):
            server_type = st
            break
    if not server_type:
        sys.exit('Server Type not given!\nQuitting...')
    verb = args['verbose']
    if os.path.exists('/var/cpanel/users'):
        cms_counter_cpanel(verbose=verb, server=server_type)
    else:
        cms_counter_no_cpanel(verbose=verb, server=server_type)


if __name__ == "__main__":
    main()

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists