A customer of ours recently wanted to do an audit of all the ACL changes in all their databases. Luckily, we have an ACL History LotusScript class that is able to read the ACL History (go into the ACL of a database, then go to the Log tab). There is a new NotesDatabase property with Notes/Domino 6 called ACLActivityLog that should return this same information, but for those of you who need this information (the customer hadn't upgraded to Notes/Domino 6 yet) here's how you can get it in an R4 or R5 environment.
This uses a custom class to make things easier. Custom classes go into the (Declarations) area, so create a new agent or script library and go into the (Declarations) area. Add in this code:
Declare Private Function W32_NSFDbOpen Lib "nnotes" Alias "NSFDbOpen" (Byval PathName As Lmbcs String, _
hDB As Long) As Integer
Declare Private Function W32_NSFDbReadACL Lib "nnotes" Alias "NSFDbReadACL" (Byval hDB As Long, _
hACL As Integer) As Integer
Declare Private Function W32_ACLGetHistory Lib "nnotes" Alias "ACLGetHistory" (Byval hACL As Integer, _
hHistory As Integer, HistoryCount As Integer) As Integer
Declare Private Function W32_NSFDbClose Lib "nnotes" Alias "NSFDbClose" (Byval hDB As Integer) As Integer
Declare Private Function W32_OSMemFree Lib "nnotes" Alias "OSMemFree" (Byval Handle As Integer) As Integer
Declare Private Function W32_OSLockObject Lib "nnotes" Alias "OSLockObject" _
(Byval nHandle As Long) As Long
Declare Private Function W32_OSUnlockObject Lib "nnotes" Alias "OSUnlockObject" _
(Byval nHandle As Long) As Integer
Declare Private Sub W32_RtlMoveMemory Lib "kernel32" Alias "RtlMoveMemory" (Byval pDest As Lmbcs String, _
Byval pSource As Long, Byval dwLength As Long)
Class ACLHistory
Private db As notesDatabase
Public Sub new(inputDb As notesDatabase)
Set Me.db = inputDb
End Sub
Public Function history As Variant
Dim path As String
Dim rc As Integer
Dim hDb As Long
Dim hLock As Long
Dim hHistory As Integer
Dim hACL As Integer
Dim nCount As Integer
Dim i As Integer
Dim char As String
Dim retVal As Variant
If Me.db.server = "" Then
path = Me.db.filePath
Else
path = Me.db.server & "!!" & Me.db.filePath
End If
rc = W32_NSFDbOpen(path, hDb)
If rc <> 0 Then
history = "" ' Return a value that is not an array in any error situation
Exit Function
End If
rc = W32_NSFDbReadACL(hDb, hACL)
If rc <> 0 Then
Call W32_NSFDbClose(hDb)
history = "" ' Return a value that is not an array in any error situation
Exit Function
End If
rc = W32_ACLGetHistory(hACL, hHistory, nCount)
If rc <> 0 Or nCount = 0 Then
Call W32_NSFDbClose(hDb)
history = "" ' Return a value that is not an array in any error situation
Exit Function
End If
hLock = W32_OSLockObject(hHistory)
Redim retVal(nCount-1) As String
char = String$(1,0) ' Set up initially as character zero for the API call to properly return something
For i = 0 To nCount-1
retVal(i) = ""
Call W32_RtlMoveMemory(char, hLock, 1) ' Read the first (or next) character
While char = Chr$(0) ' There are two char 0's at the end of each entry - skip over the second one
hLock = hLock + 1
Call W32_RtlMoveMemory(char, hLock, 1)
Wend
While char <> Chr$(0) ' extract history one character at a time using Win32 API call until we get a full word
retVal(i) = retVal(i) & char
hLock = hLock + 1
Call W32_RtlMoveMemory(char, hLock, 1)
Wend
Next
Call W32_OSUnlockObject(hHistory)
Call W32_OSMemFree(hHistory)
Call W32_OSMemFree(hACL)
Call W32_NSFDbClose(hDb)
history = retVal
End Function ' Ends the "History" Function
End Class ' Ends the "ACL History" class
This is one of those "black box" classes that you don't really need to know how it works. It uses the C API to grab the listory information and build an array of strings with the history. To use the custom class, you create a new ACLHistory object and pass in the database to be evaluated. Then you get the history property of the ACLHistory object and you can process this history to print out the history or scan for certain criteria (dates or user names).
For a sample agent to verify the custom class, here is some code:
Sub Initialize
Dim s As New NotesSession
Dim db As NotesDatabase
Dim acl As ACLHistory
Dim history As Variant
Dim i As Integer
Dim msg As String
Set db = s.CurrentDatabase
Set acl = New ACLHistory(db)
msg = ""
history = acl.history
For i = Lbound(history) To Ubound(history)
msg = msg & Chr$(10) & history(i)
Next
Msgbox "ACL History = " & msg, 64, db.title & " ACL History"
End Sub
This agent just builds a message box containing the entire history so you can verify the code.
This uses a custom class to make things easier. Custom classes go into the (Declarations) area, so create a new agent or script library and go into the (Declarations) area. Add in this code:
Declare Private Function W32_NSFDbOpen Lib "nnotes" Alias "NSFDbOpen" (Byval PathName As Lmbcs String, _
hDB As Long) As Integer
Declare Private Function W32_NSFDbReadACL Lib "nnotes" Alias "NSFDbReadACL" (Byval hDB As Long, _
hACL As Integer) As Integer
Declare Private Function W32_ACLGetHistory Lib "nnotes" Alias "ACLGetHistory" (Byval hACL As Integer, _
hHistory As Integer, HistoryCount As Integer) As Integer
Declare Private Function W32_NSFDbClose Lib "nnotes" Alias "NSFDbClose" (Byval hDB As Integer) As Integer
Declare Private Function W32_OSMemFree Lib "nnotes" Alias "OSMemFree" (Byval Handle As Integer) As Integer
Declare Private Function W32_OSLockObject Lib "nnotes" Alias "OSLockObject" _
(Byval nHandle As Long) As Long
Declare Private Function W32_OSUnlockObject Lib "nnotes" Alias "OSUnlockObject" _
(Byval nHandle As Long) As Integer
Declare Private Sub W32_RtlMoveMemory Lib "kernel32" Alias "RtlMoveMemory" (Byval pDest As Lmbcs String, _
Byval pSource As Long, Byval dwLength As Long)
Class ACLHistory
Private db As notesDatabase
Public Sub new(inputDb As notesDatabase)
Set Me.db = inputDb
End Sub
Public Function history As Variant
Dim path As String
Dim rc As Integer
Dim hDb As Long
Dim hLock As Long
Dim hHistory As Integer
Dim hACL As Integer
Dim nCount As Integer
Dim i As Integer
Dim char As String
Dim retVal As Variant
If Me.db.server = "" Then
path = Me.db.filePath
Else
path = Me.db.server & "!!" & Me.db.filePath
End If
rc = W32_NSFDbOpen(path, hDb)
If rc <> 0 Then
history = "" ' Return a value that is not an array in any error situation
Exit Function
End If
rc = W32_NSFDbReadACL(hDb, hACL)
If rc <> 0 Then
Call W32_NSFDbClose(hDb)
history = "" ' Return a value that is not an array in any error situation
Exit Function
End If
rc = W32_ACLGetHistory(hACL, hHistory, nCount)
If rc <> 0 Or nCount = 0 Then
Call W32_NSFDbClose(hDb)
history = "" ' Return a value that is not an array in any error situation
Exit Function
End If
hLock = W32_OSLockObject(hHistory)
Redim retVal(nCount-1) As String
char = String$(1,0) ' Set up initially as character zero for the API call to properly return something
For i = 0 To nCount-1
retVal(i) = ""
Call W32_RtlMoveMemory(char, hLock, 1) ' Read the first (or next) character
While char = Chr$(0) ' There are two char 0's at the end of each entry - skip over the second one
hLock = hLock + 1
Call W32_RtlMoveMemory(char, hLock, 1)
Wend
While char <> Chr$(0) ' extract history one character at a time using Win32 API call until we get a full word
retVal(i) = retVal(i) & char
hLock = hLock + 1
Call W32_RtlMoveMemory(char, hLock, 1)
Wend
Next
Call W32_OSUnlockObject(hHistory)
Call W32_OSMemFree(hHistory)
Call W32_OSMemFree(hACL)
Call W32_NSFDbClose(hDb)
history = retVal
End Function ' Ends the "History" Function
End Class ' Ends the "ACL History" class
This is one of those "black box" classes that you don't really need to know how it works. It uses the C API to grab the listory information and build an array of strings with the history. To use the custom class, you create a new ACLHistory object and pass in the database to be evaluated. Then you get the history property of the ACLHistory object and you can process this history to print out the history or scan for certain criteria (dates or user names).
For a sample agent to verify the custom class, here is some code:
Sub Initialize
Dim s As New NotesSession
Dim db As NotesDatabase
Dim acl As ACLHistory
Dim history As Variant
Dim i As Integer
Dim msg As String
Set db = s.CurrentDatabase
Set acl = New ACLHistory(db)
msg = ""
history = acl.history
For i = Lbound(history) To Ubound(history)
msg = msg & Chr$(10) & history(i)
Next
Msgbox "ACL History = " & msg, 64, db.title & " ACL History"
End Sub
This agent just builds a message box containing the entire history so you can verify the code.