2479 lines
98 KiB
Plaintext
2479 lines
98 KiB
Plaintext
-----------------------------------------------------------------------
|
|
Mercury Daemon Interface for Mercury/32 v2.21 and later
|
|
-----------------------------------------------------------------------
|
|
Mercury Mail Transport System,
|
|
Copyright (c) 1993-99, David Harris, All Rights Reserved.
|
|
-----------------------------------------------------------------------
|
|
|
|
|
|
Contents
|
|
|
|
1: Introduction
|
|
1.1 - What is a Daemon?
|
|
1.2 - What uses could I have for a Daemon?
|
|
|
|
2: Technical overview
|
|
2.1 - Basic structure of a Daemon
|
|
2.2 - What do I need to write a Daemon?
|
|
2.3 - Installing and invoking a Daemon
|
|
|
|
3: Using the Mercury interface
|
|
3.1 - Including header files
|
|
3.2 - The M_INTERFACE structure
|
|
3.3 - Calling functions
|
|
|
|
4: Advanced topics
|
|
4.1 - DAEMON.INI
|
|
4.2 - "Resident" Daemons
|
|
4.3 - "Global" Daemons
|
|
4.4 - Daemon configuration
|
|
4.5 - Daemon Domains
|
|
|
|
5: Function reference
|
|
5.1 - General-purpose functions
|
|
get_variable
|
|
is_local_address
|
|
is_group
|
|
parse_address
|
|
extract_one_address
|
|
extract_cqtext
|
|
dlist_info
|
|
send_notification
|
|
get_delivery_path
|
|
get_date_and_time
|
|
write_profile
|
|
module_state
|
|
|
|
5.2 - Job-control and management functions
|
|
ji_scan_first_job
|
|
ji_scan_next_job
|
|
ji_end_scan
|
|
ji_open_job
|
|
ji_close_job
|
|
ji_rewind_job
|
|
ji_dispose_job
|
|
ji_process_job
|
|
ji_delete_job
|
|
ji_abort_job
|
|
ji_get_job_info
|
|
ji_create_job
|
|
ji_add_element
|
|
ji_add_data
|
|
ji_get_data
|
|
ji_get_next_element
|
|
ji_set_element_status
|
|
ji_set_element_resolvinfo
|
|
ji_set_diagnostics
|
|
ji_get_diagnostics
|
|
ji_increment_time
|
|
ji_get_job_by_id
|
|
ji_get_job_times
|
|
|
|
5.3 - Network and user database query functions
|
|
get_first_group_member
|
|
get_next_group_member
|
|
end_group_scan
|
|
is_valid_local_user
|
|
is_group_member
|
|
get_first_user_details
|
|
get_next_user_details
|
|
get_user_details
|
|
end_user_scan
|
|
read_pmprop
|
|
change_ownership
|
|
begin_single_delivery
|
|
end_single_delivery
|
|
create_object
|
|
verify_password
|
|
set_password
|
|
|
|
5.4 - Miscellaneous functions
|
|
mercury_command
|
|
get_date_string
|
|
rfc822_time
|
|
rfc821_time
|
|
select_printer
|
|
print_file
|
|
|
|
5.5 - File I/O and parsing routines
|
|
fm_open_file
|
|
fm_open_message
|
|
fm_close_message
|
|
fm_gets
|
|
fm_getc
|
|
fm_ungetc
|
|
fm_read
|
|
fm_getpos
|
|
fm_setpos
|
|
fm_get_folded_line
|
|
fm_find_header
|
|
fm_extract_message
|
|
parse_header
|
|
mime_prep_message
|
|
parse_mime
|
|
free_mime
|
|
fake_imessage
|
|
decode_mime_header
|
|
encode_mime_header
|
|
encode_base64_str
|
|
decode_base64_str
|
|
|
|
5.6 - Message composition routines
|
|
om_create_message
|
|
om_dispose_message
|
|
om_add_field
|
|
om_add_attachment
|
|
om_write_message
|
|
om_send_message
|
|
Notes on composing messages
|
|
|
|
5.7 - Statistics and logging interface
|
|
st_register_module
|
|
st_unregister_module
|
|
st_create_category
|
|
st_remove_category
|
|
st_set_hcategory
|
|
st_set_category
|
|
st_get_next_module
|
|
st_get_next_category
|
|
st_get_category_data
|
|
st_export_stats
|
|
logstring
|
|
logdata
|
|
|
|
|
|
Appendix A: Technical issues
|
|
|
|
|
|
|
|
-----------------------------------------------------------------------
|
|
1: Introduction
|
|
-----------------------------------------------------------------------
|
|
|
|
1.1 - What is a Daemon?
|
|
|
|
A Mercury/32 "Daemon" (a term inherited from the unix world) is a program
|
|
that provides extended services within the Mercury/32 Mail Transport
|
|
System. Daemons are associated with a particular e-mail address, and
|
|
when a message is sent to that address, Mercury invokes the Daemon to
|
|
process it.
|
|
|
|
A Daemon has access to extensive internal Mercury services, and can
|
|
easily perform complex operations such as address parsing and message
|
|
creation using simple function calls.
|
|
|
|
Daemons are implemented as Windows DLLs that (in their simplest form)
|
|
export a single function. There are no restrictions on what they can do,
|
|
and they have access to the full range of Windows programming services.
|
|
|
|
|
|
1.2 - What uses could I have for a Daemon?
|
|
|
|
The most obvious use for a Daemon is to perform custom processing on mail
|
|
messages; for instance, you might create a Daemon that accepts orders by
|
|
e-mail, checks their validity, verifies a credit card number, then
|
|
submits the order to a central database for processing. Another example:
|
|
you might create a Daemon that sends faxes: when a message arrives, the
|
|
Daemon looks for a fax number on the first line, then calls some other
|
|
service on the workstation and asks it to send the remainder of the mail
|
|
message as a fax to that number.
|
|
|
|
The only real limit on the uses a Daemon might have is your imagination
|
|
and the extent to which you are prepared to do some Windows programming
|
|
to realise what you have imagined.
|
|
|
|
|
|
-----------------------------------------------------------------------
|
|
2: Technical Overview
|
|
-----------------------------------------------------------------------
|
|
|
|
2.1 - Basic structure of a Daemon
|
|
|
|
In its simplest form, a Daemon is a simple 32-bit Windows DLL that
|
|
exports a single function, called "daemon". To invoke the Daemon, Mercury
|
|
loads the DLL and calls the "daemon" function with a reference to the
|
|
mail message (or "job"), a parameter block, and the delivery address that
|
|
triggered the call (so the same Daemon can be attached to multiple e-mail
|
|
addresses and can distinguish between them as required).
|
|
|
|
The prototype for the "daemon" function is as follows:
|
|
|
|
short _export daemon (void *job, M_INTERFACE *m, char *address,
|
|
char *parameter);
|
|
|
|
"job" is a handle to the mail job that triggered the call to the
|
|
daemon. Using this handle, you can access the data in the message,
|
|
by passing it to functions like "ji_get_data" (see below). You
|
|
must not close or delete this job. Note: on entry to your daemon
|
|
function, this job will be open - you should not attempt to open
|
|
it using "ji_open_job", nor should you close it.
|
|
|
|
"m" points to an M_INTERFACE structure: this structure contains pointers
|
|
to various Mercury internal functions that your Daemon can use to
|
|
parse addresses, query information and send mail.
|
|
|
|
"address" points to the address that triggered this invocation of the
|
|
Daemon. This allows a single Daemon to service multiple addresses,
|
|
and to adjust its behaviour depending on which address it is
|
|
servicing at any given time. You must not alter the contents of
|
|
this string in any way.
|
|
|
|
"parameter" points to any optional data specified in the Daemon's
|
|
alias entry (see below for more information on optional
|
|
parameters). This parameter will point to an empty string ("") if
|
|
there are no parameters. The maximum length of parameter data is
|
|
128 characters.
|
|
|
|
When the "daemon" function returns control to Mercury, Mercury will
|
|
unload the Daemon's DLL and delete the job - no further attempt will be
|
|
made to deliver it. The return value from the "daemon" function is
|
|
currently ignored and must be set to 0. The return value may be
|
|
meaningful in future.
|
|
|
|
|
|
2.2 - What do I need to write a Daemon?
|
|
|
|
A Daemon is simply a standard Windows DLL that exports a single entry
|
|
point; as such, you can use most standard tools to create one. The
|
|
functions exported to the Daemon via the M_INTERFACE structure all use
|
|
the C calling convention and expect C-type parameters (so, strings are
|
|
NUL-terminated arrays of characters).
|
|
|
|
The most logical tools for developing Daemons are Borland C++ v4.5 or
|
|
later, Borland C++/Builder, or Microsoft Visual C++. The sample code
|
|
provided with this documentation has been developed and tested using
|
|
Borland C++ v5.02.
|
|
|
|
|
|
2.3 - Installing and invoking a Daemon
|
|
|
|
Installing a Daemon so that Mercury can invoke it requires the creation
|
|
of an alias in a special form. The reason Daemons can only be invoked
|
|
via an alias is to ensure that only approved Daemons are run, for
|
|
security reasons. Aliases can be easily created and maintained from
|
|
within Mercury/32, using the "Aliases" option on the "Configuration"
|
|
menu.
|
|
|
|
The alias must be of the following form:
|
|
|
|
daemon_address@host.domain == daemon:path_to_dll[;parameter]
|
|
|
|
"daemon_address" should be whatever address will invoke the Daemon, while
|
|
"path_to_dll" should be the fully-qualified path to the Daemon's DLL
|
|
file. If your Daemon needs a parameter passed to it when its "daemon"
|
|
function is invoked, you can specify that parameter by placing a
|
|
semicolon after the filename, followed by the text you want your Daemon
|
|
to receive. The maximum length of the parameter is 128 characters.
|
|
|
|
Example: you wish to install a Daemon called "cookie"; the DLL file for
|
|
the Daemon is "c:\mercury\daemons\cookie.dll", and your domain is
|
|
"biscuit.com". You would create the following alias:
|
|
|
|
cookie@biscuit.com == daemon:c:\mercury\daemons\cookie.dll
|
|
|
|
If the Daemon's DLL file is found in the same directory as MERCURY.EXE,
|
|
then you can omit the path from the DLL's file specification.
|
|
|
|
Daemons can add aliases to or remove aliases from the system alias file
|
|
by themselves: an elegantly-written Daemon would provide a configuration
|
|
interface that automates the process of adding the aliases, rather than
|
|
relying on the user to do it.
|
|
|
|
|
|
-----------------------------------------------------------------------
|
|
3: Using the Mercury interface
|
|
-----------------------------------------------------------------------
|
|
|
|
3.1 - Including header files
|
|
|
|
In order to use the Mercury Daemon interface, you will need to add the
|
|
following line near the top of your C or C++ source file:
|
|
|
|
#include "daemon.h"
|
|
|
|
Note that you must include this line *after* you have included the
|
|
standard "windows.h" master header file.
|
|
|
|
|
|
3.2 - The M_INTERFACE structure
|
|
|
|
When your Daemon is invoked, Mercury passes it a large structure called
|
|
an M_INTERFACE. This structure contains some static data, and a number of
|
|
pointers to internal Mercury functions that your Daemon can call.
|
|
|
|
"dsize" The "dsize" parameter is the size of the M_INTERFACE
|
|
structure in bytes. A Daemon can use this as part of a version-
|
|
checking process.
|
|
|
|
"vmajor" The major version number of the copy of Mercury that is
|
|
running. For instance, for Mercury v2.15, this value will be "2".
|
|
|
|
"vminor" The minor version number of the copy of Mercury that is
|
|
running. For instance, for Mercury v2.15, this value will be "15".
|
|
|
|
|
|
3.3 - Calling functions
|
|
|
|
The remaining variables in the M_INTERFACE structure are pointers to
|
|
functions within Mercury that the Daemon can call to access core
|
|
services. To call one of these functions, simply use the pointer as if it
|
|
were a normal function - so, if you want to call the "get_variable"
|
|
function to retrieve the GV_QUEUENAME variable, you would use this line
|
|
of code
|
|
|
|
char *str
|
|
str = (char *) (m->get_variable (GV_QUEUENAME));
|
|
|
|
Note the C cast to a (char *) - this simply suppresses compiler warnings,
|
|
because the "get_variable" function always returns its results as a DWORD
|
|
value.
|
|
|
|
|
|
-----------------------------------------------------------------------
|
|
4: Advanced topics
|
|
-----------------------------------------------------------------------
|
|
|
|
4.1 - DAEMON.INI
|
|
|
|
For advanced Daemon functions such as Residency and Configuration,
|
|
Mercury scans a file called DAEMON.INI for parameters. DAEMON.INI uses a
|
|
slightly different syntax from MERCURY.INI, in that it uses "=" as a
|
|
separator between keyword and parameter instead of the ":" used in
|
|
MERCURY.INI. This design difference is intended to allow installers and
|
|
Daemons to use the Windows WritePrivateProfileString and
|
|
GetPrivateProfileString API functions to update and access the file.
|
|
|
|
|
|
4.2 - "Resident" Daemons
|
|
|
|
There may be occasions when a Daemon operates better by remaining in
|
|
memory at all times, instead of being loaded and unloaded as required.
|
|
Internally, a "Resident" Daemon is no different from a normal Daemon,
|
|
except that it can optionally export a "startup" function that Mercury
|
|
will call the when the Daemon is loaded.
|
|
|
|
To install a "Resident" Daemon, add a [Daemons] section to a file called
|
|
DAEMON.INI in the same directory as MERCURY.EXE, and include a name and the
|
|
full path to your Daemon's DLL on a line in that section.
|
|
|
|
Example: your Daemon is called "Cookie Daemon" and is located in
|
|
C:\MERCURY\DAEMONS\COOKIE.DLL
|
|
|
|
[Daemons]
|
|
Cookie Daemon = c:\mercury\daemons\cookie.dll
|
|
|
|
Mercury will load the Daemon at startup and will not unload it until
|
|
exiting. The Daemon can export a function with the following prototype:
|
|
|
|
short _export startup (M_INTERFACE *mi, UINT_32 *flags,
|
|
char *name, char *param)
|
|
|
|
If this function is present in the DLL, Mercury will call it with an
|
|
Interface block as soon as the DLL is loaded. Note that the Interface
|
|
block is not persistent - if you need to store it for later use, you must
|
|
allocate your own storage and make a copy of the structure in it.
|
|
|
|
The "flags" parameter to "startup" is a location where the Daemon can
|
|
return certain indicators about itself. At the time of writing this
|
|
specification, no flag values are defined - you should write 0 into the
|
|
location pointed to by the "flags" pointer.
|
|
|
|
The "name" parameter is the name string defined for the Daemon in
|
|
DAEMON.INI - you will typically use this if you have to create a message
|
|
box or error dialog.
|
|
|
|
The "param" parameter to "startup" is an optional string, presumably
|
|
containing configuration or run data for your Daemon. You can specify a
|
|
parameter by placing a semicolon and the parameter after the name of the DLL
|
|
in the entry in the [Daemons] section; so, using our Cookie Daemon above
|
|
as an example, if you wanted to pass the parameter "autocookie" to the
|
|
startup function, you would have an entry like this in DAEMON.INI:
|
|
|
|
[Daemons]
|
|
Cookie Daemon = c:\mercury\daemons\cookie.dll;autocookie
|
|
|
|
If a Daemon has no parameters, an empty string ("") will be passed in
|
|
"param". The maximum length of parameter data is 128 characters.
|
|
|
|
The "daemon" function of a Resident Daemon is called in exactly the same
|
|
way as it would be for a normal Daemon - the only difference is that the
|
|
Daemon's DLL is not loaded and unloaded as part of the process. A
|
|
Resident Daemon will commonly create a copy of the job in a file using
|
|
the "fm_extract_message" function, then spin off a thread to process the
|
|
file at its leisure.
|
|
|
|
Resident Daemons may fire threads at will if they wish to perform routine
|
|
processing (this is usually done from the "startup" function). All the
|
|
functions in the M_INTERFACE parameter block are thread-safe.
|
|
|
|
Resident Daemons may export a function called "closedown": if they do,
|
|
Mercury will call it as part of its shutdown process prior to unloading
|
|
the resident Daemon. The function should have this protoype:
|
|
|
|
short _export closedown (M_INTERFACE *m, DWORD code,
|
|
char *name, char *param);
|
|
|
|
The "code" parameter is reserved for future use and should be ignored
|
|
at present. The return from this function is currently unused but must
|
|
be set to 0. Only Resident Daemons (including "Global" Daemons) will
|
|
have this function called.
|
|
|
|
The "name" parameter is the name string defined for the Daemon in
|
|
DAEMON.INI - you will typically use this if you have to create a message
|
|
box or error dialog.
|
|
|
|
The "param" parameter is the same as was passed to the "startup" function
|
|
- see above for more details. If a Daemon has no parameters, an empty
|
|
string ("") will be passed in "param". The maximum length of parameter
|
|
data is 128 characters.
|
|
|
|
|
|
4.3 - "Global" Daemons
|
|
|
|
A "Global" Daemon is a specialized form of Resident Daemon that is
|
|
passed all messages processed by the Mercury core module. Global Daemons
|
|
are called before any other processing is done on the job, and can
|
|
instruct Mercury to process, delete or defer a job through their return
|
|
value. Examples of uses for Global Daemons include a Daemon that scans
|
|
all incoming and outgoing mail for viruses, or a Daemon that makes
|
|
archival copies of all incoming and outgoing mail for auditing purposes.
|
|
|
|
To install a "Global" Daemon, add a [Global Daemons] section to a file
|
|
called DAEMON.INI in the same directory as MERCURY.EXE, and include a
|
|
name and the full path to your Daemon's DLL on a line in that section.
|
|
|
|
Example: your Daemon is called "Spam Killer" and is located in
|
|
C:\MERCURY\DAEMONS\SPAMKILL.DLL
|
|
|
|
[Global Daemons]
|
|
Spam Killer = c:\mercury\daemons\spamkill.dll
|
|
|
|
Global Daemons are always Resident Daemons - see the preceding section
|
|
for information on this. When a Global Daemon's "daemon" function is
|
|
called, the "address" parameter is always set to "[Global]".
|
|
|
|
A Global Daemon may not delete a job directly, but it can return the
|
|
following values to Mercury to control the processing of the job:
|
|
|
|
0 - Process the job normally
|
|
1 - Delete the job without further processing
|
|
2 - Defer the job for the system defer time
|
|
|
|
If a Global Daemon instructs Mercury to delete a job, the job is deleted
|
|
at once, without further ado. No error notification is sent, nor is there
|
|
any indication other than a comment on the core module console display
|
|
that the job has been killed.
|
|
|
|
|
|
4.4 - Daemon configuration
|
|
|
|
Daemons may wish to add a configuration option to the Mercury
|
|
"Configuration" menu to allow the user to change settings or otherwise
|
|
control the behaviour of the Daemon. To do this, add a [Daemon_Config]
|
|
section to a file called DAEMON.INI in the same directory as MERCURY.EXE,
|
|
and include a name and the full path to your Daemon's configuration DLL
|
|
on a line in that section. The name is used to create your Daemon's
|
|
configuration menu line.
|
|
|
|
Example: your Daemon's configuration module is called "Cookie Daemon"
|
|
and is located in C:\MERCURY\DAEMONS\COOKIECF.DLL
|
|
|
|
[Daemon_Config]
|
|
Cookie Daemon = c:\mercury\daemons\cookiecf.dll
|
|
|
|
The configuration DLL can be either your Daemon's main processing DLL, or
|
|
a subsidiary DLL that only performs configuration functions - the choice
|
|
of which method to use will depend on your needs.
|
|
|
|
Mercury will add your Daemon's name to its Configuration menu, and when
|
|
the option is selected, will load your DLL and look for a function with
|
|
the following prototype:
|
|
|
|
short _export configure (M_INTERFACE *mi, char *name,
|
|
char *param);
|
|
|
|
"M_INTERFACE" is a regular parameter block. "name" is the name defined
|
|
in the [Daemon_Config] section (this allows the same DLL to service
|
|
multiple functions keyed on the name).
|
|
|
|
The "param" parameter to "config" is an optional string, presumably
|
|
containing configuration or run data for your Daemon. You can specify a
|
|
parameter by placing a semicolon and the parameter after the name of the
|
|
DLL in the entry in the [Daemons] section; so, using our Cookie Daemon
|
|
above as an example, if you wanted to pass the parameter "autoconfig" to
|
|
the config function, you would have an entry like this in DAEMON.INI:
|
|
|
|
[Daemon_Config]
|
|
Cookie Daemon = c:\mercury\daemons\cookiecf.dll;autoconfig
|
|
|
|
If a Daemon has no parameters, an empty string ("") will be passed in
|
|
"param". The maximum length of parameter data is 128 characters.
|
|
|
|
If your DLL refers to a Resident Daemon, Mercury will look for the
|
|
configuration function in the loaded copy and will not load the DLL
|
|
again. If your DLL needs to be loaded to perform configuration, its
|
|
"startup" function will not be called.
|
|
|
|
Both resident and non-resident Daemons can use configuration services if
|
|
they wish. What the "configure" function does when called is up to the
|
|
individual developer, but it is normal for it to show a dialog allowing
|
|
the user to change its configuration options.
|
|
|
|
|
|
4.5 - Daemon Domains
|
|
|
|
Occasionally, you may wish to have a Daemon that services all mail sent
|
|
to a particular subdomain. As an example, imagine a fax server Daemon
|
|
that treats the address portion as a fax number: for a server of this
|
|
kind, you would want to be able to send any "username" and have it
|
|
processed by the Daemon.
|
|
|
|
To create a "Daemon Domain" of this kind, select the "Mercury Core Module"
|
|
configuration option on the "Configuration" menu, and locate the group of
|
|
controls near the bottom of the dialog labelled "Domains recognized as
|
|
local by this server". Create a new domain entry where the "Domain name"
|
|
portion is the domain name to be handled by your Daemon, and the
|
|
"Host/server" portion is "daemon:path_to_dll". Once this domain entry has
|
|
been created, all mail sent to any user at the domain you have defined
|
|
will result in the Daemon being invoked.
|
|
|
|
Example: you have a fax server Daemon, C:\MERCURY\FAX.DLL; you want
|
|
all mail addressed to "fax.biscuit.com" to be passed to this Daemon.
|
|
You would create the following domain definition:
|
|
|
|
Host/Server Domain name
|
|
daemon:c:\mercury\fax.dll fax.biscuit.com
|
|
|
|
Note that this kind of operation will almost always require special name
|
|
server entries called "MX Entries" to advertise the domain name - contact
|
|
your service provider for more details on creating MX entries.
|
|
|
|
|
|
-----------------------------------------------------------------------
|
|
5: Function reference
|
|
-----------------------------------------------------------------------
|
|
|
|
5.1 - General-purpose functions
|
|
|
|
-------------------------------
|
|
DWORD get_variable (int index);
|
|
-------------------------------
|
|
|
|
Returns the value of an internal Mercury variable. The following
|
|
values can be passed for "index":
|
|
|
|
GV_QUEUENAME (1) (Returns "char *")
|
|
The name of the directory where Mercury's queue manager is
|
|
looking for jobs. The return value is a (char *) pointing to
|
|
a path, which may be in either drive letter or UNC format.
|
|
|
|
GV_SMTPQUEUENAME (2) (Returns "char *")
|
|
The name of the directory where Mercury's queue manager is
|
|
looking for outgoing mail jobs. This variable is not meaningful
|
|
under current versions of Mercury and should not be used.
|
|
|
|
GV_MYNAME (3) (Returns "char *")
|
|
The Internet name for this copy of Mercury (the domain portion
|
|
it will use when forming addresses).
|
|
|
|
GV_TTYFONT (4) (Returns "HFONT")
|
|
A handle to the font Mercury is currently using to draw text
|
|
in console listings.
|
|
|
|
GV_MAISERNAME (5) (Returns "char *")
|
|
The name of the Mercury mail server (usually "MAISER")
|
|
|
|
GV_FRAMEWINDOW (6) (Returns "HWND")
|
|
The handle to the Mercury Frame window: Daemons can use this
|
|
value to send messages to the Mercury core process.
|
|
|
|
GV_SYSFONT (7) (Returns "HFONT")
|
|
A handle to the font Mercury is using by default to draw
|
|
controls and general dialog text.
|
|
|
|
GV_BASEDIR (8) (Returns "char *")
|
|
Returns the directory from which MERCURY.EXE was run.
|
|
|
|
GV_SCRATCHDIR (9) (Returns "char *")
|
|
Returns a temporary working directory if one has been defined
|
|
in Mercury. If no temporary directory has been defined, this
|
|
value returns the same value as "GV_BASEDIR".
|
|
|
|
Daemons must treat all returned values as read-only and must not
|
|
attempt to modify them.
|
|
|
|
|
|
--------------------------------------------------------------
|
|
int is_local_address (char *address, char *uic, char *server);
|
|
--------------------------------------------------------------
|
|
|
|
Determine whether or not the address in "address" refers to a local
|
|
account (i.e, one to which Mercury can complete final delivery).
|
|
|
|
"uic" Receives the local username matching the address when
|
|
the function is successful. Allocate at least 256
|
|
characters for this string.
|
|
|
|
"server" Receives network-specific information associated with the
|
|
local user when the function is successful. This value may
|
|
have to be passed to other functions. Allocate at least
|
|
256 characters for this string.
|
|
|
|
Returns: 2 if the address is non-local but served by an alias
|
|
1 if the address is local;
|
|
0 if the address is not local;
|
|
-1 on error (local domain but no such user)
|
|
|
|
This function resolves aliases and synoyms, and performs any
|
|
necessary network lookups to validate the address.
|
|
|
|
|
|
---------------------------------------------------------------
|
|
int is_group (char *address, char *host, char *gname);
|
|
---------------------------------------------------------------
|
|
|
|
Determine whether the address contained in "address" refers to a valid
|
|
group to which Mercury can perform delivery.
|
|
|
|
"address" The simplified form of the address, with no domain portion
|
|
(so, pass "everyone", not "everyone@host.domain").
|
|
|
|
"host" Receives network-specific host information when the
|
|
function is successful. You may need to pass this value
|
|
to other functions.
|
|
|
|
"gname" Receives the name of the group on success.
|
|
|
|
Returns 1 if the address refers to a known group
|
|
0 if the address does not refer to a known group
|
|
|
|
|
|
----------------------------------------------------------
|
|
int parse_address (char *target, char *source, int limit);
|
|
----------------------------------------------------------
|
|
|
|
Reduce an RFC822 address to its simplest form, by discarding any
|
|
textual components it contains.
|
|
|
|
Example
|
|
"David Harris" (Pegasus Mail Author) <david@pmail.gen.nz>
|
|
would be reduced by this function to
|
|
david@pmail.gen.nz
|
|
|
|
"source" points to the address that is to be reduced. This string
|
|
is not changed as part of the process.
|
|
|
|
"target" points to the location where the reduced address should be
|
|
written. You may pass the same value as "source" if you
|
|
want to overwrite the address with the reduced form.
|
|
|
|
"limit" the maximum length of the resulting string. You should
|
|
pass the allocated size of "target" less one.
|
|
|
|
Returns 1 if the reduction was successful
|
|
0 on error (the address was malformed in some way)
|
|
|
|
|
|
---------------------------------------------------------------
|
|
int extract_one_address (char *dest, char *source, int offset);
|
|
---------------------------------------------------------------
|
|
|
|
Given a string potentially containing multiple addresses separated
|
|
by commas, extract the next address from the string into "dest".
|
|
The first time you call this function, pass zero for "offset"; for
|
|
each subsequent call, pass the value returned by the previous call
|
|
to the function, until it returns 0.
|
|
|
|
"dest" receives the next address from the string
|
|
|
|
"source" The string containing comma-separated addresses
|
|
|
|
"offset" 0 on the first call, the return from the previous call
|
|
to the function on subsequent calls.
|
|
|
|
Returns: > 0 on success ("dest" contains a valid address)
|
|
0 when no more addresses exist ("dest" is invalid)
|
|
|
|
|
|
--------------------------------------------------------
|
|
void extract_cqtext (char *dest, char *source, int len);
|
|
--------------------------------------------------------
|
|
|
|
Given a string containing a single address, strip out the address
|
|
part and return only the extra textual information.
|
|
|
|
Example:
|
|
"David Harris" <david@pmail.gen.nz>
|
|
will be reduced by this function to
|
|
David Harris
|
|
|
|
"dest" receives the reduced textual form
|
|
|
|
"source" points to the address to reduce
|
|
|
|
"len" the maximum number of characters to write into "dest".
|
|
|
|
Returns: Nothing.
|
|
|
|
|
|
------------------------------------------------------
|
|
int dlist_info (DLIST *dlist, char *lname, int number,
|
|
char *address, char *errbuf, LIST *modlist);
|
|
------------------------------------------------------
|
|
|
|
Returns information about the distribution list named in "lname".
|
|
For information on the DLIST data structure, see "daemon.h".
|
|
|
|
"dlist" points to a DLIST data structure into which the data
|
|
for the list should be written.
|
|
|
|
"lname" points to the simple name of the list (no domain part).
|
|
|
|
"number" if "lname" is NULL, then this variable is used to
|
|
determine which list to retrieve. Calling functions
|
|
can iterate through all the lists on the server by
|
|
setting "lname" to NULL then repeatedly incrementing
|
|
this variable in successive calls.
|
|
|
|
"address" if non-NULL, contains a simplified address form which
|
|
should be compared with the list of moderators for the
|
|
list. If the address matches any moderator in the list,
|
|
the "matched" field of the DLIST structure will be set
|
|
to 1.
|
|
|
|
"errbuf" receives a textual error message if the function fails.
|
|
Allocate at least 128 characters for this variable.
|
|
|
|
"modlist" points to a LIST data structure that receives a list
|
|
of all the moderators defined for the list. This value
|
|
can be NULL.
|
|
|
|
Returns: 0 on success
|
|
-1 on failure
|
|
|
|
Note the non-standard return value convention for this function.
|
|
|
|
|
|
-------------------------------------------------------------------
|
|
void send_notification (char *username, char *host, char *message);
|
|
-------------------------------------------------------------------
|
|
|
|
Send a short, one-line message directly to the given user.
|
|
|
|
"username" a username, as returned by "is_local_user"
|
|
|
|
"host" network-specific host information as returned by
|
|
"is_local_user".
|
|
|
|
"message" A message of up to 65 characters to send to the user
|
|
|
|
Returns Nothing
|
|
|
|
This function only works if a network enabler supporting broadcast
|
|
messages (such as the NetWare 3.x and 4.x enablers) has been installed
|
|
and is currently loaded in Mercury/32.
|
|
|
|
|
|
---------------------------------------------------------------
|
|
int get_delivery_path (char *path, char *username, char *host);
|
|
---------------------------------------------------------------
|
|
|
|
Retrieve the delivery path for the specified user/host combination.
|
|
"user" and "host" should be the values returned by "is_local_address".
|
|
|
|
"path" Receives the directory in which files should be created
|
|
for final delivery to the specified user. The string will
|
|
not end in "\" or "/". Allocate at least 256 characters
|
|
for this string
|
|
|
|
Returns: 1 if "path" contains a valid delivery path
|
|
0 on error ("path" is invalid)
|
|
|
|
|
|
---------------------------------
|
|
int get_date_and_time (BYTE *tm);
|
|
---------------------------------
|
|
|
|
Get the current date and time, querying the file server for the
|
|
information if possible. The information is written into a seven
|
|
byte structure laid out as follows:
|
|
|
|
Byte 0 - Years since 1900 (i.e, 2000 == 100)
|
|
Byte 1 - Month (ie, January == 1)
|
|
Byte 2 - Day (1 .. 31)
|
|
Byte 3 - Hour (0 - 24)
|
|
Byte 4 - Minute (0 - 59)
|
|
Byte 5 - Second (0 - 60)
|
|
Byte 6 - Day of week (Sunday == 0)
|
|
|
|
This function will obtain the date and time from the local workstation
|
|
if no server connection is available to provide it.
|
|
|
|
Returns: 1 if the time and date were correctly retrieved
|
|
0 on system error.
|
|
|
|
|
|
-----------------------------------------------
|
|
int write_profile (char *section, char *fname);
|
|
-----------------------------------------------
|
|
|
|
Write a profile section into MERCURY.INI. "section" should contain
|
|
the name of the section to write, enclosed in square brackets (so,
|
|
if you want to write a section called "CookieServ", you would pass
|
|
"[CookieServ]" as the "section" parameter). "fname" should be a full
|
|
path to a file containing the data to write into the section. The
|
|
data is written exactly as it appears and may use any syntax you
|
|
wish, although it is conventional to use "keyword : parameter".
|
|
The data in the file is expected to be line-oriented, and no single
|
|
line may exceed 254 characters.
|
|
|
|
Returns: 1 on success
|
|
0 on failure ("fname" is not accessible)
|
|
|
|
|
|
-----------------------------------------------------------
|
|
int module_state (char *modname, int set_value, int state);
|
|
-----------------------------------------------------------
|
|
|
|
Query or set the state for a protocol module. "modname" should point
|
|
to a string identifying the module name of the module whose state is
|
|
to be queried or set. If "set_value" is non-zero, then this function
|
|
call will attempt to set the module's state to the value contained
|
|
in "state". If "set_value" is zero, then "state" is ignored, and the
|
|
module's current state is returned.
|
|
|
|
A module's state is a bitmap where the low sixteen bits are reserved
|
|
and the high sixteen bits can be defined by individual modules for
|
|
whatever purpose they wish. The reserved bits have the following
|
|
possible values:
|
|
|
|
Bit 0: Online state, 1 = online, 0 = offline.
|
|
Bit 1: ("get" only) 1 = busy, 0 = idle.
|
|
|
|
Returns: -1: No such module
|
|
-2: Module does not support this operation
|
|
< -255: Module-specific error return value
|
|
>= 0: Previous state (success)
|
|
|
|
|
|
|
|
5.2 - Job control and interface functions
|
|
|
|
The functions in this section allow you to create, scan and manipulate
|
|
mail jobs in the Mercury queue.
|
|
|
|
|
|
----------------------------------------------------------
|
|
void *ji_scan_first_job (int type, int mode, void **data);
|
|
void * ji_scan_next_job (void **data);
|
|
void ji_end_scan (void **data);
|
|
----------------------------------------------------------
|
|
|
|
Routines for enumerating jobs in the queue. First, call
|
|
"ji_scan_first_job" and if it returns a non-NULL value, call
|
|
"ji_scan_next_job" until NULL is returned. A non-NULL return value
|
|
is the job handle for the job that is found - use this handle in
|
|
calls to other job management functions.
|
|
|
|
"type" Can have any one of the following values:
|
|
JT_GENERAL Messages for local delivery
|
|
JT_OUTGOING Messages scheduled for off-server delivery
|
|
JT_ANY Any type of message
|
|
|
|
|
|
"mode" Used to select jobs in particular states - choose from:
|
|
JE_READY Messages ready for processing
|
|
JE_COMPLETED Messages where all processing is finished
|
|
JE_FAILED Messages with delivery failures pending
|
|
JE_ANY Messages in any state
|
|
|
|
The "data" parameter is used by these routines to store state
|
|
information, and should be passed as part of each call. If
|
|
"ji_scan_first_job" returns a non-NULL value, then you *must* call
|
|
"ji_end_scan" when you have finished scanning with jobs, even if you
|
|
do not completely iterate through all the jobs in the queue.
|
|
|
|
None of these routines actually opens the job handle - you must call
|
|
ji_open_job, passing the job handle, if you need to access the data in
|
|
the job or modify its settings.
|
|
|
|
Returns: NULL if no further jobs exist
|
|
non-NULL (the job handle) on success
|
|
|
|
|
|
----------------------------------
|
|
int ji_open_job (void *jobhandle);
|
|
----------------------------------
|
|
|
|
Open a job. You must call this function before calling any function
|
|
that accesses the job's data or changes the job's settings. The job
|
|
is opened and locked, which will prevent it from being processed
|
|
during normal polling operations, and will prevent other processes
|
|
from opening it.
|
|
|
|
The job handle passed to this call should be a value returned by
|
|
"ji_scan_first_job", "ji_scan_next_job" or "ji_create_job".
|
|
|
|
Returns: 1 if the job was opened successfully.
|
|
0 on failure.
|
|
|
|
|
|
----------------------------------
|
|
int ji_close_job (void *jobhandle);
|
|
----------------------------------
|
|
|
|
Close a job opened with "ji_open_job". The job is unlocked and
|
|
released for normal processing. Other processes may access the job
|
|
once this routine has been called.
|
|
|
|
Calling this routine forces the job's overall status to be updated
|
|
(so, a job may change from JE_READY to JE_COMPLETED after this
|
|
function has been called).
|
|
|
|
Returns: 1 on success
|
|
0 on failure (job invalid).
|
|
|
|
|
|
------------------------------------------------
|
|
void ji_rewind_job (void *jobhandle, int flags);
|
|
------------------------------------------------
|
|
|
|
"Rewind" a job - reposition its file pointer at 0. "flags" can have
|
|
either of two values, "JR_CONTROL", in which case the control
|
|
information for the job is reset to the first address element, or
|
|
"JR_DATA", in which case the data associated with the file is set
|
|
for reading from the start.
|
|
|
|
Example: say you are scanning through the addresses to which a message
|
|
should be sent using "ji_get_next_element", and you strike a
|
|
condition that means that you need to start processing from the
|
|
first address element in the message again, you would call this
|
|
function with the "JR_CONTROL" parameter.
|
|
|
|
|
|
-------------------------------------
|
|
int ji_dispose_job (void *jobhandle);
|
|
-------------------------------------
|
|
|
|
Call this function when you have finished working with a job handle
|
|
returned by ji_scan_first_job, ji_scan_next_job, or ji_create_job.
|
|
This routine deallocates memory associated with the job - it does not
|
|
delete or in any other manner change the actual disk structures
|
|
associated with the job. This function must be called for every
|
|
non-NULL return value from the functions listed above.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (job invalid).
|
|
|
|
|
|
-------------------------------------
|
|
int ji_process_job (void *jobhandle);
|
|
-------------------------------------
|
|
|
|
Indicates to the job manager that a formal attempt to process the
|
|
job has begun. The retry count for the job is incremented and (if
|
|
applicable) the last retry time is updated. Daemons should seldom
|
|
if ever have any cause to call this function.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (invalid or closed job handle)
|
|
|
|
|
|
------------------------------------
|
|
int ji_delete_job (void *jobhandle);
|
|
------------------------------------
|
|
|
|
Permanently remove a job from the queue. This routine deletes all
|
|
files associated with the job (including diagnostic files) and then
|
|
discards the job handle. It is invalid to access the job handle after
|
|
it has been passed to this routine.
|
|
|
|
You should not call ji_dispose_job for handles passed to this routine.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (invalid job handle)
|
|
|
|
|
|
----------------------------------------------
|
|
int ji_abort_job (void *jobhandle, int fatal);
|
|
----------------------------------------------
|
|
|
|
Abort processing a job. If the job was created using ji_create_job,
|
|
then it is discarded, otherwise its retry count is incremented by the
|
|
system default retry increment, the job is closed, and the job handle
|
|
is invalidated. It is an error to use or access the job handle after
|
|
calling this routine.
|
|
|
|
If "fatal" is non-zero, then the job is unilaterally failed (normally,
|
|
a job is only marked as failed if any of its addresses cannot be
|
|
delivered - setting this flag will fail even a job where all the
|
|
recipients have successfully received the mail).
|
|
|
|
You should not call ji_dispose_job for handles passed to this routine.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (job handle was invalid)
|
|
|
|
|
|
---------------------------------------------------
|
|
int ji_get_job_info (void *jobhandle, JOBINFO *ji);
|
|
---------------------------------------------------
|
|
|
|
Get information about the specified job. The fields in the JOBINFO
|
|
structure are filled out with information from the current element
|
|
in the message's control stream.
|
|
|
|
typedef struct
|
|
{
|
|
int structlen; // Size of this structure
|
|
char jobstatus; // JS_READY, JS_COMPLETE or JS_FAILED
|
|
long jobflags; // Mercury internal processing flags
|
|
char status; // As jobstatus but for current element only
|
|
char *from; // Envelope address for message (read-only)
|
|
char *to; // Recipient address for current element
|
|
long dsize; // Total RFC822 size of the message
|
|
long rsize; // Number of bytes read since last rewind
|
|
int total_rcpts; // Total recipients for message
|
|
int total_failures; // Total failed recipients to date
|
|
int total_retries; // Total recipients marked for retry
|
|
char ip1 [16]; // Primary IP address for current element
|
|
char ip2 [16]; // Secondary IP address for " " "
|
|
char ip3 [16]; // Third-choice IP address for " " "
|
|
char ip4 [16]; // Fourth-choice IP address for " " "
|
|
char jobid [20]; // Unique identifier for message
|
|
} JOBINFO;
|
|
|
|
Returns: 1 on success
|
|
0 on failure
|
|
|
|
|
|
----------------------------------------------------------------------
|
|
void *ji_create_job (int type, char *from, unsigned char *start_time);
|
|
----------------------------------------------------------------------
|
|
|
|
Create a job in the mail queue.
|
|
|
|
"type" can be either JT_GENERAL, or JT_OUTGOING. You should usually
|
|
use "JT_GENERAL" unless you are explicitly creating a job which is
|
|
to be processed directly by the MercuryC SMTP client.
|
|
|
|
"from" is the address Mercury should write in the envelope of the
|
|
message.
|
|
|
|
"start_time" is a date and time in the 7-byte format described under
|
|
"get_date_and_time", which specifies the earliest time at which the
|
|
job can be processed. If NULL, the job can be processed as soon as
|
|
it has been closed.
|
|
|
|
The job handle returned by this function can be passed to any other
|
|
job management function in order to manipulate the queue job.
|
|
|
|
Returns: non-NULL on success (the job handle)
|
|
NULL on failure.
|
|
|
|
|
|
----------------------------------------------------
|
|
int ji_add_element (void *jobhandle, char *address);
|
|
----------------------------------------------------
|
|
|
|
Add an address to a job created using "ji_create_job". "address"
|
|
should be the full e-mail address of a single recipient for the
|
|
message. You should call this function for each address to which the
|
|
message should be sent.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (invalid job handle)
|
|
|
|
|
|
----------------------------------------------
|
|
int ji_add_data (void *jobhandle, char *data);
|
|
----------------------------------------------
|
|
|
|
Add data for the message body to a job created using "ji_create_job".
|
|
You should call this function repeatedly for the headers and text of
|
|
the message until all the data has been written. This function does
|
|
not modify the data in any way - it is up to you to ensure that line
|
|
endings are correct (CR/LF pairs) and that the data conforms to the
|
|
standards for Internet mail. There is a clear expectation that the
|
|
data for the message will be passed to this function a line at a time.
|
|
|
|
Note: When you use this function to build an outgoing mail message,
|
|
you are effectively building the message exactly as it will be sent -
|
|
it is up to you to add all the necessary RFC822 headers, the blank
|
|
line that separates them from the message body, and the message body
|
|
itself.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (invalid job handle)
|
|
|
|
|
|
--------------------------------------------------------------
|
|
char *ji_get_data (void *jobhandle, char *buffer, int buflen);
|
|
--------------------------------------------------------------
|
|
|
|
Get the next line of data from the current job. "buffer" should be
|
|
allocated to be at least 1024 characters in order to comply with
|
|
RFC821, but any length will be honoured. The line returned will
|
|
usually end in a CRLF pair, but may not if the buffer was too
|
|
small to accommodate the entire line.
|
|
|
|
Returns: "buffer" on success
|
|
NULL on failure (end of data)
|
|
|
|
|
|
-----------------------------------------------------
|
|
char *ji_get_next_element (void *jobhandle, int type,
|
|
JOBINFO *jobinfo);
|
|
-----------------------------------------------------
|
|
|
|
Get the next element from the job. Information about the element
|
|
is returned in "jobinfo". "type" can be JE_ANY for any type of entry,
|
|
JE_READY for the next entry marked as "ready for processing", or
|
|
JE_FAILED for the next entry marked as failed.
|
|
|
|
Returns: The e-mail address associated with the entry on success
|
|
NULL on failure, or no more elements.
|
|
|
|
|
|
-----------------------------------------------------
|
|
int ji_set_element_status (void *jobhandle, int mode,
|
|
unsigned char *date);
|
|
-----------------------------------------------------
|
|
|
|
Set the status fields of the current job element. "mode" can be one
|
|
of the following values:
|
|
|
|
JS_COMPLETED - "date" is ignored
|
|
JS_FAILED - "date" is ignored
|
|
JS_RETRY - "date" is used for requeuing if non-NULL
|
|
JS_PENDING - "date" is ignored
|
|
|
|
"JS_PENDING" status indicates a status that should be reset to
|
|
"JS_RETRY" if a job is aborted: it is set by the SMTP client
|
|
module when the remote SMTP server has accepted a RCPT TO: line
|
|
for the address, and is needed so that the job can be "rolled
|
|
back" if there's a subsequent network error.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (invalid job handle or element)
|
|
|
|
|
|
----------------------------------------------------------------------
|
|
int ji_set_element_resolvinfo (void *jobhandle, char *ip1, char *ip2);
|
|
----------------------------------------------------------------------
|
|
|
|
Set the resolver information fields of the current job element. "ip1"
|
|
and "ip2" point to ASCII versions of IP addresses for servers that
|
|
should be used to deliver the element within the job. Daemons should
|
|
never need to use this function.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (invalid job handle or element)
|
|
|
|
|
|
------------------------------------------------------------------
|
|
int ji_set_diagnostics (void *jobhandle, int forwhat, char *text);
|
|
------------------------------------------------------------------
|
|
|
|
Set the diagnostic field of the current job element to the text
|
|
contained in "text". It is legal (and probably even essential)
|
|
to call this function repeatedly; each time it is called, a line
|
|
is added to the diagnostic stream for the entry.
|
|
|
|
"forwhat" can be JD_ELEMENT to set information specific to
|
|
the current element, or JD_JOB to set diagnostics pertaining
|
|
to the job in general.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (bad job handle or element record)
|
|
|
|
|
|
-------------------------------------------------------------------
|
|
int ji_get_diagnostics (void *jobhandle, int forwhat, char *fname);
|
|
-------------------------------------------------------------------
|
|
|
|
Get the diagnostics for the job or current job element. "fname"
|
|
should point to a buffer at least 128 characters long where the
|
|
diagnostic information should be placed: the buffer will be filled
|
|
in with a filename by this routine. If this routine returns 2,
|
|
it is the calling routine's responsibility to delete the file in
|
|
"fname" when it is finished with the data; if this routine returns
|
|
1, then the calling routine must NOT delete the file in "fname".
|
|
If 0 is returned, then no diagnostic information is available
|
|
for the element.
|
|
|
|
"forwhat" can be JD_ELEMENT to retrieve information specific to
|
|
the current element, or JD_JOB to retrieve diagnostics pertaining
|
|
to the job in general.
|
|
|
|
Returns: 2 "fname" is valid - delete it when finished
|
|
1 "fname" is valid - do NOT delete it when finished
|
|
0 "fname" is invalid - no diagnostic information available.
|
|
|
|
|
|
------------------------------------------------------------------
|
|
void ji_increment_time (unsigned char *tm, unsigned int add_secs);
|
|
------------------------------------------------------------------
|
|
|
|
Add "add_secs" seconds to the date and time specified in "tm",
|
|
which should be in the format described under "get_date_and_time".
|
|
You can subtract seconds from a time by passing a negative value for
|
|
"add_secs" to this function.
|
|
|
|
|
|
----------------------------------
|
|
void *ji_get_job_by_id (char *id);
|
|
----------------------------------
|
|
|
|
Given the ID number of a job (the "jobid" element of a JOBSTRUCT
|
|
structure), return a usable handle for that job if it still exists in
|
|
the queue. This function allows you to re-open a job by reference
|
|
without holding on to its jobhandle between calls. If this function is
|
|
successful, the job handle it returns can be used in any other ji_*
|
|
function expecting a job handle. The job handle is not open on return.
|
|
|
|
Returns: Non-NULL on success
|
|
NULL on failure (job no longer exists, or is locked).
|
|
|
|
|
|
---------------------------------------------------------------------
|
|
int ji_get_job_times (void *jobhandle, char *submitted, char *ready);
|
|
---------------------------------------------------------------------
|
|
|
|
Get the time of original submission for a job and the time it is
|
|
scheduled for processing. "submitted" and "ready" are the submission
|
|
and ready times respectively, in the same format as returned by
|
|
get_date_and_time. You can pass NULL for either of these parameters if
|
|
you do not require the value. The ready time for a job can be
|
|
inspected at any time, but the submission time for a job can only be
|
|
retrieved if the job has been successfully opened using ji_open_job.
|
|
|
|
Returns: Non-zero on success
|
|
0 on failure (invalid job handle)
|
|
|
|
5.3 - Network and user database query functions
|
|
|
|
|
|
------------------------------------------------------------------
|
|
int get_first_group_member (char *group, char *host, char *member,
|
|
int mlen, void **data);
|
|
int get_next_group_member (char *member, int mlen, void **data);
|
|
int end_group_scan (void **data);
|
|
------------------------------------------------------------------
|
|
|
|
Routines for enumerating the members of a group. First, call
|
|
"get_first_group_member", passing the name of the group in "group".
|
|
The host and username information will be returned in "host" and
|
|
"member" respectively, with "mlen" controlling the maximum nunber of
|
|
characters written into the member name (allocate at least 128
|
|
characters for both "host" and "member"). If "get_first_group_member"
|
|
returns non-zero, call "get_next_group_member" repeatedly to iterate
|
|
through the group's membership. When you have finished scanning
|
|
through the membership, call "end_group_scan". You should pass the
|
|
same address for "data" to all calls - these functions store state
|
|
information there.
|
|
|
|
If "get_first_group_member" returns a non-zero value, then you must
|
|
call "end_group_scan" when you have finished scanning, whether or
|
|
not you actually reach the end of the group's membership.
|
|
|
|
|
|
|
|
--------------------------------------------------------------------
|
|
int is_valid_local_user (char *address, char *username, char *host);
|
|
--------------------------------------------------------------------
|
|
|
|
Check whether "username" identifies a valid local user. This function
|
|
differs from "is_local_address" in that it does not resolve aliases
|
|
or synonyms, and will only return a success result if the address
|
|
represents a genuine, existing local user. Note that the "address"
|
|
parameter must have no domain portion - this routine expects a user
|
|
name portion only.
|
|
|
|
As with "is_local_address", this routine may modify the "username"
|
|
and "host" parameters - allocate at least 256 characters for each
|
|
of these parameters.
|
|
|
|
Returns 1 if the address is a valid local user
|
|
0 if the address does not represent a known user
|
|
|
|
|
|
------------------------------------------------------------------
|
|
int is_group_member (char *host, char *username, char *groupname);
|
|
------------------------------------------------------------------
|
|
|
|
Determine whether or not a user is a member of a specified group.
|
|
|
|
Returns: 1: the group exists and the user is a member
|
|
0: the group exists and the user is not a member
|
|
-1: the group does not exist
|
|
|
|
|
|
--------------------------------------------------------------------
|
|
int get_first_user_details (char *host, char *match, char *username,
|
|
int ulen, char *address, int alen, char *fullname, int flen,
|
|
void **data);
|
|
int get_next_user_details (char *username, int ulen, char *address,
|
|
int alen, char *fullname, int flen, void **data);
|
|
int end_user_scan (void **data);
|
|
--------------------------------------------------------------------
|
|
|
|
Enumerate the local users on the current system.
|
|
|
|
"match" - currently ignored; pass an empty string.
|
|
"username" - receives at most "ulen" bytes of the user's
|
|
login name
|
|
"address" - receives at most "alen" bytes of the user's
|
|
e-mail address.
|
|
"fullname" - receives at most "flen" bytes of the user's
|
|
full (personal) name.
|
|
|
|
If "get_first_user_details" returns a non-NULL value, then you must
|
|
call "end_user_scan" when you have finished scanning users, whether
|
|
or not you actually reach the end of the user list.
|
|
|
|
Returns: 1 if valid user details were found and returned
|
|
0 on error, or if no further users exist.
|
|
|
|
|
|
--------------------------------------------------------------------
|
|
int get_user_details (char *host, char *match, char *username,
|
|
int ulen, char *address, int alen, char *fullname, int flen);
|
|
--------------------------------------------------------------------
|
|
|
|
Return information about a single specific user, whose username is
|
|
contained in "match". All other parameters are the same as those in
|
|
"get_first_user_details".
|
|
|
|
Returns: 1 on success
|
|
0 on failure (no such user)
|
|
|
|
|
|
---------------------------------------------------------
|
|
void read_pmprop (char *userid, char *server, PMPROP *p);
|
|
---------------------------------------------------------
|
|
|
|
Get extended features settings for the specified user. "Extended
|
|
features" is a Pegasus Mail term and covers things like automatic
|
|
mail forwarding and address synonyms. Any given user will not
|
|
necessarily have an extended features record.
|
|
|
|
The principal field of the "PMPROP" structure from Mercury's
|
|
point of view is the "gw_auto_forward", which contains the address
|
|
to which all mail delivered by Mercury should be forwarded.
|
|
|
|
|
|
---------------------------------------------------------------
|
|
int change_ownership (char *fname, char *host, char *newowner);
|
|
---------------------------------------------------------------
|
|
|
|
Change the network ownership of the specified file. This function
|
|
is only meaningful when Mercury is using a network interface
|
|
module that supports the concept of changing the ownership of a
|
|
file.
|
|
|
|
Returns: 1 if the file's ownership was successfully changed
|
|
0 on error, or if the function is not supported.
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
int begin_single_delivery (char *uic, char *server, void **data);
|
|
void end_single_delivery (void **data)
|
|
-----------------------------------------------------------------
|
|
|
|
These functions should not be called by Daemons; they are only
|
|
meaningful to true Mercury protocol modules.
|
|
|
|
|
|
-----------------------------------------------------------
|
|
INT_32 create_object (char *objectname, INT_32 objecttype,
|
|
char *id, INT_32 flags);
|
|
-----------------------------------------------------------
|
|
|
|
Create a new object in the system. "objectname" can be any valid
|
|
filename for the system in use, but must not contain spaces, and
|
|
should be 8 characters or less if compatibility with 16-bit systems
|
|
is required. "id" is the object's default identification (or personal
|
|
name) string.
|
|
|
|
"objecttype" can be OBJ_USER, or OBJ_ADMINISTRATOR
|
|
|
|
"flags" is a bitmap containing user creation control flags. The
|
|
following values are possible:
|
|
1 Copy any default messages into the new user's mailbox
|
|
|
|
This routine will fail if a user already exists with the name "username",
|
|
or if the user's mail directory cannot be created.
|
|
|
|
This routine is only available in Standalone Mercury systems - it is
|
|
not implemented in Network-level plugins.
|
|
|
|
Returns: 1 on success
|
|
0 on failure
|
|
|
|
|
|
----------------------------------------------------------------
|
|
int verify_password (char *username, char *host, char *password,
|
|
int select);
|
|
----------------------------------------------------------------
|
|
|
|
Given a username and host as returned by "is_local_user", verify
|
|
a password for that user.
|
|
|
|
"password" The password to verify. This may or may not be case-
|
|
sensitive, depending on the underlying operating system.
|
|
|
|
"select" indicates the type of password to verify: this variable
|
|
can have the following values
|
|
|
|
SYSTEM_PASSWORD (1) The user's login password
|
|
APOP_SECRET (2) The user's APOP shared secret
|
|
|
|
Returns: 1 if the password appears to be valid
|
|
0 if the password is invalid
|
|
|
|
|
|
----------------------------------------------------------------
|
|
INT_32 set_password (char *username, char *host, char *password,
|
|
INT_32 select);
|
|
----------------------------------------------------------------
|
|
|
|
Set the user's System password or APOP secret. "username" and "host"
|
|
should be values returned by a previous call to either
|
|
"is_valid_local_user" or "get_*_user_details". "select" should be
|
|
either APOP_SECRET or SYSTEM_PASSWORD. This function may not be
|
|
available on all systems.
|
|
|
|
Returns: 1 on success
|
|
0 on failure
|
|
|
|
|
|
|
|
5.4 - Miscellaneous functions
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
DWORD mercury_command (DWORD selector, DWORD parm1, DWORD parm2);
|
|
-----------------------------------------------------------------
|
|
|
|
This function provides an extended interface into Mercury's core
|
|
services. In general, less-frequently-used functions, or functions
|
|
with simple parameter lists may be exposed via this mechanism.
|
|
|
|
The function operates much like the Windows "SendMessage" function:
|
|
you indicate which function is required in the "selector" parameter,
|
|
and the meanings of the "parm1" and "parm2" parameters will depend
|
|
on the selector. The return from this function also depends on the
|
|
selector value. Mercury does not return from this function until
|
|
the command has completed.
|
|
|
|
At the time of writing, the following selectors and their parameter
|
|
lists are defined; others will be added over time:
|
|
|
|
Selector: GET_MODULE_INTERFACE
|
|
Parm1: (char *) Pointer to name of module to locate
|
|
Parm2: Unused, must be 0
|
|
Returns: Protocol module command interface function pointer
|
|
Comments: This message is only for use by Mercury protocol
|
|
modules and should not be used by Daemons.
|
|
|
|
Selector: ADD_ALIAS
|
|
Parm1: (char *) Pointer to alias to add
|
|
Parm2: (char *) Pointer to address to associate with alias
|
|
Returns: Non-zero on success, 0 on failure
|
|
Comments: Add an alias to the system alias file. This function
|
|
will fail if an alias already exists using the string
|
|
supplied. Any type of alias may be added, including
|
|
Daemon:, File: and TFile: aliases.
|
|
|
|
Selector: DELETE_ALIAS
|
|
Parm1: (char *) Pointer to alias to delete
|
|
Parm2: Unused, must be 0
|
|
Returns: Non-zero on success, 0 on failure
|
|
Comments: Deletes the specified alias from the system alias file.
|
|
A Daemon that adds its own aliases for the addresses
|
|
it services will typically call this function before
|
|
adding its alias.
|
|
|
|
Selector: RESOLVE_ALIAS
|
|
Parm1: (char *) Pointer to alias to resolve
|
|
Parm2: (char *) Pointer to buffer to place address
|
|
Returns: Non-zero on match, 0 on no match or failure
|
|
Comments: Attempts to resolve the specified alias to its real-
|
|
world address form. You must allocate at least 180
|
|
characters to the buffer (parm2) parameter.
|
|
|
|
Selector: RESOLVE_SYNONYM
|
|
Parm1: (char *) Pointer to synonym to resolve
|
|
Parm2: (char *) Pointer to buffer to place address
|
|
Returns: Non-zero on match, 0 on no match or failure
|
|
Comments: Attempts to resolve the specified string as a
|
|
synonym (two-way alias). For more information on
|
|
synonyms, see the Mercury documentation. You must
|
|
allocate at least 180 characters for the buffer
|
|
(parm2) parameter.
|
|
|
|
Selector: QUEUE_STATE
|
|
Parm1: 0 to query current state, 1 to set state
|
|
Parm2: 1 to pause processing, 0 to enable it
|
|
Returns: The state of queue processing prior to the call
|
|
Comments: Allows you to pause and unpause the core module's
|
|
processing of the mail queue.
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
char *get_date_string (int selector, char *buf, BYTE *datetime);
|
|
----------------------------------------------------------------
|
|
|
|
Return a properly-formatted date string.
|
|
|
|
"selector" - either RFC_821_TIME or RFC_822_TIME
|
|
"buf" - where the formatted string should be written
|
|
"datetime" - NULL for the current time, or a 7-byte time structure
|
|
|
|
RFC821 time format uses a 2-digit year and is only used in timestamp
|
|
headings in "Received" headers for messages.
|
|
|
|
RFC822 time is conventional RFC822/RFC1123 date format, using a
|
|
4-digit year.
|
|
|
|
Both formats include a time zone if one is defined on the system.
|
|
|
|
"buf" must be at least 40 characters in length.
|
|
|
|
See "get_date_and_time" for a description of the format of "datetime"
|
|
|
|
Returns: buf
|
|
|
|
|
|
---------------------------------
|
|
char *rfc822_time (char *buffer);
|
|
char *rfc821_time (char *buffer);
|
|
---------------------------------
|
|
|
|
Provided for backwards-compatibility only. Use "get_date_string"
|
|
instead of these functions.
|
|
|
|
|
|
------------------------------------------------------
|
|
INT_32 select_printer (char *device_name, int maxlen);
|
|
------------------------------------------------------
|
|
|
|
Bring up a dialog that prompts the user to select a printer from
|
|
those available on the current system. Both local and network
|
|
printers are listed. The printer name is returned in "device_name"
|
|
if the user clicks "OK". The name returned is suitable for use in
|
|
the "print_file" function (see below).
|
|
|
|
Returns: 1 if OK was clicked
|
|
0 if Cancel was clicked
|
|
|
|
|
|
---------------------------------------------------------------------
|
|
INT_32 (* PRINT_FILE) (char *fname, char *printername, UINT_32 flags,
|
|
INT_32 lrmargin, INT_32 tbmargin, char *title, char *username,
|
|
char *fontname, INT_32 fontsize);
|
|
---------------------------------------------------------------------
|
|
|
|
Print a file or mail message. This function provides a simple way of
|
|
printing textual data, and has useful formatting options designed to
|
|
handle normal textual mail messages. Only textual data can be printed
|
|
- this routine will not print HTML, RTF or other formatted data types.
|
|
|
|
"fname" is the full pathname of the file to print
|
|
|
|
"printername" is the name of the printer on which the file is to be
|
|
printed; this can be any local or network printer. You can use the
|
|
"select_printer" function (see above) to allow the user to choose
|
|
a printer. If you pass NULL for this parameter, Mercury will use
|
|
the system's default printer.
|
|
|
|
"flags" is a bitmap of flags controlling the way the file will be
|
|
printed; the following values are available:
|
|
|
|
PRT_MESSAGE Print as an RFC822 mail message
|
|
PRT_REFORMAT Reformat long lines when printing
|
|
PRT_TIDY Print only "important" headers
|
|
PRT_FOOTER Print a footer on each page
|
|
PRT_NOHEADERS Print no message headers
|
|
PRT_FIRSTONLY Print only first line of headers
|
|
PRT_ITALICS Print quoted text in italics
|
|
|
|
"lrmargin" is the margin in millmetres (mm, 25.4mm == 1 inch) to
|
|
allow at the left and right sides of the printed page.
|
|
|
|
"tbmargin" is the margin in millimetres to allow at the top and
|
|
bottom of the printed page.
|
|
|
|
"title" is an optional string Mercury will use to identify the
|
|
document in the Windows print manager queue; the title is not
|
|
itself printed anywhere on the document. This paramter can be
|
|
NULL if not required.
|
|
|
|
"username" is the username Mercury should print in the footer on
|
|
each page if that option is selected. This parameter can be NULL
|
|
if it is not required.
|
|
|
|
"fontname" is the name of the font to use when printing. If this
|
|
parameter is NULL or zero-length, the default font and size will
|
|
be used.
|
|
|
|
"fontsize" is the size in points of the printer font. This
|
|
parameter is ignored if "fontname" is NULL or zero-length.
|
|
|
|
Returns: 1 on successful printing
|
|
0 on printing error.
|
|
|
|
|
|
|
|
5.5 - File I/O and parsing routines
|
|
|
|
The functions in this section allow Daemons to perform message and MIME
|
|
parsing on mail messages. The first group of functions allow the
|
|
manipulation of files in a portable manner (since open file handles
|
|
cannot be passed between DLLs and programs), while the second group of
|
|
functions perform parsing and analysis of complex mail messages.
|
|
|
|
|
|
------------------------------------------------
|
|
INT_32 fm_open_file (char *path, UINT_32 flags);
|
|
------------------------------------------------
|
|
|
|
Open any file. The file is opened in binary mode, so line endings
|
|
are returned as CRLF, not as simply LFs. The handle returned by this
|
|
function can be passed to any of the parsing or file manipulation
|
|
functions that expect an internal file handle.
|
|
|
|
"flags" can have any of the following values:
|
|
|
|
FF_NO_LINE_ENDINGS - tells "fm_gets" to remove CRLF terminators
|
|
from each line it reads from the file
|
|
|
|
Returns: > 0 (the file reference) on success
|
|
0 on failure
|
|
|
|
|
|
-----------------------------------------------------
|
|
INT_32 fm_open_message (IMESSAGE *im, UINT_32 flags);
|
|
-----------------------------------------------------
|
|
|
|
Provided for backwards compatibility with Pegasus Mail. Daemons should
|
|
use "fm_open_file" instead of calling this function.
|
|
|
|
|
|
---------------------------------
|
|
int fm_close_message (INT_32 id);
|
|
---------------------------------
|
|
|
|
Close a file opened using "fm_open_file" or "fm_open_message".
|
|
|
|
Returns: 1 on success
|
|
0 on failure
|
|
|
|
|
|
--------------------------------------------------
|
|
char (*fm_gets (char *buf, INT_32 max, INT_32 id);
|
|
--------------------------------------------------
|
|
|
|
Read the next line from a file opened using "fm_open_file" or
|
|
"fm_open_message". At most "max - 1" characters are read from the
|
|
file, and the line is always nul-terminated. CRLF characters will
|
|
be present unless the file was opened using the "FF_NO_LINE_ENDINGS"
|
|
flag, or possibly for the last line of the file.
|
|
|
|
Returns: "buf" on success
|
|
NULL on failure or EOF.
|
|
|
|
|
|
---------------------------
|
|
INT_16 fm_getc (INT_32 id);
|
|
---------------------------
|
|
|
|
Read the next character from the file.
|
|
|
|
Returns: the character on success
|
|
EOF on failure
|
|
|
|
|
|
-------------------------------------
|
|
void fm_ungetc (INT_16 c, INT_32 id);
|
|
-------------------------------------
|
|
|
|
"unget" the last character retrieved using "fm_gets" or "fm_getc".
|
|
Exactly one level of character may be "ungot" using this function.
|
|
|
|
|
|
---------------------------------------------------------
|
|
INT_32 fm_read (INT_32 id, char *buffer, INT_32 bufsize);
|
|
---------------------------------------------------------
|
|
|
|
Read at most "bufsize" bytes from the file. No character conversions
|
|
are done, and the data may not necessarily end on a line boundary.
|
|
|
|
Returns: > 0 on success (the number of bytes read)
|
|
= 0 on EOF
|
|
< 0 on error
|
|
|
|
|
|
------------------------------
|
|
INT_32 fm_getpos (INT_32 fil);
|
|
------------------------------
|
|
|
|
Get the current offset in the open file. The return from this function
|
|
may be passed to "fm_setpos" to reposition the file at a given point.
|
|
|
|
Returns: Current file offset.
|
|
|
|
|
|
---------------------------------------------
|
|
INT_16 fm_setpos (INT_32 fil, INT_32 offset);
|
|
---------------------------------------------
|
|
|
|
Reposition the file pointer for an open file. "offset" must be a
|
|
value returned by "fm_getpos", or 0 to rewind the file. You should
|
|
not assume a linear relationship between "offset" and the data in the
|
|
file.
|
|
|
|
Returns: > 0 on success
|
|
0 on error.
|
|
|
|
|
|
--------------------------------------------------------------
|
|
INT_32 fm_get_folded_line (INT_32 fil, char *line, int limit);
|
|
--------------------------------------------------------------
|
|
|
|
Read a header from a message, "unfolding" continuation lines as
|
|
required. This function understands RFC822 line folding rules for
|
|
message headers, and will return a single line containing the full
|
|
extent of a header. This is an extremely useful function for reading
|
|
through the headers of a message without worrying about dealing with
|
|
continuation lines. This routine guarantees that the returned line
|
|
will be nul-terminated and will not end in CRLF.
|
|
|
|
There is usually no reason to use this function to read the body of
|
|
a mail message, and doing so may lead to unexpected results.
|
|
|
|
Example: if the next lines in the message are:
|
|
|
|
From: David Harris
|
|
<david@pmail.gen.nz>
|
|
(Pegasus Mail Author)
|
|
|
|
Then this function will return the following single string:
|
|
|
|
From: David Harris <david@pmail.gen.nz> (Pegasus Mail Author)
|
|
|
|
Returns: > 0 on success (number of characters in line)
|
|
0 if the end of headers has been reached.
|
|
|
|
|
|
-------------------------------------------------------------------
|
|
char (*fm_find_header (INT_32 fil, char *name, char *buf, int len);
|
|
-------------------------------------------------------------------
|
|
|
|
Locate the header whose keyword is passed in "name" in the specified
|
|
message file. The entire line without the keyword is returned, with any
|
|
continuations unfolded into the data.
|
|
|
|
Returns: "buf" on success (points to start of field body)
|
|
NULL on failure.
|
|
|
|
|
|
-----------------------------------------------------------
|
|
int fm_extract_message (void *job, char *fname, int flags);
|
|
-----------------------------------------------------------
|
|
|
|
Take an open Mercury mail job and extract its contents to the file
|
|
specified in "fname".
|
|
|
|
"flags" can have combinations of the following values:
|
|
|
|
FFX_APPEND - append to the file if it exists - don't overwrite
|
|
FFX_NO_HEADERS - omit the message headers when writing to the file
|
|
FFX_TIDY_HEADERS - write only "significant" headers to the file
|
|
FFX_NOT_OPEN - the job is not currently open
|
|
|
|
If the "FFX_NOT_OPEN" flag is specified, then this routine will open
|
|
and close the job. When passing the "job" parameter supplied to the
|
|
"daemon" function, you must not specify this flag - the job is already
|
|
open and must remain open during processing.
|
|
|
|
|
|
-------------------------------------------
|
|
int parse_header (INT_32 fil, IMESSAGE *m);
|
|
-------------------------------------------
|
|
|
|
Perform comprehensive parsing on the open message file "fil", which
|
|
should have been opened using "fm_open_file". The information gleaned
|
|
from the message's headers is stored in the IMESSAGE structure, "m".
|
|
Applications using this function will be particularly interested in
|
|
the "flags" field of this structure, since it contains extensive
|
|
information about attachments and MIME formatting that might be
|
|
present in the message.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (very rare)
|
|
|
|
|
|
---------------------------------------------------------------
|
|
int mime_prep_message (INT_32 fil, char *fname, int headers);
|
|
---------------------------------------------------------------
|
|
|
|
Given a non-multipart MIME message, create a "sanitized" version of
|
|
the message data in the file named by "fname". MIME encodings are
|
|
decoded as required, and any character set conversions that might
|
|
be needed are performed. If you pass an empty string for "fname",
|
|
Mercury will create a temporary file for you, returning the name
|
|
of that file in "fname".
|
|
|
|
Note that you must not call this function for a multipart message:
|
|
to extract a sanitized section from a multipart message, call
|
|
"parse_mime", traverse the multipart list until you find the section
|
|
you need, then pass that section to "fake_imessage".
|
|
|
|
If "headers" is non-zero, then the message headers from the message
|
|
will be included in the output file, otherwise they will be omitted.
|
|
|
|
Returns: > 0 on success
|
|
0 on failure
|
|
|
|
|
|
----------------------------------------
|
|
int parse_mime (INT_32 fil, IMIME *m);
|
|
----------------------------------------
|
|
|
|
Given any valid MIME message, parse its contents and return them in
|
|
the "IMIME" structure, "m".
|
|
|
|
The IMIME structure is a relatively complicated variable record which
|
|
can represent any MIME format, including arbitrarily nested Multipart
|
|
MIME messages. The fields in an IMIME structure are as follows:
|
|
|
|
primary - the logical message type, e.g MP_TEXT
|
|
secondary - the logical message sub-type, e.g. MPT_PLAIN
|
|
encoding - the encoding for this part, e.g ME_BASE_64
|
|
disposition - either MD_ATTACHMENT or MD_INLINE
|
|
p_string - the actual primary type string, e.g "TEXT"
|
|
s_string - the actual secondary sub-type string, e.g "PLAIN"
|
|
description - "Content-description" for this part, if any
|
|
section - the number of this section within the message
|
|
fname - the filename associated with this part, if any
|
|
|
|
d - a union, one data element from which will contain
|
|
part-specific information about this part. You
|
|
should select the appropriate record based on the
|
|
value of "primary".
|
|
primary == MP_TEXT : use "mpt"
|
|
primary == MP_MULTIPART : use "mpp"
|
|
primary == MP_APPLICATION : use "mpa"
|
|
primary == MP_MESSAGE : use "mpm"
|
|
|
|
The "mpm" record within the structure contains a list of other parts
|
|
in the message. It should be traversed by dereferencing the "top"
|
|
field of the "partlist" entry and casting its data field to "IMIME *".
|
|
|
|
Example: code to traverse the parts in a multipart message
|
|
|
|
IMIME *m, *m2;
|
|
LNODE *cur;
|
|
|
|
if (m->primary == MP_MULTIPART)
|
|
{
|
|
for (cur = m->d.mpp.partlist.top; cur != NULL; cur = cur->next)
|
|
{
|
|
m2 = (IMIME *) (cur->data);
|
|
// do whatever you need with that section here.
|
|
}
|
|
}
|
|
|
|
Note that it is valid, or even normal for a sub-part of a multipart
|
|
message to be itself a Multipart item. When this happens, the parts
|
|
of the nested item will have been properly parsed into its own "mpp"
|
|
structure, and can be followed by traversing its partlist using the
|
|
same technique.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (rare, indicates serious malformatting)
|
|
|
|
|
|
--------------------------
|
|
void free_mime (IMIME *m);
|
|
--------------------------
|
|
|
|
Call this function when you have finished with an IMIME structure
|
|
successfully parsed by "parse_mime". This function deallocates any
|
|
lists or other memory allocation created to store the structure of
|
|
the MIME message.
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
int fake_imessage (IMESSAGE *im, char *dest, char *src, IMIME *m,
|
|
char *boundary);
|
|
-----------------------------------------------------------------
|
|
|
|
"Sanitize" a single section of a multipart message. This function
|
|
extracts the section of the message contained in "src" and identified
|
|
by "m" (which is presumed to be a part from the "partlist" list of a
|
|
multipart message) to the file named in "dest". Any necessary decoding
|
|
and conversion is done on the section. "Boundary" should point to the
|
|
boundary string that delimits the section in the file (you should
|
|
usually pass "d.mpp.boundary" from the enclosing message's IMIME
|
|
structure for this).
|
|
|
|
Parsing information about the extracted section is stored in the
|
|
IMESSAGE structure "im".
|
|
|
|
It is the responsibility of the calling function to delete the file
|
|
"dest" when it is no longer required.
|
|
|
|
Returns: 1 on success
|
|
0 on failure
|
|
|
|
|
|
-----------------------------------------------
|
|
int decode_mime_header (char *dest, char *src);
|
|
-----------------------------------------------
|
|
|
|
Search for and decode 8-bit data encapsulated according to RFC-1522
|
|
rules in a string of data (presumably a header extracted from a message
|
|
using fm_get_folded_line()). No line breaks are permitted in the input
|
|
data.
|
|
|
|
"dest" should point to a buffer where the decoded header string should
|
|
be written. The destination buffer should be at least as large as
|
|
the input buffer.
|
|
|
|
"src" should point to the candidate string, which may contain multiple
|
|
RFC-1522-encoded strings.
|
|
|
|
Returns: 1 if encoded data was found (dest is valid)
|
|
0 if no encoded data was found (dest is invalid)
|
|
-1 on error (bad format or unrecognized char set)
|
|
|
|
|
|
--------------------------------------------------------
|
|
int encode_mime_header (char *dest, char *src, int raw);
|
|
--------------------------------------------------------
|
|
|
|
Encode a string using RFC1522 rules; transformation only occurs if the
|
|
input string contains 8-bit data.
|
|
|
|
"dest" should point to a buffer where the encoded string should be
|
|
written. The destination buffer should be at least twice as large
|
|
as the input buffer. Note that this routine NEVER generates
|
|
multiple encoded blocks in the string - either the whole string is
|
|
encoded, or none of it is. Furthermore, this routine only ever
|
|
generates the quoted-printable RFC1522 variant, never the BASE64
|
|
encoding, which seems to me to be a ludicrous thing to place in the
|
|
headers of a mail message.
|
|
|
|
"src" should point to the source string, which may or may not contain
|
|
8-bit data. "src" is expected to be in the WinANSI character set.
|
|
|
|
Returns: 1 if data was encoded (dest is valid)
|
|
0 if no data was encoded (dest is invalid)
|
|
|
|
|
|
----------------------------------------------------------
|
|
int encode_base64_str (char *dest, char *src, int srclen);
|
|
----------------------------------------------------------
|
|
|
|
Encode an arbitrary string of data in BASE64 format. Note that this
|
|
encoding process will produce an output line approximately a third
|
|
larger again than the input, and that MIME message conventions on
|
|
line length are ignored - the output will be a single line. This
|
|
function is useful for protocols like RFC2554 that require string
|
|
data to be transmitted as a BASE64-encoded quantity.
|
|
|
|
"dest" should point to a buffer where the BASE64 encoded data
|
|
should be written. The destination buffer should be at least twice
|
|
as large as the input buffer.
|
|
|
|
"src" is the source string, which may contain any data (it is not
|
|
limited to textual or ASCII characters).
|
|
|
|
"srclen" is the length in bytes of "src".
|
|
|
|
Returns 1 on success
|
|
0 on failure (very, very rare).
|
|
|
|
|
|
-----------------------------------------------------------
|
|
int decode_base64_str (char *dest, char *src, char *table);
|
|
-----------------------------------------------------------
|
|
|
|
Decode a string encoded using BASE64.
|
|
|
|
"dest" should point to a location where the decoded data is to
|
|
be written. This buffer should be at least the same size as
|
|
the input buffer. The decoded data may be binary in nature -
|
|
there is no guarantee that it will be a nul-terminated C
|
|
string.
|
|
|
|
"src" should point to the string to decode
|
|
|
|
"table" can point to a 128-byte character table used to convert
|
|
high-bit characters in the decoded data to an alternative
|
|
character set. This value will typically be NULL.
|
|
|
|
Returns: 1 on success
|
|
0 on failure (malformatted BASE64 data)
|
|
|
|
|
|
|
|
5.6 - Message composition routines
|
|
|
|
Mercury provides an easy way to create relatively complex mail messages
|
|
in MIME formats. You can create simple messages, messages with
|
|
attachments, multipart/alternative messages containing different versions
|
|
of the same data, and MIME digests containing other mail messages.
|
|
|
|
|
|
-------------------------------------------------------
|
|
void *om_create_message (UINT_32 mtype, UINT_32 flags);
|
|
-------------------------------------------------------
|
|
|
|
Create a message. The message type is set to "mtype" and the message
|
|
flags are set to "flags". The handle returned by this function must be
|
|
passed to all subsequent message building functions. Messages created
|
|
by Mercury are always valid MIME messages - non-MIME message types can
|
|
not be created (nor is it any longer desirable to do so).
|
|
|
|
"mtype" must be one of the following values:
|
|
|
|
OM_MT_PLAIN - A simple, single-part text/plain message
|
|
OM_MT_MULTIPART - A multipart/mixed message
|
|
OM_MT_ALTERNATIVE - A multipart/alternative message
|
|
OM_MT_DIGEST - A multipart/digest message
|
|
|
|
"flags" is a bitmap composed of the following possible values:
|
|
|
|
OM_M_8BIT - The message body contains 8-bit data
|
|
|
|
The "flags" field can be set later using the "om_add_field" function
|
|
and the OM_MF_FLAGS selector.
|
|
|
|
Returns: Non-NULL on success (message handle)
|
|
NULL on failure
|
|
|
|
|
|
------------------------------------------
|
|
INT_32 om_dispose_message (void *mhandle);
|
|
------------------------------------------
|
|
|
|
Release the storage used by a message. It is an error to use the
|
|
"mhandle" parameter after it has been passed to this routine. This
|
|
function must be called when the message is no longer needed, to
|
|
deallocate internal buffers.
|
|
|
|
It is the calling routine's responsibility to deal with the body file
|
|
and any files attached to the message.
|
|
|
|
Returns: > 0 on success
|
|
= 0 on failure (invalid handle)
|
|
|
|
|
|
------------------------------------------------------------------
|
|
INT_32 om_add_field (void *mhandle, UINT_32 selector, char *data);
|
|
------------------------------------------------------------------
|
|
|
|
Add a field to a message.
|
|
|
|
"selector" can be any one of the following values:
|
|
|
|
OM_MF_TO - set the master recipient of the message
|
|
OM_MF_SUBJECT - set the subject field for the message
|
|
OM_MF_CC - set the secondary recipients of the message
|
|
OM_MF_FROM - set the originator of the message.
|
|
OM_MF_BODY - set the filename containing the message body
|
|
OM_MF_RAW - add a raw header for the message.
|
|
OM_MF_FLAGS - set the message's global flags
|
|
|
|
If the "OM_MF_FROM" field is not set for a message, it will default
|
|
to the postmaster address for the system. It is an error to attempt
|
|
to send a message for which "OM_MF_TO" has not been set; all other
|
|
fields are optional.
|
|
|
|
The "OM_MF_RAW" selector allows the calling process to add any header
|
|
it wishes to the message. The header should be passed complete,
|
|
including the header keyword, but without a terminating CRLF pair.
|
|
Mercury guarantees that headers added to a message will be written to
|
|
the message in the order in which they are added, so you can add
|
|
headers with continuations if you wish. You cannot add any of the
|
|
following headers using "OM_MF_RAW": "To", "Cc", "Subject", "BCC",
|
|
"Content-type", "Content-transfer-encoding", "MIME-Version", or
|
|
"Content-disposition". You can reset all custom headers added using
|
|
OM_MF_RAW by using OM_MF_RAW as a selector and passing NULL for the
|
|
"data" parameter.
|
|
|
|
It is explicitly legal to add a field you have already added to a
|
|
message. This allows you to create a message then send it to multiple
|
|
recipients in separate jobs, for example.
|
|
|
|
Returns: > 0 on success
|
|
= 0 on error
|
|
|
|
|
|
-----------------------------------------------------
|
|
INT_32 om_add_attachment (void *mhandle, char *fname,
|
|
char *ftype, char *description, UINT_32 encoding,
|
|
UINT_32 flags, void *reserved);
|
|
-----------------------------------------------------
|
|
|
|
Add an attachment or part to a message.
|
|
|
|
"fname" - the fully-qualified path to the file to attach
|
|
"ftype" - a content-type string - see below
|
|
"description" - optional free text description string
|
|
"encoding" - encoding method - see below
|
|
"flags" - special settings for the attachment - see below
|
|
"reserved" - must be 0
|
|
|
|
The "ftype" string is a free text atom and can have any value, provided
|
|
it does not contain spaces, tabs or commas.
|
|
|
|
"encoding" can have the following values
|
|
|
|
OM_AE_DEFAULT - default encoding (MIME BASE64 encoding)
|
|
OM_AE_TEXT - simple textual data, unencoded
|
|
OM_AE_UUENCODE - uuencoding
|
|
OM_AE_BINHEX - Macintosh Binhex format (data fork only)
|
|
|
|
"flags" can have the following values
|
|
|
|
OM_AF_INLINE - write the file as a simple textual section
|
|
OM_AF_MESSAGE - write the message as a Message/RFC822 part
|
|
|
|
The file need not exist at the time it is attached.
|
|
|
|
Mercury/32 guarantees that attachments will appear in the message in
|
|
the order in which they were attached, so you can create messages
|
|
that depend on the sequence of attachments with confidence.
|
|
|
|
Returns: > 0 on success
|
|
= 0 on failure.
|
|
|
|
|
|
-----------------------------------------------------
|
|
INT_32 om_write_message (void *mhandle, char *fname);
|
|
-----------------------------------------------------
|
|
|
|
Given a message handle that has been populated with fields, body and
|
|
attachments, create a simple disk file containing the final form of
|
|
the mail message.
|
|
|
|
Note that this function does *not* create a job, nor does it send
|
|
the message. It simply renders the message structures into their
|
|
final form.
|
|
|
|
This routine is called internally by "om_send_message" to create the
|
|
final form of the Mercury queue job. It is guaranteed that calling
|
|
this function then calling "om_send_message" will generate a file
|
|
and a job that are identical (so, this routine can be used to create
|
|
archive copies of outgoing messages).
|
|
|
|
Returns: > 0 on success
|
|
= 0 on failure
|
|
|
|
|
|
------------------------------------------------------
|
|
void *om_send_message (void *mhandle, char *envelope);
|
|
------------------------------------------------------
|
|
|
|
Given a message handle that has been populated with fields, body
|
|
and attachments, create a job in the Mercury queue containing the
|
|
final form of the mail message.
|
|
|
|
"envelope" is an optional envelope ("Return-path") address for the
|
|
created message. If NULL or 0-length, the envelope will default to
|
|
the From field, or the postmaster address.
|
|
|
|
The return from this job is an open Mercury job handle. It is the
|
|
responsibility of the calling process to make any last-minute job
|
|
settings, then to close or discard the job.
|
|
|
|
Returns: Open Mercury job handle on success
|
|
0 on failure
|
|
|
|
|
|
Notes on composing messages:
|
|
|
|
1: To create a MIME digest, you must ensure that all the files you attach
|
|
to your message are validly-formatted RFC822 mail messages, and that each
|
|
has the OM_AF_MESSAGE bit set in its "flags" field. The message body is
|
|
ignored for MIME digests, so you may wish to consider creating a "dummy"
|
|
first message containing a summary, subscription information or other
|
|
information useful to the recipient.
|
|
|
|
2: The message body file and any attachments must be closed when either
|
|
"om_write_message" or "om_send_message" is called.
|
|
|
|
3: If the "OM_M_8BIT" flag is set for the message, the body of the
|
|
message will be sent using the MIME "quoted-printable" encoding scheme.
|
|
At present, only the ISO-8859-1 character set (which is the same as the
|
|
default MS-Windows character set) is supported for 8-bit data. It is
|
|
safe to set the OM_M_8BIT flag for a message that contains only 7-bit
|
|
data, but it is an error not to set it for a message that *does* contain
|
|
8-bit character data.
|
|
|
|
4: Address fields ("To:" and "Cc:") can be up to 32000 bytes in length,
|
|
and may contain multiple addresses separated by commas. Mercury/32 will
|
|
wrap the address fields according to RFC822 line folding rules. Mercury
|
|
will *not* expand partial addresses to fully-qualified domain name forms.
|
|
It is up to your Daemon to ensure that all addresses are legal and
|
|
fully-qualified.
|
|
|
|
|
|
|
|
5.7 - Statistics and logging interface
|
|
|
|
Mercury incorporates a comprehensive Statistics Manager that can keep
|
|
running statistics and information about the throughput, performance and
|
|
operation of the system. The entire Statistics Manager interface is
|
|
available to Daemons and protocol modules. Mercury also provides a System
|
|
Messages window into which console-type messages can be written at
|
|
various priority levels.
|
|
|
|
|
|
----------------------------------------------
|
|
INT_32 st_register_module (char *module_name);
|
|
----------------------------------------------
|
|
|
|
Register a module with the statistics interface. "Module name" is the
|
|
name that should be presented in the statistics manager list for this
|
|
item. This function creates a top-level statistics category in the
|
|
Statistics Manager's list.
|
|
|
|
Returns: Module handle > 0 on success
|
|
0 on failure (very rare)
|
|
|
|
|
|
---------------------------------------------
|
|
INT_32 st_unregister_module (INT_32 mhandle);
|
|
---------------------------------------------
|
|
|
|
"Unregister" a module and remove its entries from the statistics list.
|
|
|
|
Returns 1 on success
|
|
0 on failure (not registered)
|
|
|
|
|
|
----------------------------------------------------------
|
|
INT_32 st_create_category (INT_32 mhandle, char *cname,
|
|
INT_32 ctag, INT_32 ctype, INT_32 dlen, UINT_32 flags);
|
|
----------------------------------------------------------
|
|
|
|
Create a single statistical category within a specified module.
|
|
|
|
"mhandle" module's registered handle
|
|
|
|
"cname" display name for this category
|
|
|
|
"ctag" module's reference handle for this item; this is an
|
|
arbitrary value supplied by the module that identifies
|
|
this category, and it must be unique within the items
|
|
created within a single module.
|
|
|
|
"ctype" type of data represented
|
|
- STC_INTEGER for integral data
|
|
- STC_STRING for string data
|
|
- STC_DATE for time/date data
|
|
|
|
"dlen" maximum size of data (for strings)
|
|
|
|
"flags" attributes of this category
|
|
- STF_CUMULATIVE data accumulates
|
|
- STF_PEAK record only the highest value
|
|
- STF_UNIQUE allow only one named instance
|
|
|
|
Returns: > 0 (category handle) on success
|
|
0 on failure
|
|
|
|
|
|
---------------------------------------------------------
|
|
INT_32 st_remove_category (INT_32 mhandle, UINT_32 ctag);
|
|
---------------------------------------------------------
|
|
|
|
Remove a statistical category and delete it from any lists currently
|
|
displayed. "ctag" should be the category tag handle passed when the
|
|
category was created.
|
|
|
|
Returns 1 on success
|
|
0 on failure (category not found)
|
|
|
|
|
|
-------------------------------------------------------
|
|
INT_32 st_set_hcategory (INT_32 chandle, UINT_32 data);
|
|
-------------------------------------------------------
|
|
|
|
Set the data for a category given its category handle only. Note that
|
|
a "category handle" is the return from st_create_category, not the
|
|
ctag value passed to that function. The system guarantees that
|
|
category handles will always be globally unique, and may change from
|
|
session to session.
|
|
|
|
"data" must be of a type appropriate for the registered category. If
|
|
the category has the "STF_CUMULATIVE" attribute set, then the data are
|
|
accumulated to any data already existing for the category.
|
|
|
|
Returns: > 0 on success
|
|
0 on failure
|
|
|
|
|
|
-------------------------------------------------------------------
|
|
INT_32 st_set_category (INT_32 mhandle, INT_32 ctag, UINT_32 data);
|
|
-------------------------------------------------------------------
|
|
|
|
Set the data for a category given its module handle and the module's
|
|
internal category handle. "data" must be of a type appropriate for the
|
|
registered category. If the category has the "STF_CUMULATIVE" attribute
|
|
set, then the data are accumulated to any data that already exist for
|
|
the category.
|
|
|
|
Returns: > 0 on success
|
|
0 on failure
|
|
|
|
|
|
----------------------------------------------------------
|
|
INT_32 st_get_next_module (INT_32 mhandle, char *modname);
|
|
----------------------------------------------------------
|
|
|
|
Return the next module in the statistics list. To enumerate the modules
|
|
in the list, set "mhandle" to -1 on the first call, then pass the return
|
|
value in subsequent calls until -1 is returned.
|
|
|
|
The module's name is copied into "modname", which should be at least 128
|
|
characters in length.
|
|
|
|
Returns: > 0 on success
|
|
-1 on no more modules.
|
|
|
|
|
|
-------------------------------------------------------------
|
|
INT_32 st_get_next_category (INT_32 mhandle, INT_32 chandle,
|
|
char *cname, INT_32 *ctype, INT_32 *clen, INT_32 *cflags);
|
|
-------------------------------------------------------------
|
|
|
|
Return the next category belonging to the module "mhandle" in the
|
|
statistics list. To enumerate all the categories in the category list,
|
|
pass the module handle (returned by "st_get_next_module", set "chandle"
|
|
to -1 on the first call then pass the return value in subsequent calls.
|
|
Repeat until the function returns -1.
|
|
|
|
"cname" receives the category's descriptive text; allocate
|
|
at least 128 characters
|
|
|
|
"ctype" receives the type of the category's data; possible
|
|
values are STC_INTEGER, STC_STRING or STC_DATE
|
|
|
|
"clen" receives the maximum length of the data type.
|
|
|
|
"cflags" receives the flags associated with the data item;
|
|
possible bit values are STF_CUMULATIVE, STF_PEAK and
|
|
STF_UNIQUE.
|
|
|
|
Returns: > 0 on success
|
|
-1 on no more categories
|
|
|
|
|
|
---------------------------------------------------------
|
|
INT_32 st_get_category_data (INT_32 chandle, void *data);
|
|
---------------------------------------------------------
|
|
|
|
Copy the current data value for the specified category into the
|
|
location pointed to by "data". It is the calling module's
|
|
responsibility to ensure that sufficient space is allocated. For
|
|
STC_INTEGER types, you should allocate 4 bytes; for STC_DATE types,
|
|
you should allocate 4 bytes (the returned value is a C "time_t"
|
|
type); for STC_STRING types, you should allocate the "clen" returned
|
|
by a call to st_get_next_category (which will always be correct for
|
|
integer and date types as well).
|
|
|
|
Returns: 1 on succes
|
|
0 on failure
|
|
|
|
|
|
--------------------------------------------------------------------
|
|
INT_32 st_export_stats (INT_32 mhandle, char *fname, UINT_32 flags);
|
|
--------------------------------------------------------------------
|
|
|
|
Export a single module's statistics, or all modules' stats
|
|
to the file named in "fname" in plain text format.
|
|
|
|
"mhandle" The module handle for which statistics should
|
|
be exported. If 0, export all modules.
|
|
|
|
"fname" Filename to receive data
|
|
|
|
"flags" If (flags & 1), append data to the file
|
|
If (flags & 2), omit 0 or undefined categories
|
|
|
|
Returns 1 on success
|
|
0 on failure
|
|
|
|
|
|
|
|
----------------------------------------------------------
|
|
void logstring (INT_16 ltype, INT_16 priority, char *str);
|
|
----------------------------------------------------------
|
|
|
|
Log a message in the System Messages window
|
|
|
|
"ltype" An arbitrary integer representing the "type" of
|
|
the message. It is a convention that "major" types
|
|
should be divisible by 10; other types of data
|
|
(minor types) will be indented.
|
|
|
|
"priority" The level of importance of the message. The user
|
|
can set the level they want displayed, and messages
|
|
with a lower priority than that will be discarded.
|
|
Possible values for this field are:
|
|
- LOG_DEBUG
|
|
- LOG_INFO
|
|
- LOG_NORMAL
|
|
- LOG_SIGNIFICANT
|
|
- LOG_URGENT
|
|
- LOG_NONE
|
|
|
|
Developers are strongly urged to use priority values correctly and
|
|
responbsibly, for the benefit of users.
|
|
|
|
|
|
-------------------------------------------------------------
|
|
void logdata (INT_16 ltype, INT_16 priority, char *fmt, ...);
|
|
-------------------------------------------------------------
|
|
|
|
The same as "logstring", but supporting "sprintf"-type formatting.
|
|
See the documentation for "sprintf" in your compiler manual for
|
|
information on valid formatting codes.
|
|
|
|
Note that because of the way macros are handled in the C language,
|
|
this function cannot be accessed using the convenience macros in
|
|
"daemon.h" - you must access the function directly from the protocol
|
|
block structure.
|
|
|
|
|
|
|
|
Appendix A - Technical issues
|
|
|
|
A.1 - Function names
|
|
|
|
When Mercury loads a Daemon, it attempts to locate the functions exported
|
|
by the Daemon using the function names. Different compilers may export
|
|
the names of your functions in different ways - even though your function
|
|
may be called "startup", the compiler may actually export it as
|
|
"_startup", for historical reasons.
|
|
|
|
Mercury always attempts to locate your functions in two ways: the first
|
|
attempt prepends a "_" to the function name, while the second searches
|
|
for the function name with no leading "_", all in uppercase. The first of
|
|
these methods will reliably detect functions exported by Borland
|
|
compilers, while the second mechanism will usually detect functions
|
|
exported by the Microsoft Visual C++ compiler family.
|
|
|
|
If you find that your functions do not seem to be getting called by
|
|
Mercury, check on the format of the name exported by your compiler - this
|
|
is the most likely source of the problem.
|
|
|