Oracle® Mail Application Developer's Guide 10g Release 1 (10.1.1) Part Number B15789-01 |
|
|
View PDF |
The Oracle Collaboration Suite Mail server plug-in interface provides a framework to pass mail and messaging information between existing Oracle Mail protocol servers and third-party vendor and partner products. This interface can be used to extend the functionality of mail servers in many ways - for example, to scan the mail system for viruses, provide efficient workflow integration, or track and archive mail messages that have entered or left the system.
The Mail Server Framework allows two types of plug-ins. The first takes the form of a separate executable, written in any language, and spawned from the SMTP processes. The second is a shared library written in C and linked into the SMTP processes. Libraries are written and compiled like any non-Oracle Mail Server library, without added precompilation or preprocessing. However, plug-in libraries must be linked as shared libraries. The library plug-ins will be loaded into the Mail server address space during server startup. Configuration of both types of plug-ins is done through policy pages. For more information on configuring plug-ins, see Chapter 8, "Oracle Mail Policies"" of Oracle Mail Administrator's Guide.
If the plug-in is a separate application or executable, the appropriate mail process will spawn the executable for each mail message. The mail message is passed to the application through standard input. If the custom executable is called from a mail process that uses envelope information, then the envelope information will be passed on the command line as well.
The Mail Server processes can call the plug-ins at specific event control points, of which there are two types. There are high-level event control points, which specify when plug-ins are called, and low-level event control points, which define the information passed to the plug-in. A system administrator will set the high-level event control points through the administration pages. Low-level control points apply only to C plug-ins (shared libraries) and the plug-in specifies these control points as a list of services supported by the filter interface.
Mail processes that route or send mail messages will eventually pass through high-level event control points. There are three such control points. The first point is called Incoming, which is reached as the message enters the mail process that routes or sends a message, regardless of the sender or recipient of the message. Note that the scanner plug-in interface is not called until after the message passes through the protocol server's mail relay control logic. That is, if the SMTP servers are not open relays, then all messages that pass the relay constraints will go through this event control point.
After the protocol server reviews the recipient list of the mail message, all messages that are destined for one or more recipients in one of the local domains will pass through the Local event. Afterwards, messages that are destined for one or more recipients in a mail domain that is not local will pass through the Outgoing event. Mail messages destined for recipients both a local domain and an external domain will pass through both Outgoing and Local event control points.
INCOMING - A plug-in configured to be called on the routing of all messages will be called for every message when a connection is made to the mail protocol service or when a message is submitted into a mail store.
OUTGOING - A plug-in configured to be called for outgoing will be called just before a connect request is made to the next message transfer agent.
LOCAL - A plug-in configured to be called for local will be called just before local delivery to the inbox or news store.
Each high-level event control point can call an array of one or more custom plug-ins. The order by which each plug-in is called can be set in the administration pages. If both types of plug-ins are configured for a Mail server, the framework will call C plug-ins in the specified order before external process plug-ins. Messages and connections will pass through the Incoming high-level event control point before passing through either the Outgoing or Local control points.
Information passed to a plug-in depends upon the services supported by the plug-in. The information is passed at low-level control points, which include Connect, Reset, Envelope, Message, and Authentication.
Connect - Processing a connection event is mandatory. For mail protocol servers that route mail or news, this event corresponds to the client connect or server connect request event. For the background tasks of the mail Information Store (virus scrubber), the event corresponds to the start of a new mail message. The host and IP address information from the connection are made available to the plug-in. When called by the virus scrubber, the host and IP address information will be those of the local host.
Reset - Processing a reset event is mandatory. It is used to finish the processing at the end of a message, a message reset request from the client, or a connection close.
Envelope - A plug-in can process envelope information prior to receiving the mail message. In addition to the information available at the Connect control point, the plug-in has access to fully resolved originator and recipient mail address information and authentication information. This information is not the same information provided at the Authentication event control point. This authentication information will only exist if there was a successful authentication of the connection. The information provided will be the fully qualified email address. The plug-in can trust the email address as the authenticated address of the user that connected. Supporting this control point is optional.
Message - A plug-in can process message information. In addition to the information available at the Connect and Envelope control points, the plug-in has access to details of the message as well as the complete, unaltered message. Supporting this control point is optional.
Authentication - This control point allows a plug-in to take control of authentication. A plug-in that publishes its support of the authentication event will be sent an email account and a password or key at this control point. At this point, authentication has not yet taken place. The plug-in can then perform verification of the address and password/key pair, and return either a success or a failure of this verification. Supporting this control point is optional.
Table 4-1 Services Supported by Server Type
Server Type | CONNECT | AUTH | ENVELOPE | MESSAGE | RESET |
---|---|---|---|---|---|
SMTP_IN |
MANDATORY |
No |
Yes |
Yes |
MANDATORY |
SMPT_OUT |
MANDATORY |
No |
Yes |
Yes |
MANDATORY |
NNTP_IN |
MANDATORY |
Yes |
Yes |
Yes |
MANDATORY |
LS |
MANDATORY |
No |
No |
Yes |
MANDATORY |
VS |
MANDATORY |
No |
No |
Yes |
MANDATORY |
The above table shows the low-level services each of the six mail protocol server types are able to call. A plug-in must write a CONNECT and a RESET function. All protocol server types always call these two functions. Protocol servers optionally support AUTH, ENVELOPE, and MESSAGE functions. In the case of AUTH, only the NNTP_IN process supports this low-level event control point.
Calling the Plug-In From the Mail Protocol Server
All custom plug-ins must be placed in a specific directory, or the plug-in shared library must be in the LD_LIBRARY_PATH environment variable. Each middle-tier Oracle home has the directory structure ./oes/lib
below the Oracle home where the plug-ins should be located. If the shared library is not in this directory, then the LD_LIBRARY_PATH environment variable must include the directory where the library is located. The name (or complete path, if an executable) of the plug-in must be entered in the filter maintenance page when managing policies through the administration tools.
Furthermore, if the plug-in is a shared library, the plug-in must support a list of mandatory functions. The functions for the initialization, registration, and close actions are mandatory and must have specific names so that the mail protocol servers know how to call them. Within the initialization function, a plug-in will tell the protocol server which low-level event control points the plug-in can process. The registration function provides function pointers to the plug-in to request services of the mail protocol server.
Header File Inclusion for C Plug-ins
All custom plug-ins must include the C interface definition located in the esefif.h
header file. This header file contains:
platform-specific calling conventions;
platform-specific export symbols for shared libraries
platform independent oracle datatypes, and
an extern
wrapper for C++ code.
The Mail server framework provides a way to integrate external processes into a mail server. The Mail server invokes a configured filter at each of a series of high-level control points and communicates with the spawned process using standard input and output. The external filter executable must have its permissions set to Execute. The Mail server will throw errors in its log if it fails to spawn the filter process.
Both the message body and envelope are passed when the filter process is spawned by the Mail server. The call to the external process will pass the envelope information, as well as other information (such as message size) by command-line arguments in the syntax described below. The filter process reads the envelope information using argv[]
. The message body is passed to the standard input (stdin) of the external process; the filter process must read its standard input until EOF (end of file) occurs, indicating the end of the passed message. The Mail server will return errors if the filter process exits or aborts before the complete message body is sent by the Mail server.
The external process can apply its own filtering on the envelope and the received message body. The process must return a status code (success, failure or modify) to the Mail server on the standard output (stdout). If a success status is received, the Mail server proceeds with running the remaining filters (if any) or with delivery processing of the mail message. If a failure status is received from the external filter, the message will be rejected by the Mail server and will not be delivered. The external filter can also pass a modified mail message back to the server, which is then delivered to the recipients. If the external process aborts or exits during processing of a mail message, the Mail server will assume a temporary processing error. In this case, the server will re-queue the message and retry according to the Mail server's retry mechanism.
The call to the external process has the following syntax:
filter_process host=host mailfrom=mailfrom rcptto=rcptto msgsize=msg_size
where
filter_process
is the path of external filter executable as given in the administration pages,
host
is the host name of mail client,
mailfrom
is the address sent by the client during the Mail server protocol exchange,
rcptto
is the list of recipients from the Mail server protocol exchange. This is a list separated by a comma and is enclosed in brackets and,
msg_size
is the size of the mail message.
The external filter process should rely on the keywords in the arguments rather than the contents of the argv
array. For example, host information will not necessarily be passed in argv[1].
After the filter process has finished processing the mail, it should return the status and the changed message (if any) back to the server. To do this, the process must write the following information to its standard output (stdout):
status_code [version_definition] repaired_message
where
possible value for status_code
are
0
- The message is clean and is to be sent to the recipient
1
- The message is not clean and is to be rejected
2
- The message was not clean but was repaired, or the message was modified for Mail policy reasons. The changed message will be sent to recipients.
version_definition
can be returned optionally by the filter process and is stored with the message. This can be used to store version information, such as a virus definition database identifier. Because it is stored with the message, it can be used later in the virus scrubber process to selectively re-scan messages.
repaired_message
is the modified message that should be sent to recipients.
Initialization and Registration
The plug-in tells the protocol server which services it supports during the initialization phase so that the protocol server or virus scrubber knows how to call the plug-in. Through a series of callback registrations during the initialization phase, the plug-in learns how to communicate back to the server. When a mail protocol server first loads, the server calls the plug-in's esefifInit()
function, and then calls the esefifRegister()
function repeatedly, once for each framework callback service supported by MTA. Each time the server calls esefifRegister()
, it passes the plug-in a function pointer to one of the services it could provide. For example, it could tell the plug-in how to communicate with the framework and log a message in the mail protocol server log file.
The mail protocol servers are multi-threaded, and each thread can handle a connection. During the connection, the thread will call the plug-in at each supported low-level event control point.
Processing Low-Level Control Points
The five low-level event control points are passed through on a per-connection basis. The exception is the virus scrubber process, which runs against a mail store. For example, assume a plug-in written for the Oracle Message Transfer Agent (SMTP_IN process in this case) notifies the Oracle protocol server that it supports Connect, Message, and Reset servers (assume it does not support Envelope). Now suppose a remote MTA sends three messages over a single connection to the Oracle Message Transfer Agent. The protocol server would call esefifConnect()
once after the remote MTA has opened a connection. Then it would call esefifMessage()
three times; once after each message is received. Finally esefifReset()
would be called when the connection is dropped or closed. If the plug-in published supported "ENVELOPE" as well, then esefifEnvelope()
and esefifMessage()
would be called three times each.
esefifReset()
gets called at the end of the connection or if the client (or remote MTA) requests a temporary reset for the currently processed message. The plug-in can determine which type of reset is being called (i.e. the difference between End of Message/Connection and temporary message reset). The plug-in is responsible for any message level and connection-level cleanup of structures.
The plug-in framework allows a single plug-in (shared library) to be called by multiple protocol server types. The above plug-in could also apply to the virus scrubber. The virus scrubber would call esefifConnect()
, then esefifMessage()
and finally esefifReset()
for each message that met the filter requirements.
When a mail protocol server shuts down, the server calls the plug-in's esefifClose()
function. The plug-in can then release resources allocated during esefifInit()
as well as perform any other necessary cleanup.
Information is passed between a protocol server and a plug-in through function pointers, which are registered with the plug-in when the protocol server starts. Each protocol server type will publish callback service functions to the plug-in, which can then call these functions as needed.
Framework Send function - This function asks the framework to send the message to the plug-in. Since an email message can be quite large, calling this function causes the Oracle protocol server to, in turn, call the plug-in esefifSend()
function in a loop. Each time esefifSend()
is called, the protocol server sends a chunk of the message until the complete message is accepted.
Framework Receive function - This function asks the framework to receive the message from the plug-in. This is called if the plug-in needs to modify the message before handing it back to the protocol server. For example, a virus scanning plug-in might "clean" a message and return it, or a disclaimer-adding plug-in might append a disclaimer and pass the message on. As an email message can be quite large, calling this function causes the Oracle protocol server to, in turn, call the plug-in esefifRecv()
function in a loop. Each time esefifRecv()
is called, the plug-in sends a chunk of the message until the complete message is accepted.
Framework Get Envelope function - This function asks the framework to send the envelope information associated with this message. This information includes recipient list, sender, host, and authenticated user. When called at the Incoming event control point, the function returns a complete list of unresolved recipients and if available, a complete list of resolved recipients.. When called at Relay or Local control points, the recipient list is resolved but only contains relay or local domain recipients.
Framework Get Header Information function - This function asks the framework to send the header information associated with this message. This will return the top-level header of the message in the format it was received from the client (or other MTA).
Framework Get Message Size function - This function asks the framework to send the size of the message, in bytes.
Framework Get Message Identifier function - Each mail protocol server has a default mail store where it performs certain tasks. This function asks the framework to send the mail store message identifier. Note that a single message stored in two different mail stores will have two different message identifiers.
Framework Allocate Memory function - A plug-in can request the protocol server calling it to allocate memory on its behalf. Doing so uses the heap of the protocol server as opposed to the plug-in, which saves operating system calls.
Framework Free Memory function - All memory explicitly requested by a plug-in from the framework must be freed with the framework's free memory function call.
Framework Set Version definition - The Oracle mail store schema includes a history table (es_scan_history
) of the messages that passed through the plug-in interface. The table consists of the mail store message identifier, scanner (plug-in) name, a definition, and a date stamp when the message passed through the plug-in. Each time a message passes through a plug-in, the plug-in can tell the protocol server to insert a record into the es_scan_history
table.
Framework Add a Recipient function - This function adds a "rcpt to:" entry to the message envelope.
Framework Delete a Recipient function - This function removes a "rcpt to:" entry from the message envelope.
Framework Log function - This function logs a message from a plug-in.
Framework Get Policy Identifier - This function returns the archive policy ID (if any) of the calling plug-in. This function applies only to plug-ins used for archiving purposes.
Copying a Message Between the Server and Plug-in
The esefifMessage()
is called by the protocol server when that protocol server has a message it wants the plug-in to process. Once the plug-in receives this notification, it would then call a framework function (for example, "Send Message" or "Get Message Size") within its esefifMessage()
function.
Upon receiving a "Send Message" request from the plug-in, the protocol server will call the function esefifSend()
in a loop, each time passing a chunk of the message, until the whole message has been passed. The plugin must collect these "chunks" and rebuild the message inside their esefifSend()
function.
To return a modified message back to the protocol server, the plug-in would call the "Receive Message" framework function, and the protocol server would in turn call the plug-in's esefifRecv()
function in a loop until the protocol server receives the complete modified message.
Plug-ins are written in C. The plug-in must be built as a shared library. A plug-in should contain the following functions:
Table 4-2 Mandatory Functions for Implementation by C Plug-Ins
Function | Mandatory? | Description |
---|---|---|
esefifInit() |
Yes |
The initialization function for the custom library. At a minimum, a global set of contexts must be initialized to pass between the Oracle mail protocol servers and the custom library. |
esefifClose() |
Yes |
Performs any necessary tasks prior to shutdown. For example, freeing resources allocated in |
esefifRegister() |
Yes |
Registers, in the context of the plug-in, all the mail protocol server routines the framework provides. |
esefifConnect() |
Yes |
SERVICE: Processing at the end of the connect request. |
esefifEnvelope() |
No |
SERVICE: Called when envelope information is received. |
esefifMessage() |
No |
This function is called by the protocol server when that protocol server has a message it wants the plug-in to process. |
esefifReset() |
Yes |
Processing at the end of each Message |
esefifSend() |
No |
Receives the message sent from the mail protocol server to the plug-in. |
esefifRecv() |
No |
Sends a modified (cleaned, appended, etc.) message received by the mail protocol server from the plug-in. |
All plug-in functions must be written to return one of the following four return values. Note: some functions do not modify or reject mail messages and hence do not return those codes.
Table 4-3 Return Codes for C Plug-In Functions
Code | Description |
---|---|
ESEFIF_SUCCESS |
Success. |
ESEFIF_REJECT |
Failure. A plug-in service function should return this code to the mail protocol server if it does not want the protocol server to allow the message to be sent or the connection to continue. |
ESEFIF_MODIFIED |
Message Modified. The return code a plug-in service function returns in order to tell the mail protocol server that it needs to modify or clean the message before the message should be allowed to continue. |
ESEFIF_ERROR |
Error. |
int esefifInit(void **ifgctx, void *efgctx, void **services, char *scanflags, char *sysflags);
Purpose
Initializes the plug-in.
Processes parameters from plug-in administration screens.
Allocates a global context for the plug-in.
Tells the mail protocol server what low-level services it supports.
Parameters
This is pointer to the global context of the plug-in. This context gets passed back to the plug-in by the Oracle mail protocol server every time a protocol server calls the plug-in and for every message.
This is pointer to the global context for the framework. This pointer must be passed in each framework function call.
This is a text string specifying the services supported by the plug-in and the name the function the protocol server must call to process the service. The syntax of the string is a single character ("E" or "F") followed by a colon followed by a list of services or service function names separated by commas. A string starting with "E:" is a variable number of elements listing the service names that the plug-in supports. A developer must write the plug-in using the expected function names. A string starting with "F:" is a fixed number of elements (eight) listing the function names that the plug-in developer expects the protocol server to call. When using the "F" option, the position 8 function (esefifReset) must be terminated with a comma. A developer will write up to nine functions. Any plug-in function can be renamed except the initialization function, which must be named "esefifInit".
Consider a plug-in written to look at the envelope and message of every message passed to it. The following two examples would be equivalent service strings for this plug-in:
"E:CONNECT,ENVELOPE,MESSAGE,RESET"
"F: , , , , esefifConnect, esefifEnvelope, esefifMessage, esefifReset,"
Note the terminating comma after esefifReset.
The following example would tell the protocol server calling the plug-in that the plug-in function names are different from the default.
"F:pi_close, pi_reg, pi_send, pi_recv, pi_connect, pi_env, pi_msg, pi_reset,"
This is a text string retrieved from the scanner flags field for plug-in (filter) policy management pages. These flags provide a way to pick up necessary administration variables.
This is a text string describing the system flags, or flags that are understood by the mail protocol server. The current release of the plug-in API only understands a single flag, repairmsg
, with possible values of 1 and 0 (e.g. repairmsg=1
). If the value is 1 then a protocol server will assume that it may get a request from the plug-in to modify a message. Otherwise it will ignore a request by the plug-in to receive a modified message.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
The initialization function is the one function written by the plug-in developer that must be called "esefifInit". This function must always be implemented.
void esefifClose(void *ifgctx);
Purpose
To shut down the plug-in.
To free a global context for the plug-in.
Parameters
This is a pointer to the global context of the plug-in.
Returns
none
int esefifRegister(void *ifgctx, int (*cb)(void*), void*,void*), unsigned int flags);
Purpose
To register function pointers to framework services.
Parameters
This is a pointer to the global context of the plug-in.
This is the function pointer to one of the twelve framework functions.
An integer specifying which function pointer is being passed in.
Table 4-5 Flags for esefifRegister() function
Flag | Framework Function |
---|---|
ESEFIF_CALLBACK_SENDMSG |
Send a message to the plug-in |
ESEFIF_CALLBACK_RECVMSG |
Receive modified message from the plug-in |
ESEFIF_CALLBACK_GETENVELOPE |
Get envelope information |
ESEFIF_CALLBACK_GETMSGSIZE |
Get the size of a message |
ESEFIF_CALLBACK_GETMSGID |
Get the mail store message ID of a message |
ESEFIF_CALLBACK_FREE |
Free memory that was allocated by the framework |
ESEFIF_CALLBACK_SETVERSION |
Set the plug-in history definition text |
ESEFIF_CALLBACK_GETMSGHDR |
Get the message's top-level header |
ESEFIF_CALLBACK_WRITELOG |
Log a message in the protocol server log file |
ESEFIF_CALLBACK_ALLOC |
Allocate memory |
ESEFIF_CALLBACK_ADDRCPT |
Add recipients to the recipient list |
ESEFIF_CALLBACK_DELRCPT |
Delete recipients from the recipient list |
ESEFIF_CALLBACK_GETPOLICYID |
Get policy identifier of the archive plug-in, if any |
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
The protocol server will call the registration function of a plug-in once for each framework function. The plug-in should update its global context with each function pointer.
int esefifSend(void *ifgctx, char *buf, int buflen);
Purpose
A wrapper function to copy a mail message sent from a protocol server to the plug-in.
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to the block of memory containing a chunk of the message.
The size of each chunk of the message.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
This function is a wrapper used by the Oracle mail protocol server to send a message to the plug-in. In order to not have to build a complete message (which could be megabytes in size) and then copy it over to the library, the scanning interface uses this send function to retrieve the message in chunks. The buflen parameter specifies the size of each chunk.
This function must be written to receive the message from the sending protocol server. The protocol server will call this wrapper function within a loop, each time passing it "buflen" bytes of the mail message until the complete message has been sent. The protocol server will then stop calling this function and the framework function will return.
int esefifRecv(void *ifgctx, char *buf, int *buflen);
Purpose
A wrapper function to copy a message received by a protocol server from a plug-in.
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to the block of memory containing a piece of the message.
The protocol server will set buflen to the maximum memory size it will accept, when it calls this function. The plug-in sets this value to the size of the buffer being returned. The protocol server will continue to call esefifRecv() until this function returns a NULL buf and a buflen of zero.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
This function is a wrapper function used by the Oracle mail protocol server to receive a message from the plug-in. In order to not have to allocate memory and move a complete message all at once (which could be megabytes in size), the interface uses this receive function to copy the message in chunks. This version of the interface sends in a maximum buffer size of 1024 bytes.
This function must be written to send a modified or cleaned message to the receiving protocol server. When a plug-in needs to return a modified message, it will call the framework function for sending the message. The protocol server will call this wrapper function within a loop, each time sending a piece of the mail message until the complete message has been sent. The protocol server will then stop calling this function and the framework function will return.
int esefifConnect(void *ifgctx, void **iflctx, void *efcbctx, char *host, char *ip, int flags, char *errmsg)
Purpose
Allocate thread specific local context
Process any connection information from the remote MTA or Client (SMTP specific)
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to the local context of this connection, which will hold information to be passed between processing (CONNECTION, ENVELOPE, MESSAGE, RESET).
This is a pointer to the filter framework context. This context is only sent to the plug-in as a part of the mandatory CONNECT service. The context needs to be stored in the local context of this connection so that it can be made available to the other services. A service cannot call a framework function without the filter framework context.
This is a text string containing the host name of the client (or remote process) that is contacting the protocol server. If the mail protocol server calling the plug-in is the housekeeper, which does not accept connections from remote clients or processes, then this is filled the name of the local host running the housekeeper.
This is a text string containing the decimal representation for the 32-bit IPv4 address ("nnn.nnn.nnn.nnn") of the client (or remote process) Internet protocol address that is contacting the protocol server. If the mail protocol server calling the plug-in is the housekeeper, which does not accept connections from remote clients or processes, then this is filled the name of the local host IP address.
Unused. Will always be zero.
The mail protocol server passes a pointer to a 512-byte buffer that holds error message text, should the plug-in need to reject the connection.
Returns
ESEFIF_SUCCESS Success - Allow connection ESEFIF_REJECT Failure - Reject connection ESEFIF_ERROR Error
Comments
Information available to the plug-in consists only of the bind information for the connection.
The plug-in developer must implement this service. The protocol server calls this function first, once for each session connection from a client or other protocol server. In the case of the housekeeper protocol server, the connect function will be called once (and first) for each message.
The plug-in developer must allocate a context (iflctx), for the scope of the connection, which will be passed to all other plug-in processes when the mail protocol server calls them. It must also populate the context with the pointer to efcbctx so these subsequent services have access to all the framework functions.
Framework functions available to the plug-in developer:
ESEFIF_CALLBACK_FREE - Free Memory
ESEFIF_CALLBACK_WRITELOG - Log a message to the logfile
ESEFIF_CALLBACK_ALLOC - Allocate Memory
int esefifEnvelope(void *ifgctx, char *envinfo, unsigned int flags, char *errmsg)
Purpose
To read and process the envelope information.
Parameters
This is a pointer to the connection context of the plug-in. This is the same context allocated and populated in the esefifConnect() function.
This is a pointer to the envelope information.
These flags can be used to specify envelope properties, such as sender-only or rcpts-only.
0 ALL env info (default). 1 HOST (envinfo will be hostname) 2 SENDER (envinfo will be sender) 3 RECIPIENT (envinfo will be <rcpt1>,<rcpt2>,... 4 AUTH (envinfo will be auth info) 5 HELO/EHLO DOMAIN (envinfo will be domain).
The mail protocol server passes a pointer to a buffer holding error message text that would be returned to the remote client or process in the case when the plug-in needs to reject the message based upon envelope information. The maximum buffer length is 512 bytes.
Returns
ESEFIF_SUCCESS Success - Allow Login ESEFIF_REJECT Failure - Reject Login ESEFIF_ERROR Error
Comments
This is the service function that Oracle mail protocol servers use to send envelope information to the plug-in. Envelope information is only persistent for the life of the message as it passes through a message transfer agent (MTA). Only the Oracle SMTP and LIST SERVER processes would have this information or call this function. The envelope information is returned in a single buffer, which must be parsed and understood by this function.
The buffer contains up to five different pieces of information. Each piece of information is a keyword/value pair in the syntax "<keyword>=<value>". Pairs are separated by the pipe (|) character. One field, the recipient list, can contain multiple values. In this case, each value is separated by the comma (,) character.
host=<hostname>|mailfrom=<sender info>|rcptto=(<rcpt1>,<rcpt2>...)[|authinfo=<authinfo>]|domain=<helo domain>[|resrcpt=(<rrcpt1>,<rrrcpt2>...)]
Table 4-6 Description of Envelope Keywords
Keyword | Description |
---|---|
Host |
The host name making the connection with the server and requesting the mail to be sent. An SMTP server configured to verify the connect request with a reverse DNS lookup will populate this field with the value returned by the DNS. Otherwise, this will contain the text for a decimal represented IPv4 address. |
mailfrom |
The sender's email address. This is the fully qualified email address from whom the message claims to be sent |
rcptto |
A list of comma delimited, fully qualified email addresses. The list in its entirety is enclosed in parentheses |
authinfo |
The fully qualified email address of the authenticated sender. This keyword/value pair is a part of the envelope only when the connection is authenticated. Optional. |
domain |
The helo or ehlo domain of the remote client or MTA. |
resrcpt |
A list of comma delimited, fully qualified resolved email addresses. The list in its entirety is enclosed in parentheses. |
The header buffer will not include white space. The four keywords are in lower case; the order is always be the same (host, mailfrom, rcptto, [authinfo], domain). The "authinfo" field is optional, and is only provided if the remote system has authenticated the SMTP connection. The "rcptto=" list will always be enclosed in parentheses, even when the mail is only being delivered to a single recipient.
Note: When a plug-in reaches the INCOMING high-level event control point, the "rcptto" list will include all recipients. However, the list will be unresolved. The list will include email addresses, and public distribution lists. When the plug-in reaches either the OUTGOING or LOCAL high-level event control points, the "rcptto" list will be completely resolved but could be a subset of the complete recipient list. If OUTGOING is reached, only those recipients external to the system will be listed. If LOCAL is reached, only those recipients in local domains will be listed.
Note: Adding and deleting recipient addresses are only available at the INCOMING high-level event control point.
Framework functions available to plug-in developer:
ESEFIF_CALLBACK_FREE - Free Memory
ESEFIF_CALLBACK_WRITELOG - Log message to logfile
ESEFIF_CALLBACK_ALLOC - Allocate Memory
ESEFIF_CALLBACK_ADDRCPT - Add Recipient
ESEFIF_CALLBACK_DELRCPT - Delete Recipient
int esefifMessage(void *ifgctx, unsigned int flags, char *errmsg)
Purpose
To read and process envelope, header, and message information.
Parameters
This is a pointer to the global context of the plug-in.
Unused. Always zero.
The mail protocol server passes a pointer to a 512-byte buffer holding error message text that would be returned to the remote client or process in the case when the plug-in needs to reject the message based upon envelope information.
Returns
ESEFIF_SUCCESS Success - Allow Message ESEFIF_REJECT Failure - Reject Message ESEFIF_MODIFIED Allow the modified message ESEFIF_ERROR Error
int esefifReset(void *ifgctx, unsigned int flags, char *errmsg)
Purpose
To free the thread-specific local context if it is the end of the connection
To clean up current message-specific data
Parameters
This is a pointer to the global context of the plug-in.
The mail protocol server passes a pointer to a 512-byte buffer holding error message text that would be returned to the remote client or process in the case when the plug-in needs to reject the message.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
All framework functions are available to the plug-in developer at this service point.
The plug-in framework provides various callback services to be used when developing plug-ins. Function pointers passed to the esefifRegistration() function are used to tell the plug-in code how to call these framework functions. Developers can make these functions globally available, either by defining global function pointers or by recording them in the global context for the plug-in. All framework functions share the same interface, each returns an integer, and each takes three void pointers as parameters.
int (*function)(void*, void*,void*);
Purpose
To tell the framework to send a copy of the message to the plug-in.
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to the local context of the plug-in.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Purpose
To tell the framework to accept a copy of the message from the plug-in. Replaces a message with a cleaned or modified message.
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to the local context of the plug-in.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Purpose
To tell the framework to send the envelope information to the plug-in.
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to a buffer containing the envelope information. (See "envinfo" parameter in esefifEnvelope() for the buffer syntax).
This is pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
This framework function implicitly asks the framework to allocate memory on behalf of the plug-in. The developer must free the memory "envinfo" points to if this function is called.
Purpose
To request from the framework the mail store identifier for the message.
Parameters
This is a pointer to the global context of the plug-in.
This is the mail store message identifier for the message.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Message IDs are Oracle-specific and unique to the mail store. This ID is not the RFC822 GUID of the message. A message is retained only once in each mail store, along with instance records for all recipients of the message who use that mail store. If an email message is sent to two recipients, each on a different mail store, then two copies of the message are stored, one for each store. Each store would have a distinct and different message identifier for the message. Should a plug-in require a globally unique message ID, it should call the framework function for getting the message header and retrieve the RFC822 GUID from the header.
Purpose
To request the framework to free any memory previously allocated by the framework for the plug-in
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to the memory to be freed.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Purpose
To insert a record in the ES_SCAN_HISTORY table
Parameters
This is a pointer to the global context of the plug-in.
This is a character string that contains the definition of the knowledge base of virus definitions. Maximum length of 240 characters.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Setting the history definition will create a record in the mail store schema. The record description is:
SCANNED - An Oracle date for when this framework function was called.
MSG_ID - The mail store message identifier for the message
SCANNER - The vendor name of the plug-in
DESCRIPTION - This text field
This record can be shared between plug-in instances on multiple mail protocol servers to ensure processing executes only once. For example, a plug-in that scans for viruses at the MTA as a message enters a mail store can create a record with definition text that describes the knowledge base of the virus definitions. The same vendor's plug-in could then scrub the mail store later on and bypass those messages which have already been checked with the same virus definition.
Purpose
To request from the framework the top-level header information for the message
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to a buffer that contains the top-level header.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Because the protocol server allocates the memory for this buffer, plug-ins that use this function must free the header buffer with the framework Free Memory function.
Purpose
To log a message in the mail protocol server log file
Parameters
This is a pointer to the global context of the plug-in.
This is a character string to be logged. The maximum length is 4000 bytes. The log string will have the filter library specified in the module parameter to identify which filter logged the message. The input string is logged as is.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Purpose
To request the framework to allocate memory for the plug-in.
Parameters
This is a pointer to the global context of the plug-in.
This is a pointer to the local context of the plug-in.
This is a pointer to the global context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
Purpose
To add additional email addresses to the recipient list for a message (blind carbon copy)
Parameters
This is a pointer to the global context of the plug-in.
Fully qualified email address to be added to the recipient list.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
This function can be called multiple times. Each call will add an additional email address.
Purpose
To remove an email address from the recipient list
Parameters
This is a pointer to the global context of the plug-in.
This is a fully qualified email address to be removed from the recipient list.
This is pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
This function can be called multiple times, each call removing an additional email address.
This framework function only works on envelope information. An address removed from the envelope may still exist in the header of the mail message.
Purpose
To request from the framework the size in bytes of the mail message currently being processed.
Parameters
Message size in bytes.
This is a pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
The plug-in can determine the size of the message prior to obtaining the message from the framework. This size can be used or buffer allocation for the message. This function is applicable in the context of esefifMessage()
and can be called multiple times.
Purpose
To request from the framework the policy identifier of the archive plug-in.
Parameters
A pointer to the global context of the plug-in.
Archive policy identifier.
A pointer to the local context of the framework.
Returns
ESEFIF_SUCCESS Success ESEFIF_REJECT Failure
Comments
The policy identifier should be used to index built by plug-in from the list of policies passed in scanner flags during plug-in initialization to fetch current information related to this policy such as mailbox, xheader. If the plug-in is not associated with any archive policy, -1 is returned as the policy identifier.
Testing a Process Plug-In
To test a filter, run it as a standalone program and send sample message input using the terminal (stdin), with a ctrl-D character to end the input. Once testing is complete, you can configure and use this filter as an OCS filter. This method will reduce troubleshooting time if errors appear in the Mail Server logs due to problems such as incorrect permissions, incorrect reading of input parameters, or incorrect status line in the output from the filter program.
Code Listing for Sample Script
The following C-shell script logs the command line arguments, receives the message from Mail server, saves it to a file and accepts the message by passing a status code of 0. The log file and input message file should be different for different invocations of the executable and they will be created in the same directory as the executable. The directory must have write access for this executable to create these files.
#!/bin/csh echo `date` >> $0.$$.log echo "dummyscript started with arguments:" $* >> $0.$$.log echo "Received Message on stdin:" >> $0.$$.msg # save the message to a file cat >> $0.$$.msg # accept the message echo "0" echo `date` >> $0.$$.log echo "dummyscript accepted the message" >> $0.$$.log # send version info in the following way # echo "0 [dummyscriptversion 1.0]" exit 0
C Plug-in
The following code sample demonstrates how to construct a plug-in to be called by the Mail Protocol Server. The code provides a rudimentary implementation of each of the twelve framework callback functions.
/* Copyright (c) 2005, Oracle Corporation. All rights reserved. */ /* NAME dummyif.c - An example dummy scanner/filter interface. DESCRIPTION An example dummy scanner/filter interface to illustrate usage: - Implements services CONNECT, ENVELOPE, MESSAGE, RESET - Handles multiple messages in single connect. - Fetches envelope/header/message size/message id from MTA - Gets message from MTA. - Inserts a dummy header field; If repairmsg flag is on, posts this message back to MTA. - Sets Version definition. - Output is written to /tmp/dummyif.<pid>.out where pid is the MTA's process id. */ #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> /*--------------------------------------------------------------------------- PRIVATE TYPES AND CONSTANTS ---------------------------------------------------------------------------*/ /* building shared library ** compile with -G option: cc -G dummyif.c -o dummyif.so ** use -g option for debugging: cc -g -G dummyif.c -o dummyif.so */ #define ESEF_VSCANIF_SUCCESS 0 #define ESEF_VSCANIF_FAIL 1 #define ESEF_VSCANIF_REPAIR 2 #define ESEF_VSCANIF_ERROR 3 typedef struct dummy_gcx { /* MTA's call back functions */ int (*frsend)(void*, void*,void*); int (*frrecv)(void*, void*,void*); int (*frgetenvl)(void*, char **,void*); int (*frgetmsgsize)(void*, int *,void*); int (*frgetmsgid)(void*, int *,void*); int (*frfreemem)(void*, void*,void*); int (*frsetversiondef)(void*, char *,void*); int (*frgethdr)(void*, char **,void*); int (*frlogmsg)(void*, char *,void*); int (*frallocmem)(void **, int ,void*); int (*fraddrcpt)(void*, char *,void*); int (*frdelrcpt)(void*, char *,void*); char services[1024]; void *efgctx; char scanflags[256]; char sysflags[256]; unsigned int repair; } dummy_gcx; typedef struct dummy_lcx { signed char *bufp; char *envbufp; int len; int pos; dummy_gcx *gctx; void *efcbctx; } dummy_lcx; FILE *fpout; /*--------------------------------------------------------------------------- STATIC FUNCTION DECLARATIONS ---------------------------------------------------------------------------*/ int esefifInit( void **ifgctx, void *efgctx, void **services, char *scanflags, char *sysflags) { dummy_gcx *gctx; int pid; char outfile[256]; pid = getpid(); sprintf(outfile,"/tmp/dummyif.%d.out",pid); fpout = fopen(outfile,"w"); if (!fpout) fpout=stdout; fprintf(fpout,"In esefifInit()\n"); gctx = *ifgctx = (void *) calloc(1,sizeof(dummy_gcx)); gctx->efgctx = efgctx; if (scanflags) strcpy(gctx->scanflags,scanflags); if (sysflags) strcpy(gctx->sysflags,sysflags); if (strstr(gctx->sysflags,"repairmsg=1")) gctx->repair = 1; fprintf(fpout,"scanflags=%s,sysflags=%s\n",gctx->scanflags,gctx->sysflags); sprintf(gctx->services,"E:CONNECT,ENVELOPE,MESSAGE,RESET"); *services = gctx->services; fflush(fpout); return ESEF_VSCANIF_SUCCESS; } void esefifClose(void *ifgctx) { fprintf(fpout, "In esefifClose()\n"); fflush(fpout); free(ifgctx); fclose(fpout); return; } int esefifRegister( void *ifgctx, int (*cb)(void*, void*,void*), unsigned int flags) { dummy_gcx *ifgctx1; fprintf(fpout,"In esefifRegister()\n"); ifgctx1 = (dummy_gcx *) ifgctx; switch (flags) { case 0: ifgctx1->frsend = cb; break; case 1: ifgctx1->frrecv = cb; break; case 2: ifgctx1->frgetenvl = (int (*) (void*, char **,void*)) cb; break; case 3: ifgctx1->frgetmsgsize = (int (*) (void*, int *,void*)) cb; break; case 4: ifgctx1->frgetmsgid = (int (*) (void*, int *,void*)) cb; break; case 5: ifgctx1->frfreemem = cb; break; case 6: ifgctx1->frsetversiondef = (int (*) (void*, char *,void*)) cb; break; case 7: ifgctx1->frgethdr = (int (*) (void*, char **,void*)) cb; break; case 8: ifgctx1->frlogmsg = (int (*) (void*, char *,void*)) cb; break; case 9: ifgctx1->frallocmem = (int (*) (void**, int ,void*)) cb; break; case 10: ifgctx1->fraddrcpt = (int (*) (void*, char *, void*)) cb; break; case 11: ifgctx1->frdelrcpt = (int (*) (void*, char *, void*)) cb; break; default: return ESEF_VSCANIF_ERROR; } fflush(fpout); return ESEF_VSCANIF_SUCCESS; } int esefifConnect( void *ifgctx, void **iflctx, void *efcbctx, char *host, char *ip, int flags, char *errmsg) { dummy_lcx *iflctx1; int size=0; dummy_gcx *ifgctx1; fprintf(fpout,"In esefifConnect()\n"); ifgctx1 = (dummy_gcx *) ifgctx; ifgctx1->frallocmem((void **)&(iflctx1),sizeof(dummy_lcx),ifgctx1->efgctx); memset(iflctx1,0, sizeof (dummy_lcx)); iflctx1->gctx = ifgctx;; *iflctx = iflctx1; iflctx1->efcbctx = efcbctx; if (host) fprintf(fpout,"esefifConnect(): Host=%s\n",host); if (ip) fprintf(fpout,"esefifConnect(): IP=%s\n",ip); fflush(fpout); return ESEF_VSCANIF_SUCCESS; } int esefifEnvelope( void *iflctx, char *envinfo, unsigned int flags, char *errmsg) { fprintf(fpout,"In esefifEnvelope()\n"); if (envinfo) fprintf(fpout, "esefifEnvelope(): envinfo=%s\n",envinfo); fflush(fpout); return ESEF_VSCANIF_SUCCESS; } int esefifAuth(void *iflctx, char *authinfo, unsigned int flags, char *errmsg) { fprintf(fpout,"In esefifAuth()\n"); fflush(fpout); return ESEF_VSCANIF_SUCCESS; } int esefifMessage(void *iflctx, unsigned int flags, char *errmsg) { int rc = ESEF_VSCANIF_SUCCESS; int msgsize, msgid, size=0; char versiondef[128] = "X-Dummy-VirusScan:01192004"; char *hdr = NULL; dummy_gcx *gctx; dummy_lcx *lctx; fprintf(fpout,"In esefifMessage()\n"); lctx = (dummy_lcx *)iflctx; gctx = lctx->gctx; /* SET DUMMY HEADER */ gctx->frallocmem((void **)&(lctx->bufp), 128, gctx->efgctx); lctx->len = 0; lctx->pos = 0; sprintf((char *)&(lctx->bufp[lctx->len]), "X-Dummy-VirusScan: 1\n"); lctx->len += 21; /* GET ENVELOPE */ fprintf(fpout, "esefifMessage(): calling MTA get envelope\n"); if (gctx->frgetenvl(gctx,&(lctx->envbufp),lctx->efcbctx)) { fprintf(fpout, "esefifMessage(): error in MTA get envelope\n"); rc = ESEF_VSCANIF_ERROR; goto exit; } else { fprintf(fpout, "esefifMessage(): Envelope Received=%s\n",lctx->envbufp); fflush(stdout); gctx->frfreemem(gctx,lctx->envbufp,gctx->efgctx); lctx->envbufp = NULL; } /* GET MESSAGE ID AND SIZE */ fprintf(fpout, "esefifMessage(): calling MTA msg size and msg Id\n"); if (gctx->frgetmsgsize(gctx,&msgsize,lctx->efcbctx)) { fprintf(fpout, "esefifMessage(): error in MTA get msg size\n"); rc = ESEF_VSCANIF_ERROR; goto exit; } else fprintf(fpout, "esefifMessage(): MSG SIZE=%d\n",msgsize); if (gctx->frgetmsgid(gctx,&msgid,lctx->efcbctx)) { fprintf(fpout, "esefifMessage(): error in MTA get msg id\n"); rc = ESEF_VSCANIF_ERROR; goto exit; } else fprintf(fpout, "esefifMessage(): MSG ID=%d\n",msgid); /* GET MESSAGE HEADER */ fprintf(fpout, "esefifMessage: calling MTA get header\n"); if (gctx->frgethdr(gctx, &hdr, lctx->efcbctx)) { fprintf(fpout, "esefifMessage(): error in MTA get header\n"); rc = ESEF_VSCANIF_ERROR; goto exit; } if (hdr) { fprintf(fpout, "esefifMessage(): Message Header=%s\n",hdr); } /* GET MESSAGE */ fprintf(fpout,"esefifMessage(): calling MTA send\n"); if (gctx->frsend(gctx, lctx, lctx->efcbctx)) { fprintf(fpout,"esefifMessage(): error in MTA send\n"); rc = ESEF_VSCANIF_ERROR; goto exit; } if (gctx->repair) /* if repair mode is on - post message with additional header field */ { /* MTA RECV */ rc = 2; fprintf(fpout,"esefifMessage(): calling MTA recv\n"); if (gctx->frrecv(gctx, lctx, lctx->efcbctx)) { fprintf(fpout,"esefifMessage(): error in MTA recv\n"); rc=ESEF_VSCANIF_ERROR; goto exit; } } /* set version def */ if (gctx->frsetversiondef(gctx,versiondef,lctx->efcbctx)) { fprintf(fpout,"esefifMessage(): error in MTA set version definition\n"); rc=ESEF_VSCANIF_ERROR; goto exit; } exit: if (lctx->bufp) { gctx->frfreemem(gctx,lctx->bufp,gctx->efgctx); lctx->bufp = NULL; } if (lctx->envbufp) { gctx->frfreemem(gctx,lctx->envbufp,gctx->efgctx); lctx->envbufp = NULL; } /* if header contains "virus" treat it as one for negative testing */ if (hdr) { if (strstr(hdr, "virus")) rc = ESEF_VSCANIF_FAIL; gctx->frfreemem(gctx,hdr,gctx->efgctx); } fflush(fpout); return rc; } int esefifSend(void *iflctx, char *buf, int buflen) { dummy_lcx *lctx; dummy_gcx *gctx; signed char *bufp; fprintf(fpout,"In esefifSend()\n"); lctx = (dummy_lcx *) iflctx; gctx = lctx->gctx; fprintf(fpout,"esefifSend(): buffer size=%d buffer=%s\n",buflen,buf); fflush(fpout); bufp = lctx->bufp; gctx->frallocmem((void **)&(lctx->bufp), (lctx->len+buflen), (lctx->gctx->efgctx)); memcpy(lctx->bufp,bufp,lctx->len); gctx->frfreemem(gctx, bufp, gctx->efgctx); memcpy(&(lctx->bufp[lctx->len]), buf, buflen); lctx->len += buflen; fflush(fpout); return ESEF_VSCANIF_SUCCESS; } int esefifRecv(void *iflctx, char *buf, int *buflen) { dummy_lcx *lctx; dummy_gcx *gctx; fprintf(fpout,"In esefifRecv()\n"); lctx = (dummy_lcx *) iflctx; gctx = lctx->gctx; if (lctx->len > *buflen) { memcpy(buf, &(lctx->bufp[lctx->pos]), *buflen); lctx->len -= *buflen; lctx->pos += *buflen; } else if (lctx->len > 0) { memcpy(buf, &(lctx->bufp[lctx->pos]), lctx->len); *buflen = lctx->len; lctx->pos += lctx->len; lctx->len =0; } else { *buflen = 0; gctx->frfreemem(gctx,lctx->bufp,gctx->efgctx); lctx->bufp = NULL; } buf[*buflen] = '\0'; fprintf(fpout,"esefifRecv(): buffer size=%d buffer=%s\n", *buflen, buf); fflush(fpout); return *buflen; } int esefifReset(void *iflctx, unsigned int flags, char *errmsg) { dummy_lcx *lctx; dummy_gcx *gctx; fprintf(fpout,"In esefifReset()\n"); lctx = (dummy_lcx *) iflctx; gctx = lctx->gctx; if (lctx->bufp) { gctx->frfreemem(gctx, lctx->bufp, gctx->efgctx); lctx->bufp = NULL; } if (lctx->envbufp) { gctx->frfreemem(gctx, lctx->envbufp, gctx->efgctx); lctx->envbufp = NULL; } lctx->len = 0; lctx->pos = 0; if (flags == 1) { gctx->frfreemem(gctx, lctx, gctx->efgctx); } fflush(fpout); return ESEF_VSCANIF_SUCCESS; } /* end of file dummyif.c */