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.