#!/usr/bin/env python
# -*- coding: utf-8 -*-
# +--------------------------------------------------------------------------+
# | OpenFW UTM Community                                                          |
# +--------------------------------------------------------------------------+

import os
import sys
import subprocess
import tempfile
import socket
from datetime import datetime

SWROOT = "/var/efw"
CONF_FILE = SWROOT + "/external-backups/settings"
BASE_PATH = "/var/efw/external-backups"
CRON_FILE = "/etc/cron.d/external-backups"
LOG_FILE = "/var/log/messages"

INCLUDE_SOURCE = "/var/efw/external-backups/include"
INCLUDE_TARGET = "/usr/lib/efw/backup/include.system"

BACKUP_TYPE = ""
GLOBAL_MIRROR = "off"

MEGA_ENABLED = "off"
MEGA_USER = ""
MEGA_PASSWORD = ""
MEGA_DIR = ""

FTP_ENABLED = "off"
FTP_HOST = ""
FTP_USER = ""
FTP_PASSWORD = ""
FTP_DIR = ""
FTP_PORT = "21"
FTP_PASSIVE = "off"

SFTP_ENABLED = "off"
SFTP_HOST = ""
SFTP_USER = ""
SFTP_PASSWORD = ""
SFTP_DIR = ""
SFTP_PORT = "22"

SMB_ENABLED = "off"
SMB_HOST = ""
SMB_DOMAIN = ""
SMB_USER = ""
SMB_PASSWORD = ""
SMB_DIR = ""

GDRIVE_ENABLED = "off"
GDRIVE_DIR = "CONFIGS"

LOCAL_BACKUP_DIR = "/var/backups/"

if not os.path.exists(BASE_PATH):
    os.makedirs(BASE_PATH)


def append_include_file():
    """Appends the content of the include file to the end of include.system"""
    if os.path.isfile(INCLUDE_SOURCE):
        try:
            with open(INCLUDE_SOURCE, "r") as src:
                content = src.read()
            with open(INCLUDE_TARGET, "a") as dst:
                dst.write(content)
        except Exception:
            pass


def load_config():
    global BACKUP_TYPE, GLOBAL_MIRROR
    global MEGA_ENABLED, MEGA_USER, MEGA_PASSWORD, MEGA_DIR
    global FTP_ENABLED, FTP_HOST, FTP_USER, FTP_PASSWORD, FTP_DIR, FTP_PORT, FTP_PASSIVE
    global SFTP_ENABLED, SFTP_HOST, SFTP_USER, SFTP_PASSWORD, SFTP_DIR, SFTP_PORT
    global SMB_ENABLED, SMB_HOST, SMB_DOMAIN, SMB_USER, SMB_PASSWORD, SMB_DIR
    global GDRIVE_ENABLED, GDRIVE_DIR

    if os.path.isfile(CONF_FILE):
        with open(CONF_FILE, "r") as f:
            for line in f:
                if "=" in line:
                    key, value = line.split("=", 1)
                    key = key.replace("\r", "").strip()
                    value = value.replace("\r", "").strip()
                    if not key:
                        continue

                    if key == "BACKUP_TYPE":
                        BACKUP_TYPE = value
                    elif key == "GLOBAL_MIRROR":
                        GLOBAL_MIRROR = value
                    elif key == "MEGA_ENABLED":
                        MEGA_ENABLED = value
                    elif key == "MEGA_USER":
                        MEGA_USER = value
                    elif key == "MEGA_PASSWORD":
                        MEGA_PASSWORD = value
                    elif key == "MEGA_DIR":
                        MEGA_DIR = value
                    elif key == "FTP_ENABLED":
                        FTP_ENABLED = value
                    elif key == "FTP_HOST":
                        FTP_HOST = value
                    elif key == "FTP_USER":
                        FTP_USER = value
                    elif key == "FTP_PASSWORD":
                        FTP_PASSWORD = value
                    elif key == "FTP_DIR":
                        FTP_DIR = value
                    elif key == "FTP_PORT":
                        FTP_PORT = value
                    elif key == "FTP_PASSIVE":
                        FTP_PASSIVE = value
                    elif key == "SFTP_ENABLED":
                        SFTP_ENABLED = value
                    elif key == "SFTP_HOST":
                        SFTP_HOST = value
                    elif key == "SFTP_USER":
                        SFTP_USER = value
                    elif key == "SFTP_PASSWORD":
                        SFTP_PASSWORD = value
                    elif key == "SFTP_DIR":
                        SFTP_DIR = value
                    elif key == "SFTP_PORT":
                        SFTP_PORT = value
                    elif key == "SMB_ENABLED":
                        SMB_ENABLED = value
                    elif key == "SMB_HOST":
                        SMB_HOST = value
                    elif key == "SMB_DOMAIN":
                        SMB_DOMAIN = value
                    elif key == "SMB_USER":
                        SMB_USER = value
                    elif key == "SMB_PASSWORD":
                        SMB_PASSWORD = value
                    elif key == "SMB_DIR":
                        SMB_DIR = value
                    elif key == "GDRIVE_ENABLED":
                        GDRIVE_ENABLED = value
                    elif key == "GDRIVE_DIR":
                        GDRIVE_DIR = value


def get_cron_value(prefix, field):
    val = ""
    target_key = "%s_CRON_%s" % (prefix, field)
    if os.path.isfile(CONF_FILE):
        with open(CONF_FILE, "r") as f:
            for line in f:
                if line.startswith(target_key + "="):
                    val = line.split("=", 1)[1].replace("\r", "").strip()
                    break

    if not val or "|" in val:
        return "*"
    return val


def obscure_password(plaintext):
    try:
        devnull = open(os.devnull, 'w')
        p = subprocess.Popen(["rclone", "obscure", plaintext], stdout=subprocess.PIPE, stderr=devnull)
        out, _ = p.communicate()
        devnull.close()
        return out.replace("\n", "").replace("\r", "")
    except Exception:
        return ""


def generate_mega_conf():
    conf_path = BASE_PATH + "/mega.conf"
    if MEGA_ENABLED == "on" and MEGA_USER and MEGA_PASSWORD:
        obscured_pass = obscure_password(MEGA_PASSWORD)
        content = "[mega]\ntype = mega\nuser = %s\npass = %s\n" % (MEGA_USER, obscured_pass)
        with open(conf_path, "w") as f:
            f.write(content)
        os.chmod(conf_path, 0600)
    else:
        if os.path.exists(conf_path):
            os.remove(conf_path)


def generate_ftp_conf():
    conf_path = BASE_PATH + "/ftp.conf"
    if FTP_ENABLED == "on" and FTP_HOST:
        obscured_pass = obscure_password(FTP_PASSWORD)
        content = "[ftp]\ntype = ftp\nhost = %s\nport = %s\nuser = %s\npass = %s\n" % (FTP_HOST, FTP_PORT, FTP_USER, obscured_pass)
        with open(conf_path, "w") as f:
            f.write(content)
        os.chmod(conf_path, 0600)
    else:
        if os.path.exists(conf_path):
            os.remove(conf_path)


def generate_sftp_conf():
    conf_path = BASE_PATH + "/sftp.conf"
    if SFTP_ENABLED == "on" and SFTP_HOST:
        obscured_pass = obscure_password(SFTP_PASSWORD)
        content = "[sftp]\ntype = sftp\nhost = %s\nport = %s\nuser = %s\npass = %s\n" % (SFTP_HOST, SFTP_PORT, SFTP_USER, obscured_pass)
        with open(conf_path, "w") as f:
            f.write(content)
        os.chmod(conf_path, 0600)
    else:
        if os.path.exists(conf_path):
            os.remove(conf_path)


def generate_smb_conf():
    conf_path = BASE_PATH + "/smb.conf"
    if SMB_ENABLED == "on" and SMB_HOST and SMB_DIR:
        obscured_pass = obscure_password(SMB_PASSWORD)
        content = "[smb]\ntype = smb\nhost = %s\nuser = %s\npass = %s\ndomain = %s\n" % (SMB_HOST, SMB_USER, obscured_pass, SMB_DOMAIN)
        with open(conf_path, "w") as f:
            f.write(content)
        os.chmod(conf_path, 0600)
    else:
        if os.path.exists(conf_path):
            os.remove(conf_path)


def generate_gdrive_conf():
    token_file = BASE_PATH + "/gdrive-token.json"
    conf_file = BASE_PATH + "/gdrive.conf"

    if os.path.isfile(token_file) and os.path.getsize(token_file) > 0:
        need_generate = False
        if not os.path.isfile(conf_file):
            need_generate = True
        else:
            if os.path.getmtime(token_file) > os.path.getmtime(conf_file):
                need_generate = True

        if need_generate:
            with open(token_file, "r") as f:
                raw_token = f.read().replace("\n", "").replace("\r", "")

            if raw_token:
                content = "[gdrive]\ntype = drive\nscope = drive\ntoken = %s\n" % raw_token
                with open(conf_file, "w") as f:
                    f.write(content)
                os.chmod(conf_file, 0600)


def update_cron():
    lines = ["# Auto-generated by external-backups script. Do not edit directly.\n"]

    if MEGA_ENABLED == "on":
        cron_time = "%s %s %s %s %s" % (
            get_cron_value('MEGA', 'MIM'),
            get_cron_value('MEGA', 'HOR'),
            get_cron_value('MEGA', 'DIA_MES'),
            get_cron_value('MEGA', 'MES'),
            get_cron_value('MEGA', 'DIA_SEM')
        )
        lines.append("%s root /usr/local/bin/external-backups --mega-cron\n" % cron_time)

    if FTP_ENABLED == "on":
        cron_time = "%s %s %s %s %s" % (
            get_cron_value('FTP', 'MIM'),
            get_cron_value('FTP', 'HOR'),
            get_cron_value('FTP', 'DIA_MES'),
            get_cron_value('FTP', 'MES'),
            get_cron_value('FTP', 'DIA_SEM')
        )
        lines.append("%s root /usr/local/bin/external-backups --ftp-cron\n" % cron_time)

    if SFTP_ENABLED == "on":
        cron_time = "%s %s %s %s %s" % (
            get_cron_value('SFTP', 'MIM'),
            get_cron_value('SFTP', 'HOR'),
            get_cron_value('SFTP', 'DIA_MES'),
            get_cron_value('SFTP', 'MES'),
            get_cron_value('SFTP', 'DIA_SEM')
        )
        lines.append("%s root /usr/local/bin/external-backups --sftp-cron\n" % cron_time)

    if SMB_ENABLED == "on":
        cron_time = "%s %s %s %s %s" % (
            get_cron_value('SMB', 'MIM'),
            get_cron_value('SMB', 'HOR'),
            get_cron_value('SMB', 'DIA_MES'),
            get_cron_value('SMB', 'MES'),
            get_cron_value('SMB', 'DIA_SEM')
        )
        lines.append("%s root /usr/local/bin/external-backups --smb-cron\n" % cron_time)

    if GDRIVE_ENABLED == "on":
        cron_time = "%s %s %s %s %s" % (
            get_cron_value('GDRIVE', 'MIM'),
            get_cron_value('GDRIVE', 'HOR'),
            get_cron_value('GDRIVE', 'DIA_MES'),
            get_cron_value('GDRIVE', 'MES'),
            get_cron_value('GDRIVE', 'DIA_SEM')
        )
        lines.append("%s root /usr/local/bin/external-backups --gdrive-cron\n" % cron_time)

    with open(CRON_FILE, "w") as f:
        f.writelines(lines)

    os.chmod(CRON_FILE, 0644)
    devnull = open(os.devnull, 'w')
    subprocess.call("/etc/init.d/fcron restart", shell=True, stdout=devnull, stderr=devnull)
    devnull.close()


def write_log(msg):
    timestamp = datetime.now().strftime("%b %d %H:%M:%S")
    hostname = socket.gethostname().split(".")[0]
    pid = os.getpid()
    log_entry = "%s %s backup[%d]: %s\n" % (timestamp, hostname, pid, msg)
    with open(LOG_FILE, "a") as f:
        f.write(log_entry)


def run_rclone_backup(protocol, remote_dir, conf_filename):
    action = "sync" if GLOBAL_MIRROR == "on" else "copy"
    conf_path = "%s/%s" % (BASE_PATH, conf_filename)

    tmp = tempfile.NamedTemporaryFile("w+", delete=False)
    rclone_log_path = tmp.name
    tmp.close()

    cmd = [
        "rclone", action, LOCAL_BACKUP_DIR, "%s:%s" % (protocol, remote_dir),
        "--config", conf_path, "--log-level", "NOTICE"
    ]

    devnull = open(os.devnull, 'w')
    err_file = open(rclone_log_path, "w")
    res = subprocess.call(cmd, stdout=devnull, stderr=err_file)
    devnull.close()
    err_file.close()

    status = res

    if os.path.exists(rclone_log_path) and os.path.getsize(rclone_log_path) > 0:
        with open(rclone_log_path, "r") as err_file:
            for line in err_file:
                line = line.strip()
                if line:
                    write_log("rclone: %s" % line)

    if os.path.exists(rclone_log_path):
        os.remove(rclone_log_path)

    return status == 0


def do_mega_backup():
    if MEGA_ENABLED != "on" or not os.path.isfile(BASE_PATH + "/mega.conf"):
        return False
    return run_rclone_backup("mega", MEGA_DIR, "mega.conf")


def do_ftp_backup():
    if FTP_ENABLED != "on" or not os.path.isfile(BASE_PATH + "/ftp.conf"):
        return False
    return run_rclone_backup("ftp", FTP_DIR, "ftp.conf")


def do_sftp_backup():
    if SFTP_ENABLED != "on" or not os.path.isfile(BASE_PATH + "/sftp.conf"):
        return False
    return run_rclone_backup("sftp", SFTP_DIR, "sftp.conf")


def do_smb_backup():
    if SMB_ENABLED != "on" or not os.path.isfile(BASE_PATH + "/smb.conf"):
        return False
    return run_rclone_backup("smb", SMB_DIR, "smb.conf")


def do_gdrive_backup():
    if GDRIVE_ENABLED != "on" or not os.path.isfile(BASE_PATH + "/gdrive.conf"):
        return False
    return run_rclone_backup("gdrive", GDRIVE_DIR, "gdrive.conf")


def main():
    load_config()
    append_include_file()

    arg = sys.argv[1] if len(sys.argv) > 1 else ""

    if arg == "--mega-cron":
        write_log("Starting scheduled MEGA backup task.")
        if do_mega_backup():
            write_log("MEGA backup completed successfully.")
        else:
            write_log("Error: MEGA backup task failed.")
    elif arg == "--mega-sendnow":
        do_mega_backup()
    elif arg == "--ftp-cron":
        write_log("Starting scheduled FTP backup task.")
        if do_ftp_backup():
            write_log("FTP backup completed successfully.")
        else:
            write_log("Error: FTP backup task failed.")
    elif arg == "--ftp-sendnow":
        do_ftp_backup()
    elif arg == "--sftp-cron":
        write_log("Starting scheduled SFTP backup task.")
        if do_sftp_backup():
            write_log("SFTP backup completed successfully.")
        else:
            write_log("Error: SFTP backup task failed.")
    elif arg == "--sftp-sendnow":
        do_sftp_backup()
    elif arg == "--smb-cron":
        write_log("Starting scheduled SMB backup task.")
        if do_smb_backup():
            write_log("SMB backup completed successfully.")
        else:
            write_log("Error: SMB backup task failed.")
    elif arg == "--smb-sendnow":
        do_smb_backup()
    elif arg == "--gdrive-cron":
        write_log("Starting scheduled Google Drive backup task.")
        if do_gdrive_backup():
            write_log("Google Drive backup completed successfully.")
        else:
            write_log("Error: Google Drive backup task failed.")
    elif arg == "--gdrive-sendnow":
        do_gdrive_backup()
    else:
        generate_mega_conf()
        generate_ftp_conf()
        generate_sftp_conf()
        generate_smb_conf()
        generate_gdrive_conf()
        update_cron()


if __name__ == "__main__":
    main()