Extension Manager
Chapter 12-11
Extension Manager
The Extension Manager allows an executable program library in an application to register a callback routine that will be called before, after, or before and after Domino or Notes performs selected internal operations. This allows C API applications to perform database shadowing or add additional access controls. The header file extmgr.h defines names that identify the notification events that are supported.
Extension manager applications are built as executable program libraries (for example, dynamic link libraries for Windows or shared object libraries for UNIX environments). The structure and naming conventions for program libraries are platform-dependent; for more information, see the "Platform-Specific Naming Conventions" chapter.
You must identify the executable program library for an extension in the notes.ini file by adding an EXTMGR_ADDINS entry. There should only be one such entry in notes.ini; list all extension manager libraries in the same entry, separated by commas. For example, to run both sample programs extmngr and extpwd on a Win32 system, the entry in notes.ini should be:
- EXTMGR_ADDINS=nextmngr.dll,nextpwd.dll
If there are multiple extension managers that process the same notification, each extension manager is notified of that event in the order listed in the EXTMGR_ADDINS entry of notes.ini .
An extension manager may be installed on either a Notes client workstation or a Domino server. However, there is not necessarily a one-to-one correspondence between the C API calls made on a client workstation and extension manager notifications on a Domino server. The extension manager on the server may not receive the expected notifications. For example, when a client workstation performs an NSFNoteUpdate on a remote database, different requests are sent to the Domino server depending on the state of the document and the nature of the update. In particular, the function NSFNoteUpdate may not be called on the server, therefore the notification EM_NSFNOTEUPDATE will not occur.
Registering a Callback Function
The first step in registering a callback function to receive notification events is to call EMCreateRecursionID to obtain a recursion ID. Although this step is optional, it is usually desirable because it prevents Domino or Notes from calling that extension if the current thread has already called the extension once. For example, this prevents an endless cycle if your application is expecting notification on NSFDbOpen and calls NSFDbOpen when processing that notification.
You only need one recursion ID per extension manager DLL or shared object. The same recursion ID applies to all threads in a process. You can set it individually on each extension you register to allow recursion on some extensions but not others. You can use the same ID across multiple extensions. You do not have to remember the recursion ID, because all the checking is done in Domino or Notes and it is freed on shutdown.
Once the application obtains a recursion ID, call EMRegister with the ID of the desired notification, flags indicating when to call the function, the address of the callback function, and the recursion ID. The flag values may be:
- EM_REG_BEFORE - The routine will be called before the operation is attempted
EM_REG_AFTER - The routine will be called after the operation is completed
Both - The routine will be called before the operation is attempted and again after the operation is completed
Do not make any C API function calls for Domino and Notes, other than the extension manager EMxxx function calls, in the entry point routine that calls EMRegister. The extension manager DLL or shared object is loaded as part of Domino or Notes initialization and the extension manager entry point routine is called as part of that startup. The initialization of the other components of Domino or Notes is not yet completed when the extension manager entry point routine is called.
When an application is finished with the Extension Manager, use EMDeregister to remove all the callback functions registered. Failure to de-register a callback function may result in a crash of Domino or Notes. Do not de-register callback functions in the callback routine for the EM_TERMINATENSF notification.
Extensions are registered on a per-process basis. Once an extension is registered, all threads in a process invoke that extension. If a recursion ID is used, it applies to all threads in that process.
The Callback Function
The callback function has the following declaration:
- STATUS LNCALLBACK ExtMgrCallback (EMRECORD far *pExtRec);
The single argument supplied by Domino or Notes is a pointer to an EMRECORD structure. The fields of this structure are:
- EId Notification ID
NotificationType EM_BEFORE or EM_AFTER
Status Core error code
Ap Pointer to the arguments supplied to the Domino or Notes core function
The EId is the value supplied when the function was registered (or one of the values, if the same function was registered for more than one notification). The NotificationType informs the function whether this notification is before the operation is attempted or after it was completed. If the NotificationType is EM_BEFORE, the Status field is undefined; if EM_AFTER, this field contains the return value from the operation. Ap points to the arguments supplied to the core function; since this varies from one function to another, you must use the notification ID to determine the corresponding function for the notification so that the arguments can be correctly interpreted using the VARARG_xxx macros in global.h.
An important note: If the Notification Type is EM_BEFORE (before the operation has been performed), input parameters to the function will be valid. For example if one of the input parameters is a note handle, the value will be active and valid when interpreted using the VARARG_xxx. However if the Notification Type is EM_AFTER (after the operation has been performed) input parameters, such as note handles, will no longer be valid due to internal Notes processing freeing or deallocating handles once the operation has been completed. Keep this in mind when processing EMxxx function calls.
A note can be opened in "canonical" format. When this occurs, the data type fields and data values that are normally converted to host format are left unconverted. Extension manager callback functions that examine the data in a note must check for the flag NOTE_FLAG_CANONICAL by calling NSFNoteGetInfo() with the header member _NOTE_FLAGS before attempting to interpret the contents of the note.
NOTE: The Extension Manager notification EM_AGENTISENABLED has a slighly different callout signature due to the function returning something other than STATUS. The signature is as follows: STATUS LNPUBLIC AgentIsEnabled(HAGENT hAgent, BOOL return_value)
To retrieve the Core error code of this function check the return_value parameter instead of the Status variable of the EMRECORD structure.
Callback Return Codes
The return code from the callback function controls the processing that Domino or Notes performs following the return. It is helpful to know the steps Domino or Notes uses for an internal operation that supports notifications:
- Domino or Notes calls any functions registered with EM_REG_BEFORE. If any function returns a value other than ERR_EM_CONTINUE, Domino or Notes does not call any subsequent functions and does not perform the operation.
- Domino or Notes performs the operation.
- Domino or Notes calls any functions registered with EM_REG_AFTER. If any function returns a value other than ERR_EM_CONTINUE, Domino or Notes does not call any subsequent functions and returns that value as the result of the operation (either internally or to the API application that invoked the operation).
In most instances, callback functions registered with EM_REG_BEFORE should return ERR_EM_CONTINUE, to ensure that Domino or Notes performs the requested operation. Exceptions are noted in this chapter and in the Reference, under the specific callback function entry.
Writing an Extension Manager that transforms a Non-Domino Database into a Domino Database.
In the Extension Manager Sample \misc\extmngr a non-Domino database is transformed into a Domino database when the EM_NSFCREATEDB and EM_NSFCLOSEDB notifications are trapped for the sample database "animals.nsf". This sample demonstrates the use of an Extension Manager to perform complex Database operations upon trapping specific notifications.
When the user creates a new database with the title "animals", the Extension Manager traps the EM_NSFCREATEDB and also the EM_NSFCLOSEDB notifications. Before the database is closed and the EM_NSFCLOSEDB notification has been trapped, the program creates the forms, views, and documents needed for the Domino database "animals.nsf" to reflect the data contained in the non-Domino database "animals.db".
Other Database operations can be handled by the Extension Manager by trapping the specific notification before or after and before and after completion. See the header file "extmgr.h" for a list of the supported Extension Manager Notifications.
Capturing the Domino or Notes Password Prompt Request
One of the operations you can trap using the Extension Manager is the Domino or Notes password prompt request. The extension ID code is EM_GETPASSWORD. There is no corresponding API function for this operation. The header file extmgr.h documents the arguments to the get password operation.
The sample program misc\extpwd uses the extension manager to capture the password prompt request. For simplicity, the program displays a dialog box to obtain the password. However, this is not necessary; all Domino or Notes requires is that the characters for the password be written to the address provided in the request.
An extension manager hook for the EM_GETPASSWORD request is only called before the operation is attempted. Registering a callback function with the EM_REG_AFTER flag has no effect.
The EM_GETPASSWORD request uses the following special status codes to indicate the status of the operation to Domino or Notes:
- ERR_BSAFE_EXTERNAL_PASSWORD
The password has been supplied by an external source. (An extension manager hook is considered an external source.) This status code must be returned to Domino or Notes by an extension manager hook that provides a password. - ERR_BSAFE_USER_ABORT
The password request was canceled. This corresponds to the user clicking Cancel in the Notes password dialog box. - ERR_EM_CONTINUE
Domino or Notes performs the usual processing (that is, displaying the password dialog prompt).
Since this extension manager hook works with user passwords, there are security issues to consider. First, it is not practical to attempt to guess a user's password through an API program. Domino or Notes supplies a built-in delay between attempts to provide the password, and the delay increases if the correct password is not provided. The estimated average time to guess a password using the extension manager is 99,260 years.
A more plausible attack is to provide an extension that prompts the user for the password, then both supplies the password to Domino or Notes and stores the password in an unsecured location. Users should be aware that a user interface other than the Domino or Notes password dialog prompt may not be secure; system administrators and application developers must implement processes to ensure that user passwords are not compromised in this way.
Another risk comes from applications that store the password for legitimate purposes. For example, if the password is stored in a disk file, an unscrupulous individual who was aware of that fact could obtain the password from the file. If at all possible, applications should not store passwords anywhere an unauthorized person could obtain them.
Capturing the Domino or Notes Replication Conflict Notification
By trapping the replication conflict notification, you can have an extension manager automatically handle replication conflicts. The extension ID code is EM_NSFCONFLICTHANDLER. There is no corresponding API function for this operation. The header file extmgr.h documents the arguments to the replication conflict handler.
The Notes C API Release 4.5 introduced several new functions that can be used by the extension manager to determine how to handle a replication conflict. These functions include:
- NSFNoteFindMatchingItem Find item in one note that matches the same item in another.
NSFNoteFindDivergenceTime Find when two notes were last in synch.
NSFItemGetModifiedTime Get the last modified time for an item.
NSFItemGetModifiedTimeByBLOCKID Get the last modified time for an item, given the item's BLOCKID.
Use the extension manager callback routine to handle the replication conflict. In this handler routine, you can use NSFNoteFindMatchingItems to pair items from one note that match items from another. An item pair may be in conflict if one of the item's modified time is later than the note's divergence time. You can then specify the desired value for the item by setting this item value in the item of one note and deleting this item from the other. Then, pass CONFLICT_ACTION_MERGE back so that the two matching notes are merged with your specified resolution to the conflict.
If you want Domino or Notes to handle the replication conflict, pass back ERR_EM_CONTINUE. For example, if an error occurs in your replication conflict handler you may want to abort handling the conflict yourself and pass ERR_EM_CONTINUE back to Domino or Notes.
The replication conflict handler needs to be installed on each Domino or Notes installation that can be involved in the replication and subsequent conflict resolution. Domino or Notes calls the extension manager replication conflict handler (if any) on the installation that lost the replication conflict.
The sample program misc\extconf demonstrates an extension manager that handles a replication conflict.
Writing an Extension Manager to Process Outgoing Mail
The extension manager notification, EM_MAILSENDNOTE, can be used to process outgoing mail. The sample program, \mail\extmail, demonstrates how to intercept certain outgoing mail messages. If the notification type is EM_BEFORE, and the mail message's subject text is "Extension Manager", the Extension Manager modifies the mail message's body text notifying the user that the mail has been intercepted. If the notification type is EM_AFTER, the Extension Manager creates a new mail message with user mail information, attaches an Extension Manager log file and sends the message to the originator of the outgoing mail message.
Extension Manager Limitations
- Do not make any C API function calls for Domino and Notes, other than the extension manager EMxxx function calls, in the entry point routine that calls EMRegister. The initialization of the other components of Domino or Notes is not yet completed at this point.
- All Domino and Notes applications activate the extension managers specified in the notes.ini file. In the entry point of your extension manager, you can register your extension manager for the Notes client only by checking the name of the calling application before you call EMRegister. For example, with a Windows 32-bit application, if the name of the calling application is not nnotes.dll, then do not call EMRegister.
- Do not de-register callback functions in the callback routine for the EM_TERMINATENSF notification.
- Do not call C API functions (such as NSFItemScan) that require callback functions in your extensions manager.
- Be wary of processing messages in a mail.box file. If you intercept the EM_NSFNOTEOPEN notification to make changes to the document that affects the routing of the message (for example, change the send to field to redirect a message), you may not get the desired results. At this point the Router may already contain state information about the message and may deliver the message to the original recipient.
- There has been a reported anomaly that when trapping EM_NSFNOTECLOSE, and then using the associated object handle, Notes returned a "handle out of range" error. This is a rare condition and due to Notes internally discarding the object before actually freeing it. To guard against this error occurring, it has been suggested that you test the validity of the handle by calling OSLockObject(). If the call returns NULLHANDLE do not use the handle in any further processing.
- Beginning with R5.0, the output of printf in the Extension Manager routines are buffered. To see the output, you can use the fflush function to flush the buffer, or, use the AddInLogErrorText function instead.
- Be aware that Extension Manager callback routines may be called when the corresponding database's semaphore is locked. In this case, you must not call a C API routine that needs to acquire the database semaphore, or a deadlock will occur. For example, you must not call NSFDbGetUnreadNoteTable in the routine that traps EM_NSFMARKREAD. Similarly, you must not call NSFFolderGetIDTable in the routine that traps EM_NSFADDTOFOLDER with the same Folder ID.