Adding an Issuance License to the Compound File

[The AD RMS SDK leveraging functionality exposed by the client in Msdrm.dll is available for use in Windows Server 2008, Windows Vista, Windows Server 2008 R2, Windows 7, Windows Server 2012, and Windows 8. It may be altered or unavailable in subsequent versions. Instead, use Active Directory Rights Management Services SDK 2.1, which leverages functionality exposed by the client in Msipc.dll.]

In the following example, the signed issuance license is added in a new stream to the compound file. This stream must be called \006Primary to enable the Microsoft Rights Management Add-on for Internet Explorer to read it.

// PaddingLength is a custom function that returns the number of 
// padding bytes required to bring a submitted number up to 
// a submitted block size. VERSIONSTAMP is a custom structure.

// CHECK_STG_ERROR is an error-handling macro.
#define CHECK_STG_ERROR(api,hresult) if(FAILED(hresult))           \
{                                                                  \
printf("Error (%s): %S [%d]\n",api,StorageError(hresult),hresult); \
goto e_Exit;                                                       \
}


WCHAR StorageName_DataSpaces[12]    =    L"*DataSpaces";
WCHAR StorageName_DataSpaceInfo[14] =    L"DataSpaceInfo";
WCHAR StorageName_TransformInfo[14] =    L"TransformInfo";
WCHAR StorageName_DRMTransform[14]  =    L"*DRMTransform";
WCHAR StreamName_DRMViewerContent[18] =  L"*DRMViewerContent";
WCHAR StreamName_Version[8]         =    L"Version";
WCHAR StreamName_DataSpaceMap[13]   =    L"DataSpaceMap";
WCHAR StreamName_DRMDataSpace[14]   =    L"*DRMDataSpace";
WCHAR StreamName_Primary[9]         =    L"*Primary";

...

CDOCLIB_API HRESULT __stdcall AddPublishingLicense(
                                 WCHAR* wcsPubLicense, 
                                 WCHAR* wcsRmhFilePath)
{
    int HeaderLen           = 0;
    int EntryCount          = 0;
    int EntryLength         = 0;
    int RefComponentCount   = 0;
    int RefComponentType    = 0;
    int RefComponentLen     = 0;
    int RefComponentPad     = 0;
    int DataSpaceNameLen    = 0;
    int DataSpaceNamePad    = 0;
    int TotalLength         = 0;

    LPWSTR RefComponent     = NULL;
    LPWSTR DataSpaceName    = NULL;

    // Storage and stream objects.
    IStorage *pStorage              = NULL;
    IStorage *pDataSpaceStorage     = NULL;
    IStorage *pDataSpaceInfoStorage = NULL;
    IStorage *pTransformInfoStorage = NULL;
    IStorage *pDRMTransformStorage  = NULL;
    IStream  *pStream               = NULL;

    HRESULT hResult     = NULL;
    ULONG bytesWritten  = 0;
    BYTE *dataspacemap  = NULL;
    LPSTR szB64Username = NULL;
    LPSTR szRightsLabel = NULL;
    BYTE *drm_transform = NULL;

    // Insert special characters into storage/stream names.
    short char_nine = 9;
    short char_six  = 6;
    memcpy(StorageName_DataSpaces, &char_six, 2);
    memcpy(StorageName_DRMTransform, &char_nine, 2);
    memcpy(StreamName_DRMDataSpace, &char_nine, 2);
    memcpy(StreamName_Primary, &char_six, 2);


    // Structure needed for the transform header.
    VERSIONSTAMP version;
    version.ReaderVersionMajor  = 1;
    version.ReaderVersionMinor  = 0;
    version.UpdaterVersionMajor = 1;
    version.UpdaterVersionMinor = 0;
    version.WriterVersionMajor  = 1;
    version.WriterVersionMinor  = 0;


    // Open the output compound file.
    hResult = StgOpenStorageEx( 
                 wcsRmhFilePath,         // File name
                 STGM_READWRITE|         // File access
                   STGM_SHARE_EXCLUSIVE, 
                 STGFMT_STORAGE,         // Compound file
                 0,              
                 NULL,           
                 0,              
                 IID_IStorage,           // IStorage IID
                 (void **)&pStorage);    // IStorage pointer

    CHECK_STG_ERROR("StgCreateStorageEx", hResult);

    // Create \006DataSpaces storage.
    hResult = pStorage->CreateStorage(
                 StorageName_DataSpaces,
                 STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                 0,
                 0,
                 &pDataSpaceStorage);
    CHECK_STG_ERROR("IStorage::CreateStorage", hResult);

   // Create Version Stream.
   // Stream Format:
   // STAMP
   //   int32 FeatureIdentifierLength
   //   WCHAR FeatureIdentifier[]
   //   int16 ReaderVersionMajor
   //   int16 ReaderVersionMinor
   //   int16 UpdaterVersionMajor
   //   int16 UpdaterVersionMinor
   //   int16 WriterVersionMajor
   //   int16 WriterVersionMinor
   // END STAMP
    BYTE version_stamp[76];
    ZeroMemory(version_stamp, 76);
    version_stamp[0]=60;
    memcpy(version_stamp+4, L"Microsoft.Container.DataSpaces", 60);
    memcpy(version_stamp+64, &version, 12);
    hResult = pDataSpaceStorage->CreateStream(
                StreamName_Version,    // Required name
                STGM_READWRITE |       // File access
                STGM_SHARE_EXCLUSIVE,
                0,                     
                0,                     
                &pStream);             // IStream pointer
   CHECK_STG_ERROR("IStorage::CreateStream", hResult);
   hResult = pStream->Write(version_stamp, 76, &bytesWritten);
   CHECK_STG_ERROR("IStream::Write", hResult);


   // Create DataSpaceMap stream.
   // Stream Format:
   // HEADER
   //   int32 HeaderLen
   //   int32 EntryCount
   // END HEADER
   // MAP ENTRY
   //   int32 EntryLength
   //   int32 RefComponentCount
   //   REFCOMPONENT
   //     int32 RefComponentType
   //     int32 RefComponentLen
   //     WCHAR RefComponent[]
   //   END REFCOMPONENT (DWORD ALIGN)
   //   int32 DataSpaceNameLen
   //   WCHAR DataSpaceName[] 
   // END MAP ENTRY (DWORD ALIGN)
   hResult = pDataSpaceStorage->CreateStream(
                StreamName_DataSpaceMap,   // Required name
                STGM_READWRITE |           // File access 
                    STGM_SHARE_EXCLUSIVE, 
                0,                     
                0,                     
                &pStream);                 // IStream pointer
   CHECK_STG_ERROR("IStorage::CreateStream", hResult);

   // Calculate the size of the buffer needed to hold all
   // data for the DataSpaceMap stream.
   HeaderLen         = 8;
   EntryCount        = 1;
   RefComponentCount = 1;
   RefComponentType  = 0;
   RefComponentLen   = (int)wcslen(
                       StreamName_DRMViewerContent)*sizeof(WCHAR);
   RefComponentPad   = PaddingLength(RefComponentLen,sizeof(DWORD));
   RefComponent      = StreamName_DRMViewerContent;
   DataSpaceNameLen  = (int)wcslen(StreamName_DRMDataSpace)
                       *sizeof(WCHAR);
   DataSpaceNamePad  = PaddingLength(DataSpaceNameLen, 
                       sizeof(DWORD));
   DataSpaceName     = StreamName_DRMDataSpace;
   EntryLength       = sizeof(DWORD)*5+
                       RefComponentLen+RefComponentPad+
                       DataSpaceNameLen+DataSpaceNamePad;
   TotalLength       = sizeof(DWORD)*2+EntryLength;

   // Allocate buffer and copy headers and data to the buffer.
   dataspacemap = (BYTE *)HeapAlloc(
                            GetProcessHeap(), 
                            0, 
                            TotalLength);
   if (dataspacemap == NULL)
   {
       hResult = E_OUTOFMEMORY;
       goto e_Exit;
   }
   ZeroMemory(dataspacemap, TotalLength);
   memcpy(dataspacemap+0, &HeaderLen, 4);
   memcpy(dataspacemap+4, &EntryCount, 4);
   memcpy(dataspacemap+8, &EntryLength, 4);
   memcpy(dataspacemap+12, &RefComponentCount, 4);
   memcpy(dataspacemap+16, &RefComponentType, 4);
   memcpy(dataspacemap+20, &RefComponentType, 4);
   memcpy(dataspacemap+24, RefComponent, RefComponentLen);
   memcpy(dataspacemap+24+RefComponentLen+RefComponentPad, 
          &DataSpaceNameLen, 4);
   memcpy(dataspacemap+28+RefComponentLen+RefComponentPad,
          DataSpaceName,DataSpaceNameLen);

   // Copy buffer to the new DataSpaceMap stream.
   hResult = pStream->Write(
                (void *)dataspacemap, 
                TotalLength, 
                &bytesWritten); 
   CHECK_STG_ERROR("IStream::Write", hResult);

   // Release the stream. 
   if (pStream != NULL)
   {
        pStream->Release();
        pStream = NULL;
   }

   // Create DataSpaceInfo storage.
   hResult = pDataSpaceStorage->CreateStorage(
                 StorageName_DataSpaceInfo,
                 STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                 0,
                 0,
                 &pDataSpaceInfoStorage);
   CHECK_STG_ERROR("IStorage::CreateStorage[DataSpaceInfo]", 
                 hResult);

   // Create \009DRMDataSpace stream.
   // Stream Format:
   // HEADER
   //   int32 HeaderLen
   //   int32 TransformCount
   // END HEADER
   // TRANSFORM
   //   Int32 TransformNameLen
   //   WCHAR TransformName[]
   // END TRANSFORM
   hResult = pDataSpaceInfoStorage->CreateStream(
                  StreamName_DRMDataSpace,    // Required name
                  STGM_READWRITE |            // Eile access
                     STGM_SHARE_EXCLUSIVE, 
                  0,                          // Required
                  0,                          // Required 
                  &pStream);                  // IStream pointer
   CHECK_STG_ERROR("IStorage::CreateStream", hResult);

   HeaderLen            = 8;
   int TransformCount   = 1;
   int TransformNameLen = 26;
   LPWSTR TransformName = StorageName_DRMTransform;

   // Copy data to buffer. 
   BYTE dataspace[40];
   ZeroMemory(dataspace, 40);
   memcpy(dataspace+0, &HeaderLen, 4);
   memcpy(dataspace+4, &TransformCount, 4);
   memcpy(dataspace+8, &TransformNameLen, 4);
   memcpy(dataspace+12, TransformName, TransformNameLen+2);

   // Write \009DRMDataSpace stream.
   hResult = pStream->Write(
                  (void *)dataspace, 
                  40, 
                  &bytesWritten); 
   CHECK_STG_ERROR("IStream::Write", hResult);

   // Create TransformInfo storage.
   hResult = pDataSpaceStorage->CreateStorage(
                  StorageName_TransformInfo,
                  STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                  0,
                  0,
                  &pTransformInfoStorage);
   CHECK_STG_ERROR("IStorage::CreateStorage[TransformInfo]", 
                  hResult);

   // Create \009DRMTransform storage.
   memcpy(StorageName_DRMTransform, &char_nine, 2);
   hResult = pTransformInfoStorage->CreateStorage( 
                  StorageName_DRMTransform,
                  STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
                  0,
                  0,
                  &pDRMTransformStorage);
   CHECK_STG_ERROR("IStorage::CreateStorage[DRMTransform]", 
                  hResult);

   // Create \006Primary stream.
   hResult = pDRMTransformStorage->CreateStream(
                  StreamName_Primary,      // Required name
                  STGM_READWRITE |         // File access
                     STGM_SHARE_EXCLUSIVE, 
                  0,                       // Required value
                  0,                       // Required value
                  &pStream);               // IStream pointer
   CHECK_STG_ERROR("IStorage::CreateStream", hResult);

   LPWSTR ClassID  = L"{C73DFACD-061F-43B0-8B64-0C620D2A8B50}";
   LPWSTR FeatureIdentifier = L"Microsoft.Metadata.DRMTransform";
   LPWSTR wszSignedIL  = wcsPubLicense;
   int IndexOfFeatureIdentifierLength   = 88;
   int TheNumberOne  = 1;
   int ClassIDLength  = (int)wcslen(ClassID)*sizeof(WCHAR);
   int FeatureIdentifierLength = (int)wcslen(FeatureIdentifier)
                                 *sizeof(WCHAR);
   int TheNumberFour  = 4;
   short TheNumberZero  = 0;
   szRightsLabel = (CHAR *)HeapAlloc(GetProcessHeap(),
                   0, 
                   wcslen(wszSignedIL)+1);
   if ( NULL == szRightsLabel )
   {
       printf("Error (%s): E_OUTOFMEMORY\n", "HeapAlloc");
       goto e_Exit;
   }
   StringCchPrintf(szRightsLabel, 
                   wcslen(wszSignedIL)+1, 
                   "%S", 
                   wszSignedIL);
   int RightsLabelLength  = (int)strlen(szRightsLabel);
   int TransformLength    = 38 + ClassIDLength + 
                            FeatureIdentifierLength + 
                            RightsLabelLength +     
                            PaddingLength(
                               RightsLabelLength, 
                               sizeof(DWORD));
   
   // Allocate buffer to hold \006Primary stream data.
   drm_transform = (BYTE *)HeapAlloc(GetProcessHeap(),
                  0, 
                  TransformLength);
   if ( NULL == drm_transform )
   {
       printf("Error (%s): E_OUTOFMEMORY\n", "HeapAlloc");
       goto e_Exit;
   }
   ZeroMemory(drm_transform, TransformLength);

   // Copy headers and issuance license to buffer.
   memcpy(drm_transform+0, &IndexOfFeatureIdentifierLength, 4);
   memcpy(drm_transform+4, &TheNumberOne, 4);
   memcpy(drm_transform+8, &ClassIDLength, 4);
   memcpy(drm_transform+12, ClassID, ClassIDLength);
   memcpy(drm_transform+12+ClassIDLength,
          &FeatureIdentifierLength, 
          4);
   memcpy(drm_transform+16+ClassIDLength, 
          FeatureIdentifier, 
          FeatureIdentifierLength);
   memcpy(drm_transform+16+ClassIDLength+FeatureIdentifierLength,
          &TheNumberZero, 
          2);
   memcpy(drm_transform+18+ClassIDLength+FeatureIdentifierLength,
          &version,
          12);
   memcpy(drm_transform+30+ClassIDLength+FeatureIdentifierLength, 
          &TheNumberFour,
          4);
   memcpy(drm_transform+34+ClassIDLength+FeatureIdentifierLength,
          &RightsLabelLength,
          4);
   memcpy(drm_transform+38+ClassIDLength+FeatureIdentifierLength,
          szRightsLabel,
          RightsLabelLength);

   // Copy buffer to \006Primary stream.
   hResult = pStream->Write(
                 (void *)drm_transform, 
                 TransformLength, 
                 &bytesWritten); 
   CHECK_STG_ERROR("IStream::Write", hResult);
   
   // Commit changes to root storage.
   pStorage->Commit(STGC_DEFAULT);

e_Exit:
   // Clean up.
   if (NULL != dataspacemap) HeapFree(GetProcessHeap(),
                  0, 
                  dataspacemap);
   if (NULL != szB64Username) HeapFree(GetProcessHeap(),
                  0, 
                  szB64Username);
   if (NULL != szRightsLabel) HeapFree(GetProcessHeap(),
                  0, 
                  szRightsLabel);
   if (NULL != drm_transform) HeapFree(GetProcessHeap(), 
                  0, 
                  drm_transform);

   if (pStorage != NULL)
       pStorage->Release();
   if (pDataSpaceStorage != NULL)
       pDataSpaceStorage->Release();
   if (pDataSpaceInfoStorage != NULL)
       pDataSpaceInfoStorage->Release();
   if (pTransformInfoStorage != NULL)
       pTransformInfoStorage->Release();
   if (pDRMTransformStorage != NULL)
       pDRMTransformStorage->Release();
   if (pStream != NULL)
       pStream->Release();
    
   return hResult;
}

Adding a Use License to the Compound File

Creating a Compound File and Adding Protected Content

Understanding Compound Files