Compare commits

..

44 Commits

Author SHA1 Message Date
Martijn Alberts
4d37f44e32 Pull request #28: make username optional when replacing the signature
Merge in KSC/webapp-tools from fix-signature to master

* commit '001f3cd1456c16a82890bff48db3f48fd4867dcc':
  make username optional when replacing the signature
2021-01-28 14:33:23 +01:00
Robin van Genderen
829da666d1 Pull request #29: README.md move apostrophe to correct place
Merge in KSC/webapp-tools from malberts/READMEmd-1611392752255 to master

* commit '67cf6fe3587ec9dfdfdd6f34688108662c500e2e':
  README.md move apostrophe to correct place
2021-01-25 09:08:29 +01:00
Martijn Alberts
67cf6fe358 README.md move apostrophe to correct place 2021-01-23 10:06:32 +01:00
Robin van Genderen
8b1f21da6b Pull request #27: Add signature injection examples
Merge in KSC/webapp-tools from ~MALBERTS/webapp-tools-martijn:docu to master

* commit 'e588ef3992cbece93a47ac121c29334f23580390':
  Add signature injection examples
2021-01-22 15:38:35 +01:00
Martijn Alberts
e588ef3992 Add signature injection examples 2021-01-22 15:37:44 +01:00
rvangenderen
001f3cd145 make username optional when replacing the signature 2021-01-22 15:26:10 +01:00
Martijn Alberts
90471e39e0 Pull request #26: restore-signature not needed when replace-signature is used
Merge in KSC/webapp-tools from why-martijn-why to master

* commit '2a3a4157296115a1d0a98b22599f3d55db4ff48a':
  restore-signature not needed when replace-signature is used
2021-01-21 15:10:53 +01:00
rvangenderen
2a3a415729 restore-signature not needed when replace-signature is used 2021-01-21 15:08:12 +01:00
Martijn Alberts
54cf3a0678 Pull request #25: replace to replace-signature
Merge in KSC/webapp-tools from replace-signature to master

* commit 'af885f2c32437a491a6800b99d500f0079355b66':
  replace  to replace-signature
2021-01-21 14:47:11 +01:00
rvangenderen
af885f2c32 replace to replace-signature 2021-01-21 14:41:35 +01:00
Robin van Genderen
be1d9e2308 Pull request #24: add support for modifying shared stores
Merge in KSC/webapp-tools from open-shared-store to master

* commit 'c485022ffc4eae672e015f09bd4452a727c1b331':
  add support for modifying shared stores
2020-12-24 11:57:15 +01:00
rvangenderen
c485022ffc add support for modifying shared stores 2020-12-24 11:56:57 +01:00
rvangenderen
70bfdf098a add option to remove expired public S/MIME Certificates 2020-12-14 10:23:53 +01:00
Martijn Alberts
38fc9c6b64 Merge pull request #23 in KSC/webapp-tools from fix-mail-keyerror to master
* commit '713cfe225580746f4699a6e5130ad1392648c19b':
  create mail key if not exist
2020-07-09 10:21:17 +02:00
rvangenderen
713cfe2255 create mail key if not exist 2020-07-09 10:10:18 +02:00
Robin van Genderen
384cb70e4b Merge pull request #22 in KSC/webapp-tools from malberts/files_adminpy-1575907755669 to master
* commit '0bb89137943a7406064185c8f6299334cfa39a0a':
  Add check for settings key
2019-12-09 17:15:51 +01:00
Martijn Alberts
0bb8913794 Add check for settings key 2019-12-09 17:10:38 +01:00
Robin van Genderen
d515051244 Merge pull request #21 in KSC/webapp-tools from ~MALBERTS/webapp-tools-martijn:add_calendar_resolution to master
* commit 'a708768c7a1c248bbfd5fb8c7ee793ad113257f2':
  Add calendar resolution setting
2019-09-12 12:39:39 +02:00
Martyn Alberts
a708768c7a Add calendar resolution setting 2019-09-12 12:00:34 +02:00
Robin van Genderen
5048d33cf1 add hint that python3-pkg-resources is required for debian 10 2019-08-13 16:38:14 +02:00
Robin van Genderen
87a5ab0b07 create empty prop if not exist 2019-07-22 16:00:07 +02:00
Martijn Alberts
7a75569e5d Merge pull request #20 in KSC/webapp-tools from ~MALBERTS/webapp-tools-martijn:norecipient to master
* commit '7477f89a54af5b5fb10d1c46a9f81e88eb4d77df':
  Manage recipient: Display error when PR_EC_RECIPIENT_HISTORY_JSON_W does not exist
2019-07-18 13:56:21 +02:00
Martyn Alberts
7477f89a54 Manage recipient: Display error when PR_EC_RECIPIENT_HISTORY_JSON_W does not exist 2019-07-18 13:08:13 +02:00
Robin van Genderen
7b4845a877 Merge pull request #19 in KSC/webapp-tools from ~MALBERTS/webapp-tools-martijn:python3 to master
* commit '35b4bbeec1bde647cbf0da316ef0984f5a23bf00':
  Add brackets around prints
2019-07-18 12:40:29 +02:00
Martyn Alberts
35b4bbeec1 Add brackets around prints 2019-07-18 12:36:41 +02:00
Robin van Genderen
8d8e3f3e50 Merge pull request #17 in KSC/webapp-tools from ~MALBERTS/webapp-tools-martijn:addInterval to master
* commit 'e976f230da31301ab16531caf9997fa6216f7ff3':
  WebApp Admin: Add polling interval injection
2019-07-12 09:32:20 +02:00
Martyn Alberts
e976f230da WebApp Admin: Add polling interval injection 2019-07-11 16:02:30 +02:00
Robin van Genderen
2e97a42720 if value is true or false convert it to a boolean 2019-07-02 12:25:00 +02:00
Martijn Alberts
145a4e4352 Merge pull request #14 in KSC/webapp-tools from readme_license to master
* commit 'debee2fd7bca15ae55ea726be1a646edc52c70f1':
  Add license and update readme
2019-06-27 07:50:05 +02:00
Martyn Alberts
debee2fd7b Add license and update readme 2019-06-25 16:05:54 +02:00
rvangenderen
a95dfca22f change order to get the right username 2019-06-04 16:09:58 +02:00
Robin van Genderen
aff40b21b0 Merge pull request #13 in KSC/webapp-tools from remove_version_check_server to master
* commit '420c02a0fa90dc270b8d4ef16a61765d2fe5938e':
  Remove version check for server
2019-05-23 18:37:56 +02:00
Martijn Alberts
420c02a0fa Remove version check for server 2019-05-23 18:13:47 +02:00
Robin van Genderen
30f07c076f Check for version number and only complain about PYopenSSL een dotty_dict when it's needed
modified:   webapp_admin/webapp_admin.py
2019-05-02 13:53:05 +02:00
Robin van Genderen
20896cbbc2 set type to string for language option 2019-05-02 13:12:14 +02:00
Robin van Genderen
8381c1b700 Merge pull request #11 in KSC/webapp-tools from ftpsmbupdate to master
* commit 'e3338ba61e8cf99eca49b5e5759bd4e84c2c74c5':
  Configs for ftp and smb
2019-04-26 14:31:02 +02:00
Martyn Alberts
e3338ba61e Configs for ftp and smb 2019-04-26 14:29:38 +02:00
Robin van Genderen
76f300984d remove text 2019-04-17 14:51:13 +02:00
Robin van Genderen
d055b4dd8e Merge pull request #10 in KSC/webapp-tools from ~MALBERTS/webapp-tools-martijn:master to master
* commit 'e49df89f29e83d93a0d9fe18eccbcda2ad6b8b94':
  Option to add user to safesenders
2019-04-15 20:57:11 +02:00
Martijn
e49df89f29 Option to add user to safesenders 2019-04-15 17:07:55 +02:00
Robin van Genderen
b835ddde91 load settings as json 2019-03-26 12:49:24 +01:00
Robin van Genderen
c755fbdb99 change file optin 2019-03-26 12:43:31 +01:00
Robin van Genderen
3026d1ce87 Merge pull request #9 in KSC/webapp-tools from ~MALBERTS/webapp-tools-martijn:state_settings to master
* commit '0634ec9ef49d733275a02bebbdda90abe85fadf0':
  Add option to remove state settings
2019-03-18 14:01:39 +01:00
Martijn
0634ec9ef4 Add option to remove state settings 2019-03-18 13:01:02 +01:00
13 changed files with 387 additions and 42 deletions

12
LICENSE.txt Normal file
View File

@ -0,0 +1,12 @@
Copyright (C) 2019 Kopano and its licensors
This program is free software: you can redistribute it and/or modify it under the terms of the
GNU Affero General Public License as published by the Free Software Foundation, either version
3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program.
If not, see http://www.gnu.org/licenses/.

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# WebApp tools
WebApp tools repository can be found [here](https://stash.kopano.io/projects/KSC/repos/webapp-tools/) and consist of various sub-projects:
## WebApp_Admin
A CLI tool that can be used to manage WebApp user settings, signatures, S/MIME certificates and more.
More info [here](https://stash.kopano.io/projects/KSC/repos/webapp-tools/browse/webapp_admin/README.md).
## Files_Admin
A CLI tool to inject files accounts into a user settings. More info [here](https://stash.kopano.io/projects/KSC/repos/webapp-tools/browse/files_admin/README.md).
## Manage_recipients
A simple script to manage the recipient history list for a user. More info [here](https://stash.kopano.io/projects/KSC/repos/webapp-tools/browse/manage_recipients/readme.md).
# How to contribute
1) Clone the repository from `https://stash.kopano.io/` or `https://github.com/Kopano-mirror/webapp_tools`.
2) Commit and sign your work (git commit -s).
3) Upload commits to a git store of your choosing, or export the series as a patchset using git format-patch.
4) Send the patch(es) or git link to `contributing @ kopano .io` and we will consider the submission.
# License
All software found in this repostory and sub-projects are licensed under GNU Affero General Public License v3 unless stated otherwise. A copy of this license can be found in the main directory of this project and in every sub-project.

12
files_admin/LICENSE.txt Normal file
View File

@ -0,0 +1,12 @@
Copyright (C) 2019 Kopano and its licensors
This program is free software: you can redistribute it and/or modify it under the terms of the
GNU Affero General Public License as published by the Free Software Foundation, either version
3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program.
If not, see http://www.gnu.org/licenses/.

View File

@ -20,3 +20,7 @@ Use the username and password provided in the config file
- python-kopano
- python-mapi
# License
licensed under GNU Affero General Public License v3.

View File

@ -54,11 +54,19 @@ def files(options):
files = options.file.split(',')
for file in files:
configfile = ConfigObj(file)
if configfile['setting']['use_zarafa_credentials']:
username = configfile['setting']['default_user']
else:
# Check if the settings section key is present in the file
try:
value = configfile['setting']
except KeyError:
print('Setting does not exist in', file)
continue
if configfile['setting'].as_bool('use_zarafa_credentials'):
username = options.user
password = encode(configfile['setting']['default_password'])
else:
username = configfile['setting'].get('default_user', 'does not matter')
password = encode(configfile['setting'].get('default_password', 'does not matter'))
backendoptions = {
'ftp': {"backend_features": {
"Streaming": "true", }},
@ -93,6 +101,7 @@ def files(options):
address = configfile['setting']['server_address']
ssl = configfile['setting']['server_ssl']
id = str(uuid.uuid4())
filesjson['accounts'][id] = {
"status": "ok",
"backend_config": {
@ -101,7 +110,7 @@ def files(options):
"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'],
"use_zarafa_credentials": configfile['setting'].as_bool('use_zarafa_credentials'),
"user": encode(username).decode('utf-8'),
"password": password.decode('utf-8'),
"server_port": encode(port).decode('utf-8')

13
files_admin/ftp.cfg Normal file
View File

@ -0,0 +1,13 @@
[setting]
name = FTP server
type = FTP
workgroup =
server_path = /srv/ftp
server_address = 10.10.11.9
server_ssl = false
use_zarafa_credentials = false
server_port = 21
default_user = ftpuser
default_password = ftpuser
server_pasv = false

View File

@ -4,7 +4,7 @@ name = Samba share
type = SMB
workgroup = zarafa
server_path = files
server_address = 192.168.1.230
server_address = 10.10.11.9
server_ssl = false
use_zarafa_credentials = true
server_port = 80

View File

@ -0,0 +1,12 @@
Copyright (C) 2019 Kopano and its licensors
This program is free software: you can redistribute it and/or modify it under the terms of the
GNU Affero General Public License as published by the Free Software Foundation, either version
3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program.
If not, see http://www.gnu.org/licenses/.

View File

@ -31,4 +31,8 @@ Remove all recipients that have example.com in there display_name, smtp_address
python remove_recipients.py --user user --remove example.com
```
# License
licensed under GNU Affero General Public License v3.

View File

@ -2,8 +2,10 @@
#encoding: utf-8
import kopano
from kopano.errors import NotFoundError
from MAPI.Util import *
import json
import sys
def opt_args():
@ -24,14 +26,22 @@ def main():
options, args = opt_args()
if not options.user:
print 'Please use:\n %s --user <username>' % (sys.argv[0])
print('Please use:\n %s --user <username>' % (sys.argv[0]))
sys.exit(0)
user = kopano.Server(options).user(options.user)
try:
webapp = user.store.prop(0X6773001F).value
except NotFoundError:
webapp = dict(recipients=[])
webapp = json.loads(webapp)
if options.backup:
if len(webapp['recipients']) == 0:
print('Property PR_EC_RECIPIENT_HISTORY_JSON_W not found . User might have never used recipient history before.', file=sys.stderr)
sys.exit(1)
f = open('%s.json' % user.name, 'w')
f.write(json.dumps(webapp, sort_keys=True,
indent=4, separators=(',', ': ')))
@ -50,8 +60,8 @@ def main():
sys.exit(0)
if options.list:
print json.dumps(webapp, sort_keys=True,
indent=4, separators=(',', ': '))
print(json.dumps(webapp, sort_keys=True,
indent=4, separators=(',', ': ')))
sys.exit(0)
if options.remove:
@ -59,7 +69,7 @@ def main():
for rec in webapp['recipients']:
if options.remove in rec['display_name'] or options.remove in rec['smtp_address'] \
or options.remove in rec['email_address']:
print 'removing contact %s [%s]' % (rec['display_name'], rec['smtp_address'])
print('removing contact %s [%s]' % (rec['display_name'], rec['smtp_address']))
else:
newlist['recipients'].append(rec)

12
webapp_admin/LICENSE.txt Normal file
View File

@ -0,0 +1,12 @@
Copyright (C) 2019 Kopano and its licensors
This program is free software: you can redistribute it and/or modify it under the terms of the
GNU Affero General Public License as published by the Free Software Foundation, either version
3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program.
If not, see http://www.gnu.org/licenses/.

View File

@ -1,11 +1,14 @@
# WebApp Admin
>**This tool is under contruction. Use caution on a live server. Always make a backup of the user settings and test first before modifing**
>**Always make a backup of the user settings and test the new settings afterwards**
WebApp admin is a command-line interface to modify, inject and export WebApp settings.
# Example Usage
Overview of all options:
> python3 webapp_admin -h
Reset WebApp settings
> python3 webapp_admin -u john --reset
@ -15,6 +18,35 @@ Change free/busy to 36 months
If you want to make a change for all users pass the --all-users parameter. Example:
> python3 webapp_admin --all-users --icons Breeze
## Signatures
To restore, replace and backup signatures we need a two part, underscore separated filename consisting of a `name` and `id`.\
Example single user: `this-is-my-signature_1234.html`\
---
**Note**\
The hypens in the filename will be displayed as spaces in WebApp\
The username can also be part of the .html file, but is then ignored by the script.
In WebApp the ID is created based on the unix time, so the ID can be anything
---
Examples
Backup signature for user `henk`
> python3 webapp_admin -u henk --backup-signature
Restore signature for user `henk`
> python3 webapp_admin -u henk --restore-signature my-cool-signature_1615141312112.html
Replace signature for user `henk`
> python3 webapp_admin -u henk --replace-signature my-cool-signature_1615141312112.html
Restore signatures for all users
> python3 webapp_admin --all-users --restore-signature mycompany-signature_1412130992124.html
# Dependencies
- python3
@ -22,3 +54,9 @@ If you want to make a change for all users pass the --all-users parameter. Examp
- python-mapi
- OpenSSL
- dotty_dict
For debian 10 python3-pkg-resources is required
# License
licensed under GNU Affero General Public License v3.

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python3
# encoding: utf-8
from pkg_resources import parse_version
import sys
try:
import kopano
@ -16,17 +17,17 @@ import base64
try:
import OpenSSL.crypto
except ImportError:
print('pip3 install pyOpenSSL')
sys.exit(1)
from datetime import datetime
pass
from datetime import datetime, timedelta
from time import mktime
import getpass
import time
from optparse import OptionGroup
from tabulate import tabulate
try:
from dotty_dict import dotty
except ImportError:
print('dotty_dict not found on your system. Run pip3 install dotty_dict')
pass
"""
@ -50,11 +51,20 @@ def opt_args(print_help=None):
group.add_option("--reset", dest="reset", action="store_true", help="Reset WebApp settings")
parser.add_option_group(group)
# Addionals stores group
group = OptionGroup(parser, "Store", "")
group.add_option("--add-store", dest="add_store", action="store", help="Add shared store")
group.add_option("--del-store", dest="del_store", action="store", help="Delete shared store")
group.add_option("--folder-type", dest="folder_type", action="store", help="Folder to add")
group.add_option("--subfolder", dest="sub_folder", action="store_true", help="Add subfolders")
group.add_option("--list-stores", dest="list_stores", action="store_true", help="List shared stores")
parser.add_option_group(group)
# Signature option group
group = OptionGroup(parser, "Signature", "")
group.add_option("--backup-signature", dest="backup_signature", action="store_true", help="Backup signature")
group.add_option("--restore-signature", dest="restore_signature", action="store", help="Restore signature (need file name)")
group.add_option("--replace", dest="replace", action="store_true", help="Replace existing signature, file layout must be: username_signature-name_signatureid.html")
group.add_option("--replace-signature", dest="replace_signature", action="store", help="Replace existing signature, file layout must be: username_signature-name_signatureid.html or signature-name_signatureid.html ")
group.add_option("--default-signature", dest="default_signature", action="store_true", help="Set signature as default one")
parser.add_option_group(group)
@ -62,12 +72,13 @@ def opt_args(print_help=None):
group = OptionGroup(parser, "Categories", "")
group.add_option("--export-categories", dest="export_categories", action="store_true", help="Export Categories (name and color)")
group.add_option("--import-categories", dest="import_categories", action="store_true", help="Import Categories (name and color)")
parser.add_option_group(group)
# S/MIME option group
group = OptionGroup(parser, "S/MIME", "")
group.add_option("--export-smime", dest="export_smime", action="store_true", help="Export private S/MIME certificate")
group.add_option("--import-smime", dest="import_smime", action="store", help="Import private S/MIME certificate")
group.add_option("--remove-expired", dest="remove_expired", action="store_true", help="Remove expired public S/MIME certificates")
group.add_option("--public", dest="public_smime", action="store_true", help="Export/Import public S/MIME certificate")
group.add_option("--password", dest="password", action="store", help="set password")
group.add_option("--ask-password", dest="ask_password", action="store_true", help="ask for password if needed")
@ -75,11 +86,15 @@ 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("--language", dest="language", action="store", type="string", 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)")
group.add_option("--htmleditor", dest="htmleditor", action="store", help="Change the HTML editor (e.g. full_tinymce)")
group.add_option("--remove-state", dest="remove_state", action="store_true", help="Remove all the state settings")
group.add_option("--add-safesender", dest="add_sender", action="store", help="Add domain to safe sender list")
group.add_option("--polling-interval", dest="polling_interval", action="store", help="Change the polling interval (seconds)")
group.add_option("--calendar-resolution", dest="calendar_resolution", action="store", help="Change the calendar resolution (minutes)")
parser.add_option_group(group)
# Advanced option group
@ -194,6 +209,73 @@ def language(user, language):
write_settings(user, json.dumps(settings))
"""
Add shared store
"""
def add_store(user, user_to_add, folder_type, subfolder=False):
allowed_folder_types = ["all", "inbox", "calendar", "contact", "note", "task"]
if folder_type not in allowed_folder_types:
print("Unknown folder type allowed: {}".format(','.join(allowed_folder_types)))
sys.exit(1)
settings = read_settings(user)
if not settings['settings']['zarafa']['v1']['contexts'].get('hierarchy') or not settings['settings']['zarafa']['v1']['contexts']['hierarchy'].get('shared_stores'):
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'] = {}
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'][user_to_add]= {folder_type : {'folder_type': folder_type, 'show_subfolders': subfolder}}
print("Saving settings")
write_settings(user, json.dumps(settings))
"""
Delete shared store
"""
def del_store(user, user_to_del, folder_type=None):
if folder_type:
allowed_folder_types = ["all", "inbox", "calendar", "contact", "note", "task"]
if folder_type not in allowed_folder_types:
print("Unknown folder type allowed: {}".format(','.join(allowed_folder_types)))
sys.exit(1)
settings = read_settings(user)
if not settings['settings']['zarafa']['v1']['contexts'].get('hierarchy') or not settings['settings']['zarafa']['v1']['contexts']['hierarchy'].get('shared_stores'):
print("No additional stores found")
return
shared_store = settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'].get(user_to_del)
if not shared_store:
print("No additional stores found")
return
try:
if not folder_type:
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'].pop(user_to_del)
else:
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'][user_to_del].pop(folder_type)
except KeyError:
pass
print("Saving settings")
write_settings(user, json.dumps(settings))
"""
List all added stores
"""
def list_stores(user):
settings = read_settings(user)
try:
stores = settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores']
except KeyError:
print("No additional stores found")
return
table_header = ["User", 'Folder type', 'Show subfolders']
table_data =[]
for user in stores:
for folder in stores[user]:
table_data.append([user, folder, stores[user][folder]['show_subfolders']])
print(tabulate(table_data, headers=table_header,tablefmt="grid"))
"""
Backup signature from the users store
@ -210,6 +292,8 @@ def backup_signature(user, location=None):
except Exception as e:
print('Could not load WebApp settings for user {} (Error: {})'.format(user.name, repr(e)))
sys.exit(1)
if not settings['settings']['zarafa']['v1']['contexts'].get("mail"):
settings['settings']['zarafa']['v1']['contexts']['mail'] = {}
if settings['settings']['zarafa']['v1']['contexts']['mail'].get('signatures'):
for item in settings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all']:
name = settings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all'][item]['name']
@ -233,16 +317,31 @@ Restore signature into the users store
def restore_signature(user, filename, replace=None, default=None):
restorefile = filename
filename_split = filename.split('_')
if len(filename_split) == 2:
signaturename = filename_split[0].replace('-',' ')
if replace:
signatureid = filename_split[1].split('.')[0]
elif len(filename_split) == 3:
signaturename = filename_split[1].replace('-',' ')
if replace:
signatureid = filename_split[2].split('.')[0]
else:
if replace:
print('File format is not supported')
sys.exit(1)
with open(restorefile, 'r') as sigfile:
signaturehtml = sigfile.read()
if replace:
signatureid = filename.split('_')[2].split('.')[0]
action = 'Replacing'
else:
signaturename = filename.split('.')[0]
signatureid = int(time.time())
action = 'Adding'
signaturename = filename.split('_')[1].replace('-',' ')
signaturecontent = dict(
{u'name': signaturename, u'content': signaturehtml, u'isHTML': True})
settings = read_settings(user)
@ -269,6 +368,7 @@ def restore_signature(user, filename, replace=None, default=None):
write_settings(user, json.dumps(settings))
"""
Export categories from users store
@ -315,11 +415,11 @@ def import_categories(user, filename=None):
if not user.store.get_prop(PR_EC_WEBAPP_PERSISTENT_SETTINGS_JSON_W):
persistent_settings ={'settings': {'kopano': {'main': {'categories':data}}}}
else:
persistent_settings = user.store.get_prop(PR_EC_WEBAPP_PERSISTENT_SETTINGS_JSON_W)
persistent_settings = json.loads(user.store.get_prop(PR_EC_WEBAPP_PERSISTENT_SETTINGS_JSON_W).value)
persistent_settings['settings']['kopano']['main']['categories'] = data
print('Restoring categories for user {}'.format(user.name))
user.store.create_prop(PR_EC_WEBAPP_PERSISTENT_SETTINGS_JSON_W, json.dumps(persistent_settings).decode('utf-8'))
user.store.create_prop(PR_EC_WEBAPP_PERSISTENT_SETTINGS_JSON_W, json.dumps(persistent_settings))
"""
@ -330,6 +430,7 @@ Export S/MIME certificate from users store
:param public: Export public certificate part
"""
def export_smime(user, location=None, public=None):
if location:
backup_location = location
else:
@ -342,7 +443,7 @@ def export_smime(user, location=None, public=None):
return
for cert in certificates:
if public and cert.prop(PR_MESSAGE_CLASS_w).value == 'WebApp.Security.Public':
if public and cert.prop(PR_MESSAGE_CLASS_W).value == 'WebApp.Security.Public':
extension = 'pub'
body = cert.text
else:
@ -364,6 +465,9 @@ Import S/MIME certificate into users store
:param public: Import public certificate part
"""
def import_smime(user, cert_file, passwd, ask_password=None, public=None):
if not sys.modules.get('OpenSSL'):
print('PyOpenSSl not installed \npip3 install pyOpenSSL')
sys.exit(1)
if ask_password:
passwd = getpass.getpass()
elif not passwd:
@ -419,6 +523,27 @@ def import_smime(user, cert_file, passwd, ask_password=None, public=None):
else:
print('Email address doesn\'t match')
"""
Remove expired S/MIME Public certificates
:param user: The user
"""
def remove_expired_smime(user):
# unable to loop over the associated items so getting the items in a list instead
certificates =list(user.store.root.associated.items())
if len(certificates) == 0:
print('No certificates found')
return
now = datetime.now()
for cert in certificates:
# We only want to remove the public certificate
if cert.prop(PR_MESSAGE_CLASS_W).value == 'WebApp.Security.Public':
if cert.prop(PR_MESSAGE_DELIVERY_TIME).value < now:
print('deleting public certificate {} ({})'.format(cert.subject, cert.prop(PR_MESSAGE_DELIVERY_TIME).value))
user.store.root.associated.delete(cert)
"""
Custom function to merge two dictionaries.
@ -428,6 +553,7 @@ 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:
@ -447,11 +573,21 @@ Inject webapp settings into the users store
:param user: The user
:param data: The webapp setting
"""
def advanced_inject(user, data):
def advanced_inject(user, data, value_type='string'):
if not sys.modules.get('dotty_dict'):
print('dotty_dict not found on your system. \nRun pip3 install dotty_dict')
sys.exit(1)
settings = read_settings(user)
split_data = data.split('=')
value = split_data[1].lstrip().rstrip()
if value.lower() == 'true':
value = True
elif value.lower() == 'false':
value = False
if value_type == 'list':
value = value.split(',')
dot = dotty()
dot[split_data[0].rstrip()] = value
@ -469,14 +605,15 @@ 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):
server = kopano.Server(options)
for user in server.users(options.users):
# Backup and restore
if options.backup:
backup(user, options.location)
@ -487,22 +624,36 @@ def main():
if options.language:
language(user, options.language)
if options.add_store:
add_store(user, options.add_store, options.folder_type, options.sub_folder)
if options.del_store:
del_store(user, options.del_store, options.folder_type)
if options.list_stores:
list_stores(user)
#Categories
if options.export_categories:
export_categories(user, options.change_locale)
export_categories(user, options.file)
if options.import_categories:
import_categories(user, options.file)
# S/MIME import/export
if options.export_smime:
export_smime(user, options.location, options.public_smime)
if options.import_smime:
import_smime(user, options.import_smime, options.password, options.ask_password, options.public_smime)
if options.remove_expired:
remove_expired_smime(user)
# Signature
if options.backup_signature:
backup_signature(user, options.location)
if options.restore_signature:
restore_signature(user, options.restore_signature, options.replace, options.default_signature)
restore_signature(user, options.restore_signature, False, options.default_signature)
if options.replace_signature:
restore_signature(user, options.replace_signature, True, options.default_signature)
# Advanced injection option
if options.add_option:
@ -534,7 +685,7 @@ def main():
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))
print('Icon set changed to {}'.format(options.icons))
# Editor
if options.htmleditor:
@ -546,6 +697,47 @@ def main():
advanced_inject(user, setting)
print('Editor changed to {}'.format(options.htmleditor))
# State settings
if options.remove_state:
settings = read_settings(user)
settings['settings']['zarafa']['v1']['state'] = {}
write_settings(user, json.dumps(settings))
print('Removed state settings for {}'.format(user.name))
# Add sender to safe sender list
if options.add_sender:
settings = read_settings(user)
setting = 'settings.zarafa.v1.contexts.mail.safe_senders_list = {}'.format(options.add_sender)
advanced_inject(user, setting, 'list')
print('{}'.format(options.add_sender), 'Added to safe sender list for {}'.format(user.name))
# Polling interval
if options.polling_interval:
try:
value = int(options.polling_interval)
except ValueError:
print('Invalid number used. Please specify the value in seconds')
sys.exit(1)
settings = read_settings(user)
setting = 'settings.zarafa.v1.main.reminder.polling_interval = {}'.format(options.polling_interval)
advanced_inject(user, setting)
print('Polling interval changed to', '{}'.format(options.polling_interval), 'for {}'.format(user.name))
# Calendar resolution (zoom level)
if options.calendar_resolution:
try:
value = int(options.calendar_resolution)
except ValueError:
print('Invalid number used. Please specify the value in minutes')
sys.exit(1)
if value < 5 or value > 60:
print('Unsupported value used. Use a number between 5 and 60')
sys.exit(1)
settings = read_settings(user)
setting = 'settings.zarafa.v1.contexts.calendar.default_zoom_level = {}'.format(options.calendar_resolution)
advanced_inject(user, setting)
print('Calendar resolution changed to', '{}'.format(options.calendar_resolution), 'for {}'.format(user.name))
# Always at last!!!
if options.reset:
reset_settings(user)