1: <?php
   2: 
   3:    4:    5:    6:    7:    8:    9:   10:   11:   12:   13:   14:   15:   16:   17:   18: 
  19: 
  20: require_once( __DIR__ . '/includes/php-classes/AlfredBundlerLogger.php' );
  21: require_once( __DIR__ . '/includes/php-classes/AlfredBundlerIcon.php' );
  22: 
  23: 
  24: if ( ! class_exists( 'AlfredBundlerInternalClass' ) ) :
  25:   26:   27:   28:   29:   30:   31:   32:   33:   34:   35:   36:   37: 
  38: class AlfredBundlerInternalClass {
  39: 
  40:     41:   42:   43:   44:   45: 
  46:   public   $data;
  47: 
  48:     49:   50:   51:   52:   53: 
  54:   public   $cache;
  55: 
  56:     57:   58:   59:   60:   61: 
  62:   public   $major_version;
  63: 
  64:     65:   66:   67:   68:   69: 
  70:   public   $minor_version;
  71: 
  72:     73:   74:   75:   76:   77: 
  78:   private   $plist;
  79: 
  80:     81:   82:   83:   84:   85: 
  86:   public   $bundle;
  87: 
  88:     89:   90:   91:   92:   93: 
  94:   public   $name;
  95: 
  96:     97:   98:   99:  100: 
 101:   public   $workflowData;
 102: 
 103:    104:  105:  106:  107:  108: 
 109:   public   $background;
 110: 
 111:   
 112:   public $finfo;
 113: 
 114:   
 115:   public $alfredVersion;
 116: 
 117:    118:  119:  120:  121:  122: 
 123:   private $caches;
 124: 
 125:    126:  127:  128:  129:  130: 
 131:   public $log;
 132: 
 133:    134:  135:  136:  137:  138: 
 139:   public $userLog;
 140: 
 141:    142:  143:  144:  145:  146: 
 147:   public $env;
 148: 
 149: 
 150:    151:  152:  153:  154:  155:  156:  157:  158: 
 159:   public function __construct( $options = [] ) {
 160: 
 161:     if ( isset( $_ENV['AB_BRANCH'] ) ) {
 162:       $this->major_version = $_ENV['AB_BRANCH'];
 163:     } else {
 164:       $this->major_version = file_get_contents(  __DIR__ . '/meta/version_major' );
 165:     }
 166: 
 167:     $this->minor_version   = file_get_contents(  __DIR__ . '/meta/version_minor' );
 168: 
 169:     $this->data  = trim( "{$_SERVER[ 'HOME' ]}/Library/Application Support/Alfred 2/Workflow Data/alfred.bundler-{$this->major_version}" );
 170:     $this->cache = trim( "{$_SERVER[ 'HOME' ]}/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/alfred.bundler-{$this->major_version}" );
 171: 
 172:     $this->setup();
 173: 
 174:     $this->log = new AlfredBundlerLogger( "{$this->data}/data/logs/bundler-{$this->major_version}" );
 175: 
 176:     if ( isset( $options[ 'log' ] ) )
 177:       $log = $options[ 'log' ];
 178:     else
 179:       $log = 'file';
 180: 
 181:     $this->userLog = new AlfredBundlerLogger( "{$this->workflowData}/{$this->name}", $log );
 182: 
 183:     
 184:     return TRUE;
 185:   }
 186: 
 187:    188:  189:  190: 
 191:   private function setup() {
 192: 
 193:     
 194:     if ( ! file_exists( 'info.plist' ) )
 195:           throw new Exception( 'Using the Alfred Bundler requires a valid info.plist file to be found; in other words, it needs to be used in a workflow. ');
 196: 
 197:     if ( isset( $_ENV[ 'alfred_version' ] ) )
 198:       $this->setupModern();
 199:     else
 200:       $this->setupDeprecated();
 201: 
 202:     $this->setupDirStructure();
 203: 
 204:   }
 205: 
 206:    207:  208: 
 209:   private function setupModern() {
 210: 
 211:     $this->bundle       = $_ENV[ 'alfred_workflow_bundleid' ];
 212:     $this->name         = $_ENV[ 'alfred_workflow_name' ];
 213:     $this->workflowData = $_ENV[ 'alfred_workflow_data' ];
 214:     $this->env          = TRUE;
 215: 
 216:   }
 217: 
 218:    219:  220: 
 221:   private function setupDeprecated() {
 222: 
 223:     $this->bundle       = $this->readPlist( 'info.plist', 'bundleid' );
 224:     $this->name         = $this->readPlist( 'info.plist', 'name' );
 225:     $this->workflowData = $_SERVER[ 'HOME' ] .
 226:       "/Library/Application Support/Alfred 2/Workflow Data/" . $this->bundle;
 227:     $this->env          = FALSE;
 228: 
 229:   }
 230: 
 231:    232:  233: 
 234:   private function setupDirStructure() {
 235: 
 236:     
 237:     $directories = array(
 238:       "{$this->data}/data",
 239:       "{$this->cache}",
 240:       "{$this->cache}/color",
 241:       "{$this->cache}/misc",
 242:       "{$this->cache}/php",
 243:       "{$this->cache}/ruby",
 244:       "{$this->cache}/python",
 245:       "{$this->cache}/utilities",
 246:     );
 247: 
 248:     foreach ( $directories as $dir ) :
 249:       
 250:       if ( ! file_exists( $dir ) )
 251:         mkdir( $dir, 0775, TRUE );
 252:     endforeach;
 253:   }
 254: 
 255:    256:  257:  258:  259:  260:  261:  262:  263:  264:  265:  266:  267: 
 268:   public function load( $type, $name, $version, $json = '' ) {
 269: 
 270:     if ( empty( $json ) ) {
 271:       if ( file_exists( __DIR__ . "/meta/defaults/{$name}.json" ) ) {
 272:         $json_path = __DIR__ . "/meta/defaults/{$name}.json";
 273:       } else {
 274:         
 275:         $error = TRUE;
 276:       }
 277:     } else if ( file_exists( $json ) ) { $line = __LINE__;
 278:         $json_path = $json;
 279:       } else {
 280:       
 281:       $error = TRUE;
 282:     }
 283: 
 284:     
 285:     if ( ! json_decode( file_get_contents( $json_path ) ) ) { $line = __LINE__;
 286:       
 287:       $error = TRUE;
 288:     }
 289: 
 290:     
 291:     if ( isset( $error ) && ( $error === TRUE ) ) {
 292:       
 293:       
 294: 
 295:       $this->log->log( "There is a problem with the __implementation__ of " .
 296:         "the Alfred Bundler when trying to load '{$name}'. Please " .
 297:         "let the workflow author know.", 'CRITICAL', 'console' );
 298:       return FALSE;
 299:     }
 300: 
 301:     $json = json_decode( file_get_contents( $json_path ), TRUE );
 302: 
 303:     
 304:     if ( ! file_exists(
 305:         "{$this->data}/data/assets/{$type}/{$name}/{$version}/invoke" ) ) {
 306: 
 307:       if ( ! $this->installAsset(
 308:           "{$this->data}/bundler/meta/defaults/{$name}.json", $version ) ) {
 309:         return FALSE;
 310:       }
 311:     }
 312: 
 313:     
 314:     $this->register( $name, $version ); $line = __LINE__;
 315:     $this->log->log( "Registering assset '{$name}'",
 316:       'INFO', 'console' );
 317: 
 318:     
 319:     if ( ! file_exists(
 320:         "{$this->data}/data/assets/{$type}/{$name}/{$version}/invoke" ) ) {
 321: 
 322:       return FALSE;
 323:     }
 324: 
 325:     if ( $type != 'utility' ) {
 326:       
 327:       $this->log->log( "Loaded '{$name}' version {$version} of type " .
 328:         "'{$type}'", 'INFO', 'console' );
 329: 
 330:       return "{$this->data}/data/assets/{$type}/{$name}/{$version}/"
 331:         . trim( file_get_contents( "{$this->data}/data/assets/{$type}/{$name}/{$version}/invoke" ) );
 332:     } else {
 333:       
 334:       if ( ! ( isset( $json[ 'gatekeeper' ] )
 335:           && ( $json[ 'gatekeeper' ] == TRUE ) ) ) {
 336: 
 337:         
 338:         $this->log->log( "Loaded '{$name}' version {$version} of type " .
 339:           "'{$type}'", 'INFO', 'console' );
 340: 
 341:         return "{$this->data}/data/assets/{$type}/{$name}/{$version}/"
 342:           . trim( file_get_contents( "{$this->data}/data/assets/{$type}/{$name}/{$version}/invoke" ) );
 343:       }
 344:     }
 345: 
 346:     
 347: 
 348:     
 349:     if ( file_exists( 'icon.png' ) )
 350:       $icon = realpath( 'icon.png' );
 351:     else
 352:       $icon = 'default';
 353: 
 354:     
 355:     $path = "{$this->data}/data/assets/{$type}/{$name}/{$version}/"
 356:       . $json[ 'versions' ][ $version ][ 'invoke' ];
 357: 
 358:     
 359:     if ( isset( $json[ 'message' ] ) )
 360:       $message = $json[ 'message' ];
 361:     else
 362:       $message = '';
 363: 
 364:     
 365:     
 366:     if ( $this->gatekeeper( $name, $path, $message, $icon ) ) {
 367:       
 368:       $this->log->log( "Loaded '{$name}' v{$version} of type '{$type}'.",
 369:         'INFO', 'console' );
 370:       return $path;
 371:     } else {
 372:       
 373:       
 374:       return FALSE;
 375:     }
 376: 
 377:     
 378:     
 379:     $this->log->log( "There is a problem with the __implementation__ of " .
 380:       "the Alfred Bundler when trying to load '{$name}'. Please let the " .
 381:       "workflow author know.", 'ERROR', 'console' );
 382: 
 383:     return FALSE;
 384:   }
 385: 
 386: 
 387: 
 388:   public function icon( $font, $name, $color = '000000', $alter = FALSE ) {
 389:     if ( ! isset( $this->icon ) )
 390:       $this->icon = new AlfredBundlerIcon( $this );
 391: 
 392:     return $this->icon->icon([ 'font' => $font, 'name' => $name, 'color' => $color, 'alter' => $alter ]);
 393:   }
 394: 
 395:    396:  397:  398:  399:  400:  401:  402:  403:  404:  405: 
 406:   public function utility( $name, $version = 'latest', $json = '' ) {
 407:     if ( empty( $json ) ) {
 408:       return $this->load( 'utility', $name, $version );
 409:     } else {
 410:       if ( file_exists( $json ) ) {
 411:         return $this->load( 'utility', $name, $version, $json );
 412:       }
 413:     }
 414:     return FALSE;
 415:   }
 416: 
 417:    418:  419:  420:  421:  422:  423:  424:  425:  426:  427: 
 428:   public function library( $name, $version = 'latest', $json = '' ) {
 429:     $dir = "{$this->data}/data/assets/php/{$name}/{$version}";
 430:     if ( file_exists( "{$dir}/invoke" ) ) {
 431:       require_once "{$dir}/" . trim( file_get_contents( "{$dir}/invoke" ) );
 432:       return TRUE;
 433:     } else {
 434:       if ( $this->load( 'php', $name, $version, $json ) ) {
 435:         require_once "{$dir}/" . trim( file_get_contents( "{$dir}/invoke" ) );
 436:         return TRUE;
 437:       } else {
 438:         return FALSE;
 439:       }
 440:     }
 441:   }
 442: 
 443:    444:  445:  446:  447:  448:  449:  450: 
 451:   public function wrapper( $wrapper, $debug = FALSE ) {
 452:     $wrapperPointer = [
 453:         'cocoadialog'=>'cocoaDialog',
 454:         'terminalnotifier'=>'terminal-notifier'
 455:     ];
 456:     $wrappersDir = "{$this->data}/bundler/includes/wrappers/php";
 457:     if ( file_exists( "{$wrappersDir}/{$wrapper}.php" ) ) {
 458:       require_once "{$wrappersDir}/{$wrapper}.php";
 459:       $this->log->log( "Loaded '{$wrapper}' bindings", 'INFO', 'console' );
 460:       return new $wrapper( $this->utility( $wrapperPointer[strtolower( $wrapper )] ), $debug );
 461:     } else {
 462:       $this->log->log( "'{$wrapper}' not found.", 'ERROR', 'console' );
 463:       return 10;
 464:     }
 465:   }
 466: 
 467:    468:  469:  470:  471:  472:  473:  474: 
 475:   public function composer( $packages ) {
 476:     $composerDir = "{$this->data}/data/assets/php/composer";
 477:     if ( ! file_exists( $composerDir ) )
 478:       mkdir( $composerDir, 0755, TRUE );
 479: 
 480:     if ( ! file_exists( "{$composerDir}/composer.phar" ) ) {
 481:       $this->download( "https://getcomposer.org/composer.phar", "{$composerDir}/composer.phar" ); $line = __LINE__;
 482:       exec( "'{$composerDir}/composer.phar'", $output, $status );
 483:       if ( $status !== 0 ) {
 484:         $this->log->log( "Composer.phar is corrupt. Deleting...", 'CRITICAL', 'console' );
 485:       } else {
 486:         $this->log->log( "Installing Composer.phar to `{$composerDir}`", 'INFO', 'both' );
 487:       }
 488:       
 489:     }
 490: 
 491:     $install = FALSE;
 492: 
 493:     if ( file_exists( "{$composerDir}/bundles/{$this->bundle}/autoload.php" ) ) {
 494:       if ( file_exists( "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}/composer.json" ) ) {
 495:         $installDir = "{$this->cache}/{$this->bundle}/composer";
 496:         if ( ! file_exists( $installDir ) )
 497:           mkdir( "{$installDir}", 0775, TRUE );
 498:         $json = json_encode( array( "require" => $packages ) );
 499:         $json = str_replace( '\/', '/', $json ); 
 500:         file_put_contents( "{$installDir}/composer.json", $json );
 501: 
 502:         if ( hash_file( 'md5', "{$installDir}/composer.json" )
 503:           == hash_file( 'md5', "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}/composer.json" ) ) {
 504:           $this->log->log( "Loaded Composer packages for {$this->bundle}.",
 505:             'INFO', 'console' );
 506:           require_once(
 507:             "{$composerDir}/bundles/{$this->bundle}/autoload.php" );
 508: 
 509:           return TRUE;
 510:         } else {
 511:           $install = TRUE;
 512:           if ( file_exists( "{$composerDir}/bundles/{$this->bundle}" ) ) {
 513:             $this->rrmdir( "{$composerDir}/bundles/{$this->bundle}" );
 514:           }
 515:         }
 516:       }
 517:     } else {
 518:       $install = TRUE;
 519:     }
 520: 
 521:     if ( $install == TRUE ) {
 522:       if ( is_dir( "{$composerDir}/bundles/{$this->bundle}" ) ) {
 523:         $this->rrmdir( "{$composerDir}/bundles/{$this->bundle}" );
 524:       }
 525:       if ( $this->installComposerPackage( $packages ) === TRUE ) {
 526:         $this->log->log( "Loaded Composer packages for {$this->bundle}.",
 527:           'INFO', 'console' );
 528:         require_once "{$composerDir}/bundles/{$this->bundle}/autoload.php";
 529:         return TRUE;
 530:       } else {
 531:         $this->log->log( "ERROR: failed to install packages for {$this->bundle}", 'ERROR', 'both' );
 532:         return FALSE;
 533:       }
 534:     }
 535:   }
 536: 
 537: 
 538: 
 539:    540:  541:  542:  543: 
 544:   public function icns() {
 545: 
 546:     if ( ! file_exists( $this->plist ) )
 547:       return FALSE;
 548:     if ( ( ! isset( $this->bundle ) ) || empty( $this->bundle ) )
 549:       return FALSE;
 550:     if ( ! file_exists(
 551:         realpath( dirname( "{$this->plist}" ) . "/icon.png" ) ) ) {
 552:       return FALSE;
 553:     }
 554: 
 555:     if ( file_exists( "{$this->cache}/icns/{$this->bundle}.icns" ) ) {
 556:       return "{$this->cache}/icns/{$this->bundle}.icns";
 557:     } else {
 558:       $script = realpath( "'" . __DIR__ . '/includes/png_to_icns.sh' . "'" );
 559:       $icon   = realpath( dirname( "{$this->plist}" ) . "/icon.png" );
 560:       exec( "bash '{$script}' '{$icon}' '{$this->bundle}.icns'" );
 561:       if ( file_exists( "{$this->cache}/icns/{$this->bundle}.icns" ) )
 562:         return "{$this->cache}/icns/{$this->bundle}.icns";
 563:       else
 564:         return FALSE;
 565:     }
 566:   }
 567: 
 568:    569:  570: 
 571: 
 572:    573:  574:  575:  576:  577:  578: 
 579:   public function installAsset( $json, $version = 'latest' ) {
 580:     if ( ! file_exists( $json ) ) {
 581:       $this->log->log( "Cannot install asset because the JSON file ('{$json}') is not present.", 'ERROR', 'console' );
 582:       return FALSE;
 583:     }
 584: 
 585:     $json = json_decode( file_get_contents( $json ), TRUE );
 586: 
 587:     
 588:     if ( $json == null ) {
 589:       $this->log->log( "Cannot install asset because the JSON file ('{$json}') is not valid.", 'ERROR', 'console' );
 590:       return FALSE;
 591:     }
 592: 
 593:     $installDir = "{$this->data}/data/assets/{$json[ 'type' ]}/{$json[ 'name' ]}/{$version}";
 594:     
 595:     if ( ! file_exists( $installDir ) )
 596:       mkdir( $installDir, 0775, TRUE );
 597: 
 598:     
 599:     $tmpDir = "{$this->cache}/installers";
 600:     if ( ! file_exists( $tmpDir ) )
 601:       mkdir( $tmpDir, 0775, TRUE );
 602: 
 603:     $name = $json[ 'name' ];
 604:     $type = $json[ 'type' ];
 605: 
 606:     
 607:     
 608:     if ( ! isset( $json[ 'versions' ][ $version ] ) ) {
 609:       if ( ! isset( $json[ 'versions' ][ 'latest' ] ) ) {
 610:         $this->log->log( "Cannot install {$name} because no version found and cannot fallback to 'latest'.", 'ERROR', 'both');
 611:         return FALSE;
 612:       } else {
 613:         $version = 'latest';
 614:       }
 615:     }
 616:     $invoke  = $json[ 'versions' ][ $version ][ 'invoke' ];
 617:     $install = $json[ 'versions' ][ $version ][ 'install' ];
 618: 
 619:     
 620:     foreach ( $json[ 'versions' ][ $version ][ 'files' ] as $url ) {
 621:       $file = pathinfo( parse_url( $url[ 'url' ], PHP_URL_PATH ) );
 622:       $file = $file[ 'basename' ];
 623:       if ( ! $this->download( $url[ 'url' ], "{$tmpDir}/{$file}" ) ) {
 624:         $this->log->log( "Cannot download {$name} at {$url['url']}.", 'ERROR', 'console' );
 625:         return FALSE; 
 626:       }
 627: 
 628:       
 629:       if ( $url[ 'method' ] == 'zip' ) {
 630:         
 631:         exec( "unzip -qo '{$tmpDir}/{$file}' -d '{$tmpDir}'" );
 632:       } else if ( $url[ 'method' ] == 'tgz' || $url[ 'method' ] == 'tar.gz' ) {
 633:           
 634:           exec( "tar xzf '{$tmpDir}/{$file}' -C '{$tmpDir}'" );
 635:         }
 636:     }
 637:     if ( is_array( $install ) ) {
 638:       foreach ( $install as $i ) {
 639:         
 640:         $i = str_replace( "__FILE__" , "{$tmpDir}/$file", $i );
 641:         $i = str_replace( "__CACHE__", "{$tmpDir}/",      $i );
 642:         $i = str_replace( "__DATA__" , "{$installDir}/",  $i );
 643:         exec( $i );
 644:       }
 645:     }
 646:     
 647:     file_put_contents( "{$installDir}/invoke", $invoke );
 648:     $this->log->log( "Installed '{$type}': '{$name}' -- version '{$version}'.", 'INFO', 'both' );
 649:     $this->rrmdir( "{$tmpDir}" );
 650:     return TRUE;
 651:   }
 652: 
 653:    654:  655:  656:  657:  658:  659:  660: 
 661:   private function installComposerPackage( $packages ) {
 662:     if ( ! is_array( $packages ) ) {
 663:       
 664:       $this->log->log( "An array must be passed to install Composer assets.", 'ERROR', 'console' );
 665:       return FALSE;
 666:     }
 667: 
 668:     $installDir = "{$this->cache}/{$this->bundle}/composer";
 669: 
 670:     if ( ! file_exists( $installDir ) )
 671:       mkdir( "{$installDir}", 0775, TRUE );
 672: 
 673:     $json = json_encode( array( "require" => $packages ) );
 674:     $json = str_replace( '\/', '/', $json ); 
 675:     file_put_contents( "{$installDir}/composer.json", $json );
 676: 
 677:     $cmd = "php '{$this->data}/data/assets/php/composer/composer.phar' install -q -d '{$installDir}'";
 678:     exec( $cmd );
 679:     
 680: 
 681:     $packages = json_decode( file_get_contents( "{$installDir}/vendor/composer/installed.json" ), TRUE );
 682: 
 683:     
 684:     $files = array( 'autoload_psr4.php', 'autoload_namespaces.php', 'autoload_files.php', 'autoload_classmap.php' );
 685:     $destination = "{$this->data}/data/assets/php/composer/vendor";
 686:     $installed = array();
 687: 
 688:     foreach ( $packages as $package ) :
 689: 
 690:     $name        = explode( '/', $package[ 'name' ] ); 
 691:     $vendor      = $name[0];                         
 692:     $name        = $name[1];                           
 693:     $version     = $package[ 'version' ];           
 694:     $installed[] = array( 'name' => $name,
 695:       'vendor' => $vendor,
 696:       'version' => $version );
 697: 
 698:     foreach ( $files as $file ) :
 699:       if ( file_exists( "{$installDir}/vendor/composer/{$file}" ) ) {
 700:         $f = file( "{$installDir}/vendor/composer/{$file}" );
 701:         foreach ( $f as $num => $line ) :
 702:           $line = str_replace( '$vendorDir = dirname(dirname(__FILE__));',  "\$vendorDir = '{$this->data}/data/assets/php/composer/vendor';", $line );
 703:           $line = str_replace( '$baseDir = dirname($vendorDir);', "\$baseDir = '{$this->data}/data/assets/php/composer';", $line );
 704:           $line = str_replace( 'array($vendorDir . \'/' . $vendor . '/' . $name, 'array($vendorDir . \'/' . $vendor . '/' . $name . '-' . $version, $line );
 705:           $f[ $num ] = $line;
 706:         endforeach;
 707:         file_put_contents( "{$installDir}/vendor/composer/{$file}",
 708:           implode( '', $f ) );
 709:       }
 710:     endforeach;
 711:     $this->log->log( "Rewrote Composer autoload file for workflow.", 'INFO', 'console' );
 712: 
 713:     if ( ! file_exists( "{$destination}/{$vendor}/{$name}-{$version}" ) ) {
 714:       if ( ! file_exists( "{$destination}/{$vendor}" ) )
 715:         mkdir( "{$destination}/{$vendor}", 0775, TRUE ); 
 716:       rename( "{$installDir}/vendor/{$vendor}/{$name}", "{$destination}/{$vendor}/{$name}-{$version}" );
 717:     }
 718:     endforeach;
 719:     if ( ! file_exists( "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}" ) )
 720:       mkdir( "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}", 0775, TRUE );
 721: 
 722:     if ( ! file_exists( "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}/packages.json" ) ) {
 723:       $data = str_replace( '\/', '/', json_encode( $installed ) );
 724:       file_put_contents( "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}/packages.json", $data );
 725:     }
 726: 
 727:     rename( "{$installDir}/vendor/composer", "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}/composer" );
 728:     rename( "{$installDir}/vendor/autoload.php", "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}/autoload.php" );
 729:     file_put_contents( "{$this->data}/data/assets/php/composer/bundles/{$this->bundle}/composer.json", $json );
 730: 
 731:     $this->log->log( "Successfully installed composer packages. Cleaning up....", 'INFO', 'console' );
 732: 
 733:     $this->rrmdir( $installDir );
 734: 
 735:     return TRUE;
 736:   }
 737: 
 738:    739:  740: 
 741: 
 742: 
 743: 
 744:    745:  746: 
 747: 
 748:    749:  750:  751:  752: 
 753:   public function bundle() {
 754:     return $this->bundle;
 755:   }
 756: 
 757:    758:  759:  760:  761:  762:  763: 
 764:   private function readPlist( $plist, $key ) {
 765:     if ( ! file_exists( $plist ) )
 766:       return FALSE;
 767: 
 768:     return exec( "/usr/libexec/PlistBuddy -c 'Print :{$key}' '{$plist}'" );
 769:   }
 770: 
 771:    772:  773:  774:  775:  776:  777:  778:  779:  780:  781: 
 782:   public function download( $url, $file, $timeout = '5' ) {
 783:     
 784: 
 785:     
 786:     if ( ! ( file_exists( dirname( $file ) ) && is_dir( dirname( $file ) ) ) )
 787:       return FALSE;
 788: 
 789:     $ch = curl_init( $url );
 790:     $fp = fopen( $file , "w" );
 791: 
 792:     curl_setopt_array( $ch, array(
 793:         CURLOPT_FILE => $fp,
 794:         CURLOPT_HEADER => FALSE,
 795:         CURLOPT_FOLLOWLOCATION => TRUE,
 796:         CURLOPT_CONNECTTIMEOUT => $timeout
 797:       ) );
 798: 
 799: 
 800:     
 801:     if ( curl_exec( $ch ) === FALSE ) {
 802:       curl_close( $ch );
 803:       fclose( $fp );
 804:       return curl_error( $ch ); 
 805:       
 806:       
 807:     }
 808: 
 809:     $this->log->log( "Downloading `{$url}` ...", 'INFO', 'console' );
 810: 
 811:     curl_close( $ch );
 812:     fclose( $fp );
 813:     return TRUE;
 814:   }
 815: 
 816: 
 817:    818:  819:  820:  821:  822:  823:  824:  825:  826:  827:  828: 
 829:   public function log( $message, $level = 'INFO', $destination = '' ) {
 830:     $this->userLog->log( $message, $level, $destination, 3 );
 831:   }
 832: 
 833:    834:  835:  836:  837:  838:  839:  840:  841:  842:  843:  844: 
 845:   public function debug( $message, $destination = '' ) {
 846:     $this->userLog->log( $message, 'DEBUG', $destination, 3 );
 847:   }
 848: 
 849:    850:  851:  852:  853:  854:  855:  856:  857:  858:  859:  860: 
 861:   public function info( $message, $destination = '' ) {
 862:     $this->userLog->log( $message, 'INFO', $destination, 3 );
 863:   }
 864: 
 865:    866:  867:  868:  869:  870:  871:  872:  873:  874:  875:  876: 
 877:   public function warning( $message, $destination = '' ) {
 878:     $this->userLog->log( $message, 'WARNING', $destination, 3 );
 879:   }
 880: 
 881:    882:  883:  884:  885:  886:  887:  888:  889:  890:  891:  892: 
 893:   public function error( $message, $destination = '' ) {
 894:     $this->userLog->log( $message, 'ERROR', $destination, 3 );
 895:   }
 896: 
 897:    898:  899:  900:  901:  902:  903:  904:  905:  906:  907:  908: 
 909:   public function critical( $message, $destination = '' ) {
 910:     $this->userLog->log( $message, 'CRITICAL', $destination, 3 );
 911:   }
 912: 
 913:    914:  915:  916:  917:  918:  919:  920:  921:  922: 
 923:   public function console( $message, $level = 'INFO' ) {
 924:     $this->userLog->log( $message, $level, 'console', 3 );
 925:   }
 926: 
 927:    928:  929:  930:  931:  932:  933:  934:  935:  936: 
 937:   public function rrmdir( $path ) {
 938:     
 939:     $files = array_diff( scandir( $path ), array( '.', '..' ) );
 940:     foreach ( $files as $file ) {
 941:       if ( is_dir( "{$path}/{$file}" ) )
 942:         $this->rrmdir( "{$path}/{$file}" );
 943:       else if ( file_exists( "{$path}/{$file}" ) )
 944:         unlink( "{$path}/{$file}" );
 945:       else {
 946:         echo "Removing dir error... uh..." . PHP_EOL; 
 947:       }
 948:     }
 949:     return rmdir( $path );
 950:   }
 951: 
 952:    953:  954:  955:  956:  957:  958:  959:  960:  961:  962:  963:  964:  965:  966:  967: 
 968:   public function gatekeeper( $name, $path, $message = '', $icon = '' ) {
 969: 
 970:     $assetCache = "{$this->cache}/utilities";
 971: 
 972:     
 973:     if ( ! ( ( file_exists( $assetCache ) && is_dir( $assetCache ) ) ) )
 974:       mkdir( $assetCache, 0775, TRUE );
 975: 
 976:     
 977:     $key       = md5( "{$name}-{$version}-{$type}-{$json}" );
 978:     $cachePath =      "{$assetCache}/{$key}";
 979: 
 980:     if ( file_exists( "$cachePath" ) ) {
 981:       $path = file_get_contents( $cachePath );
 982:       if ( file_exists( $path ) ) {
 983:         
 984:         return $path;
 985:       }
 986:     }
 987: 
 988:     
 989: 
 990:     
 991:     $gatekeeper = realpath( __DIR__ ) . '/includes/gatekeeper.sh';
 992:     
 993:     exec( "bash '{$gatekeeper}' '{$name}' '{$path}' '{$message}' '{$icon}' '{$this->bundle}'", $output, $status );
 994: 
 995:     
 996:     
 997:     if ( $status == 0 ) {
 998:       file_put_contents( $cachePath, $path );
 999:       return $path;
1000:     }
1001: 
1002:     
1003:     
1004: 
1005:     
1006:     $this->log->log( "Bundler Error: '{$name}' is needed to properly run " .
1007:       "this workflow, and it must be whitelisted for Gatekeeper. You " .
1008:       "either denied the request, or another error occured with " .
1009:       " the Gatekeeper script.", 'ERROR', 'both' );
1010: 
1011:     
1012:     return FALSE;
1013:   }
1014: 
1015:   1016: 1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 
1027:   public function register( $asset, $version ) {
1028: 
1029:     
1030:     if ( ( ! isset( $this->bundle ) ) || empty( $this->bundle ) )
1031:       return FALSE;
1032: 
1033:     
1034:     if ( ! file_exists( "{$this->data}/data/registry.json" ) ) {
1035:       $registry = array();
1036:     } else {
1037:       if ( ! ( json_decode( file_get_contents( "{$this->data}/data/registry.json" ) ) ) ) {
1038:         
1039:         $registry = array();
1040:       } else {
1041:         $registry = json_decode( file_get_contents( "{$this->data}/data/registry.json" ), TRUE );
1042:       }
1043:     }
1044: 
1045:     if ( isset( $registry[ $asset ] ) ) {
1046:       if ( ! array_key_exists( $version , $registry[ $asset ] ) ) {
1047:         $registry[ $asset ][ $version ] = array();
1048:         $update = TRUE;
1049:       }
1050:       if ( ! is_array( $registry[ $asset ][ $version] ) ) {
1051:         $registry[ $asset ][ $version ] = array();
1052:       }
1053:       if ( ! in_array( $this->bundle , $registry[ $asset ][ $version ] ) ) {
1054:         $registry[ $asset ][ $version ][] = $this->bundle;
1055:         $update = TRUE;
1056:       }
1057:     } else {
1058:       $registry[ $asset ] = array( $version => $this->bundle );
1059:       $update = TRUE;
1060:     }
1061: 
1062:     if ( $update ) file_put_contents( "{$this->data}/data/registry.json" , utf8_encode( json_encode( $registry ) ) );
1063: 
1064:     return TRUE;
1065:   }
1066: 
1067:   1068: 1069: 
1070: 
1071:   1072: 1073: 
1074: 
1075:   1076: 1077: 1078: 1079: 1080: 1081: 1082: 
1083:   public function notify( $title, $message, $icon = null ) {
1084:     if ( gettype( $title ) !== 'string' || gettype( $message ) !== 'string' )
1085:       return false;
1086: 
1087:     $client = $this->wrapper( 'CocoaDialog' );
1088:     $icon_type = 'icon';
1089:     if ( ( ! is_null( $icon ) ) && ( gettype( $icon ) === 'string' ) ) {
1090:       if ( ! file_exists( $icon ) ) {
1091:         if ( ! in_array( $icon, $client->global_icons ) ) {
1092:           $icon_type = null;
1093:         }
1094:       } else {
1095:         $icon_type = 'icon_file';
1096:       }
1097:     } else {
1098:       $icon_type = null;
1099:     }
1100:     $notification = [
1101:       'title'=>$title,
1102:       'description'=>$message,
1103:       'alpha'=>1,
1104:       'background_top'=>'ffffff',
1105:       'background_bottom'=>'ffffff',
1106:       'border_color'=>'ffffff',
1107:       'text_color'=>'000000',
1108:       'no_growl'=>true,
1109:     ];
1110:     if ( ! is_null( $icon_type ) ) {
1111:       $notification[$icon_type] = $icon;
1112:     }
1113:     $client->notify( $notification );
1114:     return true;
1115: 
1116:    }
1117: 
1118:   1119: 1120: 
1121: 
1122: }
1123: endif;
1124: 
1125: