3.2 Integrating Sample Attachments and RTF
A user has just received a piece of e-mail that they would like to open and read. The following is a description of what a client might do to accomplish the user's intentions and the responses that a server might return.
The user opens the Message object by using the RopOpenMessage remote operation (ROP) ([MS-OXCROPS] section 2.2.6.1) for an e-mail message that just arrived. It was sent with the message ID and folder ID described in the following table.
Property name |
Property ID |
Data type |
Data |
---|---|---|---|
PidTagFolderId ([MS-OXCFOLD] section 2.2.2.2.1.6) |
0x6748 |
PtypInteger64 ([MS-OXCDATA] section 2.11.1) |
0xBFE7F00000000001 |
PidTagMid ([MS-OXCFXICS] section 2.2.1.2.1) |
0x674A |
PtypInteger64 |
0x95D9690100000001 |
The body properties are retrieved to determine which body format is appropriate to load, as described in [MS-OXBBODY]. The client sends a RopGetPropertiesSpecific ROP request ([MS-OXCROPS] section 2.2.8.3) and the server responds with the information described in the following table.
Property name |
Property ID |
Data type |
Data |
Value |
---|---|---|---|---|
PidTagRtfInSync ([MS-OXCMSG] section 2.2.1.58.5) |
0x0E1F |
PtypBoolean ([MS-OXCDATA] section 2.11.1) |
0x0001 |
True |
PidTagBody ([MS-OXCMSG] section 2.2.1.58.1) |
0x1000 |
PtypErrorCode ([MS-OXCDATA] section 2.11.1) |
0x8007000e |
NotEnoughMemory |
PidTagBodyHtml ([MS-OXCMSG] section 2.2.1.58.3) |
0x1013 |
PtypErrorCode |
0x8004010f |
NotFound |
PidTagRtfCompressed ([MS-OXCMSG] section 2.2.1.58.4) |
0x1009 |
PtypBinary ([MS-OXCDATA] section 2.11.1) |
261 Bytes 01 01 00 00 53 01 00 00 4C 5A 46 75 69 B3 B7 69 03 00 0A 00 72 63 70 67 31 32 35 16 32 00 F8 0B 60 6E 0E 10 30 33 33 4F 01 F7 02 A4 03 E3 02 00 63 68 0A C0 73 B0 65 74 30 20 07 13 02 80 7D 0A 80 9D 00 00 2A 09 B0 09 F0 04 90 61 74 05 B1 1A 52 0D E0 68 09 80 01 D0 20 35 2E C0 35 30 2E 39 39 2E 01 D0 13 A0 49 02 80 5C 76 08 90 77 6B 0B 80 64 3A 34 0C 60 63 00 50 0B 03 0B B5 20 54 8A 68 04 00 20 16 41 61 20 74 07 90 6D 05 40 65 00 C0 03 10 2E 0A A2 0A 81 6F 04 62 6A 12 A0 74 70 68 5C 27 AF 0C 01 17 84 0A B1 12 12 6F 05 30 69 02 20 E5 07 40 20 03 F0 74 68 16 90 03 A0 19 87 DA 6C 0B 80 65 0A A2 11 E1 4C 11 30 04 20 E9 10 F0 76 65 1A 51 6F 1A 30 04 90 16 90 FB 02 40 00 D0 68 07 80 02 30 17 7F 18 8A 0A 80 A8 41 64 64 0B 80 67 16 91 70 0D E0 5E 74 08 70 1B 53 1D DF 20 A2 7D 22 20 |
{\rtf1\ANSI\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0 Arial;}}<CR><LF>{\*\generator<SP>Riched20<SP>5.50.99.2050;}\viewkind4\uc1\pard\f0\fs20<SP>This<SP>is<SP>a<SP>test<SP>email.\par<CR><LF>\objattph\'20\par<CR><LF>\par{\*\optional<SP>with<SP>an<SP>optional<SP>line\par}<CR><LF>Lets<SP>have<SP>another<SP>attachment\par<CR><LF>\objattph\'20\par<CR><LF>\par<CR><LF>Adding<SP>a<SP>picture\par<CR><LF>\objattph\'20\par<CR><LF>} |
Based on the server responses, the proper body to load is the value of the PidTagRtfCompressed property.
The PidTagRtfCompressed property is stored in a packed format; by using the RTF Compression Algorithm, as described in [MS-OXRTFCP], the content is decoded and the raw RTF is as follows:
-
{\rtf1\ANSI\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0 Arial;}}<CR><LF>{\*\generator<SP>Riched20<SP>5.50.99.2050;}\viewkind4\uc1\pard\f0\fs20<SP>This<SP>is<SP>a<SP>test<SP>e-mail.\par<CR><LF>\objattph\'20\par<CR><LF>\par{\*\optional<SP>with<SP>an<SP>optional<SP>line\par}<CR><LF>Lets<SP>have<SP>another<SP>attachment\par<CR><LF>\objattph\'20\par<CR><LF>\par<CR><LF>Adding<SP>a<SP>picture\par<CR><LF>\objattph\'20\par<CR><LF>}
This algorithm is then used to determine whether the RTF is encapsulated by examining the RTF tokens before the font table destination. Because the FROMHTML and FROMTEXT control words are not found in the RTF header, the contents are not encapsulated.
As the body is loaded and the RTF reader parses the RTF, the render position of each \objattph token is calculated and stored in an array similar to that which is described in the following table.
Position array |
---|
22 |
54 |
74 |
Note There is an optional destination (\optional) that is not understood by the RTF reader. This affects the rendered token locations, as the contents "with an optional line < CRLF >" are not rendered.
When the body parsing is complete and the existence of placeholder tokens is recorded, the attachments from the message are loaded.
The following ROP requests are transmitted to the server:
The RopGetAttachmentTable ROP ([MS-OXCROPS] section 2.2.6.17).
The RopSetColumns ROP ([MS-OXCROPS] section 2.2.5.1), which requests the PidTagAttachNumber ([MS-OXCMSG] section 2.2.2.6), PidTagAttachMethod ([MS-OXCMSG] section 2.2.2.9), PidTagRenderingPosition ([MS-OXCMSG] section 2.2.2.16), PidTagAttachLongFilename ([MS-OXCMSG] section 2.2.2.10), and PidTagAttachmentHidden ([MS-OXCMSG] section 2.2.2.24) properties.
The RopQueryRows ROP ([MS-OXCROPS] section 2.2.5.4).
The response buffer from the RopQueryRows ROP contains three rows, as described in the following three tables.
Row 1
Property name |
Property ID |
Data type |
Data |
Value |
---|---|---|---|---|
PidTagAttachNumber |
0x0E21 |
PtypInteger32 ([MS-OXCDATA] section 2.11.1) |
0x00000000 |
0 |
PidTagAttachMethod |
0x3705 |
PtypInteger32 |
0x00000001 |
afByValue |
PidTagRenderingPosition |
0x370B |
PtypInteger32 |
0x00000016 |
22 |
PidTagAttachLongFilename |
0x3707 |
PtypString ([MS-OXCDATA] section 2.11.1) |
00 68 00 65 00 6C 00 6C 00 6F 00 77 00 6F 00 72 00 6C 00 64 00 2E 00 74 00 78 00 74 00 00 00 00 |
"helloworld.txt" |
PidTagAttachmentHidden |
0x7FFE |
PtypBoolean |
0x0000 |
FALSE |
Row 2
Property name |
Property ID |
Data type |
Data |
Value |
---|---|---|---|---|
PidTagAttachNumber |
0x0E21 |
PtypInteger32 |
0x00000001 |
0 |
PidTagAttachMethod |
0x3705 |
PtypInteger32 |
0x00000001 |
afByValue |
PidTagRenderingPosition |
0x370B |
PtypInteger32 |
0x00000036 |
76 |
PidTagAttachLongFilename |
0x3707 |
PtypString |
00 68 00 65 00 6C 00 6C 00 6F 00 77 00 6F 00 72 00 6C 00 64 00 2E 00 64 00 6F 00 63 00 00 00 00 |
"helloworld.doc" |
PidTagAttachmentHidden |
0x7FFE |
PtypBoolean |
0x0000 |
FALSE |
Row 3
Property name |
Property ID |
Data type |
Data |
Value |
---|---|---|---|---|
PidTagAttachNumber |
0x0E21 |
PtypInteger32 |
0x00000002 |
0 |
PidTagAttachMethod |
0x3705 |
PtypInteger32 |
0x00000006 |
afOle |
PidTagRenderingPosition |
0x370B |
PtypInteger32 |
0x0000004A |
100 |
PidTagAttachLongFilename |
0x3707 |
PtypString |
00 50 00 42 00 72 00 75 00 73 00 68 00 00 00 00 |
"PBrush" |
PidTagAttachmentHidden |
0x7FFE |
PtypBoolean |
0x0000 |
FALSE |
Because the attachments are already ordered correctly by rendering position, they do not need to be reordered.
Because the attachment list is three entries long, and the previously constructed position array is also three entries long, the insertion positions come from the position array. This results in replacing the second and third attachments at different positions than those set in the value of the PidTagRenderingPosition property. Specifically, the second attachment ("helloworld.doc") will replace position 54, not 76, and the third attachment will replace position 74, not 100.
Looping over the stored objattph positions in the position array, each attachment is prepared for insertion.
The first attachment ("helloworld.txt") replaces rendered character position 22. The second attachment ("helloworld.doc") replaces the rendered character position 54. Finally, the last attachment ("PBrush") replaces the rendered character position 74.
Because there are no additional attachments, the integration is complete.