Donnerstag, 6. Oktober 2011

8.5.3 Domino Server installs client templates

With release 8.5.3 of Lotus Domino, setup installs some templates to the server which you had to put there manually in the past, in case you wanted to register roaming users via Lotus script (read more on that here).
The following templates are installed:

bookmark.ntf (v8.5.3 - 16.09.2010)
feedcontent.ntf (v8.5.2 - 01.04.2010)
notebook8.ntf (v8.5.1 - 24.09.2009)
pernames.ntf (v8.5.3 - 18.05.2011)
roamingdata.ntf (v8.5.1 - 21.07.2009)

Dienstag, 9. August 2011

Don't panic!!!

This morning, I realized that one of our servers had trouble with an agent that seemed to be out of control. It was listed as 'fatal' in the server-monitoring view of Lotus Domino Administrator. I tried to stop the zombie-agent using tell amgr cancel "dbpath" 'agent' but that didn't seem to stop it. The agent was still showing as being executed. I tried stopping the agent-manager-task itself, but that didn't help either - it even made the situation worse, since the agent-manager couldn't be started any more. Now the server repeatedly printed thousands of "error connecting to ..." messages to the console, so I decided to restart the whole domino server. Unfortunately, the server didn't respond to the shutdown-request, so I had to use "nsd -kill". I rebooted the whole system and executed the domino startup script. And guess what - the next problem that I encountered looked even more challenging:

ti="00237CC4-C12578E7" sq="00000031" THREAD [08129:00002-4107441872] WAITING FOR WRITE LOCK ON RWSEM 0x02A2 Semaphore controlling per-process init/termination in NSF (@F452A3B8) (R=0,W=1,WRITER=08127:4107818704,OWNER=08127:4107818704) FOR 30000 ms
ti="0023887C-C12578E7" sq="00000032" THREAD [08129:00002-4107441872] WAITING FOR WRITE LOCK ON RWSEM 0x02A2 Semaphore controlling per-process init/termination in NSF (@F452A3B8) (R=0,W=1,WRITER=08127:4107818704,OWNER=08127:4107818704) FOR 30000 ms
ti="00239434-C12578E7" sq="00000033" THREAD [08129:00002-4107441872] WAITING FOR WRITE LOCK ON RWSEM 0x02A2 Semaphore controlling per-process init/termination in NSF (@F452A3B8) (R=0,W=1,WRITER=08127:4107818704,OWNER=08127:4107818704) FOR 30000 ms
ti="00239FEC-C12578E7" sq="00000034" THREAD [08129:00002-4107441872] WAITING FOR WRITE LOCK ON RWSEM 0x02A2 Semaphore controlling per-process init/termination in NSF (@F452A3B8) (R=0,W=1,WRITER=08127:4107818704,OWNER=08127:4107818704) FOR 30000 ms

Those messages seemed to repeat for ever and the server was not accessible in that state. Now I got a little nervous and wondered what could be causing this. Google found two articles that matched the error message which I encountered, but the workarounds mentioned there didn't seem to apply to our system. Now that more than a half an hour had already passed since I had initially restarted the server, I was just about to ask IBM for help. I had just finished writing the problem description to the service request page, and hit the send-button, when suddenly the server continued its operation as if nothing had happened... So - just don't panic ;-)

Synchronizing Database Quotas

Since we heavily rely on database quotas, I find it quite annoying that there's no built-in mechanism to synchronize the quota values (and many other database properties) between different servers in a cluster. Because of that, I wrote a small Lotus Script agent to accomplish that task. Here's the code:

 Option Public
  Option Declare
  Sub Initialize
  Dim dbdir As New NotesDbDirectory("mail01/srv")
  Dim s As New notessession
  Dim db As NotesDatabase
  Dim db2 As NotesDatabase
  Set db = dbdir.GetFirstDatabase(DATABASE)
  While Not(db Is Nothing)
   Print "Processing " & db.Filepath
   If db.sizequota <> 0 then
    Set db2 = s.getdatabase("mail02/srv", db.Filepath , False)
    If Not db2 Is Nothing then 
     If db2.sizequota <> db.sizequota then
      db2.sizeQuota = db.sizequota
      db2.sizewarning = db.sizewarning
      print "Quota for " & db2.filepath & " changed to " & db.sizequota & " Bytes"
      Set db2 = Nothing
     End if
    End If
   End If
   Set db = dbdir.GetNextDatabase
  Wend
 End Sub

Just save the agent-code to a database. Set the agent to run e.g. once a night or once a week, and sign it with an ID that has the right to access all the databases on both servers. The rest should be self-explanatory.

Mittwoch, 6. Juli 2011

8.5.2 cannot replicate with R5 server

Beware of updating to 8.5.2 (including FP1 and FP2) if you still need to replicate databases with a R5 server. Pushing data to the R5 server will not work as expected!

See:

https://www-304.ibm.com/support/docview.wss?uid=swg1LO59916

Quote:

"In 852 a new flag UPDATE_2_MARK_READ_NEWNOTE is being passed on the call to NSFNoteReplicateExtended. This is caught and the "This function is not implemented on this version of the server" error is set. Working on a fix."

At least for 8.5.2FP1 (on linux) there's a hotfix available from IBM: 852FP1HF484

Dienstag, 10. Mai 2011

RegisterNewUser and Roaming-Users

A while ago, we built an application that automatically registers users in our domino directory when our human resources department hires new employees. Now that we changed all of our users to be roaming users, I thought it would be easy to adapt our script, in order to immediately create roaming users during the registration process. Unfortunately, I encountered a strange error after adding the corresponding code to our script:

.IsRoamingUser = True
.Roamingcleanupsetting = REG_ROAMING_CLEANUP_NEVER
.RoamingServer = strMailSrv
.RoamingSubdir = "roaming/" + strFirstName + strLastName

Executing the agent lead to the error "Agent Manager: Agent
'Copy Of NotesBenutzerAnlage' error: Notes error: File does not exist
(Testermann)".
Since I didn't come up with a solution on my own, I asked IBM for help. Today, Mr. Ortlieb from IBM called me and gave me a crucial tip.
I just had to put some of the Lotus Notes client templates on our server! The following templates did the trick:

bookmark.ntf
feedcontent.ntf
notebook8.ntf
pernames.ntf
roamingdata.ntf

Mittwoch, 23. März 2011

ECL mystery

Last week, we wanted to roll out a new security policy containing an administrative ECL to all of our users. My tests indicated that we would have to sign the policy and the corresponding settings document with one of our server's id-files, because the only entry allowed to modify the local ECL was something like "*/SRV/ORG". We didn't want to bother out 1000+ users with unnecessary warning messages.
I thought the best way to achieve this would be to create a local replica of our Domino Directory, locally switch to the server's id in order to edit the documents, and finally replicate the changes back to the server replica. However, this procedure seemed quite error-prone, so I thought about using the good ol' signEZ by Ytria. I asked their support how I could achieve this, since the tool is normally only used for signing design-documents. Fortunately, they had a solution and told me to use a nice little addon, which is somewhat hidden at first sight. You'll find it within the EZ-Suite Installation Database, if you look at the following menu:  "Actions -> Toolbar Extras -> Add signEZ "Sign Selected Note" button".
Unfortunately, this only did half the trick. When I used that signEZ function to sign the policy and the corresponding settings document with one of our server's id-file, i found that the ECL itself was still signed by myself. I had a look at the security settings document with scanEZ, but it seems that the ECL is not saved within the security settings document. Now I'm waiting for Ytria's development team to find out what's going on.

Dienstag, 22. Februar 2011

DAOSmgr resync

Today, I had to restore a database from backup to one of our production servers. We're using Tivoli Storage Manager for Lotus Domino and have DAOS enabled for that server.
The documentation says, that a database restore makes it necessary to start a resynchronization of the DAOS catalog. Is it just me who feels uncomfortable about issuing that "tell daosmgr resync" on a production server during the normal office hours? As far as I understand, this means that the DAOSmgr has a look at every single attachment in every single DAOS enabled database  - and this is just because I had to restore one single database?!
I would imagine some kind of interface to DAOS that the backup-software of your choice could use to inform DAOSmgr about the restored database. It should then just walk though that particular database in order to collect the necessary information.

Montag, 14. Februar 2011

Managing userIDs in Lotus Notes databases

Based on a post by Leif Lagerbrand ( which you may find here: http://www.ls2capi.com/web/ls2capi/ls2capihome.nsf/vwThreadsMain/37A36B2F89E940E8852571EA002FAEF7?OpenDocument ), I once wrote a quick-and-dirty Lotus script agent in order to manage Lotus Notes user.id-files in their various possible locations depending on the usage purpose. As soon as you get in touch with Notes-encryption and/or S/MIME-Encryption in Lotus Notes in combination with Roaming-, Blackberry- or iNotes-users, this little tool could become handy to you. If you want to read Notes- and/or S/MIME-encrypted documents via iNotes, you'll quite probably need to import your Lotus Notes user.id-file into your mail database. Normally, this would be done in the preferences dialog of the iNotes webinterface. Now you may choose the help of the following Lotus Script agent. The ID-File for iNotes is usually saved to a profile document with the title "$shimmerid".
In case that you're dealing with a Blackberry user, you'll also want to import his user.id so he is able to decrypt encrypted messages on his device. In this case, the profile document which stores the user.id is called "$rimid".
The last use-case is the roaming user functionality. For roaming users, the id-file isn't stored within the mail-file, but within the personal addressbook (names.nsf). The corresponding profile document has the title "roaminguserid".
As soon as your user needs to change the password for his main user.id, you'll probably love to use the Lotus Script agent.

Here are some screenshots:



Save the following code as an agent to your mail-database:
 Option Public  
 Option Declare  
 ' Declare some c constants  
 Const OS_TRANSLATE_UNICODE_TO_LMBCS = 23%  
   
 ' Declare some c functions  
 Declare Function PathNetConstruct Lib "nnotes" Alias "OSPathNetConstruct" (Byval PortName As Lmbcs String, _  
 Byval ServerName As Lmbcs String, Byval FileName As Lmbcs String, Byval retPathName As Lmbcs String) As Integer  
 Declare Function NSFDbOpen Lib "nnotes" Alias "NSFDbOpen" (Byval dbName As Lmbcs String, hdb As Long) As Integer  
 Declare Function NSFDbClose Lib "nnotes" Alias "NSFDbClose" (Byval hdb As Long) As Integer  
 Declare Function SECAttachIdFileToDB Lib "nnotes" Alias "SECAttachIdFileToDB" (Byval hdb As Long, Byval ProfileNotesName As _  
 Lmbcs String, Byval ProfileNoteNameLength As Long, Byval UserName As Long, Byval UserNameLength As Long, _  
 Byval FileName As Lmbcs String, Byval Password As Lmbcs String, Byval Reserved As Long, Byval pReserved As _  
 Long) As Integer  
 Declare Function SECExtractIdFileFromDB Lib "nnotes" Alias "SECExtractIdFileFromDB" (Byval hdb As Long, Byval ProfileNotesName As _  
 Lmbcs String, Byval ProfileNoteNameLength As Long, Byval UserName As Long, Byval UserNameLength As Long, _  
 Byval Password As Lmbcs String, Byval FileName As Lmbcs String, Byval Reserved As Long, Byval pReserved As _  
 Long) As Integer  
 Declare Function SECRefreshIdFile Lib "nnotes" Alias "SECRefreshIdFile" (Byval FileName As Lmbcs String,Byval Password As Lmbcs String, _  
 Byval Server As Lmbcs String , Byval retFlags As Long, Byval Reserved As Long, Byval preserved As Long) As Integer  
 Declare Function SECKFMChangePassword Lib "nnotes.dll" Alias "SECKFMChangePassword" ( Byval Filename As Lmbcs String , _  
 Byval Password As Lmbcs String , Byval NewPassword As Lmbcs String ) As Integer  
 Declare Function TranslateFromStr Lib "nnotes" Alias "OSTranslate" (Byval translateMode As Integer, Byval inBuff As _  
 Unicode String, Byval inLen As Integer, Byval outBuff As Long, Byval outLen As Integer) As Integer  
 Declare Function OSLockObject Lib "nnotes" Alias "OSLockObject" (Byval handle As Long) As Long  
 Declare Sub OSUnlockObject Lib "nnotes" Alias "OSUnlockObject" (Byval handle As Long)  
 Declare Function OSMemoryAllocate Lib "nnotes"(Byval dwtype As Long, Byval size As Long, rethandle As Long) As Integer  
 Declare Sub OSMemoryFree Lib "nnotes" Alias "OSMemoryFree"(Byval handle As Long)  
 Declare Function OSMemoryLock Lib "nnotes" Alias "OSMemoryLock" (Byval handle As Long) As Long  
 Declare Sub OSMemoryUnLock Lib "nnotes" Alias "OSMemoryUnlock" (Byval handle As Long)  
 Declare Sub OSLoadString Lib "nnotes" Alias "OSLoadString" (Byval null1 As Long, _  
 Byval sError As Integer, Byval errstr As String, Byval lenstr As Integer)  
 Class memoryManager  
 OpenHandles List As Variant  
 Function LockObject (h) As Long  
  If h=0 Then Exit Function  ' make sure you do not use 0 pointer  
                ' returned in case handle is 0  
  LockObject = OSLockObject(h)  
  OpenHandles(h) = LockObject  
 End Function  
 Sub UnLockObject (h)  
  If h=0 Then Exit Sub ' do not bite  
  If Iselement(OpenHandles(h)) Then  
   OSUnlockObject h  
   Erase OpenHandles(h)  
  End If  
 End Sub  
 Sub UnLockAll  
  Forall hh In Me.OpenHandles  
   Me.unlockObject hh  
  End Forall  
 End Sub  
 Sub Delete  
  UnLockAll ' on delete release all locked handles  
 End Sub  
 End Class  
 Public Class memoryManagerExt As memoryManager  
 buffers List As Long  
 Public Function newBuffer (lenBuff As Long) As Long  
  Dim irc As Integer, hBuff As Long ' these handles are Long in all OSes  
  irc =OSMemoryAllocate (0, lenBuff, hBuff)  
  If irc=0 Then  
   If hBuff = 0 Then Exit Function ' paranoid chek - it should not be 0 if retrun code is OK  
   buffers(hBuff)= OSMemoryLock (hBuff)  
   newBuffer = buffers(hBuff)  
  Else  
   Print getError(irc)  
  End If  
 End Function  
 Public Sub Delete  
  Forall p In Me.buffers  
   OSMemoryUnlock Listtag(p)  
   OSMemoryFree Listtag(p)  
  End Forall  
 End Sub  
 End Class ' * memoryManagerExt  
 ' Autor: Patrick Tippner  
 Sub Initialize  
 On Error Goto errorHandler  
 Dim ws As New NotesUIWorkspace  
 Dim ses As New NotesSession  
 Dim db As NotesDatabase  
 Dim doc As NotesDocument  
 Dim item As NotesItem  
 Dim PortName As String  
 Dim ServerName As String  
 Dim FileName As String  
 Dim pathName As String*1024  
 Dim ret As Integer  
 Dim retflag As Long  
 Dim hdb As Long  
 Dim ProfileNoteName As String  
 Dim LmbcsLen As Long  
 Dim memMan As New memoryManagerExt  
 Dim pLmbcsStr As Long  
 Dim IdFileName As Variant  
 Dim Password As String  
 Dim newPassword1 As String  
 Dim newPassword2 As String  
 Dim response As Variant  
 Dim values(2) As Variant  
 Dim choices(4) As Variant  
 Dim itemname As String  
 Dim temppath As String  
   
 retflag = 0  
 values(0) = "Roaming"  
 values(1) = "Blackberry"  
 values(2) = "Webmail"  
   
 choices(0) = "Import"  
 choices(1) = "Export"  
 choices(2) = "Delete"  
 choices(3) = "Change Password"  
   
 response = ws.Prompt (PROMPT_OKCANCELCOMBO, "ID-Management - Choose intended usage", "Select the usage-context of the user.id to be managed.", values(0), values)  
   '  
 If response="Roaming" Then  
  PortName = ""  
  ServerName = ""  
  FileName = "names.nsf"  
 Else  
  Set db = ses.CurrentDatabase  
   ' Construct the path to the user's mail database  
  PortName = ""  
  ServerName = db.Server  
  FileName = db.FilePath  
 End If  
 ret = PathNetConstruct(PortName, ServerName, FileName, pathName)  
 If ret <> 0 Then Error 1212, "Something went wrong"  
   ' Open database to get a handle opn it  
 ret = NSFDbOpen(pathName, hdb)  
 If ret <> 0 Then Error 1212, "Something went wrong"  
   
 If Isempty (response) Then  
  Messagebox "User canceled", , "ID-File has not been imported"  
  ret = NSFDbClose(hdb)  
  If ret <> 0 Then Error 1212, "Something went wrong"  
  Goto exitOk  
 Else  
  Select Case response  
  Case "Roaming"  : ProfileNoteName = "roaminguserid"  
  Case "Blackberry" : ProfileNoteName = "$rimid"  
  Case "Webmail"  : ProfileNoteName = "$shimmerid"  
  End Select  
   
  response = ws.Prompt (PROMPT_OKCANCELCOMBO, "ID-Management - Choose management option", "Please select what you intend to do:", choices(0), choices)  
   
  Select Case response  
  Case "Import"  
   pLmbcsStr = memMan.newBuffer (3 * Lenb(ProfileNoteName))  
   LmbcsLen = TranslateFromStr(OS_TRANSLATE_UNICODE_TO_LMBCS, ProfileNoteName, Lenb(ProfileNoteName), pLmbcsStr, 3 * Lenb(ProfileNoteName))  
  ' Get filename from user  
   IDfilename = ws.OpenFileDialog( False, "ID-Management - Please choose your personal user.id","ID-Files|*.id", "%userprofile%\Lokale Einstellungen\Anwendungsdaten\Lotus\Notes\data\","user.id")  
   If Not(Isempty(IDfilename)) Then  
   While Not Len(Password)>0  
    Password = ws.Prompt(PROMPT_PASSWORD, "ID-Management - Password prompt", "Please type your Notes ID password.")  
   Wend  
   ret = SECAttachIdFileToDB(hdb, ProfileNoteName, LmbcsLen, 0, 0, IdFileName(0), Password, 0, 0)  
   If ret <> 0 Then Error 1212, "Something went wrong"  
   ' Close the database to free its resources  
   ret = NSFDbClose(hdb)  
   End If  
   If ret <> 0 Then Error 1212, "Something went wrong"  
  Case "Export"  
   pLmbcsStr = memMan.newBuffer (3 * Lenb(ProfileNoteName))  
   LmbcsLen = TranslateFromStr(OS_TRANSLATE_UNICODE_TO_LMBCS, ProfileNoteName, Lenb(ProfileNoteName), pLmbcsStr, 3 * Lenb(ProfileNoteName))  
  ' Get filename from user  
   IDfilename = ws.SaveFileDialog( False, "Please specify where the extracted user.id should be saved:","ID-Files|*.id", "%userprofile%\Desktop\","user.id")  
   If Not(Isempty(IDfilename)) Then  
   While Not Len(Password)>0  
    Password = ws.Prompt(PROMPT_PASSWORD, "ID-Management - Password prompt", "Please type your Notes ID password.")  
   Wend  
   ret = SECExtractIdFileFromDB(hdb, ProfileNoteName, LmbcsLen, 0, 0, Password, IdFileName(0), 0, 0)  
    
   If ret <> 0 Then Error 1212, "Something went wrong"  
   ' Close the database to free its resources  
   ret = NSFDbClose(hdb)  
   End If  
   If ret <> 0 Then Error 1212, "Something went wrong"  
  Case "Delete"  
   Set db=ses.GetDatabase(Servername,Filename,False)  
   Set doc=db.GetProfileDocument(ProfileNoteName)  
   If doc.HasItem("$FILE") Then  
   Forall items In doc.items  
    'Set item = doc.getfirstitem("$FILE")  
    If (items.Name = "$FILE") Then  
    If items.Values(0) = "UserID" Then  
     If ws.Prompt(PROMPT_YESNO,"ID-Management - Confirmation","Do you really want to delete the file from the profile-document?") Then  
     Call items.Remove  
     Call doc.Save(True,False)  
     End If  
    End If  
    End If  
   End Forall  
   Else  
   Call ws.prompt(PROMPT_OK,"ID-Management - no ID found","The profile-document does not contain a user.id.")  
   End If  
    
  Case "Change Password"  
   Set db=ses.GetDatabase(Servername,Filename,False)  
   Set doc=db.GetProfileDocument(ProfileNoteName)  
   If doc.HasItem("$FILE") Then  
    
   pLmbcsStr = memMan.newBuffer (3 * Lenb(ProfileNoteName))  
   LmbcsLen = TranslateFromStr(OS_TRANSLATE_UNICODE_TO_LMBCS, ProfileNoteName, Lenb(ProfileNoteName), pLmbcsStr, 3 * Lenb(ProfileNoteName))  
  ' Get filename from user  
   temppath = Environ$("TEMP")  
   IDfilename = temppath & "\tempid.id"  
   While Not Len(Password)>0  
    Password = ws.Prompt(PROMPT_PASSWORD, "ID-Management - Password prompt", "Please type your current Notes ID password.")  
   Wend  
   ret = SECExtractIdFileFromDB(hdb, ProfileNoteName, LmbcsLen, 0, 0, Password, IdFileName, 0, 0)  
   If ret <> 0 Then Error 1212, "Something went wrong"  
    
   ret = SECRefreshIdFile(IDFileName, Password, Servername, retFlag, 0, 0)  
    
   While Not Len(newPassword1)>0 And (newpassword1=newpassword2)  
    newPassword1 = ws.Prompt(PROMPT_PASSWORD, "ID-Management - Password prompt", "Please type your new Notes ID password.")  
    newPassword2 = ws.Prompt(PROMPT_PASSWORD, "ID-Management - Password prompt", "Please confirm your new Notes ID password.")  
    If newpassword1<>newpassword2 Then  
    Call ws.Prompt(PROMPT_OK, "ID-Management - Password failure", "The password confirmation did not match the previously entered password. Please re-type your new Notes ID password.")  
    End If  
   Wend  
   ret = SECKFMChangePassword(IDfilename, Password, newPassword1)  
   If ret <> 0 Then Error 1212, "Something went wrong"  
   ret = SECAttachIdFileToDB(hdb, ProfileNoteName, LmbcsLen, 0, 0, IdFileName, newPassword1, 0, 0)  
   If ret <> 0 Then Error 1212, "Something went wrong"   
   ' Close the database to free its resources  
   ret = NSFDbClose(hdb)    
   If ret <> 0 Then Error 1212, "Something went wrong"  
   Kill IdFilename  
   Else  
   Call ws.prompt(PROMPT_OK,"ID-Management - no ID found","The profile-document does not contain a user.id.")  
   End If  
    
  End Select  
 End If  
   
 exitOk:  
 Exit Sub  
 errorHandler:  
 Print Error$ + " in line " + Cstr(Erl)  
 Messagebox Error$ + " in line " + Cstr(Erl)  
 Resume exitOk  
 End Sub  
 Public Function getError (enum As Integer) As String  
 Dim s As String*256  
 OSLoadString 0, enum And &h03FFFFFFF, s, 256  
 getError = Strleft(s, Chr(0))  
 End Function