#!/usr/local/bin/python3.7
# SPDX-License-Identifier: AGPL-3.0-only

import os
import locale
import sys
import getopt
import datetime
import time
import json

import MAPI
from MAPI.Util import *
from MAPI.Time import *
from MAPI.Struct import *

from MAPI.Tags import (
    PR_EC_OUTOFOFFICE,
    PR_EC_OUTOFOFFICE_MSG_W,
    PR_EC_OUTOFOFFICE_SUBJECT_W,
    PR_EC_OUTOFOFFICE_FROM,
    PR_EC_OUTOFOFFICE_UNTIL,
)

MODE_ENABLE = 1
MODE_DISABLE = 2
MODE_UPDATE_ONLY = 3

def print_help():
    print("Usage: %s -u [username of mailbox]" % sys.argv[0])
    print("")
    print("Manage out of office messages of users")
    print("")
    print("Required arguments:")
    print("   -u, --user          user to set out of office message for")
    print("   -m, --mode          0 to disable out of office (default), 1 to enable")
    print("")
    print("optional arguments:")
    print("   --from              specify the date/time when oof should become active")
    print("   --until             specify the date/time when oof should become inactive again")
    print("   -t, --subject       specify the subject to be set in oof message")
    print("   -n, --message       text file containing body of out of office message")
    print("   -h, --host          Host to connect with. Default: file:///var/run/kopano/server.sock")
    print("   -s, --sslkey-file   SSL key file to authenticate as admin.")
    print("   -p, --sslkey-pass   Password for the SSL key file.")
    print("   --dump-json         Dumps the current status as JSON.")
    print("   --help              Show this help message and exit.")
    print("")
    print("")
    print("Example:")
    print(" Enable out of office message of mailbox user1 with subject 'test' and body from file /tmp/oof-message")
    print("  $ %s --user user1 --mode 1 --subject 'test' --message /tmp/oof-message" % sys.argv[0])
    print("")
    print(" Enable out of office message of mailbox user1 with subject 'test' and body from file /tmp/oof-message in a multi-server-environment")
    print("  $ %s --user user1 --mode 1 --subject 'test' --message /tmp/oof-message --host https://127.0.0.1:237/ --sslkey-file /etc/kopano/ssl/client.pem --sslkey-pass password" % sys.argv[0])
    print("")
    print(" Disable out of office message of mailbox user1 in a multi-server-environment")
    print("  $ %s --user user1 --mode 0 --sslkey-file /etc/kopano/ssl/client.pem --host https://127.0.0.1:237/ --sslkey-pass password" % sys.argv[0])

def PrintSubject(subject):
    if subject.ulPropTag == PR_EC_OUTOFOFFICE_SUBJECT_W:
        print("Current subject: '%s'" % subject.Value)
    else:
        print("Current subject: No subject set")

def main(argv = None):
    if argv is None:
        argv = sys.argv

    try:
        opts, args = getopt.gnu_getopt(argv, "h:s:p:u:m:t:n:he", ['from=', 'until=', 'host=', 'sslkey-file=', 'sslkey-pass=', 'user=', 'mode=', 'subject=', 'message=', 'dump-json', 'help', ])
    except getopt.GetoptError as err:
        # print help information and exit:
        print(str(err))
        print("")
        print_help()
        return 1

    # defaults
    host = os.getenv("KOPANO_SOCKET", "default:")
    sslkey_file = None
    sslkey_pass = None
    mode = MODE_UPDATE_ONLY
    date_from = None
    date_until = None
    username = None 
    subject = None
    message = None

    dump_json = False

    for o, a in opts:
        if o in ('-h', '--host'):
            host = a
        elif o in ('-s', '--sslkey-file'):
            sslkey_file = a
        elif o in ('-p', '--sslkey-pass'):
            sslkey_pass = a
        elif o in ('-u', '--user'):
            username = a
        elif o in ('-m', '--mode'):
            if a == '-':
                mode = MODE_UPDATE_ONLY
            elif a == '1':
                enabled = True
                mode = MODE_ENABLE
            else:
                enabled = False
                mode = MODE_DISABLE
        elif o == "--from":
            date_from = datetime.datetime.strptime(a, "%Y-%m-%d %H:%M")
        elif o == "--until":
            date_until = datetime.datetime.strptime(a, "%Y-%m-%d %H:%M")
        elif o in ('-t', '--subject'):
            subject = a
        elif o in ('-n', '--message'):
            message = a
        elif o == '--dump-json':
            dump_json = True
        elif o == '--help':
            print_help()
            return 0
        else:
            assert False, ("unhandled option '%s'" % o)

    if not username:
        print("No username specified.")
        print("")
        print_help()
        sys.exit(1)

    try:
        session = OpenECSession(username, '', host, sslkey_file = sslkey_file, sslkey_pass = sslkey_pass)
    except MAPIError as err:
        if err.hr == MAPI_E_LOGON_FAILED:
            print("Failed to logon. Make sure your SSL certificate is correct.")
        elif err.hr == MAPI_E_NETWORK_ERROR:
            print("Unable to connect to server. Make sure you specified the correct server.")
        else:
            print("Unexpected error occurred. hr=0x%08x" % err.hr)
        sys.exit(1)

    try:
        st = GetDefaultStore(session)
    except:
        print("Unable to open store for user '%s'" % username)

    try:
        oldstatus = st.GetProps([PR_EC_OUTOFOFFICE], 0)[0].Value
        if oldstatus == MAPI_E_NOT_FOUND:
            oldstatus = False
    except Exception:
        print("Unable to read out of office status of user '%s'" % username)

    try:
        oldsubject = st.GetProps([PR_EC_OUTOFOFFICE_SUBJECT_W], 0)[0].Value
        if oldsubject == MAPI_E_NOT_FOUND:
            oldsubject = None

    except Exception:
        print("Unable to read out of office subject of user '%s'" % username)

    try:
        oldmsg = st.GetProps([PR_EC_OUTOFOFFICE_MSG_W], 0)[0].Value
        if oldmsg == MAPI_E_NOT_FOUND:
            oldmsg = None
    except Exception:
        print("Unable to read out of office message of user '%s'" % username)

    try:
        oldfrom = st.GetProps([PR_EC_OUTOFOFFICE_FROM], 0)[0].Value
        if oldfrom == MAPI_E_NOT_FOUND:
            oldfrom = None
        else:
            oldfrom = oldfrom.datetime().isoformat()
    except Exception:
        oldfrom = None

    try:
        olduntil = st.GetProps([PR_EC_OUTOFOFFICE_UNTIL], 0)[0].Value
        if olduntil == MAPI_E_NOT_FOUND:
            olduntil = None
        else:
            olduntil = olduntil.datetime().isoformat()
    except Exception:
        olduntil = None

    if dump_json:
        result = json.dumps({
            'set': bool(oldstatus),
            'subject': oldsubject,
            'message': oldmsg,
            'from': oldfrom,
            'until': olduntil,
        })
        print(result)
        sys.exit(0)

    if mode != MODE_UPDATE_ONLY:
        try:
            if oldstatus == enabled and not enabled:
                print("Out of office for user '%s' already disabled, skipping." % username)
            elif enabled:
                st.SetProps([SPropValue(PR_EC_OUTOFOFFICE, enabled)])
                if date_from or date_until:
                    props = []

                    if date_from:
                        t1 = MAPI.Time.FileTime(0)
                        t1.unixtime = time.mktime(date_from.timetuple())
                        p1 = SPropValue(PR_EC_OUTOFOFFICE_FROM, t1)
                        props.append(p1)

                    if date_until:
                        t2 = MAPI.Time.FileTime(0)
                        t2.unixtime = time.mktime(date_until.timetuple())
                        p2 = SPropValue(PR_EC_OUTOFOFFICE_UNTIL, t2)
                        props.append(p2)

                    st.SetProps(props)
                else:
                    st.DeleteProps([PR_EC_OUTOFOFFICE_FROM, PR_EC_OUTOFOFFICE_UNTIL])
                print("Out of office enabled for user '%s'" % username)
            else:
                st.SetProps([SPropValue(PR_EC_OUTOFOFFICE, enabled)])
                print("Out of office disabled for user '%s'" % username)
        except:
            print("Unable to set out of office for user '%s'" % username)
            raise

    if mode != MODE_DISABLE:
        if date_from or date_until:
            try:
                props = []
                deleted_props = []

                if date_from:
                    t1 = MAPI.Time.FileTime(0)
                    t1.unixtime = time.mktime(date_from.timetuple())
                    p1 = SPropValue(PR_EC_OUTOFOFFICE_FROM, t1)
                    props.append(p1)
                else:
                    deleted_props.append(PR_EC_OUTOFOFFICE_FROM)

                if date_until:
                    t2 = MAPI.Time.FileTime(0)
                    t2.unixtime = time.mktime(date_until.timetuple())
                    p2 = SPropValue(PR_EC_OUTOFOFFICE_UNTIL, t2)
                    props.append(p2)
                else:
                    deleted_props.append(PR_EC_OUTOFOFFICE_UNTIL)

                st.DeleteProps(deleted_props)
                st.SetProps(props)
            except:
                print("Unable to update OOF date restriction for user '%s'" % username)
                raise

        if subject:
            try:
                if oldsubject == subject:
                    print("Not changing subject for user '%s' as it was not updated" % username)
                else:
                    print("Setting new subject '%s' for user '%s'" % (subject, username))
                    st.SetProps([SPropValue(PR_EC_OUTOFOFFICE_SUBJECT_W, subject)])
            except:
                print("Unable to set out of office subject for user '%s'" % username)

            newsubject = st.GetProps([PR_EC_OUTOFOFFICE_SUBJECT_W], 0)
            PrintSubject(newsubject[0])

        if message:
            try:
                f = open(message, 'rt')
            except IOError:
                print("The specified file '%s' does not exist - Please specify a valid message file." % message)
                sys.exit(1)

            msg = f.read()
            f.close()
            try:
                if oldmsg == msg:
                    print("Not updating message, already matching with input file.")
                else:
                    print("Setting new out of office message for user '%s'" % username)
                    st.SetProps([SPropValue(PR_EC_OUTOFOFFICE_MSG_W, msg)])
            except:
                print("Unable to set out of office message.")


if __name__ == '__main__':
    locale.setlocale(locale.LC_ALL, '')
    sys.exit(main())
