Salome HOME
b058e86a2d607bc6bc66d0d5734873d94b622d39
[modules/smesh.git] / src / SMESHUtils / SMESH_MGLicenseKeyGen.cxx
1 // Copyright (C) 2007-2021  CEA/DEN, EDF R&D, 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
64 namespace
65 {
66   static LibHandle theLibraryHandle = nullptr; //!< handle of a loaded library
67
68   const char* theEnvVar = "SALOME_MG_KEYGEN_LIB_PATH"; /* var specifies either full file name
69                                                           of libSalomeMeshGemsKeyGenerator or
70                                                           URL to download the library from */
71
72   const char* theTmpEnvVar = "SALOME_TMP_DIR"; // directory to download the library to
73
74   //-----------------------------------------------------------------------------------
75   /*!
76    * \brief Remove library file at destruction in case if it was downloaded from server
77    */
78   //-----------------------------------------------------------------------------------
79
80   struct LibraryFile
81   {
82     std::string _name; // full file name
83     bool        _isURL;
84
85     LibraryFile(): _isURL( false ) {}
86
87     ~LibraryFile()
88     {
89       if ( _isURL )
90       {
91         if ( theLibraryHandle )
92         {
93           UnLoadLib( theLibraryHandle );
94           theLibraryHandle = nullptr;
95         }
96
97         std::string tmpDir; // tmp dir that should not be removed
98         if ( const char* libPath = getenv( theTmpEnvVar ))
99         {
100           tmpDir = libPath;
101           while (( !tmpDir.empty() ) &&
102                  ( tmpDir.back() == '/' || tmpDir.back() == '\\' ))
103             tmpDir.pop_back();
104         }
105
106         while ( SMESH_File( _name ).remove() )
107         {
108           size_t length = _name.size();
109           _name = boofs::path( _name ).parent_path().string(); // goto parent directory
110           if ( _name.size() == length )
111             break; // no more parents
112
113           if ( _name == tmpDir )
114             break; // don't remove tmp dir
115
116           if ( !Kernel_Utils::IsEmptyDir( _name ))
117             break;
118         }
119       }
120     }
121   };
122
123
124   //================================================================================
125   /*!
126    * \brief Retrieve description of the last error
127    *  \param [out] error - return the description
128    *  \return bool - true if the description found
129    */
130   //================================================================================
131
132   bool getLastError( std::string& error )
133   {
134 #ifndef WIN32
135
136     if ( const char* text = dlerror() )
137     {
138       error = text;
139       return true;
140     }
141     return false;
142
143 #else
144
145     DWORD dw = GetLastError();
146     void* cstr;
147     DWORD msgLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
148                                  NULL,
149                                  dw,
150                                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
151                                  (LPTSTR) &cstr,
152                                  0,
153                                  NULL
154                                  );
155     if ( msgLen > 0 ) {
156 #  if defined( UNICODE )
157       error = Kernel_Utils::encode_s((wchar_t*)cstr);
158 #  else
159       error = (char*)cstr;
160 #  endif
161       LocalFree(cstr);
162     }
163
164     return (bool)msgLen;
165
166 #endif
167   }
168
169   //================================================================================
170   /*!
171    * \brief Adjust file extension according to the platform
172    */
173   //================================================================================
174
175   bool setExtension( std::string& fileName, std::string& error )
176   {
177     if ( fileName.empty() )
178     {
179       error = "Library file name is empty";
180       return false;
181     }
182 #if defined(WIN32)
183     std::string ext = ".dll";
184 #elif defined(__APPLE__)
185     std::string ext = ".dylib";
186 #else
187     std::string ext = ".so";
188 #endif
189
190     fileName = fileName.substr( 0, fileName.find_last_of('.')) + ext;
191     return true;
192   }
193
194   //================================================================================
195   /*!
196    * \brief Check if library file name looks like an URL
197    *  \param [in,out] libraryFile - holds file name and returns result in _isURL member field
198    *  \return bool - true if the file name looks like an URL
199    */
200   //================================================================================
201
202   bool isURL( LibraryFile & libraryFile )
203   {
204     {// round1
205       enum { SCHEME = 2, AUTHORITY = 4, PATH = 5 }; // sub-strings
206       boost::regex urlRegex ( R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)",
207                               boost::regex::extended );
208       boost::smatch matchResult;
209
210       libraryFile._isURL = false;
211       if ( boost::regex_match( libraryFile._name, matchResult, urlRegex ))
212         libraryFile._isURL = ( !matchResult.str( SCHEME    ).empty() &&
213                               !matchResult.str( AUTHORITY ).empty() &&
214                               !matchResult.str( PATH      ).empty() );
215     }
216     if(libraryFile._isURL)
217       return true;
218     {// round2
219       enum { HOST = 2, PORT = 3, PATH = 4 }; // sub-strings
220       boost::regex urlRegex ( R"(^(([^:\/?#]+):)?([^/]+)?(/[^#]*))",
221                               boost::regex::extended );
222       boost::smatch matchResult;
223
224       libraryFile._isURL = false;
225       if ( boost::regex_match( libraryFile._name, matchResult, urlRegex ))
226         libraryFile._isURL = ( !matchResult.str( HOST ).empty() &&
227                               !matchResult.str( PORT ).empty() &&
228                               !matchResult.str( PATH ).empty() );
229     }
230     return libraryFile._isURL;
231   }
232
233   //================================================================================
234   /*!
235    * \brief Download libraryFile._name URL to SALOME_TMP_DIR
236    *  \param [in,out] libraryFile - holds the URL and returns name of a downloaded file
237    *  \param [out] error - return error description
238    *  \return bool - is a success
239    */
240   //================================================================================
241
242   bool downloadLib( LibraryFile& libraryFile, std::string & error )
243   {
244     // check if can write into SALOME_TMP_DIR
245
246     std::string tmpDir = Kernel_Utils::GetTmpDirByEnv( theTmpEnvVar );
247     if ( tmpDir.empty() ||
248          !Kernel_Utils::IsExists( tmpDir ))
249     {
250       error = "Can't download " + libraryFile._name + " as SALOME_TMP_DIR is not correctly set";
251       return false;
252     }
253     if ( !Kernel_Utils::IsWritable( tmpDir ))
254     {
255       error = "Can't download " + libraryFile._name + " as '" + tmpDir + "' is not writable. "
256         "Check SALOME_TMP_DIR environment variable";
257       return false;
258     }
259
260     // Download
261
262     std::string url = libraryFile._name;
263
264 #ifdef WIN32
265
266     std::string outFile = tmpDir + "MeshGemsKeyGenerator.dll";
267
268     // use wget (== Invoke-WebRequest) PowerShell command available since Windows 7
269     std::string psCmd = "wget -Uri " + url + " -OutFile " + outFile;
270     std::string   cmd = "powershell.exe " + psCmd;
271
272 #else
273
274     std::string outFile = tmpDir + "libMeshGemsKeyGenerator.so";
275
276     std::string cmd = "wget " + url + " -O " + outFile;
277
278 #endif
279
280     if ( Kernel_Utils::IsExists( outFile )) // remove existing file
281     {
282       SMESH_File lib( outFile, /*open=*/false );
283       if ( !lib.remove() )
284       {
285         error = lib.error();
286         return false;
287       }
288     }
289
290     system( cmd.c_str() ); // download
291
292     SMESH_File resultFile( outFile, /*open=*/false );
293     bool ok = ( resultFile.exists() && resultFile.size() > 0 );
294
295     if ( ok )
296       libraryFile._name = outFile;
297     else
298       error = "Can't download file " + url;
299
300     return ok;
301   }
302
303   //================================================================================
304   /*!
305    * \brief Load libMeshGemsKeyGenerator.so
306    *  \param [out] error - return error description
307    *  \param [out] libraryFile - return library file name and _isURL flag
308    *  \return bool - is a success
309    */
310   //================================================================================
311
312   bool loadLibrary( std::string& error, LibraryFile& libraryFile )
313   {
314     if ( theLibraryHandle )
315       return true;
316
317     const char* libPath = getenv( theEnvVar );
318     if ( !libPath )
319     {
320       error = SMESH_Comment( "Environment variable ") <<  theEnvVar << " is not set";
321       return false;
322     }
323
324     libraryFile._name = libPath;
325     // if ( !setExtension( libraryFile._name, error )) // is it necessary?
326     //   return false;
327
328     if ( isURL( libraryFile ))
329     {
330       if ( !downloadLib( libraryFile, error ))
331       {
332         // try to fix extension
333         std::string url = libraryFile._name;
334         if ( !setExtension( libraryFile._name, error ))
335           return false;
336         if ( url == libraryFile._name )
337           return false; // extension not changed
338
339         if ( !downloadLib( libraryFile, error ))
340           return false;
341       }
342     }
343
344 #if defined( WIN32 ) && defined( UNICODE )
345     std::wstring encodePath = Kernel_Utils::utf8_decode_s( libraryFile._name );
346     const wchar_t*     path = encodePath.c_str();
347 #else
348     const char*        path = libraryFile._name.c_str();
349 #endif
350
351     theLibraryHandle = LoadLib( path );
352     if ( !theLibraryHandle )
353     {
354       if ( ! getLastError( error ))
355         error = "Can't load library '" + libraryFile._name + "'";
356     }
357
358     return theLibraryHandle;
359   }
360
361 } // anonymous namespace
362
363
364 namespace SMESHUtils_MGLicenseKeyGen // API implementation
365 {
366   //================================================================================
367   /*!
368    * \brief Sign a CAD
369    *  \param [in] meshgems_cad - pointer to a MG CAD object (meshgems_cad_t)
370    *  \param [out] error - return error description
371    *  \return bool - is a success
372    */
373   //================================================================================
374
375   bool SignCAD( void* meshgems_cad, std::string& error )
376   {
377     LibraryFile libraryFile;
378     if ( !loadLibrary( error, libraryFile ))
379       return false;
380
381     bool ok = false;
382     typedef bool (*SignFun)(void* );
383     SignFun signFun = (SignFun) GetProc( theLibraryHandle, "SignCAD" );
384     if ( !signFun )
385     {
386       if ( ! getLastError( error ))
387         error = SMESH_Comment( "Can't find symbol 'SignCAD' in '") << getenv( theEnvVar ) << "'";
388     }
389     else
390     {
391       SMESH_TRY;
392
393       ok = signFun( meshgems_cad );
394
395       SMESH_CATCH( SMESH::returnError );
396
397       if ( !error.empty() )
398         ok = false;
399       else if ( !ok )
400         error = "SignCAD() failed (located in '" + libraryFile._name + "')";
401     }
402     return ok;
403   }
404
405   //================================================================================
406   /*!
407    * \brief Sign a mesh
408    *  \param [in] meshgems_mesh - pointer to a MG mesh (meshgems_mesh_t)
409    *  \param [out] error - return error description
410    *  \return bool - is a success
411    */
412   //================================================================================
413
414   bool SignMesh( void* meshgems_mesh, std::string& error )
415   {
416     LibraryFile libraryFile;
417     if ( !loadLibrary( error, libraryFile ))
418       return false;
419
420     bool ok = false;
421     typedef bool (*SignFun)(void* );
422     SignFun signFun = (SignFun) GetProc( theLibraryHandle, "SignMesh" );
423     if ( !signFun )
424     {
425       if ( ! getLastError( error ))
426         error = SMESH_Comment( "Can't find symbol 'SignMesh' in '") << getenv( theEnvVar ) << "'";
427     }
428     else
429     {
430       SMESH_TRY;
431
432       ok = signFun( meshgems_mesh );
433
434       SMESH_CATCH( SMESH::returnError );
435
436       if ( !error.empty() )
437         ok = false;
438       else if ( !ok )
439         error = "SignMesh() failed (located in '" + libraryFile._name + "')";
440     }
441     return ok;
442   }
443
444   //================================================================================
445   /*!
446    * \brief Return a license key to pass as argument to a MG mesher executable
447    *  \param [in] gmfFile - path to an input mesh file
448    *  \param [in] nb* - nb of entities in the input mesh
449    *  \param [out] error - return error description
450    *  \return std::string - the key
451    */
452   //================================================================================
453
454   std::string GetKey(const std::string& gmfFile,
455                      int                nbVertex,
456                      int                nbEdge,
457                      int                nbFace,
458                      int                nbVol,
459                      std::string&       error)
460   {
461     std::string key;
462     LibraryFile libraryFile;
463     if ( !loadLibrary( error, libraryFile ))
464       return key;
465
466     typedef std::string (*GetKeyFun)(std::string const &, int, int, int, int );
467     GetKeyFun keyFun = (GetKeyFun) GetProc( theLibraryHandle, "GetKey" );
468     if ( !keyFun )
469     {
470       if ( ! getLastError( error ))
471         error = SMESH_Comment( "Can't find symbol 'GetKey' in '") << getenv( theEnvVar ) << "'";
472     }
473     else
474     {
475       key = keyFun( gmfFile, nbVertex, nbEdge, nbFace, nbVol );
476     }
477     if ( key.empty() )
478       error = "GetKey() failed (located in '" + libraryFile._name + "')";
479
480     return key;
481   }
482
483   //================================================================================
484   /*!
485    * \brief Return false if libMeshGemsKeyGenerator.so is not functional
486    *  \param [out] error - return error description
487    *  \return bool - is a success
488    */
489   //================================================================================
490
491   bool CheckKeyGenLibrary( std::string& error )
492   {
493     return !GetKey("",4,0,2,0,error ).empty();
494   }
495
496   //================================================================================
497   /*!
498    * \brief Return KeyGenerator library name
499    */
500   //================================================================================
501
502   std::string GetLibraryName()
503   {
504     std::string libName, error;
505     if ( const char* libPath = getenv( theEnvVar ))
506     {
507       libName = Kernel_Utils::GetBaseName( libPath );
508     }
509     else
510     {
511       libName = "libSalomeMeshGemsKeyGenerator";
512     }
513     setExtension( libName, error );
514     return libName;
515   }
516 }