API
@-Formulas
JavaScript
LotusScript
Reg Exp
Web Design
Notes Client
XPages
 
Get ACL History Class
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.