在了解了以上基础知识之后,我们将以 Windows 平台为例,给出一个简单而典型的 Extension Manager程序结构,本示例程序只是用来说明 Extension Manager 的程序架构和处理逻辑,不能编译运行,具体的 Extension Manager 示例程序,请参考随 Lotus C API 一起发布的 Sample。
Extension Manager程序结构示例
/* Extension Manager程序结构示例*/
/*system header file*/
#include <stdlib.h> ……
/*Notes Domino Header File*/
#include <global.h> ……
/*========================== GLOBAL VARIABLES ==============================*/
HEMREGISTRATION hHandler; //插件程序上下文句柄
EMHANDLER gHandlerProc; //插件程序回调函数句柄
WORD gRecursionID; //防止该程序被多次调用
CRITICAL_SECTION gCriticalSection; // 用于多线程同步
/*================== LOCAL FUNCTION PROTOTYES ===============================*/
STATUS LNPUBLIC MainEntryPoint( void ); //插件程序入口函数
STATUS LNPUBLIC EMHandlerProc( EMRECORD FAR * pExRecord); // 插件程序回调函数 BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved ); // DLL入口函数
/*===========================================================================*/ STATUS LNPUBLIC MainEntryPoint( void )
{ STATUS error= NOERROR;
error = EMCreateRecursionID( &gRecursionID );
error = EMRegister(EM_GETPASSWORD, EM_REG_BEFORE | EM_REG_AFTER,(EMHANDLER)gHandlerProc, gRecursionID, &hHandler);
return( error ); }
/*==========================================================================*/ STATUS LNPUBLIC EMHandlerProc( EMRECORD FAR * pExRecord )
{ STATUS error = 0; switch(pExRecord->EId)
{ case EM_GETPASSWORD: { return(ERR_BSAFE_USER_ABORT ); }
return error; }
/*============================================================================*/ BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved )
{ STATUS error=NOERROR; switch(fdwReason)
{ case DLL_PROCESS_ATTACH: InitializeCriticalSection(&gCriticalSection);
gHandlerProc = (EMHANDLER)MakeProcInstance((FARPROC)EMHandlerProc, hInstance);
break;
case DLL_PROCESS_DETACH:
error = EMDeregister(hHandler);
FreeProcInstance( gHandlerProc );
DeleteCriticalSection(&gCriticalSection);
break; }
return( TRUE );
UNREFERENCED_PARAMETER(lpReserved); }
Extension Manager 工程实例
接下来,本文将介绍 Extension Manager 的一个工程实例,并希冀由这个工程实例对 Lotus C API 应用程序开发者在使用 Extension Manager 实现定制应用程序时给予一定的启发和指导。
1) 工程背景:
某客户为了加强内部办公环境的管理,需要强制用户加入 Windows 特定域。客户希望采取绑定邮件系统访问的方式:即限制用户只有登录 Windows 域后才可以访问邮件系统。
2) 工程设计:
为实现客户需求,我们可以设想在客户端部署一个 EM 程序。该客户端 EM 程序注册 EM_GETPASSWORD 事件,在Notes弹出密码输入对话框之前判断当前 Windows 用户是否在域中,如果不在域中就返回 ERR_BSAFE_USER_ABORT,这样可以阻止 Notes 客户端连接 Domino Server。程序片断请参考 Extension Manager 程序结构示例。
一切进行的很顺利,似乎工作到此已经万事大吉了。然而且慢,如果仔细考虑一下,这种方案是不可取的,如果客户端不安装这个EM程序怎么办?或者如果用户卸载这个 EM 程序的话怎么办?这样的话客户端完全可以绕开认证过程。
所以我们必须考虑在服务器端进行统一的控制,这就需要在服务器端也部署一个 EM 程序。该服务器 EM 程序注册 EM_SECAUTHENTICATION 事件,对每位向邮件服务器发送访问请求的用户进行实时授权鉴别,只有在客户端登录 Windows 域的情况下,服务器端 EM 进程才开启该用户访问邮件服务器的限制。
现在的问题是服务器端 EM 程序如何鉴别来访客户端是否在 Windows 域中?答案是 Socket 通信方法:客户端 EM 程序在经过相关判断之后,把 Notes 用户的信息写入到安装 Domino Server 的服务器的某个文件(如Info.DAT)中,服务器端 EM 程序在认证来访 Notes 用户的时候查询该文件,如果发现来访 Notes 用户信息在 Info.DAT 文件中存在,则授权该用户访问邮件服务器,否则则拒绝其访问。
3) 模块设计与实现
系统分为三个组件,分别是:Notes 、客户端和EM 组件。
Notes 客户端 EM 组件
功能说明:负责判断当前 Notes 用户的 Windows 域信息,并把相关信息发送给通信服务器,在接收到通信服务器的确认信息之后,更新登陆时间窗信息。(时间窗是一个时间段,某 Notes 客户端在域中登陆 Domino Server 之后,允许在时间窗内该 Notes 客户端不登陆域也能访问邮件服务器)
算法如下:
I. 捕获 EM_GETPASSWORD 消息
II. 读取注册表的内容,取得当前时间跟注册表中上一次登记的时间做比较,判断时间窗是否过期。
III. 如果时间窗未过期,即该 Notes 客户端已经在指定时间段内向 Server 注册过了,则直接返回 ERR_EM_CONTINUE. 把控制权交给 Notes,允许其连接邮件服务器。
IV. 如果时间窗过期,判断当前 Windows 用户否在域中,如果在域中,从注册表中读取通信服务器的 IP 和端口,把 Notes User 的信息发送过去,然后等待 Server 的响应。如果收到 Server 的响应,那么更新注册表中日期表项
V. 如果当前 Windows 用户不在域中,说明在时间窗之内没有人在域中登陆过 Domino Server,返回 ERR_BSAFE_USER_ABORT 阻止 Notes 连接邮件服务器。