; Active Directory Function Library. Include this to allow access to Active Directory Functions

; Author : Jonthan Clelland
; Email : jclelland@statestreet.com
; Version : 3.1.1

;Version History -- Starting from v3.1

; 3.1 -- First released version
; 3.1.1 -- Bugfix to _ADGetObjectsInOU, default $filter value caused errors. Has been changed.
; 3.1.2 -- Corrections made to comments, replaced occurrences of 'Samaccountname' with 'Full Distringuished Name' where this had changed ibn the code.
; 3.1.2 -- Change to '_ADUserCreateMailbox', added '$emaildomain' and removed the hard-coded Email Domain name.

; _ADDoError  : Error event handler for COM errors. This is global so will pick up errors from your program if you include this library
; _ADCreateUser : Creates a user in a particular OU
; _ADCreateGroup : Creates a group in a particular OU
; _ADAddUserToGroup : Adds a user to a group (if the user is not already a member of the group)
; _ADRemoveUserFromGroup : Removes a user from a group
; _ADObjectExists : Returns 1 if the given object (SamAccountName) exists in the local AD Tree
; _ADModifyAttribute : Sets the attribute of the given object to the value specified
; _ADIsMemberOf : Returns 1 if the user is a member of the group
; _ADGetUserGroups : Returns an array (byreference) containing the groups that the user is a member of
; _ADRecursiveGetMemberOf : Returns a recursed list of group membership for a group or user
; _ADGetGroupMembers : Returns an array of the group members
; _ADGetGroupMemberOf : Returns a simple list of group membership for a group
; _ADHasFullRights : Returns 1 if the given user has full rights over the given group
; _ADGroupMailEnable : Mail enables a group
; _ADUserCreateMailbox : Creates a mailbox for a user
; _ADUserDeleteMailbox : Deletes the mailbox for a user
; _ADGetObjectsInOU : Returns a filtered list of objects and attributes in a given OU
; _ADDNToSamAccountName : Returns the SamAccountName of an FQDN
; _ADSamAccountNameToFQDN : Returns a FQDN from a SamAccountName
; _ADDNToDisplayName :Returns the Display Name of an FQDN
; _ADCreateObject : Creates an object of a specified type
; _ADCreateComputer : Creates a computer and assigns permissions for a user/group to add the computer
; _ADDeleteObject : Deletes an object
; _ADGetObjectClass : Returns the class of an object
; _ADGetObjectClassFromFQDN : Returns the class of an object from it's FQDN
; _ADGetObjectAttribute : Returns a (single-value) attribute of an object
; _ADListDomainControllers : Returns a list of Domain Controllers for the current domain
; _ADOUObjectNames : Returns display names of filtered objects within an OU
; _ADGroupManagerCanModify : Returns 1 if the manager of the group can modify the member list, 0 if not, -1 if there is no manager assigned
; _ADAddAccountToMailboxRights : Adds useraccount to mailbox with full rights and send as permissions.
; _ADRemoveMailboxRights : Remove mailbox rights for an account.
; _ADGetMailboxPerms : Takes a mailbox and an array, returns a list of SAMIDs with un-inherited permissions on the mailbox

; NOTES : Please use the following command to store the local user's groups for the function 'HasFullRights'. This is to allow cross-domain
; permission checks. $loggedonusergroups is a global variable declared in the 'Define AD Constants' region. If you are using this command
; in this library, move the command below the declaration of the variable and remove the comment charcater.

; _ADRecursiveGetMemberOf ($loggedonusergroups, _ADSamAccountNameToFQDN (@UserName))

; include array functions (we use _arraysearch in some functions)
#include <Array.au3>

#region  ; Define AD Constants

Global Const $ADS_GROUP_TYPE_GLOBAL_GROUP = 0x2
Global Const $ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP = 0x4
Global Const $ADS_GROUP_TYPE_UNIVERSAL_GROUP = 0x8
Global Const $ADS_GROUP_TYPE_SECURITY_ENABLED = 0x80000000
Global Const $ADS_GROUP_TYPE_GLOBAL_SECURITY = BitOR($ADS_GROUP_TYPE_GLOBAL_GROUP, $ADS_GROUP_TYPE_SECURITY_ENABLED)
Global Const $ADS_GROUP_TYPE_UNIVERSAL_SECURITY = BitOR($ADS_GROUP_TYPE_UNIVERSAL_GROUP, $ADS_GROUP_TYPE_SECURITY_ENABLED)
Global Const $ADS_GROUP_TYPE_DOMAIN_LOCAL_SECURITY = BitOR($ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP, $ADS_GROUP_TYPE_SECURITY_ENABLED)

Global Const $ADS_UF_PASSWD_NOTREQD = 0x0020
Global Const $ADS_UF_WORKSTATION_TRUST_ACCOUNT = 0x1000
Global Const $ADS_ACETYPE_ACCESS_ALLOWED = 0x0
Global Const $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = 0x5
Global Const $ADS_FLAG_OBJECT_TYPE_PRESENT = 0x1
Global Const $ADS_RIGHT_GENERIC_READ = 0x80000000
Global Const $ADS_RIGHT_DS_SELF = 0x8
Global Const $ADS_RIGHT_DS_WRITE_PROP = 0x20
Global Const $ADS_RIGHT_DS_CONTROL_ACCESS = 0x100
Global Const $ADS_UF_ACCOUNTDISABLE = 2
Global Const $ADS_OPTION_SECURITY_MASK = 3
Global Const $ADS_SECURITY_INFO_DACL = 4

Global Const $ALLOWED_TO_AUTHENTICATE = "{68B1D179-0D15-4d4f-AB71-46152E79A7BC}"
Global Const $RECEIVE_AS = "{AB721A56-1E2f-11D0-9819-00AA0040529B}"
Global Const $SEND_AS = "{AB721A54-1E2f-11D0-9819-00AA0040529B}"
Global Const $USER_CHANGE_PASSWORD = "{AB721A53-1E2f-11D0-9819-00AA0040529b}"
Global Const $USER_FORCE_CHANGE_PASSWORD = "{00299570-246D-11D0-A768-00AA006E0529}"
Global Const $USER_ACCOUNT_RESTRICTIONS = "{4C164200-20C0-11D0-A768-00AA006E0529}"
Global Const $VALIDATED_DNS_HOST_NAME = "{72E39547-7B18-11D1-ADEF-00C04FD8D5CD}"
Global Const $VALIDATED_SPN = "{F3A64788-5306-11D1-A9C5-0000F80367C1}"
Const $Member_SchemaIDGuid = "{BF9679C0-0DE6-11D0-A285-00AA003049E2}"

Global $objConnection = ObjCreate("ADODB.Connection")  ; Create COM object to AD
$objConnection.ConnectionString = "Provider=ADsDSOObject"
$objConnection.Open ("Active Directory Provider")  ; Open connection to AD

Global $objRootDSE = ObjGet("LDAP://RootDSE")
Global $strDNSDomain = $objRootDSE.Get ("defaultNamingContext")  ; Retrieve the current AD domain name
Global $strHostServer = $objRootDSE.Get ("dnsHostName") ; Retrieve the name of the connected DC
Global $strConfiguration = $objRootDSE.Get ("ConfigurationNamingContext") ; Retrieve the Configuration naming context

Global $loggedonusergroups  ; populate this with the logged on user groups in your own app

#endregion

$oMyError = ObjEvent("AutoIt.Error", "_ADDoError") ; Install a custom error handler

;  MyErrFunc
Func _ADDoError()
	$HexNumber = Hex($oMyError.number, 8)
	
	If $HexNumber = 80020009 Then
		SetError(3)
		Return
	EndIf
	
	If $HexNumber = "8007203A" Then
		SetError(4)
		Return
	EndIf
	
	MsgBox(262144, "", "We intercepted a COM Error !" & @CRLF & _
			"Number is: " & $HexNumber & @CRLF & _
			"Windescription is: " & $oMyError.windescription & @CRLF & _
			"Script Line number is: " & $oMyError.scriptline)
	
	Select
		Case $oMyError.windescription = "Access is denied."
			$objConnection.Close ("Active Directory Provider")
			$objConnection.Open ("Active Directory Provider")
			SetError(2)
		Case 1
			SetError(1)
	EndSelect
	
EndFunc   ;==>_ADDoError

; _ADCreateUser
; $userou = OU to create the group in. Form is "sampleou=ou, sampleparent=ou, sampledomain1=dc, sampledomain2=dc, sampledomain3=dc"
; $user = Username, form is SamAccountName without leading 'CN='
; $fname = First Name
; $lname = Last Name
; $description = optional - Description

Func _ADCreateUser($userou, $user, $fname, $lname, $description = "User")
	If _ADObjectExists($user) Then Return 0

	$ObjOU = ObjGet("LDAP://" & $strHostServer & "/" & $userou)
	If $fname = "" Then 
		$cnname = "CN=" & $lname
	Else
		$cnname = "CN=" & $lname & "\, " & $fname
	EndIf
	$ObjUser = $ObjOU.Create ("User", $cnname)
	
	$ObjUser.Put ("sAMAccountName", $user)
	$ObjUser.Put ("description", $description)

	$ObjUser.SetInfo
	Return 1
EndFunc   ;==>_ADCreateUser

; _ADCreateGroup
; $gruopou = OU to create the group in. Form is "sampleou=ou, sampleparent=ou, sampledomain1=dc, sampledomain2=dc, sampledomain3=dc"
; $group = groupname, form is SamAccountName without leading 'CN='
; $type = Group type. Defaults to Global Security. See the global constands for other types. NOTE Global security must be 'BitOr'ed with a scope.

Func _ADCreateGroup($groupou, $group, $type = -2147483646)
	If _ADObjectExists($group) Then Return 0
	If StringLeft($group, 3) <> "CN=" Then
		$groupcn = "CN=" & $group
	Else
		$groupcn = $group
	EndIf
	$groupcn = _ADFixSpecialChars($groupcn)
	
	$ObjOU = ObjGet("LDAP://" & $strHostServer & "/" & $groupou)
	$ObjGroup = $ObjOU.Create ("Group", $groupcn)
	
	$groupsam = StringReplace($group, ",", "")	
	$groupsam = StringReplace($groupsam, "#", "")
	$groupsam = StringReplace($groupsam, "/", "")
	$ObjGroup.Put ("sAMAccountName", $groupsam)
	$ObjGroup.Put ("grouptype", $type)
	
	$ObjGroup.SetInfo
	
	Return 1
EndFunc   ;==>_ADCreateGroup

; _ADAddUserToGroup
; Takes the group (Full Distringuished Name) and the user (Full Distringuished Name)
; Adds the user to the group
; Returns 0 if the user is already a member of the group,
; Returns 1 if the user was added to the group
; Returns -1 if there was an error

Func _ADAddUserToGroup($group, $user)
	If _ADIsMemberOf($group, $user) Then Return 0
	$oUsr = ObjGet("LDAP://" & $strHostServer & "/" & $user)  ; Retrieve the COM Object for the user
	$oGroup = ObjGet("LDAP://" & $strHostServer & "/" & $group)  ; Retrieve the COM Object for the group
	
	$OGroup.Add ($oUsr.AdsPath)
	$OGroup.SetInfo
	$oGroup = 0
	$oUser = 0
	Return _ADIsMemberOf($group, $user)
EndFunc   ;==>_ADAddUserToGroup

; _ADRemoveUserFromGroup
; Takes the group (Full Distringuished Name) and the user (Full Distringuished Name)
; Removes the user from the group (if the user is a member of the group)
; Returns 0 if the user isn't a member of the group, 1 if the user was removed from the group
; Returns -1 if the removal failed.

Func _ADRemoveUserFromGroup($group, $user)
	If _ADIsMemberOf($group, $user) = 0 Then Return 0

	$oUsr = ObjGet("LDAP://" & $strHostServer & "/" & $user)  ; Retrieve the COM Object for the user

	$oGroup = ObjGet("LDAP://" & $strHostServer & "/" & $group)  ; Retrieve the COM Object for the group
	
	$oGroup.Remove ($oUsr.AdsPath)
	$OGroup.SetInfo
	
	$oGroup = 0
	$oUser = 0
	
	If _ADIsMemberOf($group, $user) Then
		Return -1
	Else
		Return 1
	EndIf
	
EndFunc   ;==>_ADRemoveUserFromGroup

; _ADObjectExists
; Takes an object name (SamAccountName without leading 'CN=')
; Returns 1 if the object exists in the tree, 0 otherwise

Func _ADObjectExists($object)
	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $object & ");ADsPath;subtree"
	$objRecordSet = $ObjConnection.Execute ($strQuery)  ; Retrieve the FQDN for the group, if it exists
	
	If $objRecordSet.RecordCount = 1 Then
		$objRecordSet = 0
		Return 1
	Else
		$objRecordSet = 0
		Return 0
	EndIf
EndFunc   ;==>_ADObjectExists

; _ADModifyAttribute
; $object = Object to modify (samAccountName)
; $attribute = Attribute to modify
; $value = value to set attribute to, use a blank string "" to delete an attribute

Func _ADModifyAttribute($object, $attribute, $value)
	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $object & ");ADsPath;subtree"
	$objRecordSet = $ObjConnection.Execute ($strQuery)  ; Retrieve the FQDN for the object
	
	$ldap_entry = $objRecordSet.fields (0).value
	$oObject = ObjGet($ldap_entry)  ; Retrieve the COM Object for the object
	
	$oObject.GetInfo
	
	If $value = "" Then
		$oObject.PutEx (1, $attribute, 0)
	Else
		$oObject.Put ($attribute, $value)
	EndIf

	$oObject.SetInfo
	$oObject.PurgePropertyList
	
	$oObject = 0
	
	Return 1
EndFunc   ;==>_ADModifyAttribute

; _ADIsMemberOf
; Takes Full Distringuished Names for a group and a user
; Returns 1 if the the user is a member of the group, 0 otherwise, -1 if access is denied/group not found

Func _ADIsMemberOf($group, $user)
	$ObjGroup = ObjGet("LDAP://" & $strHostServer & "/" & $group)
	If Not IsObj($objGroup) Then Return -1
	$ismember = $objGroup.IsMember ("LDAP://" & $strHostServer & "/" & $user)
	$ObjGroup = 0
	Return -$ismember
EndFunc   ;==>_ADIsMemberOf

; _ADGetUserGroups
; the currently logged on user is a member of. Returns an array of Full DNs of the Group names that the user is immediately a member of
; with element 0 containing the number of groups.
; $user - optional -- SamAccountName of a user, defaults to locally logged on user

Func _ADGetUserGroups(ByRef $usergroups, $user = @UserName)
	Local $oUsr
	
	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $user & ");ADsPath;subtree"
	$objRecordSet = $objConnection.Execute ($strQuery)  ; Retrieve the FQDN for the logged on user
	$ldap_entry = $objRecordSet.fields (0).value
	$oUsr = ObjGet($ldap_entry)  ; Retrieve the COM Object for the logged on user
	
	$usergroups = $oUsr.GetEx ("memberof")
	
	$oUsr = 0
	
	$count = UBound($usergroups)
	_ArrayInsert($usergroups, 0, $count)
EndFunc   ;==>_ADGetUserGroups

; _ADRecursiveGetMemberOf
; Takes a Full DN of a group or user and returns a recursively searched list of groups the object is a member of to the array
; This will traverse through groups that the object is immediately a member of and check their group membership as well.
; The return values are full DNs. For groups that are inherited, the return is the DN of the group, and the DN of the first group
; it was inherited from, seperated by '|'.
Func _ADRecursiveGetMemberOf(ByRef $memberof, $fqdn)
	Local $objItem, $tempmemberof, $i = 0
	
	$objItem = ObjGet("LDAP://" & $strHostServer & "/" & $fqdn)  ; Retrieve the COM Object for the logged on user
	
	$memberof = $objItem.GetEx ("memberof")
	
	$objItem = 0
	
	If $memberof = 0 Then
		Dim $memberof[1] = [0]
		Return 0
	EndIf
	
	Do
		If StringInStr($memberof[$i], "|") Then
			$currgroup = StringLeft($memberof[$i], StringInStr($memberof[$i], "|") - 1)
		Else
			$currgroup = $memberof[$i]
		EndIf
		$currgroup = StringReplace($currgroup, "/", "\/")
		_ADGetGroupMemberOf($tempmemberof, $currgroup)
		For $j = 1 To $tempmemberof[0]
			$exists = 0
			$k = UBound($memberof)
			Do
				$k -= 1
				If StringInStr($memberof[$k], $tempmemberof[$j]) Then $exists = 1
			Until $exists = 1 Or $k = 0
			If $exists = 0 Then _ArrayAdd($memberof, $tempmemberof[$j] & "|" & $currgroup)
		Next
		$i += 1
	Until $i = UBound($memberof)
	
	$count = UBound($memberof)
	_ArrayInsert($memberof, 0, $count)
	
	Return 1
EndFunc   ;==>_ADRecursiveGetMemberOf

; _ADRecursiveGetGroupMembers
; Takes a Full DN of a group and returns a recursively searched list of members of that group (and groups that are members of the group)
; The return values are full DNs. For groups that are inherited, the return is the DN of the group, and the DN of the first group
; it was inherited from, seperated by '|'.
Func _ADRecursiveGetGroupMembers(ByRef $members, $fqdn)
	Local $objItem, $tempmembers, $i = 1
	
	;$objItem = ObjGet("LDAP://" & $strHostServer & "/" & $fqdn)  ; Retrieve the COM Object for the logged on user
	
	_ADGetGroupMembers($members, $fqdn)
	
	If $members[0] = 0 Then
		Return 0
	EndIf
	
	Do
		If StringInStr($members[$i], "|") Then
			$currgroup = StringLeft($members[$i], StringInStr($members[$i], "|") - 1)
		Else
			$currgroup = $members[$i]
		EndIf
		$currgroup = StringReplace($currgroup, "/", "\/")
		If _ADGetObjectClassFromFQDN($currgroup) = "group" Then
			_ADGetGroupMembers($tempmembers, $currgroup)
			For $j = 1 To $tempmembers[0]
				$exists = 0
				$k = UBound($members)
				Do
					$k -= 1
					If StringInStr($members[$k], $tempmembers[$j]) Then $exists = 1
				Until $exists = 1 Or $k = 0
				If $exists = 0 Then _ArrayAdd($members, $tempmembers[$j] & "|" & $currgroup)
			Next
		EndIf
		$i += 1
	Until $i = UBound($members)
	
	$count = UBound($members)
	;_ArrayInsert($members, 0, $count)
	$members[0] = $count - 1
	
	Return 1
EndFunc   ;==>_ADRecursiveGetGroupMembers

; _ADGetGroupMembers
; Arguments,
; $members - Array that the result will be stored in
; $groupdn - Group (Full Distringuished Name) to retrieve members from
; $sort - optional, default 0 : Set to 1 to sort the array
; Returns an array to $members where $members[0] will be the number of users in the group and
; $members[1] to $members[$members[0]] are the distinguished names of the users

Func _ADGetGroupMembers(ByRef $members, $groupdn, $sort = 0)
	;If _ADObjectExists($group) = 0 Then Return 0
	
	Local $oUsr, $objCommand, $groups

	$objCommand = ObjCreate("ADODB.Command")
	$objCommand.ActiveConnection = $objConnection
	$objCommand.Properties ("Searchscope") = 2

	Dim $members[1]
	$i = 0

	While 1
		$rangemodifier = $i * 1000
		$range = "Range=" & $rangemodifier & "-" & $rangemodifier + 999
		$strCmdText = "<LDAP://" & $strHostServer & "/" & $groupdn & ">;;member;" & $range & ";base"
		$objCommand.CommandText = $strCmdText
		$objRecordSet = $objCommand.Execute
		$membersadd = $objRecordSet.fields (0).Value
		If $membersadd = 0 Then ExitLoop
		ReDim $members[UBound($members) + 1000]
		For $j = $rangemodifier + 1 To $rangemodifier + 1000
			$members[$j] = $membersadd[$j - $rangemodifier - 1]
		Next
		$i += 1
		$objRecordSet.Close
		$objRecordSet = 0
	WEnd

	$rangemodifier = $i * 1000
	$range = "Range=" & $rangemodifier & "-*"
	$strCmdText = "<LDAP://" & $strHostServer & "/" & $groupdn & ">;;member;" & $range & ";base"
	$objCommand.CommandText = $strCmdText
	$objRecordSet = $objCommand.Execute
	$membersadd = $objRecordSet.fields (0).Value

	ReDim $members[UBound($members) + UBound($membersadd) ]

	For $j = $rangemodifier + 1 To $rangemodifier + UBound($membersadd)
		$members[$j] = $membersadd[$j - $rangemodifier - 1]
	Next

	$objRecordSet.Close
	
	$objCommand = 0
	$objRecordSet = 0
	
	$members[0] = UBound($members) - 1
	
	If $sort = 1 Then
		_ArraySort($members, 0, 1)
	EndIf
	
	Return 1
EndFunc   ;==>_ADGetGroupMembers

; _ADGetGroupMemberOf
Func _ADGetGroupMemberOf(ByRef $memberof, $groupdn, $sort = 0)
	Local $oUsr, $objCommand, $groups

	$objCommand = ObjCreate("ADODB.Command")
	$objCommand.ActiveConnection = $objConnection
	$objCommand.Properties ("Searchscope") = 2
	
	Dim $memberof[1]
	$i = 0

	While 1
		$rangemodifier = $i * 1000
		$range = "Range=" & $rangemodifier & "-" & $rangemodifier + 999
		$strCmdText = "<LDAP://" & $strHostServer & "/" & $groupdn & ">;;memberof;" & $range & ";base"
		$objCommand.CommandText = $strCmdText
		$objRecordSet = $objCommand.Execute
		$membersadd = $objRecordSet.fields (0).Value
		If $membersadd = 0 Then ExitLoop
		ReDim $memberof[UBound($memberof) + 1000]
		For $j = $rangemodifier + 1 To $rangemodifier + 1000
			$memberof[$j] = $membersadd[$j - $rangemodifier - 1]
		Next
		$i += 1
		$objRecordSet.Close
	WEnd

	$rangemodifier = $i * 1000
	$range = "Range=" & $rangemodifier & "-*"
	$strCmdText = "<LDAP://" & $strHostServer & "/" & $groupdn & ">;;memberof;" & $range & ";base"
	$objCommand.CommandText = $strCmdText
	$objRecordSet = $objCommand.Execute
	$membersadd = $objRecordSet.fields (0).Value
	ReDim $memberof[UBound($memberof) + UBound($membersadd) ]

	For $j = $rangemodifier + 1 To $rangemodifier + UBound($membersadd)
		$memberof[$j] = $membersadd[$j - $rangemodifier - 1]
	Next
	$objRecordSet.Close
	
	$objCommand = 0
	$objRecordSet = 0
	
	$memberof[0] = UBound($memberof) - 1
	
	If $sort = 1 Then
		_ArraySort($memberof, 0, 1)
	EndIf
	
	Return $memberof[0]
	
EndFunc   ;==>_ADGetGroupMemberOf

; _ADHasFullRights
; Take an object's Full Distringuished Name and a user's SamAccountName
; Returns 1 if User has full rights on the object
; Returns 0 otherwise

Func _ADHasFullRights($object, $user = @UserName)
	Dim $hfr_groups
	If $user = @UserName Then
		$hfr_groups = $loggedonusergroups
	Else
		_ADGetUserGroups($hfr_groups, $user)
	EndIf

	
	$oObject = ObjGet("LDAP://" & $strHostServer & "/" & $object)
	
	If IsObj($oObject) Then
		$security = $oObject.Get ("ntSecurityDescriptor")
		$dacl = $security.DiscretionaryAcl
		For $ace In $dacl
			$trusteearray = StringSplit($ace.Trustee, "\")
			$trusteegroup = $trusteearray[$trusteearray[0]]
			For $i = 0 To UBound($hfr_groups) - 1
				If StringInStr($hfr_groups[$i], "CN=" & $trusteegroup) And $ace.AccessMask = 983551 Then Return 1
			Next
		Next
	EndIf
	
	$oObject = 0
	$security = 0
	$dacl = 0
	
	Return 0
EndFunc   ;==>_ADHasFullRights

; _ADGroupMailEnable
; Takes a group (samaccountname) and enables mail on that group
Func _ADGroupMailEnable($group)
	If _ADObjectExists($group) = 0 Then Return 0
	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $group & ");ADsPath;subtree"
	$objRecordSet = $ObjConnection.Execute ($strQuery)  ; Retrieve the FQDN for the group, if it exists
	
	$ldap_entry = $objRecordSet.fields (0).value
	$oGroup = ObjGet($ldap_entry)  ; Retrieve the COM Object for the object
	
	$oGroup.Put ("grouptype", $ADS_GROUP_TYPE_UNIVERSAL_SECURITY)
	$oGroup.MailEnable
	$OGroup.SetInfo
	
	$oGroup = 0
	
	Return 1
EndFunc   ;==>_ADGroupMailEnable

; _ADUserCreateMailbox
; $user - User to add mailbox to
; $mdbstore - Mailbox storename
; $store - Information store
; $server - Email server
; $admingroup - Administrative group in Exchange
; $domain - Domain name
; $emaildomain - Exchange Server Group name e.g. "My Company"
Func _ADUserCreateMailbox($user, $mdbstore, $store, $server, $admingroup, $domain, $emaildomain)
	If _ADObjectExists($user) = 0 Then Return 0
	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $user & ");ADsPath;subtree"
	$objRecordSet = $ObjConnection.Execute ($strQuery)  ; Retrieve the FQDN for the group, if it exists
	
	$ldap_entry = $objRecordSet.fields (0).value
	$oUser = ObjGet($ldap_entry)  ; Retrieve the COM Object for the object
	
	If $oUser.HomeMDB <> "" Then Return 0
	
	$mailboxpath = "LDAP://CN="
	$mailboxpath = $mailboxpath & $mdbstore
	$mailboxpath = $mailboxpath & ",CN="
	$mailboxpath = $mailboxpath & $store
	$mailboxpath = $mailboxpath & ",CN=InformationStore"
	$mailboxpath = $mailboxpath & ",CN="
	$mailboxpath = $mailboxpath & $server
	$mailboxpath = $mailboxpath & ",CN=Servers,CN="
	$mailboxpath = $mailboxpath & $admingroup
	$mailboxpath = $mailboxpath & ",CN=Administrative Groups,CN=" & $emaildomain & ",CN=Microsoft Exchange,CN=Services,CN=Configuration,"
	$mailboxpath = $mailboxpath & $domain
	
	$oUser.CreateMailbox ($mailboxpath)
	$oUser.SetInfo
	
	$oUser = 0
	
	Return 1
EndFunc   ;==>_ADUserCreateMailbox

; _ADUserDeleteMailbox
; Deletes a user's mailbox
Func _ADUserDeleteMailbox($user)
	If _ADObjectExists($user) = 0 Then Return 0

	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $user & ");ADsPath;subtree"
	$objRecordSet = $ObjConnection.Execute ($strQuery)  ; Retrieve the FQDN for the group, if it exists
	
	$ldap_entry = $objRecordSet.fields (0).value
	$oUser = ObjGet($ldap_entry)  ; Retrieve the COM Object for the object
	
	If $oUser.HomeMDB = "" Then Return 0
	
	$oUser.DeleteMailbox
	$oUser.SetInfo
	
	$oUser = 0
	
	Return 1
EndFunc   ;==>_ADUserDeleteMailbox

; _ADGetObjectsInOU
; Returns an array of the objects in an OU
; $ou : The OU to retrieve from
; $filter : optional, default "name'*'". An additional LDAP filter if required.
; $searchscope : optional, default 2. 0 = base, 1 = one-level, 2 = sub-tree
; $datatoretrieve : optional, default "Name". A comma-seperated list of values to retrieve. More than one value will create
; a 2-dimensional array, array[0][0] will contain the number of items returned, which start at array[1][0]

Func _ADGetObjectsInOU(ByRef $ObjectArray, $ou, $filter = "(name=*)", $searchscope = 2, $datatoretrieve = "sAMAccountName", $sortby = "sAMAccountName")
	Local $objRecordSet
	$objCommand = ObjCreate("ADODB.Command")
	$objCommand.ActiveConnection = $objConnection
	$objCommand.Properties ("Page Size") = 256
	$objCommand.Properties ("Searchscope") = $searchscope
	$objCommand.Properties ("TimeOut") = 20
	
	$strCmdText = "<LDAP://" & $strHostServer & "/" & $ou & ">;" & $filter & ";" & $datatoretrieve & ";subtree"
	
	$objCommand.CommandText = $strCmdText
	$objRecordSet = $objCommand.Execute

	$recordcount = $objRecordSet.RecordCount
	If $recordcount = 0 Then
		$objCommand = 0
		$objRecordSet = 0
		Return 0
	EndIf
	
	If StringInStr($datatoretrieve, ",") Then

		$dtrArray = StringSplit($datatoretrieve, ",")
		
		Dim $ObjectArray[$recordcount + 1][$dtrArray[0]]
		
		$ObjectArray[0][0] = $recordcount
		$ObjectArray[0][1] = $dtrArray[0]
		
		$count = 1
		$objRecordSet.MoveFirst
		Do
			For $i = 1 To $dtrArray[0]
				$ObjectArray[$count][$i - 1] = $objRecordSet.Fields ($dtrArray[$i]).Value
			Next
			$objRecordSet.MoveNext
			$count += 1
		Until $objRecordSet.EOF
	Else
		Dim $ObjectArray[$recordcount + 1]
		$ObjectArray[0] = UBound($ObjectArray) - 1
		If $ObjectArray[0] = 0 Then
			$ObjectArray = 0
			Return 0
		Else
			$count = 1
			$objRecordSet.MoveFirst
			Do
				$ObjectArray[$count] = $objRecordSet.Fields ($datatoretrieve).Value
				$objRecordSet.MoveNext
				$count += 1
			Until $objRecordSet.EOF
		EndIf
	EndIf
	
	$objCommand = 0
	$objRecordSet = 0
	
	Return 1
	
EndFunc   ;==>_ADGetObjectsInOU

; _ADDNToSamAccountName
; Takes a FQDN and returns the SamID of the account

Func _ADDNToSamAccountName($fqdn)
	$obj = ObjGet("LDAP://" & $strHostServer & "/" & $fqdn)
	If $obj = 0 Then
		MsgBox(262144, "", "Failed to retrieve AD object")
		Return 0
	EndIf
	
	$samname = $obj.sAMAccountName
	
	$obj = 0
	
	Return $samname
EndFunc   ;==>_ADDNToSamAccountName

; _ADSamAccountNameToFQDN
; Takes a SamID and returns the FQDN of the account

Func _ADSamAccountNameToFQDN($samname)
	
	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $samname & ");distinguishedName;subtree"
	$objRecordSet = $objConnection.Execute ($strQuery)
	
	If $objRecordSet.RecordCount = 1 Then
		$fqdn = $objRecordSet.fields (0).value
		$objRecordSet = 0
		Return StringReplace($fqdn, "/", "\/")
	Else
		$objRecordSet = 0
		Return ""
	EndIf
EndFunc   ;==>_ADSamAccountNameToFQDN

; _ADDNToDisplayName
; Returns the Display Name of an FQDN
Func _ADDNToDisplayName($fqdn)
	$objItem = ObjGet("LDAP://" & $strHostServer & "/" & $fqdn)
	$name = $objItem.name
	$objItem = 0
	Return $name
EndFunc   ;==>_ADDNToDisplayName

; _ADCreateObject
; $objectou = OU to create the group in. Form is "sampleou=ou, sampleparent=ou, sampledomain1=dc, sampledomain2=dc, sampledomain3=dc"
; $object = Object name, form is SamAccountName without leading 'CN='
; $type = Type of object to create

Func _ADCreateObject($objectou, $object, $type)
	If _ADObjectExists($object) Then Return 0
	
	If StringLeft($object, 3) <> "CN=" Then
		$object = "CN=" & $object
	EndIf

	$ObjOU = ObjGet("LDAP://" & $strHostServer & "/" & $objectou)
	$ObjADObj = $ObjOU.Create ($type, $object)
	
	$ObjADObj.Put ("sAMAccountName", StringTrimLeft($object, 3))
	If $type = "Computer" Then
		$ObjADObj.Put ("UserAccountControl", BitOR(0x0020, 0x1000))
	EndIf
	
	$ObjADobj.SetInfo
	
	$ObjADObj = 0
	
	Return 1
EndFunc   ;==>_ADCreateObject

; _ADCreateComputer
; $strComputer = Name of the computer object to create
; $computerOU = Full DN of the OU to create the computer in
; $strComputerUser = User or group that will be allowed to add the computer to the domain (SamAccountName)
Func _ADCreateComputer($strComputer, $computerOU, $strComputerUser)

	$objContainer = ObjGet("LDAP://" & $strHostServer & "/" & $computerOU)

	$objComputer = $objContainer.Create ("Computer", "cn=" & $strComputer)
	$objComputer.Put ("sAMAccountName", $strComputer & "$")
	$objComputer.Put ("userAccountControl", BitOR($ADS_UF_PASSWD_NOTREQD, $ADS_UF_WORKSTATION_TRUST_ACCOUNT))
	$objComputer.SetInfo

	$objSecurityDescriptor = $objComputer.Get ("ntSecurityDescriptor")
	$objDACL = $objSecurityDescriptor.DiscretionaryAcl

	$objACE1 = ObjCreate("AccessControlEntry")
	$objACE1.Trustee = $strComputerUser
	$objACE1.AccessMask = $ADS_RIGHT_GENERIC_READ
	$objACE1.AceFlags = 0
	$objACE1.AceType = $ADS_ACETYPE_ACCESS_ALLOWED

	$objACE2 = ObjCreate("AccessControlEntry")
	$objACE2.Trustee = $strComputerUser
	$objACE2.AccessMask = $ADS_RIGHT_DS_CONTROL_ACCESS
	$objACE2.AceFlags = 0
	$objACE2.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE2.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE2.ObjectType = $ALLOWED_TO_AUTHENTICATE

	$objACE3 = ObjCreate("AccessControlEntry")
	$objACE3.Trustee = $strComputerUser
	$objACE3.AccessMask = $ADS_RIGHT_DS_CONTROL_ACCESS
	$objACE3.AceFlags = 0
	$objACE3.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE3.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE3.ObjectType = $RECEIVE_AS

	$objACE4 = ObjCreate("AccessControlEntry")
	$objACE4.Trustee = $strComputerUser
	$objACE4.AccessMask = $ADS_RIGHT_DS_CONTROL_ACCESS
	$objACE4.AceFlags = 0
	$objACE4.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE4.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE4.ObjectType = $SEND_AS

	$objACE5 = ObjCreate("AccessControlEntry")
	$objACE5.Trustee = $strComputerUser
	$objACE5.AccessMask = $ADS_RIGHT_DS_CONTROL_ACCESS
	$objACE5.AceFlags = 0
	$objACE5.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE5.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE5.ObjectType = $USER_CHANGE_PASSWORD

	$objACE6 = ObjCreate("AccessControlEntry")
	$objACE6.Trustee = $strComputerUser
	$objACE6.AccessMask = $ADS_RIGHT_DS_CONTROL_ACCESS
	$objACE6.AceFlags = 0
	$objACE6.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE6.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE6.ObjectType = $USER_FORCE_CHANGE_PASSWORD

	$objACE7 = ObjCreate("AccessControlEntry")
	$objACE7.Trustee = $strComputerUser
	$objACE7.AccessMask = $ADS_RIGHT_DS_WRITE_PROP
	$objACE7.AceFlags = 0
	$objACE7.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE7.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE7.ObjectType = $USER_ACCOUNT_RESTRICTIONS

	$objACE8 = ObjCreate("AccessControlEntry")
	$objACE8.Trustee = $strComputerUser
	$objACE8.AccessMask = $ADS_RIGHT_DS_SELF
	$objACE8.AceFlags = 0
	$objACE8.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE8.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE8.ObjectType = $VALIDATED_DNS_HOST_NAME

	$objACE9 = ObjCreate("AccessControlEntry")
	$objACE9.Trustee = $strComputerUser
	$objACE9.AccessMask = $ADS_RIGHT_DS_SELF
	$objACE9.AceFlags = 0
	$objACE9.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE9.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objACE9.ObjectType = $VALIDATED_SPN

	$objDACL.AddAce ($objACE1)
	$objDACL.AddAce ($objACE2)
	$objDACL.AddAce ($objACE3)
	$objDACL.AddAce ($objACE4)
	$objDACL.AddAce ($objACE5)
	$objDACL.AddAce ($objACE6)
	$objDACL.AddAce ($objACE7)
	$objDACL.AddAce ($objACE8)
	$objDACL.AddAce ($objACE9)

	$objSecurityDescriptor.DiscretionaryAcl = $objDACL
	$objComputer.Put ("ntSecurityDescriptor", $objSecurityDescriptor)
	$objComputer.SetInfo

EndFunc   ;==>_ADCreateComputer

; _ADDeleteObject
; $object = DisplayName of object to delete. $type="user" or "group".

Func _ADDeleteObject($ou, $object, $type)
	If StringLeft($object, 3) <> "CN=" Then
		$object = "CN=" & StringReplace($object, ",", "\,")
	EndIf
	$ObjOU = ObjGet("LDAP://" & $strHostServer & "/" & $ou)
	$objOU.Delete ($type, $object)
	$ObjOU = 0
	Return 1
EndFunc   ;==>_ADDeleteObject

; _ADGetObjectClass
; Returns the Class of an object. Returns 0 if the object does not exist.
Func _ADGetObjectClass($object)
	If _ADObjectExists($object) = 0 Then Return 0

	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $object & ");ADsPath;subtree"
	$objRecordSet = $objConnection.Execute ($strQuery)  ; Retrieve the FQDN for the object
	$ldap_entry = $objRecordSet.fields (0).value
	$oObject = ObjGet($ldap_entry)  ; Retrieve the COM Object for the object
	
	$class = $oObject.Class
	$oObject = 0
	
	Return $class
	
EndFunc   ;==>_ADGetObjectClass

; _ADGetObjectClassFromFQDN
; Returns the Class of an objectfrom an objects FQDN. Returns 0 if the object does not exist.
Func _ADGetObjectClassFromFQDN($object)
	$oObject = ObjGet("LDAP://" & $strHostServer & "/" & $object)  ; Retrieve the COM Object for the object
	If IsObj($oObject) = 0 Then Return 0
	$class = $oObject.Class
	$oObject = 0
	Return $class
EndFunc   ;==>_ADGetObjectClassFromFQDN

; _ADGetObjectAttribute
; Retrieves the specified (single-value) attribute for the given SamAccountName
; Returns 0 if the object does not exist, the attribute does not exist for that
; object or if the value is multi-string.
; Otherwise returns the result
Func _ADGetObjectAttribute($object, $attribute)
	If _ADObjectExists($object) = 0 Then Return 0

	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $object & ");ADsPath;subtree"
	$objRecordSet = $objConnection.Execute ($strQuery)  ; Retrieve the FQDN for the object
	$ldap_entry = $objRecordSet.fields (0).value
	$oObject = ObjGet($ldap_entry)  ; Retrieve the COM Object for the object
	$result = $oObject.Get ($attribute)
	$oObject.PurgePropertyList
	$oObject = 0
	If $result = "" Then
		Return ""
	Else
		Return $result
	EndIf
	
EndFunc   ;==>_ADGetObjectAttribute

; _ADListDomainControllers
; Retrieves the names of all domain controllers in the current Domain
Func _ADListDomainControllers(ByRef $DCList)
	$objCommand = ObjCreate("ADODB.Command")
	$objCommand.ActiveConnection = $objConnection
	$objCommand.Properties ("Page Size") = 1000
	$objCommand.Properties ("Searchscope") = 2
	
	$objCommand.CommandText = "Select distinguishedName FROM 'LDAP://" & $strHostServer & "/ou=Domain Controllers," & $strDNSDomain & "' WHERE objectclass='computer'"
	
	$objRecordSet = $objCommand.Execute
	Dim $DCList[$objRecordSet.RecordCount + 1]
	$objRecordSet.MoveFirst
	
	Do
		$DCList[0] += 1
		
		$objCommand.CommandText = "<LDAP://" & $strHostServer & "/" & $objRecordSet.Fields ("distinguishedName").Value & ">;;serverReferenceBL;Range=0-*;base"
		$objRecSet2 = $objCommand.Execute
		
		$objRecSet2.MoveFirst
		Do
			$temparray = $objRecSet2.Fields (0).Value
			$DCList[$DCList[0]] = $temparray[0]
			$objRecSet2.MoveNext
		Until $objRecSet2.EOF
		
		$objRecordSet.MoveNext
	Until $objRecordSet.EOF
	
	$objCommand = 0
	$objRecordSet = 0
	Return
EndFunc   ;==>_ADListDomainControllers

; _ADOUObjectNames
; A faster call for returning the Display Name of objects in a given OU
Func _ADOUObjectNames(ByRef $objects, $ou, $filter = 0)
	Local $i
	
	$ObjOU = ObjGet("LDAP://" & $strHostServer & "/" & $ou)

	If $filter <> 0 Then $objOU.Filter = $filter

	Dim $tempobjects[100000]
	
	$i = 0

	For $object In $ObjOU
		$tempobjects[$i] = $object.name
		$i += 1
	Next
	
	If $i = 0 Then
		$objects = 0
		$ObjOU = 0
		Return
	EndIf
	
	Dim $objects[$i + 1]
	$objects[0] = $i
	
	For $j = 1 To $i
		$tempstr = $tempobjects[$j - 1]
		$tempstr = StringTrimLeft($tempstr, 3)
		$objects[$j] = StringReplace($tempstr, "\,", ",")
	Next
	$ObjOU = 0
EndFunc   ;==>_ADOUObjectNames

; _ADGroupManagerCanModify : 
; Returns 1 if the manager of the group can modify the member list, 0 if not, -1 if there is no manager assigned
; Takes full DNs to the group and user

Func _ADGroupManagerCanModify($groupdn)
	$ObjGroup = ObjGet("LDAP://" & $strHostServer & "/" & $groupdn)
	$managedBy = $objGroup.Get ("managedBy")
	
	If $managedBy = "" Then Return -1
	
	$ObjUser = ObjGet("LDAP://" & $strHostServer & "/" & $managedBy)
	
	$arrUserDN = StringSplit($managedBy, "DC=", 1)
	$strDomain = StringTrimRight($arrUserDN[2], 1)
	$strSamAccountName = $objUser.Get ("sAMAccountName")
	
	$objNTSecurityDescriptor = $objGroup.Get ("ntSecurityDescriptor")
	$objDiscretionaryAcl = $objNtSecurityDescriptor.DiscretionaryAcl
	
	$blnMatch = False
	For $objAce In $objDiscretionaryAcl
		If StringLower($objAce.Trustee) = StringLower($strDomain & "\" & $strSamAccountName) And _
				$objAce.ObjectType = $Member_SchemaIDGuid And _
				$objAce.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT And _
				BitAND($objAce.AccessMask, $ADS_RIGHT_DS_WRITE_PROP) = $ADS_RIGHT_DS_WRITE_PROP Then $blnMatch = True
	Next
	
	$ObjGroup = 0
	$ObjUser = 0
	$objNTSecurityDescriptor = 0
	$objDiscretionaryAcl = 0
	
	If $blnMatch Then
		Return 1
	Else
		Return 0
	EndIf
EndFunc   ;==>_ADGroupManagerCanModify

; _ADSetGroupManagerCanModify - Sets the Group manager to be able to modify the member list
; Takes a group DN
; Returns
; -1 if no manager is assigned
; 0 if the manager can already modify the list
; 1 if successful


Func _ADSetGroupManagerCanModify($groupdn)
	If _ADGroupManagerCanModify($groupdn) = 1 Then Return 0
	
	$ObjGroup = ObjGet("LDAP://" & $strHostServer & "/" & $groupdn)
	$managedBy = $objGroup.Get ("managedBy")
	
	If $managedBy = "" Then Return -1
	
	$ObjUser = ObjGet("LDAP://" & $strHostServer & "/" & $managedBy)
	
	$arrUserDN = StringSplit($managedBy, "DC=", 1)
	$strDomain = StringTrimRight($arrUserDN[2], 1)
	$strSamAccountName = $objUser.Get ("sAMAccountName")
	
	$objNTSecurityDescriptor = $objGroup.Get ("ntSecurityDescriptor")
	
	$objNtSecurityDescriptor.Owner = $strDomain & "\" & @UserName
	$objDiscretionaryAcl = $objNtSecurityDescriptor.DiscretionaryAcl
	
	$objAce = ObjCreate("AccessControlEntry")
	$objAce.Trustee = $strDomain & "\" & $strSamAccountName
	$objAce.AccessMask = $ADS_RIGHT_DS_WRITE_PROP
	$objACE.AceFlags = 0
	$objAce.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$objACE.Flags = $ADS_FLAG_OBJECT_TYPE_PRESENT
	$objAce.ObjectType = $Member_SchemaIDGuid
	
	$objDiscretionaryAcl.AddAce ($objAce)
	$objNtSecurityDescriptor.DiscretionaryAcl = $objDiscretionaryAcl
	$objGroup.Put ("ntSecurityDescriptor", $objNTSecurityDescriptor)
	$ObjGroup.SetInfo
	If @error = 3 Then
		MsgBox(262144, "Error", "An error occurred assigning permissions on the group.")
		$objGroup = 0
		$objDiscretionaryAcl = 0
		$objNtSecurityDescriptor = 0
		$objAce = 0
		Return 0
	EndIf
	$objGroup = 0
		$objDiscretionaryAcl = 0
		$objNtSecurityDescriptor = 0
		$objAce = 0
	Return 1
EndFunc   ;==>_ADSetGroupManagerCanModify

; _ADGroupAssignManager
; Takes a group DN and a User DN, assigns the user as the manager of the group

Func _ADGroupAssignManager($groupdn, $userdn)
	$ObjGroup = ObjGet("LDAP://" & $strHostServer & "/" & $groupdn)
	
	$objGroup.Put ("managedBy", $userdn)
	$ObjGroup.SetInfo
	$objGroup = 0
	Return 1
EndFunc   ;==>_ADGroupAssignManager

; _ADGroupRemoveManager
; Takes a group DN and deletes the group's manager
; arg $option -- 0 (default) removes the manager; 1 only removes manager's modify permission

Func _ADGroupRemoveManager($groupdn, $option = 0)
	$ObjGroup = ObjGet("LDAP://" & $strHostServer & "/" & $groupdn)
	
	$managedBy = $objGroup.Get ("managedBy")
	
	If $managedBy = "" Then Return -1
	
	$ObjUser = ObjGet("LDAP://" & $strHostServer & "/" & $managedBy)
	
	$arrUserDN = StringSplit($managedBy, "DC=", 1)
	$strDomain = StringTrimRight($arrUserDN[2], 1)
	$strSamAccountName = $objUser.Get ("sAMAccountName")
	
	$objNTSecurityDescriptor = $objGroup.Get ("ntSecurityDescriptor")
	
	$objNtSecurityDescriptor.Owner = $strDomain & "\" & @UserName
	$objDiscretionaryAcl = $objNtSecurityDescriptor.DiscretionaryAcl
	$objTempAcl = ObjCreate("AccessControlList")
	
	$blnMatch = False
	For $objAce In $objDiscretionaryAcl
		If StringLower($objAce.Trustee) = StringLower($strDomain & "\" & $strSamAccountName) And _
				$objAce.ObjectType = $Member_SchemaIDGuid And _
				$objAce.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT And _
				$objAce.AccessMask = $ADS_RIGHT_DS_WRITE_PROP Then _
				$objDiscretionaryAcl.RemoveAce ($objAce)
	Next
	
	$objNtSecurityDescriptor.DiscretionaryAcl = $objDiscretionaryAcl
	$objGroup.Put ("ntSecurityDescriptor", $objNTSecurityDescriptor)
	If Not $option Then $objGroup.PutEx (1, "managedBy", 0)
	$ObjGroup.SetInfo
	
	$ObjGroup = 0
	$ObjUser = 0
	$objNTSecurityDescriptor = 0
	$objDiscretionaryAcl = 0
	
EndFunc   ;==>_ADGroupRemoveManager

; _ADEnableAccount
; Takes a DN of an object and enables it if the user has permissions
; Returns 1 if successful, @error otherwise

Func _ADEnableAccount($fqdn)
	$objAccount = ObjGet("LDAP://" & $strHostServer & "/" & $fqdn)
	
	$objAccount.AccountDisabled = False
	$objAccount.SetInfo
	$loc_error = @error
	$objAccount = 0
	If $loc_error = 0 Then
		Return 1
	Else
		Return $loc_error
	EndIf
EndFunc   ;==>_ADEnableAccount

; _ADDisableAccount
; Takes a DN of an object and disables it if the user has permissions
; Returns 1 if successful, @error otherwise

Func _ADDisableAccount($fqdn)
	$objAccount = ObjGet("LDAP://" & $strHostServer & "/" & $fqdn)
	
	$intUAC = $objAccount.Get ("userAccountControl")
	
	$objAccount.Put ("userAccountControl", BitOR($intUAC, $ADS_UF_ACCOUNTDISABLE))
	$objAccount.SetInfo
	$loc_error = @error
	$objAccount = 0
	If $loc_error = 0 Then
		Return 1
	Else
		Return $loc_error
	EndIf
EndFunc   ;==>_ADDisableAccount

; _ADGetLastLoginDate
; Takes a SamAccountName of a user account and returns the .lastlogin information for the current DC
Func _ADGetLastLoginDate($user)
	If _ADObjectExists($user) = 0 Then Return 0

	$strQuery = "<LDAP://" & $strHostServer & "/" & $strDNSDomain & ">;(sAMAccountName=" & $user & ");ADsPath;subtree"
	$objRecordSet = $objConnection.Execute ($strQuery)  ; Retrieve the FQDN for the object
	$ldap_entry = $objRecordSet.fields (0).value
	$oObject = ObjGet($ldap_entry)  ; Retrieve the COM Object for the object
	$result = $oObject.LastLogin
	$oObject.PurgePropertyList
	
	If $result = "" Then
		Return ""
	Else
		Return $result
	EndIf
EndFunc   ;==>_ADGetLastLoginDate

; _ADAddAccountToMailboxRights
; Takes $mailbox as FQDN and $accountsam as "domain\username"
; Adds Full mailbox rights and 'send as' permission for $accountsam on $mailbox.
; Returns 2 if account already has rights, otherwise returns @error (should be 0 for success, 1 for failure)
Func _ADAddAccountToMailboxRights($mailbox, $accountsam)
	$obj_mailbox = ObjGet("LDAP://" & $strhostserver & "/" & $mailbox)
	If Not IsObj($obj_mailbox) Then
		;MsgBox(0, "Error", "Mailbox was not a FQDN or was not found.")
		SetError(3)
		Return
	EndIf
	$obj_mailboxsecurity = $obj_mailbox.MailboxRights
	$mailbox_dacl = $obj_mailboxsecurity.DiscretionaryAcl
	
	For $ace In $mailbox_dacl
		If $ace.trustee = $accountsam Then
			;MsgBox(0, "", "User already has full rights to mailbox")
			SetError(2)
			Return @error
		EndIf
	Next
	
	$obj_ace1 = ObjCreate("AccessControlEntry")
	$obj_ace1.Trustee = $accountsam
	$obj_ace1.AccessMask = 1
	$obj_ace1.AceType = 0
	$obj_ace1.AceFlags = 2
	$obj_ace1.ObjectType = 0
	$obj_ace1.InheritedObjectType = 0
	$obj_ace1.Flags = 0
	
	$mailbox_dacl.AddAce ($obj_ace1)
	
	$obj_mailboxsecurity.DiscretionaryAcl = $mailbox_dacl
	$obj_mailbox.MailboxRights = $obj_mailboxsecurity
	
	$obj_ntsecurity = $obj_mailbox.Get ("ntSecurityDescriptor")
	$ntsecurity_dacl = $obj_ntsecurity.DiscretionaryAcl
	
	$obj_ace2 = ObjCreate("AccessControlEntry")
	$obj_ace2.Trustee = $accountsam
	$obj_ace2.AccessMask = $ADS_RIGHT_DS_CONTROL_ACCESS
	$obj_ace2.AceFlags = 0
	$obj_ace2.AceType = $ADS_ACETYPE_ACCESS_ALLOWED_OBJECT
	$obj_ace2.Flags = 1 ;$ADS_FLAG_OBJECT_TYPE_PRESENT
	$obj_ace2.ObjectType = $SEND_AS
	
	$ntsecurity_dacl.AddAce ($obj_ace2)
	
	$obj_ntsecurity.DiscretionaryAcl = $ntsecurity_dacl
	$obj_mailbox.Put ("ntSecurityDescriptor", $obj_ntsecurity)
	$obj_mailbox.SetOption ($ADS_OPTION_SECURITY_MASK, $ADS_SECURITY_INFO_DACL)
	$obj_mailbox.SetInfo
	
	Return @error
EndFunc   ;==>_ADAddAccountToMailboxRights

Func _ADRemoveMailboxRights($mailbox, $accountsam)
	$obj_mailbox = ObjGet("LDAP://" & $strhostserver & "/" & $mailbox)
	If Not IsObj($obj_mailbox) Then
		;MsgBox(0, "Error", "Mailbox was not a FQDN or was not found.")
		SetError(3)
		Return
	EndIf
	$obj_mailboxsecurity = $obj_mailbox.MailboxRights
	$mailbox_dacl = $obj_mailboxsecurity.DiscretionaryAcl
	
	For $ace In $mailbox_dacl
		If $ace.trustee = $accountsam Then
			$mailbox_dacl.RemoveAce ($ace)
		EndIf
	Next
	
	$obj_mailboxsecurity.DiscretionaryAcl = $mailbox_dacl
	$obj_mailbox.MailboxRights = $obj_mailboxsecurity
	
	$obj_ntsecurity = $obj_mailbox.Get ("ntSecurityDescriptor")
	$ntsecurity_dacl = $obj_ntsecurity.DiscretionaryAcl
	
	For $ace In $ntsecurity_dacl
		If $ace.trustee = $accountsam Then
			$ntsecurity_dacl.RemoveAce ($ace)
		EndIf
	Next
	
	$obj_ntsecurity.DiscretionaryAcl = $ntsecurity_dacl
	$obj_mailbox.Put ("ntSecurityDescriptor", $obj_ntsecurity)
	$obj_mailbox.SetOption ($ADS_OPTION_SECURITY_MASK, $ADS_SECURITY_INFO_DACL)
	$obj_mailbox.SetInfo
	Return @error
	
EndFunc
		
; _ADGetMailboxPerms
; Takes a mailbox and an array.
; Returns all SamIDs for non-inherited ACEs on the mailbox (stored in the specified array as SamIds)
; Can be filtered on an accessmask value (default = 1, full permissions)
Func _ADGetMailboxPerms($mailbox, ByRef $outputarray, $filter = 1)
	$obj_mailbox = ObjGet("LDAP://" & $strhostserver & "/" & $mailbox)
	If Not IsObj($obj_mailbox) Then
		;MsgBox(0, "Error", "Mailbox was not a FQDN or was not found.")
		SetError(3)
		Return
	EndIf
	$obj_mailboxsecurity = $obj_mailbox.MailboxRights
	$mailbox_dacl = $obj_mailboxsecurity.DiscretionaryAcl
	
	For $ace In $mailbox_dacl
		If $ace.AceFlags = 2 Then
			If BitAND($ace.AccessMask, 1) = 1 And $ace.AceType = 0 Then
				_ArrayAdd($outputarray, $ace.Trustee)
			EndIf
		EndIf
	Next
EndFunc

; _ADFixSpecialChars
; Takes text and returns either corrected (with 'escaped' chars) or uncorrected (removes 'escapes' for chars) text
; $text = text to fix/unfix
; $option = 0 (default) inserts the escape char, 1 removes it
Func _ADFixSpecialChars($text, $option = 0)
	If $option = 0 Then
		$text = StringReplace($text,"#","\#")
		$text = StringReplace($text,",","\,")
		$text = StringReplace($text,"/","\/")
		Return $text
	Else
		$text = StringReplace($text,"\#","#")
		$text = StringReplace($text,"\,",",")
		$text = StringReplace($text,"\/","/")
		Return $text
	EndIf
EndFunc
