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/