#!/usr/bin/python3 -Bsu

## Copyright (C) 2025 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## Usage:
## (append|append-once|overwrite) /path/to/file 'content'
##
## * append - Appends the provided line to the specified file unconditionally.
## * append-once - If the provided file does not already contain the provided
##   line, appends the line.
## * overwrite - Clobbers the file's current contents with the provided line.
##
## File writes are done atomically if possible, by writing data to a temporary
## file first and then moving that file to replace the original.

import sys
import os
import shutil
from pathlib import Path
from tempfile import NamedTemporaryFile


executable_name = None


def print_usage_and_bail():
    print(
        f"Usage: {executable_name} /path/to/file 'content'",
        file=sys.stderr,
    )
    sys.exit(1)


def print_info(info_msg):
    print(f"{executable_name}: INFO: {info_msg}")


def print_error_and_bail(error_msg):
    print(f"{executable_name}: ERROR: {error_msg}", file=sys.stderr)
    sys.exit(1)


def main():
    global executable_name
    if (
        len(sys.argv) < 2
        or sys.argv[1] not in ("append", "append-once", "overwrite")
    ):
        print_error_and_bail(
            "Do not call append-shared directly. Use 'append', "
            + "'append-once', or 'overwrite'."
        )
    executable_name = sys.argv[1]

    if len(sys.argv) != 4:
        print_usage_and_bail()

    file_path = Path(sys.argv[2])
    orig_file_path = file_path
    line_to_append = sys.argv[3]
    file_contents = ""

    if not executable_name == "overwrite":
        try:
            file_path = file_path.resolve()
        except Exception:
            print_error_and_bail(
                f"Error while resolving path '{orig_file_path}'!"
            )

    file_exists = file_path.exists()

    if file_exists and executable_name != "overwrite":
        if not file_path.is_file():
            print_error_and_bail(f"'{file_path}' is not a file!")
        if not os.access(file_path, os.R_OK):
            print_error_and_bail(f"File '{file_path}' not readable!")
        with open(file_path, "r", encoding="utf-8") as f:
            try:
                file_contents = f.read()
            except Exception:
                print_error_and_bail(
                    f"Error while reading file '{orig_file_path}'!"
                )
        if not os.access(file_path, os.W_OK):
            print_error_and_bail(f"File '{orig_file_path}' not writable!")
    else:
        if not file_exists:
            print_info(f"File does not exist yet: '{orig_file_path}'")
        if not os.access(file_path.parent, os.W_OK):
            print_error_and_bail(
                f"Folder '{file_path.parent}' (where file '{file_path.name}' "
                "should be created) is not writable!"
            )

    if file_contents != "" and not file_contents.endswith("\n"):
        # Fix a missing terminating newline. Some text editors will fail to
        # write these properly and it will break our line appending if we don't
        # correct it first. Skip this when there are no existing contents (e.g.
        # overwrite mode, or an empty/nonexistent file) to avoid prepending a
        # spurious leading newline.
        file_contents += "\n"
    if not line_to_append.endswith("\n"):
        # Make sure we add a terminating newline ourselves too :)
        line_to_append += "\n"

    if executable_name == "append-once":
        ## Look for a whole line match, not just a substring match. The extra
        ## newline at the beginning of line_to_append ensures that
        ## line_to_append occurs on a line by itself, the extra newline at the
        ## beginning of file_contents allows line_to_append to match the first
        ## line of the file.
        if "\n" + line_to_append in "\n" + file_contents:
            print_info(f"Line already exists in: '{orig_file_path}'")
            sys.exit(0)

    file_contents += line_to_append

    if executable_name == "overwrite":
        print_info(f"Overwriting file: '{orig_file_path}'")
    else:
        print_info(f"Appending data to: '{orig_file_path}'")

    temp_file = None
    try:
        temp_file = NamedTemporaryFile(mode="w", delete=False)
        temp_file.write(file_contents)
        temp_file.flush()
        temp_file.close()
        if file_path.exists():
            shutil.copymode(file_path, temp_file.name)
        else:
            current_umask = os.umask(0)
            os.umask(current_umask)
            new_mode = 0o666 & (current_umask ^ 0o777)
            os.chmod(temp_file.name, new_mode)
        shutil.move(temp_file.name, file_path)
    except Exception:
        try:
            if temp_file is not None:
                Path(temp_file.name).unlink()
        except Exception:
            print_error_and_bail(
                f"Error while writing file '{orig_file_path}', and could not "
                "delete temp file!"
            )
        print_error_and_bail(f"Error while writing file '{orig_file_path}'!")


if __name__ == "__main__":
    main()
