2. Conventions¶
This chapter will describe the PHP-MAPI API and how to use it. It will describe also some tricks that can be applied to make programming easier.
2.1. Properties¶
Every object in MAPI has several properties set. The properties contains information about the object like the name, the unique id, etc... A property tag consists of a type and a unique id, the property type and the property id. A property tag is made as in the following example:
Property definitions
define ('PR_SUBJECT', mapi_prop_tag(PT_TSTRING, 0x0037);
// This will result in a constant PR_SUBJECT with the value 0x001E0037
Within the PHP-code all of the properties are defined with a PR_ prefix to indicatie it is a property. All type codes are prefixed with PT_ (Property Type).
The function mapi_prop_tag
accepts the property type and the property id and returns a unique code that indicates the property tag. This code is a simple long where the first 16 bits are the type and the last 16 bits are the ID.
Possible property types are:
Special properties:
- PT_NULL
- PT_ERROR
Single properties:
- PT_I2
- PT_LONG
- PT_FLOAT
- PT_DOUBLE
- PT_APPTIME
- PT_BOOLEAN
- PT_STRING8
- PT_SYSTIME
- PT_BINARY
- PT_CLSID
Multi value properties:
- PT_MV_I2
- PT_MV_LONG
- PT_MV_R4
- PT_MV_DOUBLE
- PT_MV_APPTIME
- PT_MV_SYSTIME
- PT_MV_STRING8
- PT_MV_BINARY
- PT_MV_CLSID
Not supported:
- PT_CURRENCY
- PT_UNICODE
- PT_OBJECT
- PT_I8
- PT_MV_CURRENCY
- PT_MV_UNICODE
- PT_MV_I8
Special properties for rules table:
- PT_ACTIONS
- PT_SRESTRICTION
2.2. Checking errors¶
When an array of properties is retrieved with the function mapi_getprops
by example there could some problems with the properties. Because of optimalization MAPI has a restricition on the size of a property. When by example a PR_BODY is too big the error MAPI_E_NOT_ENOUGH_MEMORY will be returned instead of the actual data.
The type of this property will be set to PT_ERROR instead of PT_TSTRING. The value of the property will be the code of the error that occured. The numeric value of the property tag will be 0x000A0037 which is different from 0x001E0037. This requires a special way to check if there is an error and find out what the error code is.
The following program listing gives an example how to check the property on an error and how to get the error from the property:
Checking property errors in an object
// get properties
$msg_props = mapi_getprops($message);
if (array_key_exists($msg_props, PR_BODY) {
// there is a body with no error, display body here
} else {
$pr_body_error = mapi_prop_tag(PT_ERROR, mapi_prop_id(PR_BODY));
if (array_key_exists($msg_props, $pr_body_error)) {
// found an error, now check which
switch($msg_props[$pr_body_error]) {
case MAPI_E_NOT_ENOUGH_MEMORY:
// found but too big, use mapi_openproperty the get the data.
break;
default:
// other error.
break;
}
} else {
// No error found, the body doesn't exist in this object.
}
}
Also, when checking error values (such as 0x80040107, MAPI_E_NOT_FOUND), you must use the mapi_is_error function to check whether the valus is actually an error. This is due to the fact that 0x800xxxxx numbers are not handed as unsigned numbers within PHP.
Checking for errors or warnings
if(mapi_is_error($msg_props[$pr_body_error])) {
echo "error!\n";
}
When a function returns the value ‘false’, it means the function has failed. To retrieve the MAPI error, you can use the mapi_last_hresult() function, to get the last returned HRESULT value of a MAPI function. Example:
Checking last MAPI returned error code
$inbox = mapi_msgstore_getreceivefolder($store);
if ($inbox == false) {
print "no inbox available!\n";
printf("last result: %x\n",mapi_last_hresult());
}
2.3. Restrictions¶
Restrictions limit the rows queried using a set of conditions. This way messages can be selected on specific properties, such as the existance of a certain property, a property’s value larger than a certain value, etc..
A restriction is specified with an array with one value with the restriction type and one value with an array with the restriction specific properties.
Restriction types:
Restriction type | Description |
---|---|
RES_AND | Takes an array with restrictions that must all be valid. |
RES_OR | Expects an array with restrictions of which either one can be valid. |
RES_NOT | Wants an array with a single restriction that must not be valid. |
RES_CONTENT | Fuzzy search a property. The array must contain: FUZZYLEVEL, ULPROPTAG and VALUE. VALUE must be a string or binary. VALUE => array (tag => value) |
RES_PROPERTY | Takes an array with properties of a property which must be valid. This array must contain: RELOP, ULPROPTAG and VALUE. The property and VALUE can have different types. VALUE => array (tag => value) |
RES_COMPAREPROPS | Compares two props. Expects an array with the indexes: RELOP, ULPROPTAG1 and ULPROPTAG2. |
RES_BITMASK | Applies a bitmask. The given array must contain: ULPROPTAG, ULTYPE and ULMASK. |
RES_SIZE | Measures the size of a property. The array must have: ULPROPTAG, RELOP and CB. |
RES_EXIST | Checks whether a property exists. Takes only ULPROPTAG in its array. |
RES_SUBRESTRICTION | Places a restriction on a property. This is used on PR_MESSAGE_RECIPIENT properties with an RES_COMMENT restriction. |
RES_COMMENT | A comment restriction can be used to add comments to the restriction structure, or in a special case with the RES_SUBRESTRICTION. The RES_COMMENT structure holds a RES_PROPERTY restriction, which applies on the given properties list in the structure. |
Retriction operations types:
Restriction operation | Description |
---|---|
RELOP_LT | Less than. |
RELOP_LE | Less than or equal to. |
RELOP_GT | Greater than. |
RELOP_GE | Greater than or equal to. |
RELOP_EQ | Equal to. |
RELOP_NE | Not equal to. |
RELOP_RE | The attachment has just been created. |
FUZZYLEVEL can be one or bitwise more of these:
Fuzzy level | Description |
---|---|
FL_FULLSTRING | The property must be equal to this exact string. |
FL_SUBSTRING | Full text search for substring. |
FL_PREFIX | The value must start with this string. |
FL_IGNORECASE | Case should be ignored. |
FL_IGNORENONSPACE | Non-spaces should be ignored. |
FL_LOOSE | The string is allowed to match loosely. |
Simple restriction example
// Example for restrictions
$testrestriction = Array(RES_OR,
Array(
Array(RES_PROPERTY,
Array(RELOP => RELOP_LE, ULPROPTAG => PR_BODY,
VALUE => array(PR_BODY =>"test"))),
Array(RES_PROPERTY,
Array(RELOP => RELOP_LE, ULPROPTAG => PR_SUBJECT,
VALUE => array(PR_SUBJECT => "bericht")))));
$contents = mapi_table_queryallrows($table, $props, $sizerestriction);
foreach ($contents as $row)
{
echo $row[PR_SUBJECT];
echo "\n";
}
RES_SUBRESTRICTION + RES_COMMENT example
$restriction =
array(RES_SUBRESTRICTION,
array(ULPROPTAG => PR_MESSAGE_RECIPIENTS, // this is the subobject
RESTRICTION => array(RES_COMMENT,
array(PROPS => array(), // probably need to add outlook data
RESTRICTION => array(RES_PROPERTY,
array(RELOP => RELOP_EQ,
ULPROPTAG => PR_SEARCH_KEY,
VALUE => array(PR_SEARCH_KEY => "one-off entryid") )
)
)
)
)
);
2.4. Attachments¶
Attachments are used to send a binary file with a email message. There are a few different attachments types:
Property | Description |
---|---|
ATTACH_BY_VALUE | The attachment data is stored within the message object. |
ATTACH_EMBEDDED_MSG | The attachment is stored as a message object within the message object. |
ATTACH_BY_REFERENCE | These are regerences to a file by name. Since the PHP code runs on the server, these types are unsupported. |
ATTACH_BY_REF_RESOLVE | |
ATTACH_BY_REF_ONLY |
All of these types use different ways to store the name and to store the attachement data. with mapi_message_getattachmenttable()
the table with all the attachments can be read from the message. Every row in this table has a property PR_ATTACH_METHOD which defines with which method the attachment was stored (see the values above). The property to read from an attachment or the function to call depends on the method an attachment has:
Method | Description |
---|---|
NO_ATTACHMENT | The attachment has just been created. |
ATTACH_BY_VALUE | The PR_ATTACH_FILENAME and PR_ATTACH_LONG_FILENAME contains
the name of the attachment. When the attachment is opened with
mapi_message_openattach() the data can be read with
mapi_attach_openbin() |
ATTACH_EMBEDDED_MSG | The PR_DISPLAY_NAME contains the name of the attachment (This
is mostly the PR_SUBJECT of the message that has been attached).
With mapi_attach_openobj() the attachment can be read from
the message. This function returns a message object. |
ATTACH_BY_REFERENCE | These types are unsupported and should never be used. |
ATTACH_BY_REF_RESOLVE | |
ATTACH_BY_REF_ONLY |
When created, all attachment objects have an initial PR_ATTACH_METHOD value of NO_ATTACHMENT. Client applications and service providers are only required to support the attachment method represented by the ATTACH_BY_VALUE value. The other attachment methods are optional.
The message store does not enforce any consistency between the value of PR_ATTACH_METHOD and the values of the other attachment properties.
2.5. Messages¶
The reading of the contents from a message can be done in a generic way. All messages contain all types of bodies. The different body types are kept in sync automatically, and the version which is written last wins, and updates the other versions. The following body types are available:
There are 3 ways in which the body of a message can be stored:
Type | Description |
---|---|
RTF | The content of the message is stored in the PR_RTF_COMPRESSED property. |
Embedded HTML in RTF | The content of the message is stored in the PR_RTF_COMPRESSED property. This is a special type of RTF that has an embedded HTML content. This version has been depricated by normal HTML. |
HTML text | The content of the message is stored in the PR_HTML property. is normal HTML formatted text. |
Plain text | The content of the body is stored in the PR_BODY property, and contains the plain text. |
The PR_RTF_COMPRESSED property can hold RTF of HTML embedded text. If you use this property, you can convert it to HTML with the mapi_rtf2html function. Therefore, this property is only useful to detect if the original message was sent in TRF or HTML.
2.5.1. RTF¶
The RTF-text may be used when messages are stored within Outlook. RTF text can only be sent through the TNEF data in a winmail.dat attachment.
2.5.2. Embedded HTML in RTF¶
The messages that are sent in HTML will be stored as RTF in the PR_RTF_COMPRESSED property. The HTML is converted from plain HTML to Embedded HTML. With the function mapi_rtf2html
this type of RTF can be converted back to HTML to be shown in a browser. The function will return false when the RTF is not of the correct type.
While this is still true, this method of reading the body is depricated. If you want the HTML version of the body, use the normal HTML property.
2.5.3. HTML text¶
The normal HTML property is stored in the PR_HTML property. This is the preferred property if you want to read the HTML body.
2.5.4. Plain text¶
The PR_BODY will contain the message body in Plain text.
2.6. Recipients¶
Recipients are used to specify the receiving entities for a specific message.
The structure of a recipient array in PHP is as follows. When recipients are set, you should use mapi_createoneoff() to create the right PR_ENTRYIDs:
Recipient table
// Create array of recipients usable by the PHP MAPI functions
$recipientarray = Array(Array(PR_ENTRYID => mapi_createoneoff("John Doe", "SMTP", "john@example.com"
PR_DISPLAY_NAME => "John Doe",
PR_ADDRTYPE => "SMTP",
PR_EMAIL_ADDRESS => "john@example.com"
PR_RECIPIENT_TYPE => MAPI_TO),
Array(PR_ENTRYID => mapi_createoneoff("Robert Roe", "SMTP", "robert@example.com"
PR_DISPLAY_NAME => "Robert Doe",
PR_ADDRTYPE => "SMTP",
PR_EMAIL_ADDRESS => "[email protected]"
PR_RECIPIENT_TYPE => MAPI_CC),
Array(PR_ENTRYID => mapi_createoneoff("Richard Miles", "SMTP", "richard@example.com"
PR_DISPLAY_NAME => "Richard Miles",
PR_ADDRTYPE => "SMTP",
PR_EMAIL_ADDRESS => "richard@example.com"
PR_RECIPIENT_TYPE => MAPI_BCC));
To set or get recipients, use the mapi_message_modifyrecipients and mapi_message_getrecipienttable methods.