#!/usr/bin/python3
import os, random, string, sys, time, io, shutil

if sys.version_info.major != 3:
    print("Please run with python3.")
    sys.exit(1)

rPackages = ["cpufrequtils"]
rSysCtl = '# XUI.one\n\nnet.ipv4.tcp_congestion_control = bbr\nnet.core.default_qdisc = fq\nnet.ipv4.tcp_rmem = 8192 87380 134217728\nnet.ipv4.udp_rmem_min = 16384\nnet.core.rmem_default = 262144\nnet.core.rmem_max = 268435456\nnet.ipv4.tcp_wmem = 8192 65536 134217728\nnet.ipv4.udp_wmem_min = 16384\nnet.core.wmem_default = 262144\nnet.core.wmem_max = 268435456\nnet.core.somaxconn = 1000000\nnet.core.netdev_max_backlog = 250000\nnet.core.optmem_max = 65535\nnet.ipv4.tcp_max_tw_buckets = 1440000\nnet.ipv4.tcp_max_orphans = 16384\nnet.ipv4.ip_local_port_range = 2000 65000\nnet.ipv4.tcp_no_metrics_save = 1\nnet.ipv4.tcp_slow_start_after_idle = 0\nnet.ipv4.tcp_fin_timeout = 15\nnet.ipv4.tcp_keepalive_time = 300\nnet.ipv4.tcp_keepalive_probes = 5\nnet.ipv4.tcp_keepalive_intvl = 15\nfs.file-max=20970800\nfs.nr_open=20970800\nfs.aio-max-nr=20970800\nnet.ipv4.tcp_timestamps = 1\nnet.ipv4.tcp_window_scaling = 1\nnet.ipv4.tcp_mtu_probing = 1\nnet.ipv4.route.flush = 1\nnet.ipv6.route.flush = 1'
rSystemd = '[Unit]\nSourcePath=/home/xui/service\nDescription=XUI.one Service\nAfter=network.target\nStartLimitIntervalSec=0\n\n[Service]\nType=simple\nUser=root\nRestart=always\nRestartSec=1\nExecStart=/bin/bash /home/xui/service start\nExecRestart=/bin/bash /home/xui/service restart\nExecStop=/bin/bash /home/xui/service stop\n\n[Install]\nWantedBy=multi-user.target'
rRedisConfig = "bind *\nprotected-mode yes\nport 6379\ntcp-backlog 511\ntimeout 0\ntcp-keepalive 300\ndaemonize yes\nsupervised no\npidfile /home/xui/bin/redis/redis-server.pid\nloglevel warning\nlogfile /home/xui/bin/redis/redis-server.log\ndatabases 1\nalways-show-logo yes\nstop-writes-on-bgsave-error no\nrdbcompression no\nrdbchecksum no\ndbfilename dump.rdb\ndir /home/xui/bin/redis/\nslave-serve-stale-data yes\nslave-read-only yes\nrepl-diskless-sync no\nrepl-diskless-sync-delay 5\nrepl-disable-tcp-nodelay no\nslave-priority 100\nrequirepass #PASSWORD#\nmaxclients 655350\nlazyfree-lazy-eviction no\nlazyfree-lazy-expire no\nlazyfree-lazy-server-del no\nslave-lazy-flush no\nappendonly no\nappendfilename \"appendonly.aof\"\nappendfsync everysec\nno-appendfsync-on-rewrite no\nauto-aof-rewrite-percentage 100\nauto-aof-rewrite-min-size 64mb\naof-load-truncated yes\naof-use-rdb-preamble no\nlua-time-limit 5000\nslowlog-log-slower-than 10000\nslowlog-max-len 128\nlatency-monitor-threshold 0\nnotify-keyspace-events \"\"\nhash-max-ziplist-entries 512\nhash-max-ziplist-value 64\nlist-max-ziplist-size -2\nlist-compress-depth 0\nset-max-intset-entries 512\nzset-max-ziplist-entries 128\nzset-max-ziplist-value 64\nhll-sparse-max-bytes 3000\nactiverehashing yes\nclient-output-buffer-limit normal 0 0 0\nclient-output-buffer-limit slave 256mb 64mb 60\nclient-output-buffer-limit pubsub 32mb 8mb 60\nhz 10\naof-rewrite-incremental-fsync yes\nsave 60 1000\nserver-threads 4\nserver-thread-affinity true"
rChoice = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ"

rVersions = {
    "14.04": "trusty",
    "16.04": "xenial",
    "18.04": "bionic",
    "20.04": "focal",
    "20.10": "groovy",
    "21.04": "hirsute",
    "21.10": "impish"
}

class col:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

def printc(rText, rColour=col.OKBLUE, rPadding=0):
    rLeft = int(30-(len(rText)/2))
    rRight = (60-rLeft-len(rText))
    print("%s |--------------------------------------------------------------| %s" % (rColour, col.ENDC))
    for i in range(rPadding): print ("%s |                                                              | %s" % (rColour, col.ENDC))
    print("%s | %s%s%s | %s" % (rColour, " " * rLeft, rText, " " * rRight, col.ENDC))
    for i in range(rPadding): print ("%s |                                                              | %s" % (rColour, col.ENDC))
    print("%s |--------------------------------------------------------------| %s" % (rColour, col.ENDC))
    print(" ")

if __name__ == "__main__":
    ##################################################
    # START                                          #
    ##################################################
    
    if not os.path.exists("./xui_update.tar.gz"):
        print("Fatal Error: xui_update.tar.gz is missing. Please download it from XUI billing panel.")
        sys.exit(1)
    
    try: rVersion = os.popen('lsb_release -sr').read().strip()
    except: rVersion = None
    if not rVersion in rVersions:
        printc("Unsupported Operating System")
        sys.exit(1)
    
    printc("XUI", col.OKGREEN, 2)

    if not os.path.exists("/home/xui/"):
        printc("XUI Directory Doesn't Exist!")
        print("Please install XUI rather than updating.")
        sys.exit(1)

    for rFile in ["/var/lib/dpkg/lock-frontend", "/var/cache/apt/archives/lock", "/var/lib/dpkg/lock", "/var/lib/apt/lists/lock"]:
        if os.path.exists(rFile):
            try: os.remove(rFile)
            except: pass
    printc("Updating system")
    os.system("sudo apt-get update")
    for rPackage in rPackages:
        printc("Installing %s" % rPackage)
        os.system("sudo DEBIAN_FRONTEND=noninteractive apt-get -yq install %s" % rPackage)

    os.system("sudo killall -9 -u xui >/dev/null 2>&1")
    os.system("sudo systemctl stop xuione >/dev/null 2>&1")

    # Remove all PHP files to be sure nothing interferes
    
    try: os.system("sudo rm -rf /home/xui/admin/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/crons/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/includes/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/includes/cli/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/includes/api/admin/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/includes/api/reseller/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/includes/langs/*")
    except: pass
    try: os.system("sudo rm -rf /home/xui/ministra/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/reseller/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/www/admin/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/www/stream/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/www/*.php")
    except: pass
    try: os.system("sudo rm -rf /home/xui/tmp/*")
    except: pass

    ##################################################
    # UPDATE                                         #
    ##################################################
    
    printc("Updating XUI")
    os.system('sudo tar -zxvf "./xui_update.tar.gz" -C "/home/xui/"')

    # Make sure execute permissions are on files
    
    os.system("chmod +x /home/xui/service")
    os.system("chmod +x /home/xui/status")
    os.system("chmod +x /home/xui/tools")
    os.system("chmod +x /home/xui/update")
    os.system("chmod +x /home/xui/bin/daemons.sh")
    os.system("chmod +x /home/xui/bin/blkid")
    os.system("chmod +x /home/xui/bin/youtube")
    os.system("chmod +x /home/xui/bin/nginx/sbin/nginx")
    os.system("chmod +x /home/xui/bin/nginx_rtmp/sbin/nginx_rtmp")
    os.system("chmod +x /home/xui/bin/php/bin/php")
    os.system("chmod +x /home/xui/bin/php/sbin/php-fpm")
    if not os.path.exists("/home/xui/content/playlists"): os.system("mkdir /home/xui/content/playlists")
   
    ##################################################
    # CONFIGURE                                      #
    ##################################################
    
    printc("Configuring System")
    rFsTab = open("/etc/fstab").read()
    if "tmpfs /home/xui/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=2G" in rFsTab:
        rFsTab = rFsTab.replace("tmpfs /home/xui/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=2G", "tmpfs /home/xui/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=6G")
        rFile = io.open("/etc/fstab", "w", encoding="utf-8")
        rFile.write(rFsTab)
        rFile.close()
    if "tmpfs /home/xui/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=4G" in rFsTab:
        rFsTab = rFsTab.replace("tmpfs /home/xui/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=4G", "tmpfs /home/xui/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=6G")
        rFile = io.open("/etc/fstab", "w", encoding="utf-8")
        rFile.write(rFsTab)
        rFile.close()
    print("Custom sysctl.conf - If you have your own custom sysctl.conf, type N or it will be overwritten. If you don't know what a sysctl configuration is, type Y as it will correctly set your TCP settings and open file limits.")
    print(" ")
    while True:
        rAnswer = input("Overwrite sysctl configuration? Recommended! (Y / N): ")
        if rAnswer.upper() in ["Y", "N"]: break
    if rAnswer.upper() == "Y":
        try:
            os.system("sudo modprobe ip_conntrack")
            rFile = io.open("/etc/sysctl.conf", "w", encoding="utf-8")
            rFile.write(rSysCtl)
            rFile.close()
            os.system("sudo sysctl -p > /dev/null")
            rFile = open("/home/xui/config/sysctl.on", "w")
            rFile.close()
        except: print("Failed to write to sysctl file.")
    else:
        if os.path.exists("/home/xui/config/sysctl.on"): os.remove("/home/xui/config/sysctl.on")
    if not "DefaultLimitNOFILE=655350" in open("/etc/systemd/system.conf").read():
        os.system("sudo echo \"\nDefaultLimitNOFILE=655350\" >> \"/etc/systemd/system.conf\"")
        os.system("sudo echo \"\nDefaultLimitNOFILE=655350\" >> \"/etc/systemd/user.conf\"")
    if not os.path.exists("/home/xui/bin/redis/redis.conf"):
        rFile = io.open("/home/xui/bin/redis/redis.conf", "w", encoding="utf-8")
        rFile.write(rRedisConfig)
        rFile.close()
    if os.path.exists("/etc/init.d/xuione"): os.remove("/etc/init.d/xuione")
    if not os.path.exists("/etc/systemd/system/xuione.service"):
        rFile = io.open("/etc/systemd/system/xuione.service", "w", encoding="utf-8")
        rFile.write(rSystemd)
        rFile.close()
    os.system("sudo chmod +x /etc/systemd/system/xuione.service")
    os.system("sudo systemctl daemon-reload")
    os.system("sudo systemctl enable xuione")

    ##################################################
    # FINISHED                                       #
    ##################################################
    
    # Remove redundant stuff
    if os.path.exists("/home/xui/upgrade.php"): os.remove("/home/xui/upgrade.php")
    if os.path.exists("/home/xui/includes/cli/tools.php"): os.remove("/home/xui/includes/cli/tools.php")
    if os.path.exists("/home/xui/includes/handler.php"): os.remove("/home/xui/includes/handler.php")
    if os.path.exists("/home/xui/includes/nhandler.php"): os.remove("/home/xui/includes/nhandler.php")
    
    if not os.path.exists("/home/xui/bin/nginx/conf/gzip.conf"):
        rFile = open("/home/xui/bin/nginx/conf/gzip.conf", "w")
        rFile.write("gzip off;")
        rFile.close()
    if not os.path.exists("/home/xui/bin/nginx/conf/limit.conf"):
        rFile = open("/home/xui/bin/nginx/conf/limit.conf", "w")
        rFile.write("")
        rFile.close()
    if not os.path.exists("/home/xui/bin/nginx/conf/limit_queue.conf"):
        rFile = open("/home/xui/bin/nginx/conf/limit_queue.conf", "w")
        rFile.write("")
        rFile.close()
    
    os.system("sudo chown xui:xui -R /home/xui/tmp")
    os.system("sudo chown xui:xui -R /home/xui/content/streams") # Force it!
    os.system("sudo chown xui:xui -R /home/xui")
    os.system("sudo chmod 0770 /home/xui/config/license")
    os.system("sudo /home/xui/status 1 >/dev/null 2>&1")
    os.system("sudo systemctl start xuione")
    time.sleep(10)
    os.system("sudo /home/xui/status 1")
    os.system("sudo /home/xui/bin/php/bin/php /home/xui/includes/cli/startup.php >/dev/null 2>&1")
    
    printc("Update completed!", col.OKGREEN, 2)
