@@ -342,6 +342,229 @@ public function compareBinaryHash(string $url, string $binaryPath) {
342342 return true ;
343343 }
344344
345+ /**
346+ * Perform cURL to download python binary (in directory format)
347+ *
348+ * @param string $url
349+ * @param array $binariesFolder appdata binaries folder
350+ * @param string $appId target Application::APP_ID
351+ * @param string $filename archive and extracted folder name
352+ * @param bool $update flag to determine whether to update already downloaded binary or not
353+ *
354+ * @return array
355+ */
356+ public function downloadPythonBinaryDir (
357+ string $ url ,
358+ array $ binariesFolder ,
359+ string $ appId ,
360+ string $ filename = 'main ' ,
361+ bool $ update = false
362+ ): array {
363+ if (isset ($ binariesFolder ['success ' ]) && $ binariesFolder ['success ' ]) {
364+ $ dir = $ binariesFolder ['path ' ] . '/ ' ;
365+ } else {
366+ return $ binariesFolder ; // Return getAppDataFolder result
367+ }
368+ $ file_name = $ filename . '.tar.gz ' ;
369+ $ save_file_loc = $ dir . $ file_name ;
370+ $ shouldDownloadBinary = $ this ->compareBinaryDirectoryHashes ($ url , $ binariesFolder , $ appId );
371+
372+ if (!file_exists ($ dir . $ filename ) || ($ update && $ shouldDownloadBinary )) {
373+ $ cURL = curl_init ($ url );
374+ $ fp = fopen ($ save_file_loc , 'wb ' );
375+ if ($ fp ) {
376+ curl_setopt_array ($ cURL , [
377+ CURLOPT_RETURNTRANSFER => true ,
378+ CURLOPT_FILE => $ fp ,
379+ CURLOPT_FOLLOWLOCATION => true ,
380+ ]);
381+ curl_exec ($ cURL );
382+ curl_close ($ cURL );
383+ fclose ($ fp );
384+ $ unpacked = $ this ->unTarGz ($ binariesFolder , $ filename . '.tar.gz ' );
385+ unlink ($ save_file_loc );
386+ return [
387+ 'downloaded ' => true ,
388+ 'unpacked ' => $ unpacked
389+ ];
390+ }
391+ }
392+
393+ return [
394+ 'downloaded ' => true ,
395+ 'unpacked ' => true ,
396+ ];
397+ }
398+
399+ /**
400+ * Extract tar.gz file
401+ *
402+ * @param array $binariesFolder appdata binaries folder
403+ * @param string $src_filename source tar.gz file name
404+ *
405+ * @return array
406+ */
407+ public function unTarGz (array $ binariesFolder , string $ src_filename ): array {
408+ if (isset ($ binariesFolder ['success ' ]) && $ binariesFolder ['success ' ]) {
409+ $ dir = $ binariesFolder ['path ' ] . '/ ' ;
410+ $ src_file = $ dir . $ src_filename ;
411+ $ phar = new \PharData ($ src_file );
412+ $ extracted = $ phar ->extractTo ($ dir , null , true );
413+ $ filename = $ phar ->getFilename ();
414+ return [
415+ 'extracted ' => $ extracted ,
416+ 'filename ' => $ filename
417+ ];
418+ }
419+ return [
420+ 'extracted ' => false ,
421+ ];
422+ }
423+
424+ /**
425+ * Perform cURL to get binary folder hashes sha256 sum
426+ *
427+ * @param string $url url to the binary hashsums file
428+ *
429+ * @return array
430+ */
431+ public function downloadBinaryDirHashes (string $ url ): array {
432+ $ cURL = curl_init ($ url );
433+ curl_setopt_array ($ cURL , [
434+ CURLOPT_RETURNTRANSFER => true ,
435+ CURLOPT_FOLLOWLOCATION => true ,
436+ ]);
437+ $ binaryHash = curl_exec ($ cURL );
438+ curl_close ($ cURL );
439+ return [
440+ 'success ' => $ binaryHash != false ,
441+ 'binaryHash ' => json_decode ($ binaryHash , true ),
442+ ];
443+ }
444+
445+ /**
446+ * Compare binary folder hashes from release.
447+ * If hash not exists return `true` (download anyway)
448+ *
449+ * @param string $url
450+ * @param array $binariesFolder
451+ *
452+ * @return bool
453+ */
454+ public function compareBinaryDirectoryHashes (
455+ string $ url , array $ binariesFolder , string $ appId
456+ ): bool {
457+ $ currentBinaryHashes = $ this ->getCurrentBinaryDirHashes ($ binariesFolder , $ appId );
458+ $ newBinaryHashes = $ this ->downloadBinaryDirHashes (str_replace ('.tar.gz ' , '.json ' , $ url ));
459+ if ($ newBinaryHashes ['success ' ] && $ currentBinaryHashes ['success ' ]) {
460+ // Skip hash check of archive file
461+ $ archiveFilename = $ appId . '_ ' . $ this ->getBinaryName () . '.tar.gz ' ;
462+ if (isset ($ newBinaryHashes ['binaryHashes ' ][$ archiveFilename ])) {
463+ unset($ newBinaryHashes ['binaryHashes ' ][$ archiveFilename ]);
464+ }
465+ foreach ($ newBinaryHashes ['binaryHashes ' ] as $ filename => $ hash ) {
466+ $ fileExists = !isset ($ currentBinaryHashes [$ filename ]);
467+ $ currentHash = $ currentBinaryHashes ['binaryHashes ' ][$ filename ];
468+ $ hashEqual = $ currentHash == $ hash ;
469+ if (!$ fileExists || !$ hashEqual ) {
470+ return true ;
471+ }
472+ }
473+ return false ;
474+ }
475+ return true ;
476+ }
477+
478+ /**
479+ * Get current binary folder files hashes
480+ *
481+ * @param array $binariesFolder
482+ *
483+ * @return array
484+ */
485+ public function getCurrentBinaryDirHashes (array $ binariesFolder , string $ appId ): array {
486+ $ currentBinaryHashes = [];
487+ $ archiveFilename = $ appId . '_ ' . $ this ->getBinaryName () . '.tar.gz ' ;
488+ if (file_exists ($ binariesFolder ['path ' ] . '/ ' . $ archiveFilename )) {
489+ $ currentBinaryHashes [$ archiveFilename ] = hash_file (
490+ 'sha256 ' ,
491+ $ binariesFolder ['path ' ] . '/ ' . $ archiveFilename
492+ );
493+ }
494+ $ extractedBinaryFolder = $ binariesFolder ['path ' ] . '/ ' . $ appId . '_ ' . $ this ->getBinaryName ();
495+ $ files = scandir ($ extractedBinaryFolder );
496+ if ($ files !== false ) {
497+ foreach ($ files as $ file ) {
498+ if ($ file != '. ' && $ file != '.. ' ) {
499+ // Get sha256 hash of each file
500+ // If file is directory, get sha256 hash of each file in directory
501+ if (is_dir ($ extractedBinaryFolder . '/ ' . $ file )) {
502+ $ dirFiles = scandir ($ extractedBinaryFolder . '/ ' . $ file );
503+ $ currentBinaryHashes = $ this ->getFolderHashes (
504+ $ dirFiles ,
505+ $ file ,
506+ $ extractedBinaryFolder . '/ ' . $ file ,
507+ $ currentBinaryHashes ,
508+ $ appId
509+ );
510+ } else {
511+ $ binaryFolderFilePath = $ appId . '_ ' . $ this ->getBinaryName () . '/ ' . $ file ;
512+ $ currentBinaryHashes [$ binaryFolderFilePath ] = hash_file (
513+ 'sha256 ' ,
514+ $ extractedBinaryFolder . '/ ' . $ file
515+ );
516+ }
517+ }
518+ }
519+ }
520+ return [
521+ 'success ' => count ($ currentBinaryHashes ) > 0 ,
522+ 'binaryHashes ' => $ currentBinaryHashes
523+ ];
524+ }
525+
526+ /**
527+ * Get sha256 hashes of each file in binary folder
528+ * Recursive function call if file is directory
529+ *
530+ * @param array $files
531+ * @param string $folder
532+ * @param string $extractedBinaryFolder
533+ * @param array $currentBinaryHashes
534+ * @param string $appId
535+ *
536+ * @return array
537+ */
538+ private function getFolderHashes (
539+ array $ files ,
540+ string $ folder ,
541+ string $ extractedBinaryFolder ,
542+ array $ currentBinaryHashes ,
543+ string $ appId
544+ ): array {
545+ foreach ($ files as $ file ) {
546+ if ($ file != '. ' && $ file != '.. ' ) {
547+ // Get sha256 hash of each file
548+ // If file is directory, get sha256 hash of each file in directory
549+ if (is_dir ($ extractedBinaryFolder . '/ ' . $ file )) {
550+ $ dirFiles = scandir ($ extractedBinaryFolder . '/ ' . $ file );
551+ $ currentBinaryHashes = $ this ->getFolderHashes (
552+ $ dirFiles ,
553+ $ folder . '/ ' . $ file , $ extractedBinaryFolder . '/ ' . $ file ,
554+ $ currentBinaryHashes , $ appId
555+ );
556+ } else {
557+ $ binaryFolderFilePath = $ appId . '_ '
558+ . $ this ->getBinaryName () . '/ ' . $ folder . '/ ' . $ file ;
559+ $ currentBinaryHashes [$ binaryFolderFilePath ] = hash_file (
560+ 'sha256 ' , $ extractedBinaryFolder . '/ ' . $ file
561+ );
562+ }
563+ }
564+ }
565+ return $ currentBinaryHashes ;
566+ }
567+
345568 /**
346569 * Perform cURL to get binary's sha256 sum
347570 *
0 commit comments