migration management is done from command-line (using socket for communication). basic commands (see --help for all options): kopano-msr --list-users -> list users currently being migrated kopano-msr -u user1 --add --server node14 -> start migrating user kopano-msr -u user1 -> check migration status, compare store contents kopano-msr -u user1 --remove -> finalize migration (metadata, special folders..) """ CONFIG = { 'state_path': Config.path(default='/var/lib/kopano/msr/', check=True), 'server_bind_name': Config.string(default='file:///var/run/kopano/msr.sock'), 'ssl_private_key_file': Config.path(default=None, check=False), # XXX don't check when default=None? 'ssl_certificate_file': Config.path(default=None, check=False), } setproctitle.setproctitle('kopano-msr main') def init_globals(): # late init to survive daemonization global LOCK, STORE_STORE, USER_INFO, STORE_FOLDER_QUEUED, STORE_FOLDERS_TODO, USER_SINK LOCK = multiprocessing.Lock() _mgr = multiprocessing.Manager() # TODO persistency
since ICS does not know for deletion changes which store they belong to, we remember ourselves using a berkeleydb file ("serverguid_mapping"). we also maintain folder and server ICS states in a berkeleydb file, for example so we can resume initial indexing ("serverguid_state"). after initial indexing folder states are not updated anymore. the used search engine is pluggable, but by default we use xapian (plugin_xapian.py). a tricky bit is that outlook/exchange perform 'prefix' searches by default and we want to be compatible with this, so terms get an implicit '*' at the end. not every search engine may perform well for this, but we could make this configurable perhaps. """ CONFIG = { 'coredump_enabled': Config.ignore(), 'index_attachments': Config.boolean(default=False), 'index_attachment_extension_filter': Config.ignore(), 'index_attachment_mime_filter': Config.ignore(), 'index_attachment_max_size': Config.size(default=2**24), 'index_attachment_parser': Config.ignore(), 'index_attachment_parser_max_memory': Config.ignore(), 'index_attachment_parser_max_cputime': Config.ignore(), # 0x007D: PR_TRANSPORT_MESSAGE_HEADERS
kopano-backup -u user1 -f Inbox -> backup 'Inbox' folder for user 'user1' in (new) directory 'user1' kopano-backup --restore user1 -> restore data from directory 'user1' to account called 'user1' kopano-backup --restore -u user2 user1 -> same, but restore to account 'user2' kopano-backup --stats user1 -> summarize contents of backup directory 'user1', in CSV format kopano-backup --index user1 -> low-level overview of stored items, in CSV format options can be combined when this makes sense, for example: kopano-backup --index user1 -f Inbox/subfolder --recursive --period-begin 2014-01-01 """ CONFIG = { 'backup_servers': Config.string(multiple=True, default=None), } CACHE_SIZE = 64000000 # XXX make configurable def pickle_dumps(s): return pickle.dumps(s, protocol=2) def pickle_loads(s): return pickle.loads(s, encoding='bytes') def _hex(s): return codecs.encode(s, 'hex').upper() def _unhex(s): return codecs.decode(s, 'hex')
# example of using Service class, in the form of an (incomplete) python version of kopano-monitor # usage: ./kopano-monitor.py import time from datetime import timedelta, datetime import kopano from kopano import Config from kopano.utils import bytes_to_human from MAPI.Tags import PR_EC_QUOTA_MAIL_TIME CONFIG = { 'quota_check_interval': Config.integer(default=15), 'mailquota_resend_interval': Config.integer(default=1), 'servers': Config.string(default=''), # XXX 'userquota_warning_template': Config.path(default='/etc/kopano/quotamail/userwarning.mail'), 'userquota_soft_template': Config.path(default='/etc/kopano/quotamail/usersoft.mail'), 'userquota_hard_template': Config.path(default='/etc/kopano/quotamail/userhard.mail'), 'companyquota_warning_template': Config.path(default='/etc/kopano/quotamail/companywarning.mail'), } """" TODO:
import hashlib import hmac import json import os.path import signal import sys sys.path.insert(0, os.path.dirname(__file__)) # XXX for __import__ to work import time import threading import traceback import kopano from kopano import log_exc, Config CONFIG = { 'data_path': Config.string(default='/var/lib/kopano/presence/'), 'data_save_interval': Config.integer(default=5), 'plugins': Config.string(multiple=True, default=['spreed']), 'run_as_user': Config.string(default="kopano"), 'run_as_group': Config.string(default="kopano"), 'server_bind': Config.string(default="127.0.0.1"), 'server_port': Config.integer(default="1234"), 'server_auth_user': Config.string(default="presence"), 'server_auth_password': Config.string(default="presence"), 'server_secret_key': Config.string(), 'server_token_expire': Config.integer(default="5"), 'xmpp_jid': Config.string(default=None), 'xmpp_password': Config.string(default=None), 'xmpp_user_id_strip_domain': Config.boolean(default=None), 'spreed_auto_unavailable': Config.integer(default="2"), }
from contextlib import closing import grp import os import sys import time import bsddb3 as bsddb import kopano from kopano import Config, log_exc """ kopano-spamd - ICS driven spam learning daemon for Kopano / SpamAssasin """ CONFIG = { 'spam_dir': Config.string(default="/var/lib/kopano/spamd/spam"), 'ham_dir': Config.string(default="/var/lib/kopano/spamd/ham"), 'spam_db': Config.string(default="/var/lib/kopano/spamd/spam.db"), 'sa_group': Config.string(default="amavis"), 'run_as_user': Config.string(default="kopano"), 'run_as_group': Config.string(default="kopano"), 'learn_ham': Config.boolean(default=True), 'header_tag': Config.string(default="x-spam-flag") } class Service(kopano.Service): def main(self): server = self.server state = server.state # start from current state importer = Importer(self)
search queries from outlook/webapp are dealt with by a single instance of class SearchWorker. since ICS does not know for deletion changes which store they belong to, we remember ourselves using a berkeleydb file ("serverguid_mapping"). we also maintain folder and server ICS states in a berkeleydb file, for example so we can resume initial indexing ("serverguid_state"). after initial indexing folder states are not updated anymore. the used search engine is pluggable, but by default we use xapian (plugin_xapian.py). a tricky bit is that outlook/exchange perform 'prefix' searches by default and we want to be compatible with this, so terms get an implicit '*' at the end. not every search engine may perform well for this, but we could make this configurable perhaps. """ CONFIG = { 'coredump_enabled': Config.ignore(), 'index_attachments': Config.boolean(default=True), 'index_attachment_extension_filter': Config.ignore(), 'index_attachment_mime_filter': Config.ignore(), 'index_attachment_max_size': Config.size(default=2**24), 'index_attachment_parser': Config.ignore(), 'index_attachment_parser_max_memory': Config.ignore(), 'index_attachment_parser_max_cputime': Config.ignore(), # 0x007D: PR_TRANSPORT_MESSAGE_HEADERS # 0x0064: PR_SENT_REPRESENTING_ADDRTYPE # 0x0C1E: PR_SENDER_ADDRTYPE # 0x0075: PR_RECEIVED_BY_ADDRTYPE # 0x678E: PR_EC_IMAP_BODY # 0x678F: PR_EC_IMAP_BODYSTRUCTURE # 0x001A: PR_MESSAGE_CLASS # XXX add to cfg 'index_exclude_properties': Config.integer(multiple=True, base=16, default=[0x007D, 0x0064, 0x0C1E, 0x0075, 0x678E, 0x678F, 0x001A]),
it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. 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/. """ CONFIG = { 'run_as_user': Config.string(default="kopano"), 'run_as_group': Config.string(default="kopano"), #dspam specific settings: 'dspam_path': Config.string(default='/var/lib/kopano/dspam/'), 'process_delay': Config.integer(default=1), 'header_result': Config.string(default="X-DSPAM-Result"), 'header_result_spam': Config.string(default="Spam"), 'header_user': Config.string(default="X-DSPAM-Recipient"), 'header_id': Config.string(default="X-DSPAM-Signature"), 'retrain_script': Config.string(default="/etc/kopano/userscripts/kopano-dspam-retrain"), #filter dangerous characters before calling shell script
import time import kopano import grp from kopano import Config, log_exc from contextlib import closing if sys.hexversion >= 0x03000000: import bsddb3 as bsddb else: import bsddb """ kopano-spamd - ICS driven spam learning daemon for Kopano / SpamAssasin """ CONFIG = { 'spam_dir': Config.string(default="/var/lib/kopano/spamd/spam"), 'ham_dir': Config.string(default="/var/lib/kopano/spamd/ham"), 'spam_db': Config.string(default="/var/lib/kopano/spamd/spam.db"), 'sa_group': Config.string(default="amavis"), 'learn_ham': Config.boolean(default=True) } class Service(kopano.Service): def main(self): server = self.server state = server.state catcher = Checker(self) with log_exc(self.log): while True: try: