#!/usr/bin/python3
# SPDX-License-Identifier: GPL-2.0-or-later
########################################################################
# FILE:	greylist
# PURPOSE:	Command line interface to "greylistd(8)"
#
# Copyright (C) 2004, Tor Slettnes <tor@slett.net>
########################################################################

import socket
from socket import AF_UNIX, SOCK_STREAM
import os
import sys
import configparser


# Exit codes based on responses from greylistd
exitcodes = {"error:": -1,
             "white": 0,
             "grey": 1,
             "black": 2,
             "true": 0,
             "false": 1}


# File paths
conffile = "/etc/greylistd/config"
sockfile = "/var/run/greylistd/socket"

# Commands that can be given over the socket
commands = ("add", "delete", "check", "update",
            "stats", "list", "clear", "save",
            "reload", "mrtg", "status")


def usage(progname, message=None):
    if message:
        out = sys.stderr
        out.write("%s: %s\n\n" % (progname, message))
        exitstatus = 1
    else:
        out = sys.stdout
        out.write("Command line interface to \"greylistd(8)\"\n\n")
        exitstatus = 0

    out.write("\n".join([
        "Usage: %s --help" % progname,
        "       %s <action>" % progname,
        "",
        "Actions:",
        "  add [--white|--grey|--black] <data> ...",
        "    Add <data> to the specified list (\"--white\" if unspecified).",
        "",
        "  delete <data> ...",
        "    Remove <data> from any list.",
        "",
        "  check [--white|--grey|--black] <data> ...",
        "    Check the current status of <data>.",
        "",
        "  update [--white|--grey|--black] <data> ...",
        "    Check the current status of <data>; update lists accordingly.",
        "    This is how MTAs would normally use the command.",
        "",
        "  status <data> ...",
        "    Show the current status of <data>; Report \"unseen\" if not in database.",
        "",
        "  stats",
        "    Show some general list statistics.",
        "",
        "  mrtg",
        "    Show grey and white list statistics in a format that MRTG can use directly.",
        "",
        "  list [--white] [--grey] [--black]",
        "    Show all data items in the specified list(s).",
        "",
        "  save",
        "    Force an immediate dump of data to filesystem.",
        "",
        "  reload",
        "    Save data, then reload configuration and data.",
        "",
        "  clear [--white|--grey|--black]",
        "    Remove ALL data and statistics, i.e. reset greylistd(8).",
        "    This means that ALL new requests will initially receive",
        "    a \"grey\" response.  Use with caution!",
        "",
        ""]))

    sys.exit(exitstatus)


progname = os.path.basename(sys.argv[0])

if len(sys.argv) < 2:
    usage(progname, "No action specified.")


action = sys.argv[1].lower()
if action in ("-h", "-help", "--help", "help"):
    usage(progname)

elif not action in commands:
    usage(progname, "Invalid action: '%s'" % action)


confParser = configparser.ConfigParser()
confParser.read(conffile)
try:
    sockfile = confParser.get("socket", "path")
except:
    pass
del confParser


sock = socket.socket(AF_UNIX, SOCK_STREAM)

try:
    sock.connect(sockfile)
except Exception as e:
    sys.stderr.write("%s: %s\n" % (sockfile, str(e)))
    sys.exit(1)

try:
    sock.send(" ".join(sys.argv[1:]).encode('ascii', 'replace'))
except Exception as e:
    sys.stderr.write("%s: %s\n" % (sockfile, str(e)))
    sys.exit(1)


stat = True
firstword = None

while stat:
    stat = sock.recv(1024)
    try:
        sys.stdout.write("%s" % stat.decode('ascii', 'replace'))

    except IOError:
        break

    else:
        if not firstword and stat.strip():
            firstword = stat.split(None, 1)[0]

else:
    try:
        sys.stdout.write("\n")
    except IOError:
        pass


if firstword:
    sys.exit(exitcodes.get(firstword, 0))
else:
    sys.stderr.write("(No response from greylistd)\n")
    sys.exit(1)
