#!python
""" CherryPy application for file storage"""

# Copyright (C) 2025 Gwyn Ciesla

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import argparse
import getpass
import hashlib
import os
import re
import sys
import xml.etree.ElementTree

import requests
from requests.auth import HTTPDigestAuth
from urllib3.exceptions import InsecureRequestWarning

import pystorge

requests.urllib3.disable_warnings(InsecureRequestWarning)


def dehtml(text):
    """Strip HTML tags"""
    return "".join(xml.etree.ElementTree.fromstring(text).itertext())


URL = "https://127.0.0.1:8080"
PASSWORD = "storge"

TIMEOUT = 30

PARSER = argparse.ArgumentParser(description="Send a file to a Storge server")
PARSER.add_argument("-v", "--version", action="version", version="1.26")
PARSER.add_argument(
    "-l", "--list", action="store_true", dest="lst", help="List stored files"
)
PARSER.add_argument(
    "-r", "--remote", action="store", dest="remote", help="URL to sync files from"
)
PARSER.add_argument(
    "-d",
    "--download",
    action="store",
    dest="download",
    help="Download file by ID; N for newest, O for oldest, A for all",
)
PARSER.add_argument(
    "-u", "--url", action="store", dest="url", help="Specify destination URL"
)
PARSER.add_argument(
    "-p", "--password", action="store", dest="password", help="Password"
)
PARSER.add_argument(
    "-D",
    "--delete",
    action="store",
    dest="delete",
    help="Delete file by ID; N for newest, O for oldest",
)
PARSER.add_argument(
    "-V", "--vacuum", action="store_true", dest="vacuum", help="Vacuum database"
)
PARSER.add_argument(
    "-P",
    "--pop",
    action="store_true",
    dest="pop",
    help="Download and delete the oldest file",
)
PARSER.add_argument("FILE", nargs=argparse.REMAINDER)
ARGS = PARSER.parse_args()

CONFIGFILENAME = os.path.join(
    os.path.expanduser("~" + getpass.getuser()), ".storge.conf"
)

CONFIG = pystorge.client_config_handler(CONFIGFILENAME)

if "STORGE_URL" in os.environ:
    URL = os.environ["STORGE_URL"]
elif "url" in CONFIG["Options"]:
    URL = str(CONFIG.get("Options", "url")).rstrip("/")
if "STORGE_PASSWORD" in os.environ:
    PASSWORD = os.environ["STORGE_PASSWORD"]
elif "password" in CONFIG["Options"]:
    PASSWORD = str(CONFIG.get("Options", "password"))

if ARGS.url:
    URL = ARGS.url.rstrip("/")

if ARGS.lst:
    try:
        OUTPUT = requests.post(
            URL + "/api",
            files={"flag": "list"},
            auth=HTTPDigestAuth("storge", PASSWORD),
            verify=False,
            timeout=TIMEOUT,
        )
    except OSError:
        print("Unable to connect")
        sys.exit(1)
    if OUTPUT.status_code == 401:
        print("Invalid password")
        sys.exit(1)

    if "<html>" in OUTPUT.text:
        print(dehtml(OUTPUT.text.replace("|||", "\n")))
    else:
        print(OUTPUT.text.replace("|||", "\n"))

    sys.exit(0)

if ARGS.remote:
    try:
        OUTPUT = requests.post(
            URL + "/api",
            files={"flag": "sync"},
            auth=HTTPDigestAuth("storge", PASSWORD),
            verify=False,
            timeout=TIMEOUT,
        )
    except OSError:
        print("Unable to connect")
        sys.exit(1)

    if OUTPUT.status_code == 401:
        print("Invalid password")
        sys.exit(1)
    LOCALHASHES = OUTPUT.text.split("|||")
    try:
        OUTPUT = requests.post(
            ARGS.remote + "/api",
            files={"flag": "sync"},
            auth=HTTPDigestAuth("storge", PASSWORD),
            verify=False,
            timeout=TIMEOUT,
        )
    except OSError:
        print("Unable to connect")
        sys.exit(1)

    if OUTPUT.status_code == 401:
        print("Invalid password")
        sys.exit(1)
    REMOTEHASHES = OUTPUT.text.split("|||")
    for candidate in REMOTEHASHES:
        if int(len(candidate)) > 0:
            fields = candidate.split("---")
            TRANSFER = 1
            for target in LOCALHASHES:
                if int(len(target)) > 0:
                    tar_fields = target.split("---")
                    if int(len(tar_fields)) > 0:
                        if tar_fields[1] == fields[1]:
                            TRANSFER = 0
            if TRANSFER == 1:
                try:
                    OUTPUT = requests.post(
                        ARGS.remote + "/download",
                        files={"fileid": fields[0]},
                        auth=HTTPDigestAuth("storge", PASSWORD),
                        verify=False,
                        timeout=TIMEOUT,
                    )
                except OSError:
                    print("Unable to connect")
                    sys.exit(1)

                if OUTPUT.status_code == 401:
                    print("Invalid password")
                    sys.exit(1)
                FNAME = re.findall(
                    '"([^"]*)"',
                    OUTPUT.headers.get("Content-Disposition").split("filename=")[1],
                )[0]
                try:
                    UPLOAD = requests.post(
                        URL + "/upload",
                        files={"userfile": (FNAME, OUTPUT.content)},
                        auth=HTTPDigestAuth("storge", PASSWORD),
                        verify=False,
                        timeout=TIMEOUT,
                    )
                except OSError:
                    print("Unable to connect")
                    sys.exit(1)

                if UPLOAD.status_code == 401:
                    print("Invalid password")
                    sys.exit(1)

    sys.exit(0)

if ARGS.pop:
    TARGETS = []
    try:
        OUTPUT = requests.post(
            URL + "/api",
            files={"flag": "pop"},
            auth=HTTPDigestAuth("storge", PASSWORD),
            verify=False,
            timeout=TIMEOUT,
        )
    except OSError:
        print("Unable to connect")
        sys.exit(1)
    if OUTPUT.status_code == 401:
        print("Invalid password")
        sys.exit(1)
    TARGETS.append(OUTPUT.text.split("|||")[0].split(" ")[0])
    for target in TARGETS:
        try:
            OUTPUT = requests.post(
                URL + "/download",
                files={"fileid": target},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)

        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)

        try:
            FNAME = re.findall(
                '"([^"]*)"',
                OUTPUT.headers.get("Content-Disposition").split("filename=")[1],
            )[0]
        except AttributeError:
            print("No item of number " + target)
            sys.exit(1)

        hashthing = hashlib.new("sha3_512")
        hashthing.update(OUTPUT.content)
        filehash = hashthing.hexdigest()

        try:
            SOUTPUT = requests.post(
                URL + "/api",
                files={"flag": "sync"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)

        if SOUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)

        SERVERHASH = ""
        for line in SOUTPUT.text.split("|||"):
            if line.split("---")[0] == target:
                SERVERHASH = line.split("---")[1]

        if not os.path.isfile(FNAME):
            with open(FNAME, "wb") as DLOUTPUT:
                DLOUTPUT.write(OUTPUT.content)
        else:
            print(FNAME + " exists, aborting.")

        if filehash != SERVERHASH:
            print(
                "Download issue;\nlocal hash: "
                + filehash
                + "\nremote hash: "
                + SERVERHASH
            )
        else:
            try:
                OUTPUT = requests.post(
                    URL + "/delete",
                    files={"fileid": target},
                    auth=HTTPDigestAuth("storge", PASSWORD),
                    verify=False,
                    timeout=TIMEOUT,
                )
            except OSError:
                print("Unable to connect")
                sys.exit(1)

        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)

    sys.exit(0)


if ARGS.download:
    TARGETS = []
    if ARGS.download == "N":
        try:
            OUTPUT = requests.post(
                URL + "/api",
                files={"flag": "list"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)
        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)
        TARGETS.append(OUTPUT.text.split("|||")[0].split(" ")[0])
    elif ARGS.download == "O":
        try:
            OUTPUT = requests.post(
                URL + "/api",
                files={"flag": "list"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)
        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)
        TARGETS.append(OUTPUT.text.split("|||")[-1].split(" ")[0])
    elif ARGS.download == "A":
        try:
            OUTPUT = requests.post(
                URL + "/api",
                files={"flag": "list"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)
        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)
        for line in OUTPUT.text.split("|||"):
            TARGETS.append(line.split(" ")[0])
    else:
        TARGETS.append(ARGS.download)

    for target in TARGETS:
        try:
            OUTPUT = requests.post(
                URL + "/download",
                files={"fileid": target},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)

        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)

        try:
            FNAME = re.findall(
                '"([^"]*)"',
                OUTPUT.headers.get("Content-Disposition").split("filename=")[1],
            )[0]
        except AttributeError:
            print("No item of number " + target)
            sys.exit(1)

        hashthing = hashlib.new("sha3_512")
        hashthing.update(OUTPUT.content)
        filehash = hashthing.hexdigest()

        try:
            SOUTPUT = requests.post(
                URL + "/api",
                files={"flag": "sync"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)

        if SOUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)

        for line in SOUTPUT.text.split("|||"):
            if line.split("---")[0] == target:
                SERVERHASH = line.split("---")[1]

        if not os.path.isfile(FNAME):
            with open(FNAME, "wb") as DLOUTPUT:
                DLOUTPUT.write(OUTPUT.content)
        else:
            print(FNAME + " exists, aborting.")

        if filehash != SERVERHASH:
            print(
                "Download issue;\nlocal hash: "
                + filehash
                + "\nremote hash: "
                + SERVERHASH
            )

    sys.exit(0)

if ARGS.delete:
    if ARGS.delete == "N":
        try:
            OUTPUT = requests.post(
                URL + "/api",
                files={"flag": "list"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)
        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)
        TARGET = OUTPUT.text.split("|||")[0].split(" ")[0]
    elif ARGS.delete == "O":
        try:
            OUTPUT = requests.post(
                URL + "/api",
                files={"flag": "list"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)
        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)
        TARGET = OUTPUT.text.split("|||")[-1].split(" ")[0]
    else:
        TARGET = ARGS.delete

    try:
        OUTPUT = requests.post(
            URL + "/delete",
            files={"fileid": TARGET},
            auth=HTTPDigestAuth("storge", PASSWORD),
            verify=False,
            timeout=TIMEOUT,
        )
    except OSError:
        print("Unable to connect")
        sys.exit(1)

    if OUTPUT.status_code == 401:
        print("Invalid password")
        sys.exit(1)
    sys.exit(0)

if ARGS.vacuum:
    try:
        OUTPUT = requests.post(
            URL + "/vacuum",
            auth=HTTPDigestAuth("storge", PASSWORD),
            verify=False,
            timeout=TIMEOUT,
        )
        SAVINGS = OUTPUT.text.split(">")[1]
        print(SAVINGS + " saved.")
    except OSError:
        print("Unable to connect")
        sys.exit(1)

    if OUTPUT.status_code == 401:
        print("Invalid password")
        sys.exit(1)
    sys.exit(0)

FILES = ""
# Did the user provide a filename?
if int(len(ARGS.FILE)) > 0:
    # Name of target file
    FILES = ARGS.FILE
else:
    print("Must specify filename.")
    sys.exit(1)

for file in FILES:
    # Reject filenames with non-ascii characters
    NONASCII = 0
    for character in file:
        if ord(character) > 127:
            print('Non-ASCII character "' + character + '" in filename "' + file + '"')
            NONASCII = 1
    if NONASCII == 0 and os.path.isfile(file):
        with open(file, "rb") as ufile:
            CONTENT = ufile.read()

        hashthing = hashlib.new("sha3_512")
        hashthing.update(CONTENT)
        filehash = hashthing.hexdigest()

        try:
            OUTPUT = requests.post(
                URL + "/api",
                files={"flag": "sync"},
                auth=HTTPDigestAuth("storge", PASSWORD),
                verify=False,
                timeout=TIMEOUT,
            )
        except OSError:
            print("Unable to connect")
            sys.exit(1)

        if OUTPUT.status_code == 401:
            print("Invalid password")
            sys.exit(1)

        REPLYLEN = len(OUTPUT.text)
        UPLOADED = 0
        for line in OUTPUT.text.split("|||"):
            LINELEN = len(line.split("---"))
            if REPLYLEN > 0 and LINELEN > 0:
                if filehash == line.split("---")[1]:
                    print("File " + file + " already uploaded, aborting.")
                    UPLOADED = 1
            if UPLOADED == 1:
                break
        if UPLOADED == 0:
            try:
                OUTPUT = requests.post(
                    URL + "/upload",
                    files={
                        "userfile": open(  # pylint: disable=consider-using-with
                            file, "rb"
                        )
                    },
                    auth=HTTPDigestAuth("storge", PASSWORD),
                    verify=False,
                    timeout=TIMEOUT,
                )
            except OSError:
                print("Unable to connect")
                sys.exit(1)

            if OUTPUT.status_code == 401:
                print("Invalid password")
                sys.exit(1)

            if OUTPUT.status_code != 200:
                print(dehtml(OUTPUT))

            NEWUPLOADED = 0
            try:
                OUTPUT = requests.post(
                    URL + "/api",
                    files={"flag": "sync"},
                    auth=HTTPDigestAuth("storge", PASSWORD),
                    verify=False,
                    timeout=TIMEOUT,
                )
            except OSError:
                print("Unable to connect")
                sys.exit(1)

            if OUTPUT.status_code == 401:
                print("Invalid password")
                sys.exit(1)

            for line in OUTPUT.text.split("|||"):
                if filehash == line.split("---")[1]:
                    NEWUPLOADED = 1

            if NEWUPLOADED == 0:
                print(
                    "Upload issue, "
                    + file
                    + " with hash "
                    + filehash
                    + " not found on server."
                )
