Add python code and ldif files needed to create and populate the experimental
authorJulien Kerihuel <j.kerihuel@openchange.org>
Sun, 1 Feb 2009 22:07:13 +0000 (22:07 +0000)
committerJulien Kerihuel <j.kerihuel@openchange.org>
Sun, 1 Feb 2009 22:07:13 +0000 (22:07 +0000)
openchange dispatcher database.

Makefile
python/openchange/mailbox.py [new file with mode: 0644]
python/openchange/provision.py
setup/openchange_newuser
setup/openchange_provision
setup/openchangedb/oc_provision_openchange.ldif [new file with mode: 0644]
setup/openchangedb/oc_provision_openchange_init.ldif [new file with mode: 0644]
setup/openchangedb/oc_provision_openchange_mailbox.ldif [new file with mode: 0644]
setup/openchangedb/oc_provision_openchange_mailbox_folder.ldif [new file with mode: 0644]
setup/openchangedb/oc_provision_openchange_mailbox_user.ldif [new file with mode: 0644]

index f3a0d144f9e239c1fc44b5496f9a7e8fd50540b4..8c0693144ae0ec1f5e94a64afb8b84cae399131f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -827,6 +827,8 @@ provision-install: python-install
        $(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
@@ -835,6 +837,7 @@ provision-uninstall: python-uninstall
        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)           \
diff --git a/python/openchange/mailbox.py b/python/openchange/mailbox.py
new file mode 100644 (file)
index 0000000..512956e
--- /dev/null
@@ -0,0 +1,228 @@
+#!/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)
+            })
index aa2429b16b4570db2ecdab9fcf3fea2686f32acc..3a8d7a68d26c7f2043301e437298b3dfb81506b3 100644 (file)
@@ -1,12 +1,9 @@
 #!/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
@@ -24,6 +21,7 @@
 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
@@ -32,6 +30,9 @@ import ldb
 __docformat__ = 'restructuredText'
 
 DEFAULTSITE = "Default-First-Site-Name"
+FIRST_ORGANIZATION = "First Organization"
+FIRST_ORGANIZATION_UNIT = "First Organization Unit"
+
 
 class ProvisionNames:
     def __init__(self):
@@ -44,7 +45,12 @@ class ProvisionNames:
         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.
@@ -85,15 +91,20 @@ def guess_names_from_smbconf(lp, firstorg=None, firstou=None):
     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
 
 
@@ -211,6 +222,62 @@ def install_schemas(setup_path, names, lp, creds):
     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.
     
@@ -308,3 +375,60 @@ def provision(setup_path, lp, creds, firstorg=None, firstou=None):
 
     # 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)
+
index 07879ef0f3899806f2ac4e4da712e656ab770435..22c66a83a1e50f7d0f9730d69764ddbcdf4727a0 100755 (executable)
@@ -46,6 +46,8 @@ parser.add_option("--disable", action="store_true", metavar="DISABLE",
                  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()
 
@@ -56,6 +58,9 @@ if len(args) == 0:
 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)
 
@@ -64,3 +69,6 @@ if opts.disable == True:
 
 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)
index 15b4906501d147f260fdba7dbeace3ff46fd6954..96db72e9bc7498a056f61e3eb51b49b536d1a145 100755 (executable)
@@ -44,6 +44,7 @@ parser.add_option("--firstorg", type="string", metavar="FIRSTORG",
                   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:
@@ -56,4 +57,7 @@ creds = credopts.get_credentials(lp)
 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)
diff --git a/setup/openchangedb/oc_provision_openchange.ldif b/setup/openchangedb/oc_provision_openchange.ldif
new file mode 100644 (file)
index 0000000..e930b08
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# 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
diff --git a/setup/openchangedb/oc_provision_openchange_init.ldif b/setup/openchangedb/oc_provision_openchange_init.ldif
new file mode 100644 (file)
index 0000000..d158ac1
--- /dev/null
@@ -0,0 +1,9 @@
+dn: @OPTIONS
+checkBaseOnSearch: TRUE
+
+dn: @INDEXLIST
+@IDXATTR: cn
+
+dn: @ATTRIBUTES
+cn: CASE_INSENSITIVE
+dn: CASE_INSENSITIVE
\ No newline at end of file
diff --git a/setup/openchangedb/oc_provision_openchange_mailbox.ldif b/setup/openchangedb/oc_provision_openchange_mailbox.ldif
new file mode 100644 (file)
index 0000000..ce832d5
--- /dev/null
@@ -0,0 +1,4 @@
+dn: CN=${FOLDER_IDX},${USERDN}
+cn: ${FOLDER_IDX}
+fid: ${FOLDER_IDX}
+name: ${NAME}
diff --git a/setup/openchangedb/oc_provision_openchange_mailbox_folder.ldif b/setup/openchangedb/oc_provision_openchange_mailbox_folder.ldif
new file mode 100644 (file)
index 0000000..25739f6
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# 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
diff --git a/setup/openchangedb/oc_provision_openchange_mailbox_user.ldif b/setup/openchangedb/oc_provision_openchange_mailbox_user.ldif
new file mode 100644 (file)
index 0000000..89e4904
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# User object
+#
+dn: CN=${USERNAME},${FIRSTORGDN}
+objectClass: mailbox
+objectClass: container
+cn: ${USERNAME}
+MailboxGUID: ${MAILBOXGUID}
+ReplicaID: ${REPLICAID}
+ReplicaGUID: ${REPLICAGUID}
\ No newline at end of file