PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/openchange-1.0-BORG/python/openchange/mailbox.py

#
Python | 288 lines | 258 code | 9 blank | 21 comment | 0 complexity | 9ea3cc3f9dd1780285f92af4fd95f51c MD5 | raw file
Possible License(s): GPL-3.0
  1. #!/usr/bin/python
  2. # OpenChange provisioning
  3. # Copyright (C) Julien Kerihuel <j.kerihuel@openchange.org> 2009
  4. # Copyright (C) Jelmer Vernooij <jelmer@openchange.org> 2009
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. import os
  20. import samba
  21. from samba import Ldb, unix2nttime
  22. import ldb
  23. import uuid
  24. import time
  25. from openchange.urlutils import *
  26. __docformat__ = 'restructuredText'
  27. class NoSuchServer(Exception):
  28. """Raised when a server could not be found."""
  29. class OpenChangeDB(object):
  30. """The OpenChange database.
  31. """
  32. def __init__(self, url):
  33. self.url = url
  34. self.ldb = Ldb(self.url)
  35. self.nttime = samba.unix2nttime(int(time.time()))
  36. def reopen(self):
  37. self.ldb = Ldb(self.url)
  38. def setup(self):
  39. self.ldb.add_ldif("""
  40. dn: @OPTIONS
  41. checkBaseOnSearch: TRUE
  42. dn: @INDEXLIST
  43. @IDXATTR: cn
  44. dn: @ATTRIBUTES
  45. cn: CASE_INSENSITIVE
  46. dn: CASE_INSENSITIVE
  47. """)
  48. self.reopen()
  49. def add_rootDSE(self, ocserverdn, firstorg, firstou):
  50. self.ldb.add({"dn": "@ROOTDSE",
  51. "defaultNamingContext": "CN=%s,CN=%s,%s" % (firstou, firstorg, ocserverdn),
  52. "rootDomainNamingContext": ocserverdn,
  53. "vendorName": "OpenChange Team (http://www.openchange.org)"})
  54. def add_server(self, ocserverdn, netbiosname, firstorg, firstou):
  55. self.ldb.add({"dn": ocserverdn,
  56. "objectClass": ["top", "server"],
  57. "cn": netbiosname,
  58. "GlobalCount": "1",
  59. "ChangeNumber": "1",
  60. "ReplicaID": "1"})
  61. self.ldb.add({"dn": "CN=%s,%s" % (firstorg, ocserverdn),
  62. "objectClass": ["top", "org"],
  63. "cn": firstorg})
  64. self.ldb.add({"dn": "CN=%s,CN=%s,%s" % (firstou, firstorg, ocserverdn),
  65. "objectClass": ["top", "ou"],
  66. "cn": firstou})
  67. def add_root_public_folder(self, dn, fid, change_num, SystemIdx, childcount):
  68. self.ldb.add({"dn": dn,
  69. "objectClass": ["publicfolder"],
  70. "cn": fid,
  71. "PidTagFolderId": fid,
  72. "PidTagChangeNumber": change_num,
  73. "PidTagDisplayName": "Public Folder Root",
  74. "PidTagCreationTime": "%d" % self.nttime,
  75. "PidTagLastModificationTime": "%d" % self.nttime,
  76. "PidTagSubFolders": str(childcount != 0).upper(),
  77. "PidTagFolderChildCount": str(childcount),
  78. "SystemIdx": str(SystemIdx)})
  79. def add_sub_public_folder(self, dn, parentfid, fid, change_num, name, SystemIndex, childcount):
  80. self.ldb.add({"dn": dn,
  81. "objectClass": ["publicfolder"],
  82. "cn": fid,
  83. "PidTagFolderId": fid,
  84. "PidTagParentFolderId": parentfid,
  85. "PidTagChangeNumber": change_num,
  86. "PidTagDisplayName": name,
  87. "PidTagCreationTime": "%d" % self.nttime,
  88. "PidTagLastModificationTime": "%d" % self.nttime,
  89. "PidTagAttributeHidden": str(0),
  90. "PidTagAttributeReadOnly": str(0),
  91. "PidTagAttributeSystem": str(0),
  92. "PidTagContainerClass": "IPF.Note (check this)",
  93. "PidTagSubFolders": str(childcount != 0).upper(),
  94. "PidTagFolderChildCount": str(childcount),
  95. "FolderType": str(1),
  96. "SystemIdx": str(SystemIndex)})
  97. def add_one_public_folder(self, parent_fid, path, children, SystemIndex, names, dn_prefix = None):
  98. name = path[-1]
  99. GlobalCount = self.get_message_GlobalCount(names.netbiosname)
  100. ChangeNumber = self.get_message_ChangeNumber(names.netbiosname)
  101. ReplicaID = self.get_message_ReplicaID(names.netbiosname)
  102. if dn_prefix is None:
  103. dn_prefix = "CN=publicfolders,CN=%s,CN=%s,%s" % (names.firstou, names.firstorg, names.ocserverdn)
  104. fid = gen_mailbox_folder_fid(GlobalCount, ReplicaID)
  105. dn = "CN=%s,%s" % (fid, dn_prefix)
  106. change_num = gen_mailbox_folder_fid(ChangeNumber, ReplicaID)
  107. childcount = len(children)
  108. print "\t* %-40s: 0x%.16x (%s)" % (name, int(fid, 10), fid)
  109. if parent_fid == 0:
  110. self.add_root_public_folder(dn, fid, change_num, SystemIndex, childcount)
  111. else:
  112. self.add_sub_public_folder(dn, parent_fid, fid, change_num, name, SystemIndex, childcount);
  113. GlobalCount += 1
  114. self.set_message_GlobalCount(names.netbiosname, GlobalCount=GlobalCount)
  115. ChangeNumber += 1
  116. self.set_message_ChangeNumber(names.netbiosname, ChangeNumber=ChangeNumber)
  117. for name, grandchildren in children.iteritems():
  118. self.add_one_public_folder(fid, path + (name,), grandchildren[0], grandchildren[1], names, dn)
  119. def add_public_folders(self, names):
  120. pfstoreGUID = str(uuid.uuid4())
  121. self.ldb.add({"dn": "CN=publicfolders,CN=%s,CN=%s,%s" % (names.firstou, names.firstorg, names.ocserverdn),
  122. "objectClass": ["container"],
  123. "cn": "publicfolders",
  124. "StoreGUID": pfstoreGUID,
  125. "ReplicaID": str(1)})
  126. public_folders = ({
  127. "IPM_SUBTREE": ({}, 2),
  128. "NON_IPM_SUBTREE": ({
  129. "EFORMS REGISTRY": ({}, 4),
  130. "Events Root": ({}, -1),
  131. "OFFLINE ADDRESS BOOK": ({
  132. "/o=%s/cn=addrlists/cn=oabs/cn=Default Offline Address Book" % (names.firstorg): ({}, 9),
  133. }, 6),
  134. "SCHEDULE+ FREE BUSY": ({
  135. "EX:/o=%s/ou=%s" % (names.firstorg.lower(), names.firstou.lower()): ({}, 8),
  136. }, 5),
  137. }, 3),
  138. }, 1)
  139. self.add_one_public_folder(0, ("Public Folder Root",), public_folders[0], public_folders[1], names)
  140. def lookup_server(self, cn, attributes=[]):
  141. # Step 1. Search Server object
  142. filter = "(&(objectClass=server)(cn=%s))" % cn
  143. res = self.ldb.search("", scope=ldb.SCOPE_SUBTREE,
  144. expression=filter, attrs=attributes)
  145. if len(res) != 1:
  146. raise NoSuchServer(cn)
  147. return res[0]
  148. def lookup_mailbox_user(self, server, username, attributes=[]):
  149. """Check if a user already exists in openchange database.
  150. :param server: Server object name
  151. :param username: Username object
  152. :return: LDB Object of the user
  153. """
  154. server_dn = self.lookup_server(server, []).dn
  155. # Step 2. Search User object
  156. filter = "(&(objectClass=mailbox)(cn=%s))" % (username)
  157. return self.ldb.search(server_dn, scope=ldb.SCOPE_SUBTREE,
  158. expression=filter, attrs=attributes)
  159. def lookup_public_folder(self, server, displayname, attributes=[]):
  160. """Retrieve the record for a public folder matching a specific display name
  161. :param server: Server Object Name
  162. :param displayname: Display Name of the Folder
  163. :param attributes: Requested Attributes
  164. :return: LDB Object of the Folder
  165. """
  166. server_dn = self.lookup_server(server, []).dn
  167. filter = "(&(objectClass=publicfolder)(PidTagDisplayName=%s))" % (displayname)
  168. return self.ldb.search(server_dn, scope=ldb.SCOPE_SUBTREE,
  169. expression=filter, attrs=attributes)
  170. def get_message_attribute(self, server, attribute):
  171. """Retrieve attribute value from given message database (server).
  172. :param server: Server object name
  173. """
  174. return int(self.lookup_server(server, [attribute])[attribute][0], 10)
  175. def get_message_ReplicaID(self, server):
  176. """Retrieve current mailbox Replica ID for given message database (server).
  177. :param server: Server object name
  178. """
  179. return self.get_message_attribute(server, "ReplicaID")
  180. def get_message_GlobalCount(self, server):
  181. """Retrieve current mailbox Global Count for given message database (server).
  182. :param server: Server object name
  183. """
  184. return self.get_message_attribute(server, "GlobalCount")
  185. def set_message_GlobalCount(self, server, GlobalCount):
  186. """Update current mailbox GlobalCount for given message database (server).
  187. :param server: Server object name
  188. :param index: Mailbox new GlobalCount value
  189. """
  190. server_dn = self.lookup_server(server, []).dn
  191. newGlobalCount = """
  192. dn: %s
  193. changetype: modify
  194. replace: GlobalCount
  195. GlobalCount: %d
  196. """ % (server_dn, GlobalCount)
  197. self.ldb.transaction_start()
  198. try:
  199. self.ldb.modify_ldif(newGlobalCount)
  200. finally:
  201. self.ldb.transaction_commit()
  202. def get_message_ChangeNumber(self, server):
  203. """Retrieve current mailbox Global Count for given message database (server).
  204. :param server: Server object name
  205. """
  206. return self.get_message_attribute(server, "ChangeNumber")
  207. def set_message_ChangeNumber(self, server, ChangeNumber):
  208. """Update current mailbox ChangeNumber for given message database (server).
  209. :param server: Server object name
  210. :param index: Mailbox new ChangeNumber value
  211. """
  212. server_dn = self.lookup_server(server, []).dn
  213. newChangeNumber = """
  214. dn: %s
  215. changetype: modify
  216. replace: ChangeNumber
  217. ChangeNumber: %d
  218. """ % (server_dn, ChangeNumber)
  219. self.ldb.transaction_start()
  220. try:
  221. self.ldb.modify_ldif(newChangeNumber)
  222. finally:
  223. self.ldb.transaction_commit()
  224. def reverse_int64counter(GlobalCount):
  225. rev_counter = 0
  226. for x in xrange(6):
  227. sh = x * 8
  228. unsh = (7-x) * 8
  229. rev_counter = rev_counter | (((GlobalCount >> sh) & 0xff) << unsh)
  230. return rev_counter
  231. def gen_mailbox_folder_fid(GlobalCount, ReplicaID):
  232. """Generates a Folder ID from index.
  233. :param GlobalCount: Message database global counter
  234. :param ReplicaID: Message database replica identifier
  235. """
  236. return "%d" % (ReplicaID | reverse_int64counter(GlobalCount))