Salome HOME
Update of CheckDone
[modules/smesh.git] / src / SMESHUtils / SMESH_MGLicenseKeyGen.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // File      : SMESHUtils_MGLicenseKeyGen.cxx
20 // Created   : Sat Jul 31 18:54:16 2021
21 // Author    : Edward AGAPOV (OCC)
22
23 #include "SMESH_MGLicenseKeyGen.hxx"
24
25 #include "SMESH_Comment.hxx"
26 #include "SMESH_File.hxx"
27 #include "SMESH_TryCatch.hxx"
28
29 #include <Basics_DirUtils.hxx>
30 #include <Basics_Utils.hxx>
31
32 #include <cstdlib> // getenv, system
33
34 #include <boost/filesystem.hpp>
35 #include <boost/regex.hpp>
36 namespace boofs = boost::filesystem;
37
38 #ifdef WIN32
39
40 #  include <windows.h>
41 #  include <process.h>
42
43 #  define LibHandle HMODULE
44 #  define LoadLib( name ) LoadLibrary( name )
45 #  define GetProc GetProcAddress
46 #  define UnLoadLib( handle ) FreeLibrary( handle );
47
48 #else // WIN32
49
50 #  include <dlfcn.h>
51
52 #  define LibHandle void*
53 #  define LoadLib( name ) dlopen( name, RTLD_LAZY | RTLD_LOCAL )
54 #  define GetProc dlsym
55 #  define UnLoadLib( handle ) dlclose( handle );
56
57 #endif // WIN32
58
59 // to retrieve description of exception caught by SMESH_TRY
60 #undef SMESH_CAUGHT
61 #define SMESH_CAUGHT error =
62
63 constexpr char MESHGEMS_OLD_STYLE[] = "MESHGEMS_OLD_STYLE";
64 constexpr char SPATIAL_LICENSE[] = "SPATIAL_LICENSE";
65
66
67 namespace
68 {
69   static LibHandle theLibraryHandle = nullptr; //!< handle of a loaded library
70
71   const char* theEnvVar = "SALOME_MG_KEYGEN_LIB_PATH"; /* var specifies either full file name
72                                                           of libSalomeMeshGemsKeyGenerator or
73                                                           URL to download the library from */
74
75   const char* theTmpEnvVar = "SALOME_TMP_DIR"; // directory to download the library to
76
77   //-----------------------------------------------------------------------------------
78   /*!
79    * \brief Remove library file at destruction in case if it was downloaded from server
80    */
81   //-----------------------------------------------------------------------------------
82
83   struct LibraryFile
84   {
85     std::string _name; // full file name
86     bool        _isURL;
87
88     LibraryFile(): _isURL( false ) {}
89
90     ~LibraryFile()
91     {
92       if ( _isURL )
93       {
94         if ( theLibraryHandle )
95         {
96           UnLoadLib( theLibraryHandle );
97           theLibraryHandle = nullptr;
98         }
99
100         std::string tmpDir; // tmp dir that should not be removed
101         if ( const char* libPath = getenv( theTmpEnvVar ))
102         {
103           tmpDir = libPath;
104           while (( !tmpDir.empty() ) &&
105                  ( tmpDir.back() == '/' || tmpDir.back() == '\\' ))
106             tmpDir.pop_back();
107         }
108
109         while ( SMESH_File( _name ).remove() )
110         {
111           size_t length = _name.size();
112           _name = boofs::path( _name ).parent_path().string(); // goto parent directory
113           if ( _name.size() == length )
114             break; // no more parents
115
116           if ( _name == tmpDir )
117             break; // don't remove tmp dir
118
119           if ( !Kernel_Utils::IsEmptyDir( _name ))
120             break;
121         }
122       }
123     }
124   };
125
126
127   //================================================================================
128   /*!
129    * \brief Retrieve description of the last error
130    *  \param [out] error - return the description
131    *  \return bool - true if the description found
132    */
133   //================================================================================
134
135   bool getLastError( std::string& error )
136   {
137 #ifndef WIN32
138
139     if ( const char* text = dlerror() )
140     {
141       error = text;
142       return true;
143     }
144     return false;
145
146 #else
147
148     DWORD dw = GetLastError();
149     void* cstr;
150     DWORD msgLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
151                                  NULL,
152                                  dw,
153                                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
154                                  (LPTSTR) &cstr,
155                                  0,
156                                  NULL
157                                  );
158     if ( msgLen > 0 ) {
159 #  if defined( UNICODE )
160       error = Kernel_Utils::encode_s((wchar_t*)cstr);
161 #  else
162       error = (char*)cstr;
163 #  endif
164       LocalFree(cstr);
165     }
166
167     return (bool)msgLen;
168
169 #endif
170   }
171
172   //================================================================================
173   /*!
174    * \brief Adjust file extension according to the platform
175    */
176   //================================================================================
177
178   bool setExtension( std::string& fileName, std::string& error )
179   {
180     if ( fileName.empty() )
181     {
182       error = "Library file name is empty";
183       return false;
184     }
185 #if defined(WIN32)
186     std::string ext = ".dll";
187 #elif defined(__APPLE__)
188     std::string ext = ".dylib";
189 #else
190     std::string ext = ".so";
191 #endif
192
193     fileName = fileName.substr( 0, fileName.find_last_of('.')) + ext;
194     return true;
195   }
196
197   //================================================================================
198   /*!
199    * \brief Check if library file name looks like an URL
200    *  \param [in,out] libraryFile - holds file name and returns result in _isURL member field
201    *  \return bool - true if the file name looks like an URL
202    */
203   //================================================================================
204
205   bool isURL( LibraryFile & libraryFile )
206   {
207     {// round1
208       enum { SCHEME = 2, AUTHORITY = 4, PATH = 5 }; // sub-strings
209       boost::regex urlRegex ( R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)",
210                               boost::regex::extended );
211       boost::smatch matchResult;
212
213       libraryFile._isURL = false;
214       if ( boost::regex_match( libraryFile._name, matchResult, urlRegex ))
215         libraryFile._isURL = ( !matchResult.str( SCHEME    ).empty() &&
216                               !matchResult.str( AUTHORITY ).empty() &&
217                               !matchResult.str( PATH      ).empty() );
218     }
219     if(libraryFile._isURL)
220       return true;
221     {// round2
222       enum { HOST = 2, PORT = 3, PATH = 4 }; // sub-strings
223       boost::regex urlRegex ( R"(^(([^:\/?#]+):)?([^/]+)?(/[^#]*))",
224                               boost::regex::extended );
225       boost::smatch matchResult;
226
227       libraryFile._isURL = false;
228       if ( boost::regex_match( libraryFile._name, matchResult, urlRegex ))
229         libraryFile._isURL = ( !matchResult.str( HOST ).empty() &&
230                               !matchResult.str( PORT ).empty() &&
231                               !matchResult.str( PATH ).empty() );
232     }
233     return libraryFile._isURL;
234   }
235
236   //================================================================================
237   /*!
238    * \brief Download libraryFile._name URL to SALOME_TMP_DIR
239    *  \param [in,out] libraryFile - holds the URL and returns name of a downloaded file
240    *  \param [out] error - return error description
241    *  \return bool - is a success
242    */
243   //================================================================================
244
245   bool downloadLib( LibraryFile& libraryFile, std::string & error )
246   {
247     // check if can write into SALOME_TMP_DIR
248
249     std::string tmpDir = Kernel_Utils::GetTmpDirByEnv( theTmpEnvVar );
250     if ( tmpDir.empty() ||
251          !Kernel_Utils::IsExists( tmpDir ))
252     {
253       error = "Can't download " + libraryFile._name + " as SALOME_TMP_DIR is not correctly set";
254       return false;
255     }
256     if ( !Kernel_Utils::IsWritable( tmpDir ))
257     {
258       error = "Can't download " + libraryFile._name + " as '" + tmpDir + "' is not writable. "
259         "Check SALOME_TMP_DIR environment variable";
260       return false;
261     }
262
263     // Download
264
265     std::string url = libraryFile._name;
266
267 #ifdef WIN32
268
269     std::string outFile = tmpDir + "MeshGemsKeyGenerator.dll";
270
271     // use wget (== Invoke-WebRequest) PowerShell command available since Windows 7
272     std::string psCmd = "wget -Uri " + url + " -OutFile " + outFile;
273     std::string   cmd = "powershell.exe " + psCmd;
274
275 #else
276
277     std::string outFile = tmpDir + "libMeshGemsKeyGenerator.so";
278
279     std::string cmd = "smesh_wget.py " + url + " -O " + outFile;
280
281 #endif
282
283     if ( Kernel_Utils::IsExists( outFile )) // remove existing file
284     {
285       SMESH_File lib( outFile, /*open=*/false );
286       if ( !lib.remove() )
287       {
288         error = lib.error();
289         return false;
290       }
291     }
292
293 #ifndef WIN32
294     //[EDF25906]
295     std::string redirect = tmpDir + "redirect.out";
296     std::ostringstream oss;
297     oss << cmd << " " << redirect;
298     cmd = oss.str();
299 #endif
300
301     system( cmd.c_str() ); // download
302
303 #ifndef WIN32
304     {//[EDF25906]
305       std::ifstream infile(redirect);
306       infile.seekg(0, std::ios::end);
307       size_t length = infile.tellg();
308       infile.seekg(0, std::ios::beg);
309       std::unique_ptr<char []> buffer(new char[length+1]);
310       buffer[length] = '\0';
311       infile.read(const_cast<char *>( buffer.get() ),length);
312
313       MESSAGE( buffer.get() );
314     }
315     {
316       SMESH_File redirectFile( redirect, /*open=*/false );
317       redirectFile.remove();
318     }
319 #endif
320
321     SMESH_File resultFile( outFile, /*open=*/false );
322     bool ok = ( resultFile.exists() && resultFile.size() > 0 );
323
324     if ( ok )
325       libraryFile._name = outFile;
326     else
327       error = "Can't download file " + url;
328
329     return ok;
330   }
331
332   //================================================================================
333   /*!
334    * \brief Load libMeshGemsKeyGenerator.so
335    *  \param [out] error - return error description
336    *  \param [out] libraryFile - return library file name and _isURL flag
337    *  \return bool - is a success
338    */
339   //================================================================================
340
341   bool loadLibrary( std::string& error, LibraryFile& libraryFile )
342   {
343     if ( theLibraryHandle )
344       return true;
345
346     const char* libPath = getenv( theEnvVar );
347     if ( !libPath )
348     {
349       error = SMESH_Comment( "Environment variable ") <<  theEnvVar << " is not set";
350       return false;
351     }
352
353     libraryFile._name = libPath;
354     // if ( !setExtension( libraryFile._name, error )) // is it necessary?
355     //   return false;
356
357     if ( isURL( libraryFile ))
358     {
359       if ( !downloadLib( libraryFile, error ))
360       {
361         // try to fix extension
362         std::string url = libraryFile._name;
363         if ( !setExtension( libraryFile._name, error ))
364           return false;
365         if ( url == libraryFile._name )
366           return false; // extension not changed
367
368         if ( !downloadLib( libraryFile, error ))
369           return false;
370       }
371     }
372
373 #if defined( WIN32 ) && defined( UNICODE )
374     std::wstring encodePath = Kernel_Utils::utf8_decode_s( libraryFile._name );
375     const wchar_t*     path = encodePath.c_str();
376 #else
377     const char*        path = libraryFile._name.c_str();
378 #endif
379
380     theLibraryHandle = LoadLib( path );
381     if ( !theLibraryHandle )
382     {
383       if ( ! getLastError( error ))
384         error = "Can't load library '" + libraryFile._name + "'";
385     }
386
387     return theLibraryHandle;
388   }
389
390 } // anonymous namespace
391
392
393 namespace SMESHUtils_MGLicenseKeyGen // API implementation
394 {
395   //================================================================================
396   /*!
397    * \brief Sign a CAD
398    *  \param [in] meshgems_cad - pointer to a MG CAD object (meshgems_cad_t)
399    *  \param [out] error - return error description
400    *  \return bool - is a success
401    */
402   //================================================================================
403
404   bool SignCAD_After( void* meshgems_cad, std::string& error )
405   {
406     LibraryFile libraryFile;
407     if ( !loadLibrary( error, libraryFile ))
408       return false;
409
410     bool ok = false;
411     typedef bool (*SignFun)(void* );
412     SignFun signFun = (SignFun) GetProc( theLibraryHandle, "SignCAD" );
413     if ( !signFun )
414     {
415       if ( ! getLastError( error ))
416         error = SMESH_Comment( "Can't find symbol 'SignCAD' in '") << getenv( theEnvVar ) << "'";
417     }
418     else
419     {
420       SMESH_TRY;
421
422       ok = signFun( meshgems_cad );
423
424       SMESH_CATCH( SMESH::returnError );
425
426       if ( !error.empty() )
427         ok = false;
428       else if ( !ok )
429         error = "SignCAD() failed (located in '" + libraryFile._name + "')";
430     }
431     return ok;
432   }
433
434   //================================================================================
435   /*!
436    * \brief Unlock a specific MeshGems product (for products called as a library)
437    *  \param [in] product - product of MeshGems to unlock
438    *  \param [out] error - return error description
439    *  \return bool - is a success
440    */
441   //================================================================================
442   bool UnlockProduct( const std::string& product, std::string& error )
443   {
444     MESSAGE("SMESH UnlockProduct: " << product);
445     LibraryFile libraryFile;
446     if ( !loadLibrary( error, libraryFile ))
447       return false;
448
449     bool ok = false;
450     typedef bool (*SignFun)(const std::string& );
451
452     // specific function to unlock each product
453     std::string function = "UnlockProduct";
454
455     SignFun signFun = (SignFun) GetProc( theLibraryHandle, function.c_str() );
456     if ( !signFun )
457     {
458       if ( ! getLastError( error ))
459         error = SMESH_Comment( "Can't find symbol '") << function << "' in '" << getenv( theEnvVar ) << "'";
460     }
461     else
462     {
463       SMESH_TRY;
464
465       ok = signFun( product.c_str() );
466
467       SMESH_CATCH( SMESH::returnError );
468
469       if ( !error.empty() )
470       {
471         std::cerr << "error: " << error << std::endl;
472         ok = false;
473       }
474       else if ( !ok )
475         error = "UnlockProduct() failed (located in '" + libraryFile._name + "')";
476     }
477     return ok;
478   }
479
480   //================================================================================
481   /*!
482    * \brief Sign a CAD (or don't do it if env MESHGEMS_OLD_STYLE is set)
483    *  \param [in] meshgems_cad - pointer to a MG CAD object (meshgems_cad_t)
484    *  \param [out] error - return error description
485    *  \return bool - is a success
486    */
487   //================================================================================
488   bool SignCAD( void* meshgems_cad, std::string& error )
489   {
490     const char *meshGemsOldStyleEnvVar( getenv( MESHGEMS_OLD_STYLE ) );
491     if ( !meshGemsOldStyleEnvVar || strlen(meshGemsOldStyleEnvVar) == 0 )
492     {
493       if (NeedsMGSpatialEnvLicense(error))
494         // SignCAD is only called by cadsurf. Other components call SignMesh
495         return UnlockProduct("cadsurf", error);
496       else
497         return SignCAD_After(meshgems_cad, error);
498     }
499     else
500       return true;
501   }
502
503   //================================================================================
504   /*!
505    * \brief Sign a mesh
506    *  \param [in] meshgems_mesh - pointer to a MG mesh (meshgems_mesh_t)
507    *  \param [out] error - return error description
508    *  \return bool - is a success
509    */
510   //================================================================================
511
512   bool SignMesh_After( void* meshgems_mesh, std::string& error )
513   {
514     LibraryFile libraryFile;
515     if ( !loadLibrary( error, libraryFile ))
516       return false;
517
518     bool ok = false;
519     typedef bool (*SignFun)(void* );
520     SignFun signFun = (SignFun) GetProc( theLibraryHandle, "SignMesh" );
521     if ( !signFun )
522     {
523       if ( ! getLastError( error ))
524         error = SMESH_Comment( "Can't find symbol 'SignMesh' in '") << getenv( theEnvVar ) << "'";
525     }
526     else
527     {
528       SMESH_TRY;
529
530       ok = signFun( meshgems_mesh );
531
532       SMESH_CATCH( SMESH::returnError );
533
534       if ( !error.empty() )
535         ok = false;
536       else if ( !ok )
537         error = "SignMesh() failed (located in '" + libraryFile._name + "')";
538     }
539     return ok;
540   }
541
542   //================================================================================
543   /*!
544    * \brief Sign a mesh (or don't do it if env MESHGEMS_OLD_STYLE is set)
545    *  \param [in] meshgems_mesh - pointer to a MG mesh (meshgems_mesh_t)
546    *  \param [in] product - product of MeshGems to unlock
547    *  \param [out] error - return error description
548    *  \return bool - is a success
549    */
550   //================================================================================
551   bool SignMesh( void* meshgems_mesh, const std::string& product, std::string& error )
552   {
553     const char *meshGemsOldStyleEnvVar( getenv( MESHGEMS_OLD_STYLE ) );
554     if ( !meshGemsOldStyleEnvVar || strlen(meshGemsOldStyleEnvVar) == 0 )
555     {
556       if (NeedsMGSpatialEnvLicense(error))
557         // unlock product (MG 2.15)
558         return UnlockProduct(product, error);
559       else
560         // sign the mesh (MG 2.13 and 2.14)
561         return SignMesh_After(meshgems_mesh, error);
562     }
563     else
564       // use DLIM8 server (nothing to do here)
565       return true;
566   }
567
568   //================================================================================
569   /*!
570    * \brief Return a license key to pass as argument to a MG mesher executable
571    *  \param [in] gmfFile - path to an input mesh file
572    *  \param [in] nb* - nb of entities in the input mesh
573    *  \param [out] error - return error description
574    *  \return std::string - the key
575    */
576   //================================================================================
577
578   std::string GetKey_After(const std::string& gmfFile,
579                            int                nbVertex,
580                            int                nbEdge,
581                            int                nbFace,
582                            int                nbVol,
583                            std::string&       error)
584   {
585     std::string key;
586     LibraryFile libraryFile;
587     if ( !loadLibrary( error, libraryFile ))
588       return key;
589
590     typedef std::string (*GetKeyFun)(std::string const &, int, int, int, int );
591     GetKeyFun keyFun = (GetKeyFun) GetProc( theLibraryHandle, "GetKey" );
592     if ( !keyFun )
593     {
594       if ( ! getLastError( error ))
595         error = SMESH_Comment( "Can't find symbol 'GetKey' in '") << getenv( theEnvVar ) << "'";
596     }
597     else
598     {
599       key = keyFun( gmfFile, nbVertex, nbEdge, nbFace, nbVol );
600     }
601     if ( key.empty() )
602       error = "GetKey() failed (located in '" + libraryFile._name + "')";
603
604     return key;
605   }
606
607   //================================================================================
608   /*!
609    * \brief Return a license key to pass as argument to a MG mesher executable (>2.15)
610    *  \param [out] error - return error description
611    *  \return std::string - the key
612    */
613   //================================================================================
614
615   std::string GetKey_After(std::string&       error)
616   {
617     std::string key;
618     LibraryFile libraryFile;
619     if ( !loadLibrary( error, libraryFile ))
620       return key;
621
622     typedef std::string (*GetKeyFun)();
623     GetKeyFun keyFun = (GetKeyFun) GetProc( theLibraryHandle, "GetKey" );
624     if ( !keyFun )
625     {
626       if ( ! getLastError( error ))
627         error = SMESH_Comment( "Can't find symbol 'GetKey' in '") << getenv( theEnvVar ) << "'";
628     }
629     else
630     {
631       key = keyFun( );
632     }
633     if ( key.empty() )
634       error = "GetKey() failed (located in '" + libraryFile._name + "')";
635
636     return key;
637   }
638
639   //================================================================================
640   /*!
641    * \brief Get MeshGems version major/minor/patch from the environment variables
642    *  \param [out] error - return error description
643    *  \return int - the version
644    */
645   //================================================================================
646   int GetMGVersionFromEnv(const char* env_variable)
647   {
648     MESSAGE("Entering GetMGVersionFromEnv and calling " << env_variable);
649     int version = -1;
650     if (getenv(env_variable) == nullptr )
651     {
652       MESSAGE("Could not find " << env_variable << " from environment");
653     }
654     else
655     {
656       version = std::stoi(std::string(getenv(env_variable)));
657     }
658     return version;
659   }
660   //================================================================================
661   /*!
662    * \brief Get MeshGems version major/minor/patch from the keygen library and meshgems built-in functions
663    *  \param [out] error - return error description
664    *  \return int - the function implemented in the library
665    */
666   //================================================================================
667   int GetMGVersionFromFunction(const char* function_name)
668   {
669     MESSAGE("Entering GetMGVersionFromFunction and calling " << function_name);
670     int version = -1;
671     typedef int (*GetKeyFun)();
672     GetKeyFun keyFun = (GetKeyFun) GetProc( theLibraryHandle, function_name);
673     if ( !keyFun )
674     {
675       MESSAGE("Could not find " << function_name << " from library");
676     }
677     else
678     {
679       version = keyFun();
680     }
681     return version;
682   }
683
684   //================================================================================
685   /*!
686    * \brief Get MeshGems version from the keygen library or meshgems built-in functions
687    *  \param [out] error - return error description
688    *  \return int - the version
689    */
690   //================================================================================
691   int GetMGVersionHex(std::string&       error)
692   {
693     // load mgkeygen library
694     int v_min = -1;
695     LibraryFile libraryFile;
696     if ( !loadLibrary( error, libraryFile ))
697       return v_min;
698     MESSAGE("Extracting MeshGems version");
699
700     // get minor version
701     v_min = GetMGVersionFromFunction("meshgems_core_get_version_minor");
702     if (v_min == -1)
703       v_min = GetMGVersionFromFunction("GetVersionMinor");
704     if (v_min == -1)
705       v_min = GetMGVersionFromEnv("MESHGEMS_VERSION_MINOR");
706     if (v_min == -1)
707       error = "could not retrieve minor version (located in '" + libraryFile._name + "')";
708     MESSAGE("MeshGems minor version =  " << v_min);
709
710     // get major version
711     int v_maj = GetMGVersionFromFunction("meshgems_core_get_version_major");
712     if (v_maj == -1)
713       v_maj = GetMGVersionFromFunction("GetVersionMajor");
714     if (v_maj == -1)
715       v_maj = GetMGVersionFromEnv("MESHGEMS_VERSION_MAJOR");
716     if (v_maj == -1)
717       error = "could not retrieve major version (located in '" + libraryFile._name + "')";
718     MESSAGE("MeshGems major version = " << v_maj);
719
720     // get patch version
721     int v_patch = GetMGVersionFromFunction("meshgems_core_get_version_patch");
722     if (v_patch == -1)
723       v_patch = GetMGVersionFromFunction("GetVersionPatch");
724     if (v_patch == -1)
725       v_patch = GetMGVersionFromEnv("MESHGEMS_VERSION_PATCH");
726     if (v_patch == -1)
727       error = "could not retrieve patch version (located in '" + libraryFile._name + "')";
728     MESSAGE("MeshGems patch version = " << v_patch);
729
730     int v_hex = (v_maj << 16 | v_min << 8 | v_patch);
731     MESSAGE("v_hex: " << v_hex);
732
733     return v_hex;
734   }
735
736   //================================================================================
737   /*!
738    * \brief Guess if the Spatial license is needed (if MeshGems is > 2.15.0)
739    *  \param [out] error - return error description
740    *  \return bool - true if MeshGems is > 2.15.0
741    */
742   //================================================================================
743   bool NeedsMGSpatialEnvLicense(std::string& error)
744   {
745     // if MeshGems version is > 2.15.0, need to set SPATIAL_LICENSE
746     int v_hex = GetMGVersionHex(error);
747     bool ok = (v_hex > MESHGEMS_215);
748     if (ok)
749       MESSAGE("MeshGems version is > 2.15.0, need to set SPATIAL_LICENSE");
750     return ok;
751   }
752
753   //================================================================================
754   /*!
755    * \brief Set the SPATIAL_LICENSE environment variable
756    *  \param [out] error - return error description
757    *  \return bool - true in case of success
758    */
759   //================================================================================
760   bool SetMGSpatialEnvLicense(std::string& error)
761   {
762     int ok;
763     std::string key = GetKey(error);
764 #ifndef WIN32
765     ok = setenv(SPATIAL_LICENSE, key.c_str(), 0); // 0 means do not overwrite
766 #else
767     ok = Kernel_Utils::setenv(SPATIAL_LICENSE, key.c_str(), 0 );
768 #endif
769     MESSAGE("Set SPATIAL_LICENSE");
770     return (ok==0);
771   }
772
773   //================================================================================
774   /*!
775    * \brief Get the license key from libMeshGemsKeyGenerator.so or $SPATIAL_LICENSE
776    * Called by plugins calling MG products as executables.
777    * If MESHGEMS_OLD_STYLE is set, return "0", to use old DLIM8 server license
778    * instead of the key.
779    *  \param [in] gmfFile - path to an input mesh file
780    *  \param [in] nb* - nb of entities in the input mesh
781    *  \param [out] error - return error description
782    *  \return std::string - the key
783    */
784   //================================================================================
785   std::string GetKey(const std::string& gmfFile,
786                      int                nbVertex,
787                      int                nbEdge,
788                      int                nbFace,
789                      int                nbVol,
790                      std::string&       error)
791   {
792     // default key if MESHGEMS_OLD_STYLE or SPATIAL_LICENSE is set
793     std::string key("0");
794     const char *meshGemsOldStyleEnvVar( getenv( MESHGEMS_OLD_STYLE ) );
795     if ( !meshGemsOldStyleEnvVar || strlen(meshGemsOldStyleEnvVar) == 0 )
796     {
797       const char *spatialLicenseEnvVar( getenv( SPATIAL_LICENSE ) );
798       if ( !spatialLicenseEnvVar || strlen(spatialLicenseEnvVar) == 0 )
799       {
800         if (NeedsMGSpatialEnvLicense(error))
801         {
802           // if MG version > 2.15, set environment license, don't return it as a key
803           // otherwise it will be printed in the command line
804           MESSAGE("SPATIAL_LICENSE not in env => we add it from MGKeygen .so");
805           SetMGSpatialEnvLicense(error);
806         }
807         else
808         {
809           // generate the key from the mesh info (MG 2.13 and 2.14)
810           MESSAGE("MG < 2.15 => get the key from MGKeygen .so and this mesh info");
811           key = GetKey_After(gmfFile,nbVertex,nbEdge,nbFace,nbVol,error);
812         }
813       }
814       else
815         MESSAGE("SPATIAL_LICENSE already in env => we use it");
816     }
817     if (! error.empty())
818       std::cerr << error;
819     return key;
820   }
821
822   //================================================================================
823   /*!
824    * \brief Get the license key from libMeshGemsKeyGenerator.so or $SPATIAL_LICENSE
825    * Called for MG 2.15 by CADSurf and MG plugins calling MG products as library,
826    * i.e. compiled as library with -DSALOME_USE_MG_LIBS=ON
827    *  \param [out] error - return error description
828    *  \return std::string - the key
829    */
830   //================================================================================
831   std::string GetKey(std::string&       error)
832   {
833     // default key if not found in .so or in SPATIAL_LICENSE
834     std::string key("0");
835     const char *meshGemsOldStyleEnvVar( getenv( MESHGEMS_OLD_STYLE ) );
836     if ( !meshGemsOldStyleEnvVar || strlen(meshGemsOldStyleEnvVar) == 0 ){
837       const char *spatialLicenseEnvVar( getenv( SPATIAL_LICENSE ) );
838       if ( !spatialLicenseEnvVar || strlen(spatialLicenseEnvVar) == 0 )
839       {
840         MESSAGE("SPATIAL_LICENSE not in env => we add it from MGKeygen .so");
841         // use new style, i.e. key in a library
842         key = GetKey_After(error);
843       }
844       else
845       {
846         MESSAGE("SPATIAL_LICENSE already in env => we use it");
847         key = std::string(spatialLicenseEnvVar);
848       }
849     }
850     if (! error.empty())
851       std::cerr << error;
852     return key;
853   }
854
855   //================================================================================
856   /*!
857    * \brief Return false if libMeshGemsKeyGenerator.so is not functional
858    *  \param [out] error - return error description
859    *  \return bool - is a success
860    */
861   //================================================================================
862
863   bool CheckKeyGenLibrary( std::string& error )
864   {
865     return !GetKey("",4,0,2,0,error ).empty();
866   }
867
868   //================================================================================
869   /*!
870    * \brief Return KeyGenerator library name
871    */
872   //================================================================================
873
874   std::string GetLibraryName()
875   {
876     std::string libName, error;
877     if ( const char* libPath = getenv( theEnvVar ))
878     {
879       libName = Kernel_Utils::GetBaseName( libPath );
880     }
881     else
882     {
883       libName = "libSalomeMeshGemsKeyGenerator";
884     }
885     setExtension( libName, error );
886     return libName;
887   }
888 }