From 968afd482ee49fae87f1b6783f7ebe133a31c21b Mon Sep 17 00:00:00 2001 From: Martyn Alberts Date: Wed, 6 Feb 2019 15:11:53 +0100 Subject: [PATCH 1/8] Add HTML editor param --- webapp_admin/README.md | 3 +++ webapp_admin/webapp_admin.py | 51 ++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/webapp_admin/README.md b/webapp_admin/README.md index bc4421d..b45ada3 100644 --- a/webapp_admin/README.md +++ b/webapp_admin/README.md @@ -12,6 +12,9 @@ Reset WebApp settings Change free/busy to 36 months > python3 webapp_admin -u john --free-busy=36 +If you want to make a change for all users don't pass the user parameter. Example: +> python3 webapp_admin --icons Breeze + # Dependencies - python3 diff --git a/webapp_admin/webapp_admin.py b/webapp_admin/webapp_admin.py index b0fd4f1..cb827d3 100755 --- a/webapp_admin/webapp_admin.py +++ b/webapp_admin/webapp_admin.py @@ -72,6 +72,7 @@ def opt_args(print_help=None): group.add_option("--theme", dest="theme", action="store", help="Change theme (e.g. dark)") group.add_option("--free-busy", dest="freebusy", action="store", help="Change free/busy time span in months") group.add_option("--icons", dest="icons", action="store", help="Change icons (e.g. breeze)") + group.add_option("--htmleditor", dest="htmleditor", action="store", help="Change the HTML editor (e.g. full_tinymce)") parser.add_option_group(group) # Advanced option group @@ -351,25 +352,45 @@ def import_smime(user, cert_file, passwd, ask_password=None, public=None): print('Email address doesn\'t match') +""" +Custom function to merge two dictionaries. +Previously we used the internal dotty function for this, +but this function caused undesired behavior + +:param dict1: The first dictionary +:param dict2: The second dictionary +""" +def mergedicts(dict1, dict2): + for k in set(dict1.keys()).union(dict2.keys()): + if k in dict1 and k in dict2: + if isinstance(dict1[k], dict) and isinstance(dict2[k], dict): + yield (k, dict(mergedicts(dict1[k], dict2[k]))) + else: + yield (k, dict2[k]) + elif k in dict1: + yield (k, dict1[k]) + else: + yield (k, dict2[k]) + + """ Inject webapp settings into the users store :param user: The user :param data: The webapp setting -:param removed: Remove old setting and write new setting """ -def advanced_inject(user, data, removed=None): +def advanced_inject(user, data): settings = read_settings(user) split_data = data.split('=') value = split_data[1].lstrip().rstrip() - dot = dotty(settings) - if removed: - del dot[split_data[0].rstrip()] - else: - dot[split_data[0].rstrip()] = value + dot = dotty() + dot[split_data[0].rstrip()] = value + new_data = dot.to_dict() - write_settings(user, json.dumps(new_data)) + new_settings = dict(mergedicts(settings, new_data)) + + write_settings(user, json.dumps(new_settings)) """ @@ -427,10 +448,24 @@ def main(): # Icon set if options.icons: + accepted_icons = {'Breeze', 'Classic'} + if not options.icons in accepted_icons: + print('Valid syntax: Breeze or Classic') + sys.exit(1) setting = 'settings.zarafa.v1.main.active_iconset = {}'.format(options.icons) advanced_inject(user, setting) print('icon set changed to {}'.format(options.icons)) + # Editor + if options.htmleditor: + accepted_editors = {'htmleditor-minimaltiny', 'full_tinymce'} + if not options.htmleditor in accepted_editors: + print('Valid syntax: htmleditor-minimaltiny or full_tinymce') + sys.exit(1) + setting = 'settings.zarafa.v1.contexts.mail.html_editor = {}'.format(options.htmleditor) + advanced_inject(user, setting) + print('Editor changed to {}'.format(options.htmleditor)) + # Always at last!!! if options.reset: reset_settings(user) From 1425c84ce01bee97e9f3f29c6fe289c1675d0c94 Mon Sep 17 00:00:00 2001 From: Martyn Alberts Date: Fri, 8 Feb 2019 14:48:32 +0100 Subject: [PATCH 2/8] Remove files that are replaced by webapp_admin --- .../dump_webapp_signatures.py | 38 --------- dump_webapp_signatures/readme.md | 12 --- set_webapp_default_signature/readme.md | 25 ------ .../set_webapp_default_signature.py | 84 ------------------- webapp_settings/readme.md | 19 ----- webapp_settings/webapp_settings.py | 52 ------------ webapp_switch_locale/readme.md | 17 ---- webapp_switch_locale/webapp_switch_locale.py | 35 -------- 8 files changed, 282 deletions(-) delete mode 100644 dump_webapp_signatures/dump_webapp_signatures.py delete mode 100644 dump_webapp_signatures/readme.md delete mode 100644 set_webapp_default_signature/readme.md delete mode 100644 set_webapp_default_signature/set_webapp_default_signature.py delete mode 100644 webapp_settings/readme.md delete mode 100644 webapp_settings/webapp_settings.py delete mode 100644 webapp_switch_locale/readme.md delete mode 100644 webapp_switch_locale/webapp_switch_locale.py diff --git a/dump_webapp_signatures/dump_webapp_signatures.py b/dump_webapp_signatures/dump_webapp_signatures.py deleted file mode 100644 index 85d6a77..0000000 --- a/dump_webapp_signatures/dump_webapp_signatures.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -import kopano -from MAPI.Util import * - -try: - import json -except ImportError: - import simplejson as json - - -def opt_args(): - parser = kopano.parser('skpcf') - parser.add_option("--user", dest="user", action="store", help="Dump signatures for user") - return parser.parse_args() - - -def main(): - options, args = opt_args() - if not options.user: - print 'Please use:\n %s --user ' % (sys.argv[0]) - else: - user = kopano.Server(options=options).user(options.user) - try: - settings = json.loads(user.store.prop(PR_EC_WEBACCESS_SETTINGS_JSON).value) - except Exception as e: - print 'Could not load WebApp settings for user %s (Error: %s)' % (user.name, repr(e)) - else: - if len(settings['settings']['zarafa']['v1']['contexts']['mail']): - if 'signatures' in settings['settings']['zarafa']['v1']['contexts']['mail']: - for item in settings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all']: - name = settings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all'][item]['name'] - filename = '%s-%s-%s.html' % (user.name, name.replace(' ', '-'), item) - with open(filename, 'w') as outfile: - print 'Dumping: \'%s\' to \'%s\' ' % (name, filename) - outfile.write(settings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all'][item]['content'].encode('utf-8')) - -if __name__ == '__main__': - main() diff --git a/dump_webapp_signatures/readme.md b/dump_webapp_signatures/readme.md deleted file mode 100644 index 41606f2..0000000 --- a/dump_webapp_signatures/readme.md +++ /dev/null @@ -1,12 +0,0 @@ - - -dump_webapp_signatures.py -========================= -Dumps all the signatures in a users Webapp to seperate files, meant as companion to the script setdefaultsignature.py as delivered with Webapp (see /usr/share/doc/kopano-webapp/scripts/signatures/ on your Webapp server.) -The files will be written in the current directory. - -#### Usage: -``` -python dump_webapp_signatures.py --user user -``` - diff --git a/set_webapp_default_signature/readme.md b/set_webapp_default_signature/readme.md deleted file mode 100644 index c58636f..0000000 --- a/set_webapp_default_signature/readme.md +++ /dev/null @@ -1,25 +0,0 @@ - - -# set_webapp_default_signature.py - -Add and set a Default signature in Webapp for user(s), will overwrite any other default. -Please use a signature as dumped with dump_webapp_signatures.py - -## Examples - -### Set signature of a local user on the local server -``` -./set_webapp_default_signature.py -u user1 -f user2-signature.sig -``` - -### Set signature multiple local users on the local server -``` -./set_webapp_default_signature.py -u user1 -u user3 -f user2-signature.sig -``` - -### Set signature for all local users on the local server -``` -./set_webapp_default_signature.py -a -f user2-signature.sig -``` - - diff --git a/set_webapp_default_signature/set_webapp_default_signature.py b/set_webapp_default_signature/set_webapp_default_signature.py deleted file mode 100644 index 6278219..0000000 --- a/set_webapp_default_signature/set_webapp_default_signature.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 -""" -Set A Default WebApp Signature. -""" -import json -import kopano -from MAPI.Tags import * - - -def read_settings(user): - try: - mapisettings = user.store.prop(PR_EC_WEBACCESS_SETTINGS_JSON).value - settings = json.loads(mapisettings) - except Exception as e: - print '%s: Has no or no valid WebApp settings creating empty config tree' % user.name - settings = json.loads( - '{"settings": {"zarafa": {"v1": {"contexts": {"mail": {}}}}}}') - return settings - - -def write_settings(user, webappsettings): - try: - user.store.create_prop(PR_EC_WEBACCESS_SETTINGS_JSON, webappsettings) - except Exception as e: - print '%s: Error Writing WebApp settings for user: %s' % (e, user.name) - - -def main(options): - with open(options.file, 'r') as sigfile: - signaturehtml = sigfile.read() - signatureid = '1' - signaturename = options.file.replace('template', 'default').replace('-', ' ').replace('.html', '').title().split('/')[-1].replace(' Nl', ' NL').replace( - ' De', ' DE') - signaturecontent = dict( - {u'name': signaturename, u'content': signaturehtml, u'isHTML': True}) - runusers = [] - if options.allusers: - for ruser in server.users(remote=False): - runusers.append(ruser.name) - else: - runusers = options.users - - for username in runusers: - try: - user = server.user(username) - webappsettings = read_settings(user) - except Exception as e: - print e - continue - - if not len(webappsettings['settings']['zarafa']['v1']['contexts']['mail']): - print "%s: Adding config tree." % user.name - webappsettings['settings']['zarafa'][ - 'v1']['contexts']['mail'] = dict({}) - if 'signatures' not in list(webappsettings['settings']['zarafa']['v1']['contexts']['mail']): - print "%s: Adding Signature settings to config tree." % user.name - webappsettings['settings']['zarafa']['v1'][ - 'contexts']['mail']['signatures'] = dict({}) - if 'all' not in list(webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures']): - print "%s: Empty Signature settings detected." % user.name - webappsettings['settings']['zarafa']['v1']['contexts'][ - 'mail']['signatures'] = dict({'all': dict({})}) - print '%s: Adding/Replacing Default Signature with %s' % (user.name, signaturename) - webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all'][ - signatureid] = signaturecontent - webappsettings['settings']['zarafa']['v1']['contexts'][ - 'mail']['signatures']['new_message'] = signatureid - webappsettings['settings']['zarafa']['v1']['contexts']['mail']['signatures'][ - 'replyforward_message'] = signatureid - write_settings(user, json.dumps(webappsettings)) - - -if __name__ == '__main__': - parser = kopano.parser('uUPckpsC') # select common cmd-line options - parser.add_option('-a', dest='allusers', action='store_true', - default=None, help='run program for all local users') - parser.add_option('-f', dest='file', action='store', - default=None, help='signature filename') - options, args = parser.parse_args() - server = kopano.Server(options=options) - if (options.users or options.allusers) and options.file: - main(options) diff --git a/webapp_settings/readme.md b/webapp_settings/readme.md deleted file mode 100644 index 71e4bd1..0000000 --- a/webapp_settings/readme.md +++ /dev/null @@ -1,19 +0,0 @@ - -webapp_settings.py -================== - -#### Usage: - - -###### Backup - -```python -python webapp_settings.py --user user --backup -``` - - -###### Restore - -```python -python webapp_settings.py --user user --restore -``` diff --git a/webapp_settings/webapp_settings.py b/webapp_settings/webapp_settings.py deleted file mode 100644 index 079ee44..0000000 --- a/webapp_settings/webapp_settings.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -#encoding: utf-8 - -from MAPI import * -from MAPI.Util import * -import sys -try: - import kopano -except ImportError: - import zarafa as kopano -try: - import json -except ImportError: - import simplejson as json - -def opt_args(): - parser = kopano.parser('skpcfm') - parser.add_option("--user", dest="user", action="store", help="Run script for user") - parser.add_option("--backup", dest="backup", action="store_true", help="Backup webapp setting ") - parser.add_option("--restore", dest="restore", action="store_true", help="Restore webapp settings") - parser.add_option("--remove", dest="remove", action="store_true", help="Remove webapp settings") - - return parser.parse_args() - - -def main(): - options, args = opt_args() - - if not options.user or (not options.backup and not options.restore and not options.remove): - print 'Please use:\n %s --user (--backup or --restore) ' % (sys.argv[0]) - sys.exit() - - user = kopano.Server(options).user(options.user) - if options.backup: - webapp = json.loads(user.store.prop(PR_EC_WEBACCESS_SETTINGS_JSON).value) - f = open('%s.json' % user.name,'w') - - f.write(json.dumps(webapp, sort_keys=True, - indent=4, separators=(',', ': '))) - f.close() - if options.restore: - with open('%s.json' % user.name) as data_file: - data = json.load(data_file) - - print data - user.store.create_prop(PR_EC_WEBACCESS_SETTINGS_JSON, json.dumps(data)) - - if options.remove: - user.store.delete(user.store.prop('PR_EC_WEBACCESS_SETTINGS_JSON')) - -if __name__ == "__main__": - main() diff --git a/webapp_switch_locale/readme.md b/webapp_switch_locale/readme.md deleted file mode 100644 index 6bba6f0..0000000 --- a/webapp_switch_locale/readme.md +++ /dev/null @@ -1,17 +0,0 @@ - -webapp_switch_locale.py -========================= -List or change the locale currently set in the user's WebApp settings. - -#### Usage: -###### List locale -``` -python switchlocale.py --user user1 -Original locale: nl_NL.UTF-8 -``` -###### Change locale -``` -python switchlocale.py --user user1 --locale de_DE.UTF-8 -Original locale: nl_NL.UTF-8 -Setting locale to: de_DE.UTF-8 -``` diff --git a/webapp_switch_locale/webapp_switch_locale.py b/webapp_switch_locale/webapp_switch_locale.py deleted file mode 100644 index d3ad1ba..0000000 --- a/webapp_switch_locale/webapp_switch_locale.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -import json -import kopano -from MAPI.Util import * -import sys - - -def opt_args(): - parser = kopano.parser('skpcfm') - parser.add_option("--user", dest="user", action="store", help="Run script for user") - parser.add_option("--locale", dest="locale", action="store", help="Set new locale") - return parser.parse_args() - - -def main(): - options, args = opt_args() - - if not options.user: - print 'Please use:\n %s --user [--locale]' % (sys.argv[0]) - sys.exit() - - user = kopano.Server(options).user(options.user) - settings = json.loads(user.store.prop(PR_EC_WEBACCESS_SETTINGS_JSON).value) - current = settings['settings']['zarafa']['v1']['main']['language'] - print 'Original locale: %s' % current - - if options.locale: - print 'Setting locale to: %s' % options.locale - settings['settings']['zarafa']['v1']['main']['language'] = options.locale - user.store.create_prop(PR_EC_WEBACCESS_SETTINGS_JSON, json.dumps(settings)) - - -if __name__ == "__main__": - main() From 78448d4d4fb06437eff71fc7587f3e255960bc58 Mon Sep 17 00:00:00 2001 From: Martyn Alberts Date: Wed, 13 Feb 2019 16:57:23 +0100 Subject: [PATCH 3/8] Change locale to language for better readability --- webapp_admin/webapp_admin.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp_admin/webapp_admin.py b/webapp_admin/webapp_admin.py index cb827d3..0ac045a 100755 --- a/webapp_admin/webapp_admin.py +++ b/webapp_admin/webapp_admin.py @@ -45,7 +45,6 @@ def opt_args(print_help=None): group.add_option("--location", dest="location", action="store", help="Change location where scripts saves the files") group.add_option("--file", dest="file", action="store", help="Use specific file") group.add_option("--backup", dest="backup", action="store_true", help="Backup Webapp settings") - group.add_option("--change-locale", dest="change_locale", action="store", help="Set new locale (e.g. en_GB or nl_NL)") group.add_option("--restore", dest="restore", action="store_true", help="Restore Webapp settings") group.add_option("--reset", dest="reset", action="store_true", help="Reset WebApp settings") parser.add_option_group(group) @@ -69,6 +68,7 @@ def opt_args(print_help=None): # WebApp setting option group group = OptionGroup(parser, "webapp-settings", "") + group.add_option("--language", dest="language", action="store", help="Set new language (e.g. en_GB or nl_NL)") group.add_option("--theme", dest="theme", action="store", help="Change theme (e.g. dark)") group.add_option("--free-busy", dest="freebusy", action="store", help="Change free/busy time span in months") group.add_option("--icons", dest="icons", action="store", help="Change icons (e.g. breeze)") @@ -168,9 +168,9 @@ def restore(user, filename=None): Change the language :param user: The user -:param locale: The language that should be used. Format e.g. "en_GB" +:param language: The language that should be used. Format e.g. "en_GB" """ -def change_locale(user, locale): +def language(user, language): settings = read_settings(user) if not settings['settings']['zarafa']['v1'].get('main'): settings['settings']['zarafa']['v1']['main'] = {} @@ -409,8 +409,8 @@ def main(): restore(user, options.file) # Language - if options.change_locale: - change_locale(user, options.change_locale) + if options.language: + language(user, options.language) # S/MIME import/export if options.export_smime: From d761329e4069845450dda7fc5765f0d23665aafc Mon Sep 17 00:00:00 2001 From: Martyn Alberts Date: Wed, 13 Feb 2019 17:09:13 +0100 Subject: [PATCH 4/8] Rename inject files to files_admin --- Inject Files/readme.md | 26 ------------------- files_admin/README.md | 22 ++++++++++++++++ {Inject Files => files_admin}/deencode.php | 0 .../files_admin.py | 4 +-- {Inject Files => files_admin}/owncloud.cfg | 1 - {Inject Files => files_admin}/smb.cfg | 0 6 files changed, 23 insertions(+), 30 deletions(-) delete mode 100644 Inject Files/readme.md create mode 100644 files_admin/README.md rename {Inject Files => files_admin}/deencode.php (100%) rename Inject Files/inject-files.py => files_admin/files_admin.py (99%) rename {Inject Files => files_admin}/owncloud.cfg (99%) rename {Inject Files => files_admin}/smb.cfg (100%) diff --git a/Inject Files/readme.md b/Inject Files/readme.md deleted file mode 100644 index 93c1b17..0000000 --- a/Inject Files/readme.md +++ /dev/null @@ -1,26 +0,0 @@ -Inject-files.py -=============== - -Script to inject the files setting into the users profile. - - -Files backend supported -======================= - -* SMB -* Owncloud -* Webddav - - -Examples -======== - - python inject-files -user test --file owncloud.cfg,smb.cfg - -Use the username and password provided in the configfile - - python inject-files -user test --file owncloud.cfg,smb.cfg --default - - - - diff --git a/files_admin/README.md b/files_admin/README.md new file mode 100644 index 0000000..f18bbfe --- /dev/null +++ b/files_admin/README.md @@ -0,0 +1,22 @@ +# Files Admin + +Files admin is a command-line interface to modify, inject and export kopano-files settings. + +# Files backend supported + +* SMB +* Owncloud +* Webddav + +# Example Usage + +Inject settings from owncloud and smb config file +> python files_admin -user John --file owncloud.cfg,smb.cfg + +Use the username and password provided in the config file +> python files_admin -user John --file owncloud.cfg,smb.cfg --default + +# Dependencies + +- python-kopano +- python-mapi \ No newline at end of file diff --git a/Inject Files/deencode.php b/files_admin/deencode.php similarity index 100% rename from Inject Files/deencode.php rename to files_admin/deencode.php diff --git a/Inject Files/inject-files.py b/files_admin/files_admin.py similarity index 99% rename from Inject Files/inject-files.py rename to files_admin/files_admin.py index ecdac86..eba4d4c 100644 --- a/Inject Files/inject-files.py +++ b/files_admin/files_admin.py @@ -2,7 +2,6 @@ import subprocess from configobj import ConfigObj import uuid - from MAPI.Util import * import kopano # Try simplejson if json is not available @@ -129,5 +128,4 @@ def main(): if __name__ == '__main__': main() - - + \ No newline at end of file diff --git a/Inject Files/owncloud.cfg b/files_admin/owncloud.cfg similarity index 99% rename from Inject Files/owncloud.cfg rename to files_admin/owncloud.cfg index 7aa4bcd..d866cd7 100644 --- a/Inject Files/owncloud.cfg +++ b/files_admin/owncloud.cfg @@ -1,5 +1,4 @@ [setting] - name = Owncloud share type = Owncloud workgroup = diff --git a/Inject Files/smb.cfg b/files_admin/smb.cfg similarity index 100% rename from Inject Files/smb.cfg rename to files_admin/smb.cfg From 9fcef6bdcd472fef059826468b669e74470f5be9 Mon Sep 17 00:00:00 2001 From: Martyn Alberts Date: Fri, 15 Feb 2019 14:44:54 +0100 Subject: [PATCH 5/8] Add param to run script for all users --- webapp_admin/webapp_admin.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/webapp_admin/webapp_admin.py b/webapp_admin/webapp_admin.py index 0ac045a..cead462 100755 --- a/webapp_admin/webapp_admin.py +++ b/webapp_admin/webapp_admin.py @@ -42,6 +42,7 @@ def opt_args(print_help=None): # Common option group group = OptionGroup(parser, "Common", "") + group.add_option("--all-users", dest="all_users", action="store_true", help="Run for all users") group.add_option("--location", dest="location", action="store", help="Change location where scripts saves the files") group.add_option("--file", dest="file", action="store", help="Use specific file") group.add_option("--backup", dest="backup", action="store_true", help="Backup Webapp settings") @@ -401,6 +402,13 @@ def main(): opt_args(True) options, args = opt_args() + # Always first! + # If the script should execute for all users + # The admin should pass the '--all-users' parameter + if not options.users and not options.all_users: + print('There are no users specified. Use "--all-users" to run for all users') + sys.exit(1) + for user in kopano.Server(options).users(options.users): # Backup and restore if options.backup: @@ -432,7 +440,7 @@ def main(): if options.theme: setting = 'settings.zarafa.v1.main.active_theme = {}'.format(options.theme) advanced_inject(user, setting) - print('Theme changed to {}'.format(options.icons)) + print('Theme changed to {}'.format(options.theme)) # Free busy publishing if options.freebusy: From 2c564426b5644d11ac9c0a6b1031be95b583db98 Mon Sep 17 00:00:00 2001 From: rvangenderen Date: Thu, 28 Feb 2019 14:52:02 +0100 Subject: [PATCH 6/8] Fix python 3 import/export smime --- webapp_admin/webapp_admin.py | 49 ++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/webapp_admin/webapp_admin.py b/webapp_admin/webapp_admin.py index cead462..826bb08 100755 --- a/webapp_admin/webapp_admin.py +++ b/webapp_admin/webapp_admin.py @@ -276,15 +276,15 @@ def export_smime(user, location=None, public=None): return for cert in certificates: - if public and cert.prop(PR_MESSAGE_CLASS).value == 'WebApp.Security.Public': + if public and cert.prop(PR_MESSAGE_CLASS_w).value == 'WebApp.Security.Public': extension = 'pub' - body = cert.body.text + body = cert.text else: extension = 'pfx' - body = base64.b64decode(cert.body.text) + body = base64.b64decode(cert.text) - print('found {} certificate {} (serial: {})'.format(cert.prop(PR_MESSAGE_CLASS).value, cert.subject, cert.prop(PR_SENDER_NAME).value)) - with open("%s/%s-%s.%s" % (backup_location, cert.subject, cert.prop(PR_SENDER_NAME).value, extension), "w") as text_file: + print('found {} certificate {} (serial: {})'.format(cert.prop(PR_MESSAGE_CLASS_W).value, cert.subject, cert.prop(PR_SENDER_NAME_W).value)) + with open("%s/%s-%s.%s" % (backup_location, cert.subject, cert.prop(PR_SENDER_NAME_W).value, extension), "wb") as text_file: text_file.write(body) @@ -304,7 +304,7 @@ def import_smime(user, cert_file, passwd, ask_password=None, public=None): passwd = '' assoc = user.store.root.associated - with open(cert_file) as f: + with open(cert_file, 'rb') as f: cert = f.read() if not public: messageclass = 'WebApp.Security.Private' @@ -314,13 +314,13 @@ def import_smime(user, cert_file, passwd, ask_password=None, public=None): print(e) sys.exit(1) except Exception as e: - print(e.message) + print(e) sys.exit(1) - + certificate = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, p12.get_certificate()) cert_data = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, certificate) - date_before = MAPI.Time.unixtime(mktime(datetime.strptime(cert_data.get_notBefore(), "%Y%m%d%H%M%SZ" ).timetuple())) - date_after = MAPI.Time.unixtime(mktime(datetime.strptime(cert_data.get_notAfter(), "%Y%m%d%H%M%SZ" ).timetuple())) + date_before = MAPI.Time.unixtime(mktime(datetime.strptime(cert_data.get_notBefore().decode('utf-8'), "%Y%m%d%H%M%SZ" ).timetuple())) + date_after = MAPI.Time.unixtime(mktime(datetime.strptime(cert_data.get_notAfter().decode('utf-8'), "%Y%m%d%H%M%SZ" ).timetuple())) issued_by = "" dict_issued_by = dict(cert_data.get_issuer().get_components()) @@ -330,23 +330,24 @@ def import_smime(user, cert_file, passwd, ask_password=None, public=None): issued_to = "" dict_issued_to = dict(cert_data.get_subject().get_components()) for key in dict_issued_to: - if key == 'emailAddress': - email = dict_issued_to[key] + if key == b'emailAddress': + email = dict_issued_to[key].decode('utf-8') else: issued_to += "%s=%s\n" % (key, dict_issued_to[key]) - - if str(user.email) == email: + + if user.email == email: item = assoc.mapiobj.CreateMessage(None, MAPI_ASSOCIATED) - item.SetProps([SPropValue(PR_SUBJECT, email), - SPropValue(PR_MESSAGE_CLASS, messageclass), - SPropValue(PR_MESSAGE_DELIVERY_TIME, date_after), - SPropValue(PR_CLIENT_SUBMIT_TIME, date_before), - SPropValue(PR_SENDER_NAME, str(int(cert_data.get_serial_number()))), - SPropValue(PR_SENDER_EMAIL_ADDRESS, issued_by), - SPropValue(PR_SUBJECT_PREFIX, issued_to), - SPropValue(PR_RECEIVED_BY_NAME, str(cert_data.digest("sha1"))), - SPropValue(PR_INTERNET_MESSAGE_ID, str(cert_data.digest("md5"))), - SPropValue(PR_BODY, str(base64.b64encode(p12)))]) + + item.SetProps([SPropValue(PR_SUBJECT, email.encode('utf-8')), + SPropValue(PR_MESSAGE_CLASS, messageclass.encode('utf-8')), + SPropValue(PR_MESSAGE_DELIVERY_TIME, date_after), + SPropValue(PR_CLIENT_SUBMIT_TIME, date_before), + SPropValue(PR_SENDER_NAME, str(cert_data.get_serial_number()).encode('utf-8')), + SPropValue(PR_SENDER_EMAIL_ADDRESS, issued_by.encode('utf-8')), + SPropValue(PR_SUBJECT_PREFIX, issued_to.encode('utf-8')), + SPropValue(PR_RECEIVED_BY_NAME, cert_data.digest("sha1")), + SPropValue(PR_INTERNET_MESSAGE_ID, cert_data.digest("md5")), + SPropValue(PR_BODY, base64.b64encode(p12.export()))]) item.SaveChanges(KEEP_OPEN_READWRITE) print('Imported private certificate') else: From e76ae88edc14ce31dc3acabd6902cd1fa8cbf288 Mon Sep 17 00:00:00 2001 From: Robin van Genderen Date: Mon, 4 Mar 2019 11:27:28 +0100 Subject: [PATCH 7/8] add userdefined value to get the language value from PR_LANGAUGE with en_GB as fallback --- webapp_admin/webapp_admin.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/webapp_admin/webapp_admin.py b/webapp_admin/webapp_admin.py index 826bb08..e0e0668 100755 --- a/webapp_admin/webapp_admin.py +++ b/webapp_admin/webapp_admin.py @@ -173,10 +173,18 @@ Change the language """ def language(user, language): settings = read_settings(user) + # Get language from PR_LANGUAGE + if language == 'userdefined': + try: + language = user.prop(PR_LANGUAGE).value + except: + print('User language is not defined using en_GB as fallback' + language = 'en_GB' + if not settings['settings']['zarafa']['v1'].get('main'): settings['settings']['zarafa']['v1']['main'] = {} - settings['settings']['zarafa']['v1']['main']['language'] = locale - print('Setting locale to: {}'.format(locale)) + settings['settings']['zarafa']['v1']['main']['language'] = language + print('Setting locale to: {}'.format(language)) write_settings(user, json.dumps(settings)) From d64ca716e9f04cdd0aa8cd0b277178472eb07b68 Mon Sep 17 00:00:00 2001 From: Robin van Genderen Date: Mon, 4 Mar 2019 14:48:42 +0100 Subject: [PATCH 8/8] use propper json now and decode values --- files_admin/files_admin.py | 169 +++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 82 deletions(-) diff --git a/files_admin/files_admin.py b/files_admin/files_admin.py index eba4d4c..85f2803 100644 --- a/files_admin/files_admin.py +++ b/files_admin/files_admin.py @@ -1,9 +1,11 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import subprocess from configobj import ConfigObj import uuid from MAPI.Util import * + import kopano + # Try simplejson if json is not available try: import json @@ -12,120 +14,123 @@ except ImportError: def encode(value): - proc = subprocess.Popen(["php", "deencode.php", "encode", value], stdout=subprocess.PIPE) - return proc.communicate()[0] + output = subprocess.check_output(["php", "deencode.php", "encode", value]) + return output.strip() def opt_args(): - parser = kopano.parser('skpcfmUP') + parser = kopano.parser('skpcfm') parser.add_option("--user", dest="user", action="store", help="username") + parser.add_option("--ssl", dest="ssl", action="store_true", help="Use localhost on port 443") + parser.add_option("--hostname", dest="hostname", action="store", help="hostname") + parser.add_option("--file", dest="file", default=[], action="store", help="config file(s) separate by ',' ") - parser.add_option("--overwrite", dest="overwrite", action="store_true", help="overwrite files settings") parser.add_option("--default", dest="default", action="store_true", help="use default user and password in the configfile") return parser.parse_args() -def read_settings(options): - +def read_settings(user): try: - user = kopano.Server(options).user(options.user) - except MAPIErrorLogonFailed as e: - print('User \'{}\' not found ({})'.format(options.user, e)) - sys.exit(1) - - if not user.store: - print('User \'{}\' has no user store ({})'.format(options.user, e)) - sys.exit(1) - - try: - mapisettings = user.store.prop(PR_EC_WEBACCESS_SETTINGS_JSON).value - return mapisettings - except Exception: + mapisettings = user.store.prop(PR_EC_WEBACCESS_SETTINGS_JSON).value.decode('utf-8') + settings = json.loads(mapisettings) + except Exception as e: print('{}: Has no or no valid WebApp settings creating empty config tree'.format(user.name)) - return '{"settings": {"zarafa": {"v1": {"contexts": {"mail": {}}}}}}' + settings = json.loads('{"settings": {"zarafa": {"v1": {"contexts": {"mail": {}}}}}}') + return settings -def write_settings(data, options): - user = kopano.Server(options).user(options.user) - user.store.create_prop(PR_EC_WEBACCESS_SETTINGS_JSON, data.encode('utf-8')) - print('Writing settings for user \'{}\''.format(user.fullname)) +def write_settings(user, setting): + try: + user.store.create_prop(PR_EC_WEBACCESS_SETTINGS_JSON, setting.encode('utf-8')) + except Exception as e: + print('{}: Error Writing WebApp settings for user: {}'.format(e, user.name)) def files(options): - filesjson = '{' - if options.overwrite: - filesjson = '{"accounts": {' - num = 0 + filesjson = {'accounts': {}} + files = options.file.split(',') for file in files: configfile = ConfigObj(file) - if options.default: + if configfile['setting']['use_zarafa_credentials']: username = configfile['setting']['default_user'] else: username = options.user - if num != 0: - filesjson += ',' - id = uuid.uuid4() - filesjson += ''' - "%s": { - "status": "ok", - "backend_config": { - "server_path": "%s", - "workgroup": "%s", - "server_address": "%s", - "server_ssl": %s, - "current_account_id": "%s", - "use_zarafa_credentials": %s, - "user": "%s", - "password": "%s", - "server_port": "%s" - }, - "cannot_change": false, - "name": "%s", - "status_description": "Account is ready to use.", - "id": "%s", - "backend_features": { - "Sharing": true, - "VersionInfo": true, - "Quota": true - }, - "backend": "%s" - }''' % (id, encode(configfile['setting']['server_path']), encode(configfile['setting']['workgroup']), - encode(configfile['setting']['server_address']), configfile['setting']['server_ssl'], - encode('d4cacda458a2a26c301f2b7d75ada530'), configfile['setting']['use_zarafa_credentials'], - encode(username), encode(configfile['setting']['default_password']), - encode(configfile['setting']['server_port']), configfile['setting']['name'], id, configfile['setting']['type']) - num += 1 - if options.overwrite: - filesjson += '}}' - else: - filesjson += '}' + password = encode(configfile['setting']['default_password']) + backendoptions = { + 'ftp': {"backend_features": { + "Streaming": "true", }}, + 'webdav': {"backend_features": { + "Quota": "true", + "VersionInfo": "true"}}, + 'owncloud': {"backend_features": { + "Quota": "true", + "Sharing": "true", + "VersionInfo": "true"}}, + 'smb': {"backend_features": { + "Quota": "true", + "Streaming": "true", + "VersionInfo": "true"}}, + } + + if file == 'seafile.cfg': + password = encode(options.user) + username = kopano.Server(options).user(options.user).email + + if 'local' in file: + if options.ssl: + port = '443' + address = options.hostname + ssl = 'true' + else: + port = configfile['setting']['server_port'] + address = configfile['setting']['server_address'] + ssl = configfile['setting']['server_ssl'] + else: + port = configfile['setting']['server_port'] + address = configfile['setting']['server_address'] + ssl = configfile['setting']['server_ssl'] + id = str(uuid.uuid4()) + filesjson['accounts'][id] = { + "status": "ok", + "backend_config": { + "server_path": encode(configfile['setting']['server_path']).decode('utf-8'), + "workgroup": encode(configfile['setting']['workgroup']).decode('utf-8'), + "server_address": encode(address).decode('utf-8'), + "server_ssl": ssl, + "current_account_id": encode('d4cacda458a2a26c301f2b7d75ada530').decode('utf-8'), + "use_zarafa_credentials": configfile['setting']['use_zarafa_credentials'], + "user": encode(username).decode('utf-8'), + "password": password.decode('utf-8'), + "server_port": encode(port).decode('utf-8') + }, + "cannot_change": False, + "name": configfile['setting']['name'], + "status_description": "Account is ready to use.", + "id": id, + "backend_features": backendoptions[configfile['setting']['type'].lower()]['backend_features'], + "backend": configfile['setting']['type'] + } + + if configfile['setting']['type'].lower() == 'ftp': + filesjson['accounts'][id]['backend_config']['server_pasv'] = configfile['setting']['server_pasv'] + return filesjson def main(): options, args = opt_args() - data = read_settings(options) - webappsettings = json.loads(data) - + server = kopano.Server(options) + user = server.user(options.user) + webappsettings = read_settings(user) if not webappsettings['settings']['zarafa']['v1'].get('plugins'): webappsettings['settings']['zarafa']['v1']['plugins'] = {} - - if options.overwrite: - webappsettings['settings']['zarafa']['v1']['plugins']['files'] = json.loads(files(options)) - else: - if not webappsettings['settings']['zarafa']['v1']['plugins'].get('files'): - webappsettings['settings']['zarafa']['v1']['plugins']['files'] = {} - if not webappsettings['settings']['zarafa']['v1']['plugins']['files'].get('accounts'): - webappsettings['settings']['zarafa']['v1']['plugins']['files']['accounts'] = {} - webappsettings['settings']['zarafa']['v1']['plugins']['files']['accounts'].update(json.loads(files(options))) - - write_settings(json.dumps(webappsettings), options) + webappsettings['settings']['zarafa']['v1']['plugins']['files'] = files(options) + write_settings(user, json.dumps(webappsettings)) if __name__ == '__main__': main() - \ No newline at end of file