Auto signature script for kopano from ldap

Hallo, i wrote simple script to make auto signatures for Univention users for Kopano.
script use part of old kopano script to write data.
All info for signature stored in LDAP (Univention)
Also for greeting record i use univentionUMCProperty field with format “signature=some text”

script should be run on kopano server.
with “overwrite” argument it will overwrite all signatures.

signature.py

#!/usr/bin/env python3

# pylint: disable=E1101

#by Konstatin Frank

import datetime
import collections
import ldap
import html
from MAPI.Util import *
import sys
import time

# Try simplejson if json is not available
try:
    import json
except ImportError:
    import simplejson as json



LDAP_IP = "192.168.XX.XX"
LDAP_PORT = "7389"
LDAP_USER = "uid=search_ldap,cn=users,dc=domain,dc=local"
LDAP_PASSWORD = "XXXXXXXXXXXXXXx"
LDAP_USERS = "cn=users,dc=domain,dc=local"

TEST_USER = "test"
TEMPLATE_FILE = "signature.html"

#LDAP SEARCH VERIABLES
ATTR_MAIL = "mailPrimaryAddress"
ATTR_USER_DISABLED = "shadowExpire"
ATTR_UID = "uid"
ATTR_TEL = "telephoneNumber"
ATTR_POSITION = "departmentNumber"
ATTR_FULL_NAME = "displayName"
ATTR_IF_KOPANO_ACCOUNT = "kopanoAccount"
#Title can be used for some reasons. for example ppa.  
# it will be add at start to user full name. it can be empty
ATTR_TITLE = "title"
# WARNING! I using" univentionUMCProperty" LDAP ATTRIBUTE to write users greetings. univentionUMCProperty
# can has many records in format "value_name=value".  For greeting i use value "signature"
ATTR_GREETING = "univentionUMCProperty"
ATTR_GREETING_VALUE = "signature"
# If user has no greeting record script will use default greeting.
DEFAULT_GREETING  = "Mit freundlichen Grüßen / Best regards"



PR_EC_WEBACCESS_SETTINGS_JSON = PROP_TAG(PT_STRING8, PR_EC_BASE + 0x72)

def check_input():
    overwrite = False
    if len(sys.argv) > 1:
        if sys.argv[1] == "overwrite":
            overwrite = True
    return overwrite

def read_settings(username):
    data = None
    #username = sys.argv[1]

    try:
        s = OpenECSession(username, '', 'file:///var/run/kopano/server.sock')
        st = GetDefaultStore(s)

    except MAPIErrorNotFound as e:
        print('User {} has no user store ({})'.format(username, e))
        return

    except MAPIErrorLogonFailed as e:
        print('User {} not found (on this server) ({})'.format(username, e))
        return

    try:
        settings = st.OpenProperty(PR_EC_WEBACCESS_SETTINGS_JSON, IID_IStream, 0, 0)
        data = settings.Read(33554432)
    except Exception as e:
        # Return empty config tree
        data = '{"settings": {"zarafa": {"v1": {"contexts": {"mail": {}}}}}}'

    return data


def write_settings(data, username):
    s = OpenECSession(username, '', 'file:///var/run/kopano/server.sock')
    st = GetDefaultStore(s)

    settings = st.OpenProperty(PR_EC_WEBACCESS_SETTINGS_JSON, IID_IStream, 0, MAPI_MODIFY | MAPI_CREATE)
    settings.SetSize(0)
    settings.Seek(0, STREAM_SEEK_END)

    writesettings = settings.Write(data.encode('utf-8'))

    if writesettings:
        print('Writing settings for user {}'.format(username))
        settings.Commit(0)
    else:
        print('Writing Default Signature for user {} failed.'.format(username))


def ldap_connection_init():
    global connect
    try:
        connect = ldap.initialize('ldap://{}:{}'.format(LDAP_IP, LDAP_PORT))
        connect.set_option(ldap.OPT_REFERRALS, 0)
        connect.simple_bind_s(LDAP_USER, LDAP_PASSWORD)
    except Exception as error_msg:
        return error_msg
    return connect


def update(dic, upd):

# input dict and dixct update.
# update dict with new users and sort it

    for share, users in upd.items():
        if isinstance(users, collections.Mapping):
            for key, value in dic.get(share, {}).items():
                if key == next(iter(users)):
                    users[next(iter(users))].extend(value)
                    users[next(iter(users))] = list(set(users[next(iter(users))]))
                    #users[next(iter(users))].sort()
            dic[share] = update(dic.get(share, {}), users)
        else:
            dic[share] = users
    return dic



def html_decode(string):
    string = string.decode('utf-8')
#.encode('ascii', 'xmlcharrefreplace').decode
#decode("windows-1252")
#.decode('utf-8')
#.encode('ascii', 'xmlcharrefreplace').decode('de_DE.utf8')
    return string





def get_users():

    ldap_querry = connect.search_s(
        LDAP_USERS,
        ldap.SCOPE_SUBTREE,
        'objectClass=organizationalPerson',
        [ATTR_UID, ATTR_FULL_NAME , ATTR_USER_DISABLED, ATTR_GREETING, ATTR_TEL, ATTR_POSITION, ATTR_IF_KOPANO_ACCOUNT, ATTR_TITLE, ATTR_MAIL])
    output = {}
    for key, value in ldap_querry:

        shadow_expire = value.get(ATTR_USER_DISABLED, '')
        if shadow_expire:
            continue

        user_mail = value.get(ATTR_MAIL)

        #skip if user has no email
        if user_mail:
            user_mail = str(user_mail[0].decode("utf-8"))
        else:
            continue

        uid = value.get(ATTR_UID, '')
        uid = html_decode(uid[0])

        #skip for administrator
        if uid == 'Administrator':
            continue

        
        #if not uid == TEST_USER:
        #    continue
        
        #skip if user has no kopano enabled.
        kopanoAccount = value.get(ATTR_IF_KOPANO_ACCOUNT)
        if not kopanoAccount:
            continue
        kopanoAccount = str(kopanoAccount[0].decode())
        if not kopanoAccount == '1':
            continue

        #skip if user has no full name    
        user_name = value.get(ATTR_FULL_NAME, '')
        if not user_name:
            continue
        user_name = html_decode(user_name[0])
        
        title = value.get(ATTR_TITLE, '')
        if  title:
            title = html_decode(title[0])
            user_name = "{} {}".format(title, user_name)

        user_position = value.get(ATTR_POSITION)
        if user_position:
            user_position = str(user_position[0].decode("utf-8"))
        #skip if user has no position
        else:
            continue

        #signature = False
        univentionUMCProperty = value.get(ATTR_GREETING)
        if univentionUMCProperty:
            for i in univentionUMCProperty:
                i = str(i. decode("utf-8"))
                if i.split("=")[0] == ATTR_GREETING_VALUE:
                    user_greet = i.split("=")[1]
                    continue
                else:
                    user_greet=html.unescape(DEFAULT_GREETING)
        else:
            user_greet=html.unescape(DEFAULT_GREETING)


        user_tel = value.get(ATTR_TEL)
        if user_tel:
            user_tel = str(user_tel[0].decode("utf-8"))
        else:
            user_tel = '0'


        #list
        dic = {uid: [user_name, user_tel, user_position, user_greet, user_mail]}
        output.update(dic)
    return output





def main():

    #ldap connection
    ldap_connection_init()

    #get users info from ldap
    users = get_users()

    #read signature template
    signaturefile = open(TEMPLATE_FILE, 'r')
    signaturehtml = signaturefile.read()


    #start writing
    for uid, value in users.items():
        user_signature = signaturehtml.format(name=value[0], tel=value[1], position=value[2], signature=value[3], mail=value[4])

        time.sleep(1)
        data = read_settings(uid)
        signatureid = '1'
        signaturename = u'WebApp Default Signature'
        signature_content = dict({u'name': signaturename, u'content': user_signature, u'isHTML': True})
        if data:
            #print(uid, type(data))
            if not type(data) == str:
                webappsettings = json.loads(data.decode('utf-8'))
            else:
                webappsettings = json.loads(data)

            if not len(webappsettings['settings']['zarafa']['v1']['contexts']['mail']):
                print("WebApp settings are empty.")
                print("Adding config tree.")
                webappsettings['settings']['zarafa']['v1']['contexts']['mail'] = dict({})

            if u'signatures' not in list(webappsettings['settings']['zarafa']['v1']['contexts']['mail']):
                print("Adding Signature settings to config tree.")
                webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures'] = dict({})

            if u'all' not in list(webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures']):
                print("Empty Signature settings detected.")
                webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures'] = dict({'all': dict({})})
            overwrite = check_input()
            if webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all'].get(signatureid) and not overwrite:
                print('Default Signature already exists for user {}'.format(uid))
            else:
                print('Adding Default Signature for user {}'.format(uid))
                webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all'][
                    signatureid] = signature_content
                webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['new_message'] = signatureid
                webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures'][
                    'replyforward_message'] = signatureid
                write_settings(json.dumps(webappsettings),uid)
    

if __name__ == '__main__':
    main()
    

and some test signature template file.
signature.html

    <body>
        <p>
            {signature}
        </p>

        <p>
            {name}<br>
            {position}
        </p>


        <p>
            Tel.: + 11 1 11 111 111-{tel}<br>
            Mail: <a href="mailto:{mail}">{mail}</a><br>
        </p>



    </body>
1 Like

the script can be freely used or modified.

Hallo Sleidex,
die Idee finde ich super. Ich verstehe aber noch nicht wie es Funktioniert.
Wo ist das Script gespeichert?
Wo ist die Signatur gespeichert?
Wie erzeuge ich den LDAP Eintrag?
Wie wird das Script aufgerufen?

Ich würde das Script gerne testen, brauche aber noch Unterstützung bei den Fragen.

Thomas

sorry, i prefer english to answer, my German is’t perfect:)

this implementation needs some minimum linux skills

you should save this script on kopano server. in any place you want.
for example
login by ssh to server
mkdir -p /opt/signature

place both files to /opt/signature

you should also install python3 to server
sudo apt-get install python3

now you can play with script.
you should have one user with Full name , email address, kopano enabled,
Telefonnummer in Kontakte
and Abteilungsnummer as Position in Kontakte
That’s minumum info need script to work. All other users without this record wqill be ignored by script

now test lauch

firsr make script executable
sudo chmod +x /opt/signature/signature.py

and launch it
cd /opt/signature/
./signature.py

or
./signature.py overwrite
( to overwrite existing signatures)

P.S.
I will update script installation process later. it will be full automatic

That’s how I feel about English.

Not required for UCS 4.4-1

Thank you very much.
I’ll test it.

Thomas