Locating the POP3 UIDL History

When Outlook’s POP3 provider syncs with a POP3 mailbox and the user chooses to leave a copy of the messages on the server, it has to remember which messages it has already downloaded so it doesn’t accidently download the same message twice. It does this by tracking the UIDL of each message it has downloaded in a binary property on a hidden message. This post describes how Outlook locates this binary property. My next post will detail how to parse this property.

Locate the Message

  1. Get PR_SEARCH_KEY from the profile (from the profile section MUID_PROFILE_INSTANCE)

  2. Open the Associated Contents for Inbox.

  3. Build this restriction:

     SRestriction rgRes[3]; 
    SPropValue rgProps[3]; 
    rgRes[0].rt = RES_AND; 
    rgRes[0].res.resAnd.cRes = 2; 
    rgRes[0].res.resAnd.lpRes = &rgRes[1]; 
    rgRes[1].rt = RES_PROPERTY; 
    rgRes[1].res.resProperty.relop = RELOP_EQ; 
    rgRes[1].res.resProperty.ulPropTag = PR_CONVERSATION_KEY; 
    rgRes[1].res.resProperty.lpProp = &rgProps[0]; 
    rgRes[2].rt = RES_PROPERTY; 
    rgRes[2].res.resProperty.relop = RELOP_EQ; 
    rgRes[2].res.resProperty.ulPropTag = PR_MESSAGE_CLASS; 
    rgRes[2].res.resProperty.lpProp = &rgProps[1]; 
    rgProps[0].ulPropTag = PR_CONVERSATION_KEY; 
    rgProps[0].Value.bin = pVals[iSearchKey].Value.bin; // PR_SEARCH_KEY from the profile 
    rgProps[1].ulPropTag = PR_MESSAGE_CLASS; 
    rgProps[1].Value.LPSZ = (LPTSTR)"IPM.MessageManager";
  4. Find the message using FindRow.

  5. If this fails, change the restriction:

     rgRes[1].res.resProperty.ulPropTag = rgProps[0].ulPropTag = PR_SEARCH_KEY;

    And re-run the FindRow

  6. If THAT fails, for non-Exchange stores (compare PR_MDB_PROVIDER to pbExchangeProviderPrimaryUserGuid), restrict instead on PR_SUBJECT (using printf style substitution here for brevity):: 

     "Outlook Message Manager (%s) (KEY: %s)", PR_PROFILE_NAME, HexFromBin(PR_SEARCH_KEY)
  7. Open the message located in step 5, 6, or 7.

For Outlook 2010 and higher, you can substitute the following for steps 3-6:

 CHAR g_szGeneralKey[] = "General Key"; 
const SBinary g_binGeneralKey = {sizeof(g_szGeneralKey), (LPBYTE)g_szGeneralKey};

Now, use these values for PR_SEARCH_KEY and PR_PROFILE_NAME and run through steps 3-6. If this fails to find a message, fall back to the original steps 3-6.

Open the Attachment

There may be more than one attachment on the message. Try the following, in order (note again the use of printf style notation)::

  1. Look for an attachment whose PR_ATTACH_LONG_FILENAME matches "BlobPOP%s", szEmailAddress
  2. Look for an attachment whose PR_ATTACH_FILENAME matches "BlobPOP%s", szEmailAddress
  3. Look for an attachment whose PR_DISPLAY_NAME matches "BlobPOP%s", szEmailAddress
  4. Look for an attachment whose PR_ATTACH_FILENAME matches "Blob%.8x", dwAcctUID, where dwAcctUID comes from PROP_ACCT_MINI_UID.

PRO_ACCT_MINI_UID is a property you can retrieve via the Account Management API. Here’s the definition:


Open the Blob

The POP3 UIDL data blob is stored in PR_ATTACH_DATA_BIN.

Parse the Blob

We’ll discuss how to parse this blob in my next post. Stay tuned.