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