openchange dispatcher database.
$(INSTALL) -d $(DESTDIR)$(datadir)/setup/AD
$(INSTALL) -m 0644 setup/AD/oc_provision* $(DESTDIR)$(datadir)/setup/AD/
$(INSTALL) -m 0644 setup/AD/prefixMap.txt $(DESTDIR)$(datadir)/setup/AD/
+ $(INSTALL) -d $(DESTDIR)$(datadir)/setup/openchangedb
+ $(INSTALL) -m 0644 setup/openchangedb/oc_provision* $(DESTDIR)$(datadir)/setup/openchangedb/
provision-uninstall: python-uninstall
rm -f $(DESTDIR)$(datadir)/setup/AD/oc_provision_configuration.ldif
rm -f $(DESTDIR)$(datadir)/setup/AD/oc_provision_schema_ADSC.ldif
rm -f $(DESTDIR)$(datadir)/setup/AD/prefixMap.txt
rm -f $(DESTDIR)$(datadir)/setup/AD
+ rm -rf $(DESTDIR)$(datadir)/setup/openchangedb
mapiproxy-servers: mapiproxy/servers/exchange_nsp.$(SHLIBEXT) \
mapiproxy/servers/exchange_emsmdb.$(SHLIBEXT) \
--- /dev/null
+#!/usr/bin/python
+
+# OpenChange provisioning
+# Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2009
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import samba
+import provision as openchange
+from samba.samdb import SamDB
+from samba.auth import system_session
+from samba.provision import setup_add_ldif, setup_modify_ldif
+import ldb
+import uuid
+
+__docformat__ = 'restructuredText'
+
+def get_message_attribute(setup_path, creds, lp, names, server=None, attribute=None):
+ """Retrieve attribute value from given message database (server).
+
+ :param setup_path: Function that returns the path to a setup.
+ :param creds: Credentials context
+ :param lp: Loadparm context
+ :param names: Provision names context
+ :param server: Server object name
+ """
+ # Step 1. Open openchange.ldb
+ samdb = SamDB(url="openchange.ldb", session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ # Step 2. Search Attribute from 'server' object
+ filter = "(&(objectClass=server)(cn=%s))" % (server)
+ res = samdb.search("", scope=ldb.SCOPE_SUBTREE,
+ expression=filter, attrs=[attribute])
+ assert(len(res) == 1)
+
+ # Step 3. Convert result to hexadecimal
+ attribute = int(res[0][attribute][0], 16)
+
+ return attribute
+
+
+def get_message_ReplicaID(setup_path, creds, lp, names, server=None):
+ """Retrieve current mailbox Replica ID for given message database (server).
+
+ :param setup_path: Function that returns the path to a setup.
+ :param creds: Credentials context
+ :param lp: Loadparm context
+ :param names: Provision names context
+ :param server: Server object name
+ """
+
+ ReplicaID = get_message_attribute(setup_path, creds, lp, names, server=server,
+ attribute="ReplicaID")
+ return ReplicaID
+
+
+def get_message_GlobalCount(setup_path, creds, lp, names, server=None):
+ """Retrieve current mailbox Global Count for given message database (server).
+
+ :param setup_path: Function that returns the path to a setup.
+ :param creds: Credentials context
+ :param lp: Loadparm context
+ :param names: Provision names context
+ :param server: Server object name
+ """
+
+ GlobalCount = get_message_attribute(setup_path, creds, lp, names, server=server,
+ attribute="GlobalCount")
+ return GlobalCount
+
+
+def set_message_GlobalCount(setup_path, creds, lp, names, server=None, GlobalCount=None):
+ """Update current mailbox GlobalCount for given message database (server).
+
+ :param setup_path: Function that returns the path to a setup.
+ :param creds: Credentials context
+ :param lp: Loadparm context
+ :param names: Provision names context
+ :param server: Server object name
+ :param index: Mailbox new GlobalCount value
+ """
+
+ # Step 1. Open openchange.ldb
+ samdb = SamDB(url="openchange.ldb", session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ # Step 2. Search Server object
+ filter = "(&(objectClass=server)(cn=%s))" % (server)
+ res = samdb.search("", scope=ldb.SCOPE_SUBTREE,
+ expression=filter, attrs=[])
+ assert(len(res) == 1)
+
+ # Step 3. Update Server object
+ server_dn = res[0].dn
+
+ newGlobalCount = """
+dn: %s
+changetype: modify
+replace: GlobalCount
+GlobalCount: 0x%x
+""" % (server_dn, GlobalCount)
+
+ samdb.transaction_start()
+ samdb.modify_ldif(newGlobalCount)
+ samdb.transaction_commit()
+
+
+def lookup_mailbox_user(setup_path, creds, lp, names,
+ server=None, username=None):
+ """Check if a user already exists in openchange database.
+
+ :param setup_path: Function that returns the path to a setup.
+ :param creds: Credentials context
+ :param lp: Loadparm context
+ :param names: Provision names context
+ :param server: Server object name
+ :param username: Username object
+ """
+
+ # Step 1. Open openchange.ldb
+ samdb = SamDB(url="openchange.ldb", session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ # Step 2. Search Server object
+ filter = "(&(objectClass=server)(cn=%s))" % (server)
+ res = samdb.search("", scope=ldb.SCOPE_SUBTREE,
+ expression=filter, attrs=[])
+ assert(len(res) == 1)
+ server_dn = res[0].dn
+
+ # Step 3. Search User object
+ filter = "(&(objectClass=user)(cn=%s))" % (username)
+ res = samdb.search(server_dn, scope=ldb.SCOPE_SUBTREE,
+ expression=filter, attrs=[])
+ if (len(res) == 1):
+ return False
+ else:
+ return True
+
+
+def add_mailbox_user(setup_path, creds, lp, names, username=None):
+ """Add a user record in openchange database.
+
+ :param setup_path: Function that returns the path to a setup.
+ :param creds: Credentials context
+ :param lp: Loadparm context
+ :param names: Provision names context
+ :param server: Server object name
+ :param username: Username object
+ """
+
+ # Step 1. Open openchange.ldb
+ samdb = SamDB(url="openchange.ldb", session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ # Step 2. Add user object
+ mailboxGUID = str(uuid.uuid4())
+ replicaID = str(1)
+ replicaGUID = str(uuid.uuid4())
+
+ print "[+] Adding '%s' record" % (username)
+ setup_add_ldif(samdb, setup_path("openchangedb/oc_provision_openchange_mailbox_user.ldif"), {
+ "USERNAME": username,
+ "FIRSTORGDN": names.ocfirstorgdn,
+ "MAILBOXGUID": mailboxGUID,
+ "REPLICAID": replicaID,
+ "REPLICAGUID": replicaGUID
+ })
+
+
+def gen_mailbox_folder_fid(GlobalCount, ReplicaID):
+ """Generates a Folder ID from index.
+
+ :param GlobalCount: Message database global counter
+ :param ReplicaID: Message database replica identifier
+ """
+
+ folder = "0x%.12x%.4x" % (GlobalCount, ReplicaID)
+# folder = "0x%s" % tmp[::-1]
+
+ return folder
+
+
+def add_mailbox_root_folder(setup_path, creds, lp, names, username=None,
+ foldername=None, GlobalCount=None, ReplicaID=None,
+ SystemIdx=None):
+ """Add a root folder to the user mailbox
+
+ :param setup_path: Function that returns the path to a setup.
+ :param creds: Credentials context
+ :param lp: Loadparm context
+ :param names: Provision names context
+ :param username: Username object
+ :param foldername: Folder name
+ :param GlobalCount: current global counter for message database
+ :param ReplicaID: replica identifier for message database
+ :param SystemIdx: System Index for root folders
+ """
+
+ names.ocuserdn = "CN=%s,%s" % (username, names.ocfirstorgdn)
+
+ # Step 1. Open openchange.ldb
+ samdb = SamDB(url="openchange.ldb", session_info=system_session(),
+ credentials=creds, lp=lp)
+
+ # Step 2. Add root folder to user subtree
+ FID = gen_mailbox_folder_fid(GlobalCount, ReplicaID)
+ print "[+] Adding SystemRoot folder '%s' (%s) to %s" % (FID, foldername, username)
+ setup_add_ldif(samdb, setup_path("openchangedb/oc_provision_openchange_mailbox_folder.ldif"), {
+ "USERDN": names.ocuserdn,
+ "FOLDER_IDX": FID,
+ "NAME": foldername,
+ "SYSTEMIDX": "%s" % (SystemIdx)
+ })
#!/usr/bin/python
# OpenChange provisioning
-# Copyright (C) Jelmer Vernooij <jelmer@openchange.org> 2008
+# Copyright (C) Jelmer Vernooij <jelmer@openchange.org> 2008-2009
+# Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2009
#
-# Based on the original in JavaScript:
-#
-# Copyright (C) Julien Kerihuel <jkerihuel@openchange.org> 2005
-#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
from base64 import b64encode
import os
import samba
+import openchange.mailbox as mailbox
from samba.samdb import SamDB
from samba.auth import system_session
from samba.provision import setup_add_ldif, setup_modify_ldif
__docformat__ = 'restructuredText'
DEFAULTSITE = "Default-First-Site-Name"
+FIRST_ORGANIZATION = "First Organization"
+FIRST_ORGANIZATION_UNIT = "First Organization Unit"
+
class ProvisionNames:
def __init__(self):
self.domain = None
self.hostname = None
self.firstorg = None
+ self.firstou = None
self.firstorgdn = None
+ # OpenChange dispatcher database specific
+ self.ocfirstorgdn = None
+ self.ocserverdn = None
+ self.ocuserdn = None
def guess_names_from_smbconf(lp, firstorg=None, firstou=None):
"""Guess configuration settings to use from smb.conf.
names.sitename = sitename
if firstorg is None:
- firstorg = "First Organization"
+ firstorg = FIRST_ORGANIZATION;
if firstou is None:
- firstou = "First Organization Unit"
+ firstou = FIRST_ORGANIZATION_UNIT;
names.firstorg = firstorg
+ names.firstou = firstou
names.firstorgdn = "CN=%s,CN=Microsoft Exchange,CN=Services,%s" % (firstorg, configdn)
names.serverdn = "CN=%s,CN=Servers,CN=%s,CN=Sites,%s" % (netbiosname, sitename, configdn)
+ # OpenChange dispatcher DB names
+ names.ocserverdn = "CN=%s,%s" % (names.netbiosname, names.domaindn)
+ names.ocfirstorgdn = "CN=%s,CN=%s,%s" % (firstou, firstorg, names.ocserverdn)
+
return names
samdb.transaction_commit()
+def newmailbox(setup_path, creds, lp, username=None, firstorg=None, firstou=None):
+
+ names = guess_names_from_smbconf(lp, firstorg, firstou)
+
+ # Step 1. Retrieve current FID index
+ GlobalCount = mailbox.get_message_GlobalCount(setup_path, creds, lp,
+ names, names.netbiosname)
+ ReplicaID = mailbox.get_message_ReplicaID(setup_path, creds, lp,
+ names, names.netbiosname)
+ print "username is %s" % (username)
+ print "GlobalCount: 0x%x" % GlobalCount
+ print "ReplicaID: 0x%x" % ReplicaID
+
+ # Step 2. Check if the user already exists
+ ret = mailbox.lookup_mailbox_user(setup_path, creds, lp, names,
+ names.netbiosname, username)
+ assert(ret == True)
+
+ # Step 3. Create the user object
+ mailbox.add_mailbox_user(setup_path, creds, lp, names, username=username)
+
+ # Step 4. Create system mailbox folders for this user
+ system_folders = [
+ "Non IPM Subtree",
+ "Deferred Actions",
+ "Spooler Queue",
+ "Top Information Store",
+ "Inbox",
+ "Outbox",
+ "Sent Items",
+ "Deleted Items",
+ "Common Views",
+ "Schedule",
+ "Search",
+ "Views",
+ "Shortcuts"
+ ];
+
+ SystemIdx = 1
+ for i in system_folders:
+ mailbox.add_mailbox_root_folder(setup_path, creds, lp, names,
+ username=username, foldername=i,
+ GlobalCount=GlobalCount, ReplicaID=ReplicaID,
+ SystemIdx=SystemIdx)
+ GlobalCount += 1
+ SystemIdx += 1
+
+ # Step 5. Update FolderIndex
+ mailbox.set_message_GlobalCount(setup_path, creds, lp, names,
+ names.netbiosname, GlobalCount=GlobalCount)
+
+ GlobalCount = mailbox.get_message_GlobalCount(setup_path, creds, lp,
+ names, names.netbiosname)
+ print "GlobalCount: 0x%x" % GlobalCount
+
+
def newuser(lp, creds, username=None):
"""extend user record with OpenChange settings.
# Install OpenChange-specific schemas
install_schemas(setup_path, names, lp, creds)
+
+
+def setup_openchangedb(path, setup_path, credentials, names, lp):
+ """Setup the OpenChange database.
+
+ :param path: Path to the database.
+ :param setup_path: Function that returns the path to a setup.
+ :param session_info: Session info
+ :param credentials: Credentials
+ :param lp: Loadparm context
+
+ :note: This will wipe the OpenChange Database!
+ """
+
+ session_info = system_session()
+
+ openchange_ldb = SamDB(path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ # Wipes the database
+ try:
+ openchange_ldb.erase()
+ except:
+ os.unlink(path)
+
+ openchange_ldb.load_ldif_file_add(setup_path("openchangedb/oc_provision_openchange_init.ldif"))
+
+ openchange_ldb = SamDB(path, session_info=session_info,
+ credentials=credentials, lp=lp)
+
+ setup_add_ldif(openchange_ldb, setup_path("openchangedb/oc_provision_openchange.ldif"), {
+ "OCSERVERDN": names.ocserverdn,
+ "OCFIRSTORGDN": names.ocfirstorgdn,
+ "FIRSTORG": names.firstorg,
+ "FIRSTOU": names.firstou,
+ "NETBIOSNAME": names.netbiosname
+ })
+
+
+def openchangedb_provision(setup_path, lp, creds, firstorg=None, firstou=None):
+ """Create the OpenChange database.
+
+ :param setup_path: Path to the setup directory
+ :param lp: Loadparm context
+ :param creds: Credentials context
+ :param firstorg: First Organization
+ :param firstou: First Organization Unit
+ """
+
+ private_dir = lp.get("private dir")
+ path = os.path.join(private_dir, "openchange.ldb")
+
+ names = guess_names_from_smbconf(lp, firstorg, firstou)
+
+ print "Setting up openchange db"
+ setup_openchangedb(path, setup_path, creds, names, lp)
+
help="Disable access to OpenChange server")
parser.add_option("--create", action="store_true", metavar="CREATE",
help="Create the OpenChange user account")
+parser.add_option("--mailbox", action="store_true", metavar="MAILBOX",
+ help="Create the OpenChange user mailbox")
opts, args = parser.parse_args()
lp = sambaopts.get_loadparm()
creds = credopts.get_credentials(lp)
+def setup_path(*args):
+ return os.path.join(os.path.dirname(__file__), *args)
+
if opts.enable == True:
openchange.accountcontrol(lp, creds, username=args[0], value=0)
if opts.create == True:
openchange.newuser(lp, creds, username=args[0])
+
+if opts.mailbox == True:
+ openchange.newmailbox(setup_path, creds, lp, username=args[0], firstorg=None, firstou=None)
help="set OpenChange First Organization (otherwise First Organization)")
parser.add_option("--firstou", type="string", metavar="FIRSTOU",
help="set OpenChange First Organization Unit (otherwise First Organization Unit)")
+parser.add_option("--openchangedb", action="store_true", help="Initialize OpenChange dispatcher database")
opts,args = parser.parse_args()
if len(args) != 0:
def setup_path(*args):
return os.path.join(os.path.dirname(__file__), *args)
-openchange.provision(setup_path, lp, creds, firstorg=opts.firstorg, firstou=opts.firstou)
+if not opts.openchangedb:
+ openchange.provision(setup_path, lp, creds, firstorg=opts.firstorg, firstou=opts.firstou)
+else:
+ openchange.openchangedb_provision(setup_path, lp, creds, firstorg=opts.firstorg, firstou=opts.firstou)
--- /dev/null
+#
+# Describes a server object
+# It is responsible for holding the GlobalCount identifier (48 bytes)
+# and the Replica identifier
+
+dn: ${OCSERVERDN}
+objectClass: top
+objectClass: server
+cn: ${NETBIOSNAME}
+GlobalCount: 0x1
+ReplicaID: 0x1
+
+dn: CN=${FIRSTORG},${OCSERVERDN}
+objectClass: top
+objectClass: org
+cn: ${FIRSTORG}
+
+dn: CN=${FIRSTOU},CN=${FIRSTORG},${OCSERVERDN}
+objectClass: top
+objectClass: ou
+cn: ${FIRSTOU}
\ No newline at end of file
--- /dev/null
+dn: @OPTIONS
+checkBaseOnSearch: TRUE
+
+dn: @INDEXLIST
+@IDXATTR: cn
+
+dn: @ATTRIBUTES
+cn: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
\ No newline at end of file
--- /dev/null
+dn: CN=${FOLDER_IDX},${USERDN}
+cn: ${FOLDER_IDX}
+fid: ${FOLDER_IDX}
+name: ${NAME}
--- /dev/null
+#
+# Mailbox Folder object
+#
+dn: CN=${FOLDER_IDX},${USERDN}
+objectClass: systemfolder
+cn: ${FOLDER_IDX}
+fid: ${FOLDER_IDX}
+name: ${NAME}
+SystemIdx: ${SYSTEMIDX}
\ No newline at end of file
--- /dev/null
+#
+# User object
+#
+dn: CN=${USERNAME},${FIRSTORGDN}
+objectClass: mailbox
+objectClass: container
+cn: ${USERNAME}
+MailboxGUID: ${MAILBOXGUID}
+ReplicaID: ${REPLICAID}
+ReplicaGUID: ${REPLICAGUID}
\ No newline at end of file