locked
Custom IRM Protector

    Question

  • The SDK has some light documentation on creating custom IRM protectors, but no reference as to where to get the interfaces or the class definitions for the supporting classes. Is there a typelib somewhere? I can't find it. A sample project would be nice too. The documentation has a sample pseudo-implementation, but there's no way to get started without the COM interface definition.
    Monday, January 29, 2007 7:16 PM

Answers

  • You can find documentation on the interface in the ECM Starter Kit: http://www.microsoft.com/downloads/details.aspx?familyid=38CA6B32-44BE-4489-8526-F09C57CD13A5&displaylang=en

    There is a header containing the definition (msoipi.h) in the kit (look in the folder ECM Starter Kit\White Papers\Information Rights Management where the kit is installed)

    //

    // msoipi.h - Microsoft IRM Protector Interface

    //

    // Copyright (C) 2006 Microsoft Corporation

    //

    // Version 1.1

    //

    // THIS IS PRELIMINARY DOCUMENTATION AND SUBJECT TO CHANGE.

    // LAST UPDATED: May 19th, 2006

    #ifndef _MSOIPI_H

    #define _MSOIPI_H

    #include <objbase.h>

    //****************************************************************************

    // I_IrmCrypt

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmCrypt

    MIDL_INTERFACE("598F03E8-948A-425e-B7B2-2D613CEDFFEF")

    I_IrmCrypt: public IUnknown

    {

    STDMETHOD(HrGetBlockSize) (THIS_

    /*[OUT]*/ DWORD *pdwBlockSize) PURE;

    STDMETHOD(HrEncrypt) (THIS_

    /*[IN]*/ ULONG ulOffset,

    /*[IN/OUT]*/ BYTE *pbData,

    /*[IN]*/ DWORD cbData,

    /*[OUT]*/ DWORD *pcbData) PURE;

    STDMETHOD(HrDecrypt) (THIS_

    /*[IN]*/ ULONG ulOffset,

    /*[IN/OUT]*/ BYTE *pbData,

    /*[IN]*/ DWORD cbData,

    /*[OUT]*/ DWORD *pcbData) PURE;

    STDMETHOD(HrEncode) (THIS_

    /*[IN]*/ WCHAR *wszAlgID,

    /*[IN]*/ UINT uDataLen,

    /*[IN]*/ BYTE *pbDecodedData,

    /*[IN/OUT]*/ UINT *puEncodedStringLen,

    /*[OUT]*/ WCHAR *wszEncodedString) PURE;

    STDMETHOD(HrDecode) (THIS_

    /*[IN]*/ WCHAR *wszAlgID,

    /*[IN]*/ WCHAR *wszEncodedString,

    /*[IN/OUT]*/ UINT *puDecodedDataLen,

    /*[OUT]*/ BYTE *pbDecodedData) PURE;

    };

    // {598F03E8-948A-425e-B7B2-2D613CEDFFEF}

    DEFINE_GUID(IID_I_IrmCrypt,

    0x598f03e8, 0x948a, 0x425e, 0xb7, 0xb2, 0x2d, 0x61, 0x3c, 0xed, 0xff, 0xef);

    //****************************************************************************

    // I_IrmPolicyInfoRMS

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmPolicyInfoRMS

    MIDL_INTERFACE("175EF0A4-8EB8-49ac-9049-F40EC69EC0A7")

    I_IrmPolicyInfoRMS: public IUnknown

    {

    STDMETHOD(HrGetICrypt) (THIS_

    /*[OUT]*/ I_IrmCrypt **piic) PURE;

    STDMETHOD(HrGetSignedIL) (THIS_

    /*[OUT]*/ BSTR *pbstrIL) PURE;

    STDMETHOD(HrGetServerEUL) (THIS_

    /*[OUT]*/ BSTR *pbstrEUL) PURE;

    STDMETHOD(HrGetServerId) (THIS_

    /*[OUT]*/ BSTR *pbstrServerId) PURE;

    STDMETHOD(HrGetEULs) (THIS_

    /*[OUT]*/ BSTR *rgbstrEUL,

    /*[OUT]*/ BSTR *rgbstrId,

    /*[OUT]*/ UINT *pcbEULs) PURE;

    STDMETHOD(HrSetSignedIL) (THIS_

    /*[IN]*/ BSTR bstrIL) PURE;

    STDMETHOD(HrSetServerEUL) (THIS_

    /*[IN]*/ BSTR bstrEUL) PURE;

    STDMETHOD(HrGetRightsTemplate) (THIS_

    /*[OUT]*/ BSTR* pbstrRightsTemplate) PURE;

    STDMETHOD(HrGetListGuid) (THIS_

    /*[OUT]*/ BSTR* pbstrListGuid) PURE;

    };

    // {175EF0A4-8EB8-49ac-9049-F40EC69EC0A7}

    DEFINE_GUID(IID_I_IrmPolicyInfoRMS,

    0x175ef0a4, 0x8eb8, 0x49ac, 0x90, 0x49, 0xf4, 0xe, 0xc6, 0x9e, 0xc0, 0xa7);

    //****************************************************************************

    // I_IrmPolicyInfo

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmPolicyInfo

    MIDL_INTERFACE("2CDC48E9-DB49-47E6-8487-A2EA1FCE292F")

    I_IrmPolicyInfo: public IUnknown

    {

    STDMETHOD(HrGetListGuid) (THIS_

    /*[OUT]*/ BSTR* pbstrListGuid) PURE;

    STDMETHOD(HrSetListGuid) (THIS_

    /*[IN]*/ BSTR bstrListGuid) PURE;

    STDMETHOD(HrGetRightsMask) (THIS_

    /*[OUT]*/ DWORD* pdwRightsMask) PURE;

    STDMETHOD(HrGetRequestingUser) (THIS_

    /*[OUT]*/ BSTR* pbstrRequestingUser,

    /*[OUT]*/ BOOL* pfRequestingUserIsSystem) PURE;

    STDMETHOD(HrGetURL) (THIS_

    /*[OUT]*/ BSTR* pbstrURL) PURE;

    STDMETHOD(HrGetPolicyTitle) (THIS_

    /*[OUT]*/ BSTR* pbstrPolicyTitle) PURE;

    STDMETHOD(HrGetPolicyDescription) (THIS_

    /*[OUT]*/ BSTR* pbstrPolicyDescription) PURE;

    STDMETHOD(HrGetOfflineDays) (THIS_

    /*[OUT]*/ DWORD* pdwOfflineDays) PURE;

    };

    // Rights for HrGetRightsMask

    #define rightNone 0x0000

    #define rightView 0x0001

    #define rightEdit 0x0002

    #define rightSave 0x0004

    #define rightExtract 0x0008

    #define rightPrint 0x0010

    #define rightVBA 0x0020 //Not used in SharePoint

    #define rightAdmin 0x0040

    #define rightForward 0x0080 //Not used in SharePoint

    #define rightReply 0x0100 //Not used in SharePoint

    #define rightReplyAll 0x0200 //Not used in SharePoint

    #define rightAll 0xFFFF //Not used in SharePoint

     

    // {2CDC48E9-DB49-47E6-8487-A2EA1FCE292F}

    DEFINE_GUID(IID_I_IrmPolicyInfo,

    0x2cdc48e9, 0xdb49, 0x47e6, 0x84, 0x87, 0xa2, 0xea, 0x1f, 0xce, 0x29, 0x2f);

    //****************************************************************************

    // I_IrmProtector

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmProtector

    MIDL_INTERFACE("fcfbc0ac-672b-452d-80e5-40652503d96e")

    I_IrmProtector: public IUnknown

    {

    STDMETHOD(HrInit) (THIS_

    /*[OUT]*/ BSTR *pbstrProduct,

    /*[OUT]*/ DWORD *pdwVersion,

    /*[OUT]*/ BSTR *pbstrExtentions,

    /*[OUT]*/ BOOL *pfUseRMS) PURE;

    STDMETHOD(HrIsProtected) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[OUT]*/ DWORD *pdwResult) PURE;

    STDMETHOD(HrSetLangId) (THIS_

    /*[IN]*/ LANGID langid) PURE;

    STDMETHOD(HrProtectRMS) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfoRMS *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    STDMETHOD(HrUnprotectRMS) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfoRMS *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    STDMETHOD(HrProtect) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfo *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    STDMETHOD(HrUnprotect) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfo *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    };

    //HrIsProtected possible results:

    #define MSOIPI_RESULT_UNKNOWN 0 //I cannot tell. Ask me in HrProtect/HrUnprotect

    #define MSOIPI_RESULT_PROTECTED 1 //pilbInput is protected

    #define MSOIPI_RESULT_UNPROTECTED 2 //pilbInput is unprotected

    #define MSOIPI_RESULT_NOT_MY_FILE 3 //Not my file. For example, an XML file that is not an InfoPath form.

    // Status:

    #define MSOIPI_STATUS_UNKNOWN 0

    #define MSOIPI_STATUS_PROTECT_SUCCESS 1

    #define MSOIPI_STATUS_UNPROTECT_SUCCESS 2

    #define MSOIPI_STATUS_ALREADY_PROTECTED 3 //If called to protect and file is already protected

    #define MSOIPI_STATUS_CANT_PROTECT 4 //If protect fails

    #define MSOIPI_STATUS_ALREADY_UNPROTECTED 5 //If called to unprotect and file is already unprotected

    #define MSOIPI_STATUS_CANT_UNPROTECT 6 //If unprotect fails

    #define MSOIPI_STATUS_NOT_OWNER 7 //If the ServerId doesn't have an EUL that allows unprotection

    //Unused 8

    #define MSOIPI_STATUS_NOT_MY_FILE 9 //If the protector is passed a file it cannot identify

    #define MSOIPI_STATUS_FILE_CORRUPT 10 //If the parts of the file that make this an IRM file are corrupt

    #define MSOIPI_STATUS_PLATFORM_IRM_FAILED 11 //If a call back into the platform for RMS APIs fails

    #define MSOIPI_STATUS_BAD_INSTALL 12 //If the protector isn't installed properly

    // {FCFBC0AC-672B-452d-80E5-40652503D96E}

    DEFINE_GUID(IID_I_IrmProtector,

    0xfcfbc0ac, 0x672b, 0x452d, 0x80, 0xe5, 0x40, 0x65, 0x25, 0x03, 0xd9, 0x6e);

     

    #endif

    Wednesday, February 21, 2007 8:21 PM

All replies

  • I think there are lots of places where the content is there but the "samples" etc.. aren't. Give it time and all will be revealed.. or try to get one of these MS Guru's to point you in the right direction... they are a pretty knowledgable lot :-)
    Monday, January 29, 2007 10:43 PM
  • Anyone on the SharePoint team know where to find the interface definitions (IDL, typelib, assembly?) that need to be referenced to implement custom IRM protectors?
    Wednesday, January 31, 2007 5:32 PM
  • You can find documentation on the interface in the ECM Starter Kit: http://www.microsoft.com/downloads/details.aspx?familyid=38CA6B32-44BE-4489-8526-F09C57CD13A5&displaylang=en

    There is a header containing the definition (msoipi.h) in the kit (look in the folder ECM Starter Kit\White Papers\Information Rights Management where the kit is installed)

    //

    // msoipi.h - Microsoft IRM Protector Interface

    //

    // Copyright (C) 2006 Microsoft Corporation

    //

    // Version 1.1

    //

    // THIS IS PRELIMINARY DOCUMENTATION AND SUBJECT TO CHANGE.

    // LAST UPDATED: May 19th, 2006

    #ifndef _MSOIPI_H

    #define _MSOIPI_H

    #include <objbase.h>

    //****************************************************************************

    // I_IrmCrypt

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmCrypt

    MIDL_INTERFACE("598F03E8-948A-425e-B7B2-2D613CEDFFEF")

    I_IrmCrypt: public IUnknown

    {

    STDMETHOD(HrGetBlockSize) (THIS_

    /*[OUT]*/ DWORD *pdwBlockSize) PURE;

    STDMETHOD(HrEncrypt) (THIS_

    /*[IN]*/ ULONG ulOffset,

    /*[IN/OUT]*/ BYTE *pbData,

    /*[IN]*/ DWORD cbData,

    /*[OUT]*/ DWORD *pcbData) PURE;

    STDMETHOD(HrDecrypt) (THIS_

    /*[IN]*/ ULONG ulOffset,

    /*[IN/OUT]*/ BYTE *pbData,

    /*[IN]*/ DWORD cbData,

    /*[OUT]*/ DWORD *pcbData) PURE;

    STDMETHOD(HrEncode) (THIS_

    /*[IN]*/ WCHAR *wszAlgID,

    /*[IN]*/ UINT uDataLen,

    /*[IN]*/ BYTE *pbDecodedData,

    /*[IN/OUT]*/ UINT *puEncodedStringLen,

    /*[OUT]*/ WCHAR *wszEncodedString) PURE;

    STDMETHOD(HrDecode) (THIS_

    /*[IN]*/ WCHAR *wszAlgID,

    /*[IN]*/ WCHAR *wszEncodedString,

    /*[IN/OUT]*/ UINT *puDecodedDataLen,

    /*[OUT]*/ BYTE *pbDecodedData) PURE;

    };

    // {598F03E8-948A-425e-B7B2-2D613CEDFFEF}

    DEFINE_GUID(IID_I_IrmCrypt,

    0x598f03e8, 0x948a, 0x425e, 0xb7, 0xb2, 0x2d, 0x61, 0x3c, 0xed, 0xff, 0xef);

    //****************************************************************************

    // I_IrmPolicyInfoRMS

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmPolicyInfoRMS

    MIDL_INTERFACE("175EF0A4-8EB8-49ac-9049-F40EC69EC0A7")

    I_IrmPolicyInfoRMS: public IUnknown

    {

    STDMETHOD(HrGetICrypt) (THIS_

    /*[OUT]*/ I_IrmCrypt **piic) PURE;

    STDMETHOD(HrGetSignedIL) (THIS_

    /*[OUT]*/ BSTR *pbstrIL) PURE;

    STDMETHOD(HrGetServerEUL) (THIS_

    /*[OUT]*/ BSTR *pbstrEUL) PURE;

    STDMETHOD(HrGetServerId) (THIS_

    /*[OUT]*/ BSTR *pbstrServerId) PURE;

    STDMETHOD(HrGetEULs) (THIS_

    /*[OUT]*/ BSTR *rgbstrEUL,

    /*[OUT]*/ BSTR *rgbstrId,

    /*[OUT]*/ UINT *pcbEULs) PURE;

    STDMETHOD(HrSetSignedIL) (THIS_

    /*[IN]*/ BSTR bstrIL) PURE;

    STDMETHOD(HrSetServerEUL) (THIS_

    /*[IN]*/ BSTR bstrEUL) PURE;

    STDMETHOD(HrGetRightsTemplate) (THIS_

    /*[OUT]*/ BSTR* pbstrRightsTemplate) PURE;

    STDMETHOD(HrGetListGuid) (THIS_

    /*[OUT]*/ BSTR* pbstrListGuid) PURE;

    };

    // {175EF0A4-8EB8-49ac-9049-F40EC69EC0A7}

    DEFINE_GUID(IID_I_IrmPolicyInfoRMS,

    0x175ef0a4, 0x8eb8, 0x49ac, 0x90, 0x49, 0xf4, 0xe, 0xc6, 0x9e, 0xc0, 0xa7);

    //****************************************************************************

    // I_IrmPolicyInfo

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmPolicyInfo

    MIDL_INTERFACE("2CDC48E9-DB49-47E6-8487-A2EA1FCE292F")

    I_IrmPolicyInfo: public IUnknown

    {

    STDMETHOD(HrGetListGuid) (THIS_

    /*[OUT]*/ BSTR* pbstrListGuid) PURE;

    STDMETHOD(HrSetListGuid) (THIS_

    /*[IN]*/ BSTR bstrListGuid) PURE;

    STDMETHOD(HrGetRightsMask) (THIS_

    /*[OUT]*/ DWORD* pdwRightsMask) PURE;

    STDMETHOD(HrGetRequestingUser) (THIS_

    /*[OUT]*/ BSTR* pbstrRequestingUser,

    /*[OUT]*/ BOOL* pfRequestingUserIsSystem) PURE;

    STDMETHOD(HrGetURL) (THIS_

    /*[OUT]*/ BSTR* pbstrURL) PURE;

    STDMETHOD(HrGetPolicyTitle) (THIS_

    /*[OUT]*/ BSTR* pbstrPolicyTitle) PURE;

    STDMETHOD(HrGetPolicyDescription) (THIS_

    /*[OUT]*/ BSTR* pbstrPolicyDescription) PURE;

    STDMETHOD(HrGetOfflineDays) (THIS_

    /*[OUT]*/ DWORD* pdwOfflineDays) PURE;

    };

    // Rights for HrGetRightsMask

    #define rightNone 0x0000

    #define rightView 0x0001

    #define rightEdit 0x0002

    #define rightSave 0x0004

    #define rightExtract 0x0008

    #define rightPrint 0x0010

    #define rightVBA 0x0020 //Not used in SharePoint

    #define rightAdmin 0x0040

    #define rightForward 0x0080 //Not used in SharePoint

    #define rightReply 0x0100 //Not used in SharePoint

    #define rightReplyAll 0x0200 //Not used in SharePoint

    #define rightAll 0xFFFF //Not used in SharePoint

     

    // {2CDC48E9-DB49-47E6-8487-A2EA1FCE292F}

    DEFINE_GUID(IID_I_IrmPolicyInfo,

    0x2cdc48e9, 0xdb49, 0x47e6, 0x84, 0x87, 0xa2, 0xea, 0x1f, 0xce, 0x29, 0x2f);

    //****************************************************************************

    // I_IrmProtector

    //****************************************************************************

    #undef INTERFACE

    #define INTERFACE I_IrmProtector

    MIDL_INTERFACE("fcfbc0ac-672b-452d-80e5-40652503d96e")

    I_IrmProtector: public IUnknown

    {

    STDMETHOD(HrInit) (THIS_

    /*[OUT]*/ BSTR *pbstrProduct,

    /*[OUT]*/ DWORD *pdwVersion,

    /*[OUT]*/ BSTR *pbstrExtentions,

    /*[OUT]*/ BOOL *pfUseRMS) PURE;

    STDMETHOD(HrIsProtected) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[OUT]*/ DWORD *pdwResult) PURE;

    STDMETHOD(HrSetLangId) (THIS_

    /*[IN]*/ LANGID langid) PURE;

    STDMETHOD(HrProtectRMS) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfoRMS *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    STDMETHOD(HrUnprotectRMS) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfoRMS *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    STDMETHOD(HrProtect) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfo *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    STDMETHOD(HrUnprotect) (THIS_

    /*[IN]*/ ILockBytes *pilbInput,

    /*[IN]*/ ILockBytes *pilbOutput,

    /*[IN]*/ I_IrmPolicyInfo *piid,

    /*[OUT]*/ DWORD *pdwStatus) PURE;

    };

    //HrIsProtected possible results:

    #define MSOIPI_RESULT_UNKNOWN 0 //I cannot tell. Ask me in HrProtect/HrUnprotect

    #define MSOIPI_RESULT_PROTECTED 1 //pilbInput is protected

    #define MSOIPI_RESULT_UNPROTECTED 2 //pilbInput is unprotected

    #define MSOIPI_RESULT_NOT_MY_FILE 3 //Not my file. For example, an XML file that is not an InfoPath form.

    // Status:

    #define MSOIPI_STATUS_UNKNOWN 0

    #define MSOIPI_STATUS_PROTECT_SUCCESS 1

    #define MSOIPI_STATUS_UNPROTECT_SUCCESS 2

    #define MSOIPI_STATUS_ALREADY_PROTECTED 3 //If called to protect and file is already protected

    #define MSOIPI_STATUS_CANT_PROTECT 4 //If protect fails

    #define MSOIPI_STATUS_ALREADY_UNPROTECTED 5 //If called to unprotect and file is already unprotected

    #define MSOIPI_STATUS_CANT_UNPROTECT 6 //If unprotect fails

    #define MSOIPI_STATUS_NOT_OWNER 7 //If the ServerId doesn't have an EUL that allows unprotection

    //Unused 8

    #define MSOIPI_STATUS_NOT_MY_FILE 9 //If the protector is passed a file it cannot identify

    #define MSOIPI_STATUS_FILE_CORRUPT 10 //If the parts of the file that make this an IRM file are corrupt

    #define MSOIPI_STATUS_PLATFORM_IRM_FAILED 11 //If a call back into the platform for RMS APIs fails

    #define MSOIPI_STATUS_BAD_INSTALL 12 //If the protector isn't installed properly

    // {FCFBC0AC-672B-452d-80E5-40652503D96E}

    DEFINE_GUID(IID_I_IrmProtector,

    0xfcfbc0ac, 0x672b, 0x452d, 0x80, 0xe5, 0x40, 0x65, 0x25, 0x03, 0xd9, 0x6e);

     

    #endif

    Wednesday, February 21, 2007 8:21 PM
  • Excellent, this is exactly what I was looking for, thanks.
    Thursday, February 22, 2007 9:09 PM
  • Interesting, I figured out why I couldn't find it. The link you provided is to the beta version of the ECM Starter Kit. The MOSS 2007 SDK now includes the ECM Starter Kit as part of its install. That's what I have installed, and that header file is missing from the SDK install. Looks like somebody maybe missed a file when preparing the SDK for publishing.
    Thursday, February 22, 2007 9:26 PM
  • One reason why the header file is missing (I can't find it either, and I'm looking at the starter kit that came with the MOSS SDK) is that it might have changed since the beta version.

    All the more reason why I'd like to get hold of the current header *and* (hopefully) some example code.

    Josh

    Wednesday, March 21, 2007 4:43 PM
  • Dear All,

    We are also strugling a lot to impose expire date on MS office files (excel / word / power point / Info Path) using C#? 

    Kind of a custom IRM Protected files that will be uploaded in the document library of Microsoft Office Sharepoint Server 2007 (MOSS) such that

    if a user downloads the file to local machine after the expiry date is over he/she should not be able to open the file.

    This facility is not inbuilt in MOSS.

     

    If this works well, we have to try the same for PDF and Image files too.

    Sample code will be of great help 

      

    Please guide. 

     

    Regards

    Sachin

    sachin_kalse@satyam.com 

     

     

    Tuesday, May 22, 2007 2:29 AM
  • Can any one pleas tell me whether i will be able to use IRM featurs for PDF  files.

     

    Thanx

    Thursday, December 6, 2007 12:41 AM
  • There is a PDF IRM protector available for free here:

    http://pdfirmprotector.codeplex.com/

    Wednesday, February 2, 2011 7:00 PM