SNHookClass & MultiLicense



  • On 01/02/2013 at 07:40, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R14 
    Platform:      
    Language(s) :     C++  ;

    ---------
    I'm implementing the SNHookClass to allow the user enter the license for my plugin. What is the
    c4dsn String passed to ~:SNCheck(); when working on a MultiLicense environment?

    I'm asking because

    • I can't test it (don't have a MultiLicense environment lying around..)
    • GeGetSerialInfo() can be called for either retrieving the MutltiLicense or the
      generous license.

    Is it the 11 digits only and I have to manually check for a MultiLicense, or will be it be the 18
    characters long MultiLicense string (12345678912-ABCDEF) ?

    Thank you,
    Niklas



  • On 01/02/2013 at 08:14, xxxxxxxx wrote:

    Header

    ////////////////////////////////////////////////////////////////  
    // KDZSerial.h  
    ////////////////////////////////////////////////////////////////  
    // Serial Number handling  
    ////////////////////////////////////////////////////////////////  
    // V0.1 2011.04.21 Robert Templeton  
    ////////////////////////////////////////////////////////////////  
      
    // Includes  
    #include "general.h"  
    #include "lib_sn.h"  
      
    // KDZSerial: Serial Number Hook Class  
    class KDZSerial : public SNHookClass  
    {  
     private:  
         // Data  
         char            pcode[6];  
         const String    SNPluginName;  
         // Methods  
         Bool            checkSerial(const String& c4dsn, const String& sn);  
     public:  
         // Data  
         // - mode = 0 (not registered/not demo), 1 (demo), 2 (registered), 3 (C4D demo)  
         UCHAR            mode;  
         LONG            time;  
         // Methods  
         KDZSerial();  
         LONG            SNCheck(const String& c4dsn, const String& sn, LONG regdate, LONG curdate);  
         void            setPluginCode(const char* t_pcode);  
         const String&    GetTitle();  
    };
    
    ////////////////////////////////////////////////////////////////  
    // KDZSerial.cpp  
    ////////////////////////////////////////////////////////////////  
    // Serial Number handling  
    ////////////////////////////////////////////////////////////////  
    // V0.1 2011.04.21 Robert Templeton  
    ////////////////////////////////////////////////////////////////  
      
    // Includes  
    #ifdef MACOS  
      #include "Rijndael_Mac.h"  
    #else  
      #include "Rijndael.h"  
    #endif  
    #include "KDZSerial.h"  
      
    // Size of serial number string  
    #define ENTRY_SIZE                17  
      
    // KDZSerial: Serial Number Hook Class  
    // Methods  
    // - Constructor  
    //*---------------------------------------------------------------------------*  
    KDZSerial::KDZSerial() : SNPluginName(GeLoadString(KDZS_PLUGIN_NAME))  
    //*---------------------------------------------------------------------------*  
    {  
      mode =            0;  
    }  
    // - KDZSerial.setPluginCode  
    //*---------------------------------------------------------------------------*  
    void KDZSerial::setPluginCode(const char* t_pcode)  
    //*---------------------------------------------------------------------------*  
    {  
      memcpy(pcode, t_pcode, 6);  
    }  
    // - KDZSerial.checkSerial  
    //*---------------------------------------------------------------------------*  
    Bool KDZSerial::checkSerial(const String& c4dsn, const String& sn)  
    //*---------------------------------------------------------------------------*  
    {  
      // Key is 11-Digit C4D SN + 5-Char PluginCode + NullTerminator  
      char key[ENTRY_SIZE];  
      c4dsn.GetCString(&key[0], c4dsn.GetCStringLen()+1L, STRINGENCODING_7BIT);  
      memcpy(&key[11], &pcode[0], 6);  
      
      // Encrypt plugin serial no. using AES  
      char dout[ENTRY_SIZE] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";  
      // One block testing  
      CRijndael oRijndael;  
      if (!oRijndael.MakeKey(&key[0], "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16, 16))  
      {  
          return MessageSystem::Throw(GeLoadString(KDZERR_GENERAL), GeLoadString(KDZERR_SNMAKEKEY));  
      }  
      char din[ENTRY_SIZE];  
      memcpy(&din[0], &key[0], ENTRY_SIZE);  
      if (!oRijndael.EncryptBlock(din, dout))  
      {  
          return MessageSystem::Throw(GeLoadString(KDZERR_GENERAL), GeLoadString(KDZERR_SNENCRYPTION));  
      }  
      dout[16] = 0;    // Null-terminate  
      
      UINT    i;  
      UCHAR    val;  
      // Convert to AlphaNumerics for user serial number entry  
      for (i = 0; i != 16; ++i)  
      {  
          val =    (UCHAR)dout[i];  
          // Beyond AlphaNumerics  
          if (val > 122)                    val /= 3;  
          // Below AlphaNumberics  
          if (val < 48)                    val += 48;  
          // Between numerals and UC Alpha  
          if ((val > 57) && (val < 65))    val += 24;  
          // Between UC Alpha and LC Alpha  
          if ((val > 90) && (val < 97))    val += 12;  
          dout[i] =    (char)val;  
      }  
      dout[16] = 0;    // Null-terminate  
      
      sn.GetCString(&key[0], sn.GetCStringLen()+1L, STRINGENCODING_7BIT); //ENTRY_SIZE, St7bit);  
      // Check sn against Encrypted-AlphaNumericized dout  
      for (i = 0; i != 16; ++i)  
      {  
          //GePrint("key["+LongToString(i)+"]=\'"+LongToString(key[i])+"\' == chk["+LongToString(i)+"]=\'"+LongToString(dout[i])+"\'");  
          if (key[i] != dout[i])  
          {  
              return MessageSystem::Throw(GeLoadString(KDZERR_GENERAL), GeLoadString(KDZERR_SNINVALID));  
          }  
      }  
      return TRUE;  
    }  
    // - SNHookClass.SNCheck  
    //*---------------------------------------------------------------------------*  
    LONG KDZSerial::SNCheck(const String& c4dsn, const String& sn, LONG regdate, LONG curdate)  
    //*---------------------------------------------------------------------------*  
    {  
      if (!sn.Content())    return SN_WRONGNUMBER;  
      
      // Demo mode (30 day trial)  
      if (sn.GetLength() == 4L)  
      {  
          // Case-insensitive serial number 'demo'  
          String    demo =                sn.ToLower();  
          if (demo.Compare("demo"))    return SN_WRONGNUMBER;  
      
          if (!regdate)  
          {  
              mode =    1;  
              return SN_OKAY;  
          }  
      
          // Calculate timeout of Demo mode  
          // - elapsed time since registering demo  
          LONG    exptime =        curdate - regdate;  
          // - Number of demo days  
          LONG    expiration =    30L;  
          LONG    timeout =        expiration-exptime;  
          if (timeout < 0L)        return SN_EXPIRED;  
          time =                    timeout;  
          mode =                    1;  
          if (exptime >= (expiration-3L))    return SN_EXPIRE_14 - timeout;  
          return SN_OKAY;  
      }  
      // Registered user  
      else if (sn.GetLength() == 16L)  
      {  
          if (checkSerial(c4dsn, sn))  
          {  
              mode =    2;  
              return SN_OKAY;  
          }  
      }  
    #ifdef    C4D_R11  
      // Multi-License Server  
      else  
      {  
          // Check for R11 Multi-License  
          SerialInfo    mlsn;  
          GeGetSerialInfo(SERIALINFO_MULTILICENSE, &mlsn);  
          if (mlsn.nr.Content())  
          {  
              //--------------------------------------------------------------------------------  
              // Note that we also need to skip over the extra data added to the front of the  
              // plugin's license key (11 digits/characters and a dash)...  
              //--------------------------------------------------------------------------------  
              String    plug_sn =    sn;  
              LONG dashPos;  
              if (plug_sn.FindFirst("-", &dashPos, 0L))  
              {  
                  plug_sn = plug_sn.SubStr(dashPos+1L, plug_sn.GetLength()-dashPos-1L);  
                  if (checkSerial(mlsn.nr, plug_sn))  
                  {  
                      mode =    2;  
                      return SN_OKAY;  
                  }  
              }  
          }  
      }  
    #else  
      else    MessageSystem::Throw(GeLoadString(KDZERR_GENERAL), GeLoadString(KDZERR_SNSIZE));  
    #endif  
      
      return SN_WRONGNUMBER;  
    }  
    // - SNHookClass.GetTitle  
    //*---------------------------------------------------------------------------*  
    const String& KDZSerial::GetTitle()  
    //*---------------------------------------------------------------------------*  
    {  
      return SNPluginName;  
    }
    
    ////////////////////////////////////////////////////////////////  
    // Main.cpp  
    ////////////////////////////////////////////////////////////////  
    // SymMorphy  
    ////////////////////////////////////////////////////////////////  
    // V0.1 2011.05.25 Robert Templeton  
    ////////////////////////////////////////////////////////////////  
      
    // Includes  
    #include "KDZSerial.h"  
      
    // - To allow future demo for users who have already demoed a previous version  
    // - Note that it is in the code to avoid user changing ID  
    enum  
    {  
      ID_SYMMORPHY_DEMO        =    1029219  
    };  
      
    static KDZSerial* kdzSerial =    NULL;  
      
    //*---------------------------------------------------------------------------*  
    Bool RegisterKDZSerial()  
    //*---------------------------------------------------------------------------*  
    {  
      kdzSerial =            gNew KDZSerial();  
      if (!kdzSerial)        return MessageSystem::Throw(GeLoadString(KDZERR_MEMORY), "Main.RegisterKDZSerial.kdzSerial");  
      char    pcode[6] =    "symmy";  
      kdzSerial->setPluginCode(&pcode[0]);  
      if (!kdzSerial->Register(ID_SYMMORPHY, SNFLAG_OWN))  
          return kdzSerial->Register(ID_SYMMORPHY_DEMO, SNFLAG_OWN);  
      return TRUE;  
    }  
    //*---------------------------------------------------------------------------*  
    void FreeKDZSerial()  
    //*---------------------------------------------------------------------------*  
    {  
      gDelete(kdzSerial);  
    }  
      
      
    // Plugin Functions ==================================================================================================  
      
    #include "SymMorphyDoc.h"  
      
    // Declare Global Plugin Registrants  
    Bool            RegisterSymMorphyTag();  
    SymMorphyDoc*    RegisterSymMorphyDoc();  
    Bool            RegisterSymMorphySceneHook();  
      
    static SymMorphyDoc*    symmorphydoc =        NULL;  
      
    //*---------------------------------------------------------------------------*  
    Bool PluginStart()  
    //*---------------------------------------------------------------------------*  
    {  
      Bool    network =                                        FALSE;  
    #ifdef    C4D_R12  
      VERSIONTYPE    vtype =        GeGetVersionType();  
      SYSTEMINFO    stype =        GeGetSystemInfo();  
      if ((vtype == VERSIONTYPE_NET_SERVER_3) || (vtype == VERSIONTYPE_NET_SERVER_UNLIMITED) || (vtype == VERSIONTYPE_NET_CLIENT))    network = TRUE;  
      if        (stype & SYSTEMINFO_DEMO)                                                                GePrint("C4D Demo");  
      else if (stype & (SYSTEMINFO_SAVABLEDEMO|SYSTEMINFO_SAVABLEDEMO_ACTIVE))                        GePrint("C4D Savable Demo");  
    #ifdef    C4D_R13  
      else if (stype & (SYSTEMINFO_SAVABLEDEMO|SYSTEMINFO_STUDENT))                                    GePrint("C4D Student Demo");  
    #endif  
      else if (stype & SYSTEMINFO_COMMANDLINE)                                                        GePrint("C4D CommandLine");  
      else if ((vtype == VERSIONTYPE_NET_SERVER_3) || (vtype == VERSIONTYPE_NET_SERVER_UNLIMITED))    GePrint("C4D Server");  
      else if (vtype == VERSIONTYPE_NET_CLIENT)                                                        GePrint("C4D Client");  
      else  
      {  
          // serial number check  
          if (!(kdzSerial && kdzSerial->mode)) return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart:serial check failed!");  
      }  
    #elif defined    C4D_R11  
      LONG    vtype =                                            GeGetVersionType();  
      if ((vtype & VERSION_SERVER) || (vtype & VERSION_NET))    network = TRUE;  
      if        (vtype & VERSION_DEMO)                            GePrint("C4D Demo");  
      else if (vtype & VERSION_SAVABLE)                        GePrint("C4D Savable Demo");  
      else if (vtype & VERSION_SERVER)                        GePrint("C4D Server");  
      else if (vtype & VERSION_NET)                            GePrint("C4D Net");  
      // serial number check  
      else if (!(kdzSerial && kdzSerial->mode))                return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart:serial check failed!");  
    #else  
      LONG    vtype =                                            GeGetVersionType();  
      if ((vtype & VERSION_SERVER) || (vtype & VERSION_NET))    network = TRUE;  
      if (!((vtype & VERSION_DEMO) || network))  
      {  
          // serial number check  
          if (!(kdzSerial && kdzSerial->mode))                return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart:serial check failed!");  
      }  
    #endif  
      
      GePrint(" ");  
      GePrint(GeLoadString(KDZS_PLUGIN_BANNER));  
      GePrint("-- "+GeLoadString(KDZS_PLUGIN_NAME)+GeLoadString(KDZS_PLUGIN_EDITION)+"v"+GeLoadString(KDZS_PLUGIN_VERSION)+" "+GeLoadString(KDZS_PLUGIN_COPYRIGHT));  
      if (kdzSerial)  
      {  
          if        (kdzSerial->mode == 1)                        GePrint("-- Trial: "+LongToString(kdzSerial->time)+" days left");  
          else if (kdzSerial->mode == 2)  
          {  
              SerialInfo    si;  
              GeGetSerialInfo(SERIALINFO_CINEMA4D, &si);  
              GePrint("-- Licensed to: "+si.name);  
          }  
      }  
      GePrint(GeLoadString(KDZS_PLUGIN_BANNER));  
      GePrint(" ");  
      
      // Register hooks and return  
      // - SymMorphy Demo or Registered  
      symmorphydoc =                                            RegisterSymMorphyDoc();  
      if (!symmorphydoc)                                        return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart.RegisterSymMorphyDoc() failed!");  
      if (kdzSerial)  
      {  
          if (!symmorphydoc->Initialize(network, kdzSerial->mode, kdzSerial->time))    return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart.SymMorphyDoc.Initialize() failed!");  
      }  
      // - Cinema 4D Demo  
      else  
      {  
          if (!symmorphydoc->Initialize(network, 3L, 2147483647L))    return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart.SymMorphyDoc.Initialize() failed!");  
      }  
      if (!RegisterSymMorphyTag())                                return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart.RegisterSymMorphyTag() failed!");  
      if (!RegisterSymMorphySceneHook())                            return ErrPrt(GeLoadString(KDZS_PLUGIN_NAME)+".Main.PluginStart.RegisterSymMorphySceneHook() failed!");  
      return TRUE;  
    }  
      
    //*---------------------------------------------------------------------------*  
    void PluginEnd()  
    //*---------------------------------------------------------------------------*  
    {  
    }  
      
    //*---------------------------------------------------------------------------*  
    Bool PluginMessage(LONG id, void* data)  
    //*---------------------------------------------------------------------------*  
    {  
      if        (id == C4DPL_INIT_SYS)  
      {  
          // initialize global resource object  
          if (!resource.Init())    return ErrPrt("Main.PluginMessage.resource.Init() failed!");  
      
          Bool    network =        FALSE;  
          Bool    serial =        TRUE;  
          // initialize and register Serial Number Hook  
    #ifdef    C4D_R12  
          VERSIONTYPE    vtype =        GeGetVersionType();  
          SYSTEMINFO    stype =        GeGetSystemInfo();  
          if        (stype & SYSTEMINFO_DEMO)                                                            { GePrint("C4D Demo");            serial = FALSE; }  
          else if (stype & (SYSTEMINFO_SAVABLEDEMO|SYSTEMINFO_SAVABLEDEMO_ACTIVE))                    { GePrint("C4D Savable Demo");    serial = FALSE; }  
    #ifdef    C4D_R13  
          else if (stype & (SYSTEMINFO_SAVABLEDEMO|SYSTEMINFO_STUDENT))                                { GePrint("C4D Student Demo");    serial = FALSE; }  
    #endif  
          else if (stype & SYSTEMINFO_COMMANDLINE)                                                    { GePrint("C4D CommandLine");    serial = FALSE; }  
          if ((vtype == VERSIONTYPE_NET_SERVER_3) || (vtype == VERSIONTYPE_NET_SERVER_UNLIMITED))        { GePrint("C4D Server");        serial = FALSE; network = TRUE; }  
          else if (vtype == VERSIONTYPE_NET_CLIENT)                                                    { GePrint("C4D Net");            serial = FALSE; network = TRUE; }  
    #elif defined    C4D_R11  
          LONG    vtype = GeGetVersionType();  
          if        (vtype & VERSION_SERVER)    { GePrint("C4D Server");        serial = FALSE; network = TRUE; }  
          else if (vtype & VERSION_NET)        { GePrint("C4D Net");            serial = FALSE; network = TRUE; }  
          else if    (vtype & VERSION_DEMO)        { GePrint("C4D Demo");            serial = FALSE; }  
          else if (vtype & VERSION_SAVABLE)    { GePrint("C4D Savable Demo");    serial = FALSE; }  
    #else  
          LONG    vtype =    GeGetVersionType();  
          if        (vtype & VERSION_SERVER)    { serial = FALSE; network = TRUE; }  
          else if (vtype & VERSION_NET)        { serial = FALSE; network = TRUE; }  
          else if (vtype & VERSION_DEMO)        { serial = FALSE; }  
    #endif  
      
          // Allocate SymMorphy Data Storage  
          if (serial) return RegisterKDZSerial();  
          else        return TRUE;  
      }  
      else if (id == C4DPL_ENDACTIVITY)  
      {  
          FreeKDZSerial();  
          return TRUE;  
      }  
      else if (id == C4DMSG_PRIORITY)        return TRUE;  
      return FALSE;  
    }  
      
    // Return SymMorphyDoc* symmorphydoc - globally accessible  
    //*---------------------------------------------------------------------------*  
    SymMorphyDoc* GetSymMorphyDoc()  
    //*---------------------------------------------------------------------------*  
    {  
      return symmorphydoc;  
    }
    


  • On 01/02/2013 at 08:15, xxxxxxxx wrote:

    Giblet has a very informative post here that outlines the required format of the serial number for the Maxon License Server for plugin registration. The post is: "R11 License Server & SNHookClass"



  • On 05/02/2013 at 15:09, xxxxxxxx wrote:

    @NiklasR:

    Here is the Link to "R11 License Server & SNHookClass", mentioned by Robert.
    Indeed a very informative thread.

    And there is a 5 part tutorial about licensing plugins at the "C4D Programming Blog" that also might be interesting for you to read.
    Even though there is no extra information about a License Server implementation to find.

    @kuroyume0161:

    Thank you Robert for your whole licensing source code(s).
    I already have a working solution for myself but I think I'll take a look at yours too and maybe could get some ideas for improvements of my own.
    E.G. I still wasn't able to implement a time limited demo mode that really would work as expected.



  • On 06/02/2013 at 03:33, xxxxxxxx wrote:

    Great to have another open piece of source code for C4D !
    Thank you! Lets lock what new I can learn from it.

    May be we could start GitHub for all the code? :)



  • On 06/02/2013 at 03:42, xxxxxxxx wrote:

    Thank you very much for the reference, Robert! 🍺

    >>> May be we could start GitHub for all the code? :)

    + 1 !!!!!

    I already have some C4D related stuff on my GitHub profile:  https://github.com/NiklasRosenstein



  • On 06/02/2013 at 04:00, xxxxxxxx wrote:

    I have some code here:
    http://code.google.com/p/code-editor-gui/source/browse/


Log in to reply