diff --git a/MainMenu.nib/designable.nib b/MainMenu.nib/designable.nib index 578d787..2f1894d 100644 --- a/MainMenu.nib/designable.nib +++ b/MainMenu.nib/designable.nib @@ -52,21 +52,20 @@ - - + - + - + - + - + @@ -75,7 +74,7 @@ - + - + - + - - - - + + + + + - - + - - + - + - + - + - + @@ -489,11 +639,11 @@ Gw - + - + @@ -507,28 +657,36 @@ Gw - + - - + - + + - + - + - + - + @@ -537,7 +695,7 @@ Gw - + @@ -548,21 +706,12 @@ Gw - - + diff --git a/MainMenu.nib/keyedobjects.nib b/MainMenu.nib/keyedobjects.nib index 7e12bed..de94f15 100644 Binary files a/MainMenu.nib/keyedobjects.nib and b/MainMenu.nib/keyedobjects.nib differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f4cc77 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ + + +# Yuba + +**Yuba** generates a web-browsable SQLite database from an HFS+ filesystem. Its client application gathers forensic-quality data about a locally attached disk, properly interpreting bundles, reading Spotlight data, Finder flags, labels, and other contextual information. It can generate hashes, thumbnails, and gather 3rd party metadata with exiftool and mediainfo. Yuba's filesystem catalogues are comprehensive, lightweight, optimized for massive (1 million+) trees, and reflect incremental changes to contents and metadata. A server-side PHP script is provided, which allows familiar, Finder-style browsing of a catalogue. + +* **⇩ [Download Yuba 0.7.11](http://www.profiteroles.org/downloads/Yuba_0.7.11.zip)** + +## Features + +* Recursive directory scanning +* Properly interprets bundles +* Uses native macOS methods +* Spotlight metadata and icons +* Caches assets for re-scans +* Optional file hashes +* Tested with large filesystems + +## Screenshots + + + + + +## Notes + +Running Yuba requires disabling Gatekeeper by running `sudo spctl --master-disable` in the Terminal. + +## Building from source + +Building Minat requires Platypus and Pashua + +* https://github.com/sveinbjornt/Platypus +* https://github.com/BlueM/Pashua \ No newline at end of file diff --git a/Yuba.php b/Yuba.php index 7166e0b..9d3b378 100755 --- a/Yuba.php +++ b/Yuba.php @@ -3,7 +3,7 @@ // Yuba // // ////////////////////////////////////////// -$version = "0.7.10.3"; +$version = "0.7.11.5"; ini_set('memory_limit', '10240M'); date_default_timezone_set("America/Los_Angeles"); @@ -11,23 +11,22 @@ date_default_timezone_set("America/Los_Angeles"); // Includes & Prefs ////////////////////////////////////////// +$p = unserialize(file_get_contents("prefs.php")); + require("functions.php"); require("filetypes.php"); -$wopt_noprofile = 0; $wopt_steps = 10; $wopt_currstep = 1; $parser = new plistParser(); -$p = unserialize(file_get_contents("prefs.php")); - // Path & application variables ////////////////////////////////////////// $stamp = date("Y-m-d_H-i-s", time()); -if (!isset($argv[1])) { echo "Input error"; die; } +if (!isset($argv[1])) { echo "No input"; die; } $zpath = realpath(@$argv[1]); if (@$argv[2]) { $bdest = realpath($argv[2]); } else { $bdest = realpath($p['bdest']); } if (!is_dir($zpath) | !is_dir($bdest)) { echo "Filepath error"; die; } @@ -41,38 +40,6 @@ if (!is_dir($bpath)) { mkdir($bpath); } if (!is_dir($bpath."/thumbs")) { mkdir($bpath."/thumbs"); } if (!is_dir($bpath."/contents")) { mkdir($bpath."/contents"); } -// Treat these directories as files -$p['bundles'] = array( "app", - "bundle", - "sparsebundle", - "photoslibrary", - "aplibrary", - "apvault", - "abbu", - "calendar", - "framework", - "plugin", - "kext", - "rtfd" - ); - -foreach ($p['bundles'] as $bundle) { - $p['nodescend'][] = "*.".$bundle; - } - -// Ignore matching files and directories -$p['ignore'] = array( ".DS_Store", - ".Trashes", - ".DocumentRevisions-V100", - ".Spotlight-V100", - ".TemporaryItems", - ".apdisk", - ".com.apple.timemachine.donotpresent", - ".fseventsd", - ".metadata-never-index", - ".neofinder.abemeda.volinfo.xml" - ); - // Metadata tools $bin_gfi = __DIR__."/bin/GetFileInfo"; $bin_mediainfo = __DIR__."/bin/mediainfo"; @@ -187,11 +154,11 @@ if ($type == "Disk image") { $image_file = false; } -if ($wopt_noprofile) { - $profile = "disabled"; - } else { +if ($p['profile']) { echo msg("system_profiler"); $profile = shell_exec("system_profiler SPHardwareDataType SPStorageDataType SPThunderboltDataType SPUSBDataType 2>&1"); + } else { + $profile = "disabled"; } $qlmanage = shell_exec("qlmanage -m 2>&1"); $sysvers = shell_exec("sw_vers 2>&1"); @@ -460,10 +427,7 @@ echo ProgressBar::finish(); // Thow permissions error if (count($noread)) { - echo msg("Current user (".posix_getuid().") does not have read access to the following files:"); - foreach ($noread as $file) { - echo $file."\n"; - } + echo msg("Current user (".posix_getuid().") does not have read access to the following files:\n").implode("\n",$noread); if ($p['readability']) { echo msg("Exiting..."); die; @@ -546,7 +510,7 @@ if ($p['thumbs']) { $tfile = $tpath."/".$fid.".jpg"; // HACK for ql-thumbnail bug - $t_skip = array("emlx"); + $t_skip = array("emlx","flac"); if (count($t_skip) && in_array($ext, $t_skip)) { echo ProgressBar::next("Skipping ".shortlabel($pathname)); continue; @@ -571,13 +535,13 @@ if ($p['thumbs']) { // first try to make a thumb with external tools $cmd = null; - if (in_array($ext, $t_files['sips'])) { + if (in_array($ext, $p['t_files']['sips'])) { //$cmd = $bin_tv." ".$shellpath." -o ".$tfile."[Q=90,optimize_coding] --size=".$p['thumb_size']; $cmd = "sips -s format jpeg -s formatOptions 80 --resampleHeightWidthMax ".$p['thumb_size']." ".$shellpath." --out ".$tfile; $stmt->BindValue(":tool","sips"); - } elseif (in_array($ext, $t_files['ffmpeg'])) { + } elseif (in_array($ext, $p['t_files']['ffmpeg'])) { //$cmd = $bin_tf." -i ".$shellpath." -o ".$tfile." -s ".$p['thumb_size']." -c jpg -q 8.5"; - $cmd = $bin_ffmpeg." -ss $(( $(".$bin_mediainfo." --Inform='Video;%Duration%' ".$shellpath.") / 10000 )) -i ".$shellpath." -vframes 1 -filter:v scale='400:-1' -q:v 3 ".$tfile; + $cmd = $bin_ffmpeg." -ss $(( $(".$bin_mediainfo." --Inform='Video;%Duration%' ".$shellpath." | cut -d'.' -f1) / 10000 )) -i ".$shellpath." -vframes 1 -filter:v scale='400:-1' -q:v 3 ".$tfile; $stmt->BindValue(":tool","ffmpeg"); } @@ -592,7 +556,8 @@ if ($p['thumbs']) { } // success, move thumb into the bundle - if (file_exists($tfile) && @filesize($tfile)) { + // ignore generic music icon thumbs (7133) + if (file_exists($tfile) && @filesize($tfile) && @filesize($tfile) != 7133) { $stmt->BindValue(":relative_path",substr($tfile, strlen($bpath))); list($width, $height) = getimagesize($tfile); $stmt->BindValue(":width",$width); @@ -622,7 +587,7 @@ if ($p['contents']) { $ext = pathinfo($pathname,PATHINFO_EXTENSION); $cpath = $bpath."/contents/".substr($fid, 0, 2); $cfile = $cpath."/".$fid.".zip"; - if (in_array($ext, $c_files)) { + if (in_array($ext, $p['c_files'])) { if (!is_dir($cpath)) { mkdir($cpath); } if (!file_exists($cfile) && filesize($pathname) < 25000) { @@ -663,12 +628,12 @@ if ($p['meta']) { $ext = pathinfo($pathname,PATHINFO_EXTENSION); $found = 1; - if (!in_array($ext, $e_files) && !in_array($ext, $m_files)) { + if (!in_array($ext, $p['e_files']) && !in_array($ext, $p['m_files'])) { echo ProgressBar::next("Not a media file: ".shortlabel($pathname)); continue; } - if (in_array($ext, $e_files)) { + if (in_array($ext, $p['e_files'])) { $check = $dbp->query("SELECT EXISTS(SELECT 1 FROM exiftool WHERE fid='".$fid."')")->fetch()[0]; if (!$check) { $arrstring = shell_exec($bin_exiftool." -php ".$shellpath); @@ -685,13 +650,14 @@ if ($p['meta']) { } } - if (in_array($ext, $m_files)) { + if (in_array($ext, $p['m_files'])) { $check = $dbp->query("SELECT EXISTS(SELECT 1 FROM mediainfo WHERE fid='".$fid."')")->fetch()[0]; if (!$check) { $stmt = $dbp->prepare("INSERT INTO mediainfo VALUES (:fid, :info)"); $stmt->BindValue(":fid",$fid); //$stmt->BindValue(":info",serialize(parseMediaInfo(shell_exec($bin_mediainfo." --Output=OLDXML ".$shellpath." 2>&1")))); - $stmt->BindValue(":info",shell_exec($bin_mediainfo." --Output=OLDXML ".$shellpath." 2>&1")); + //$stmt->BindValue(":info",shell_exec($bin_mediainfo." --Output=OLDXML ".$shellpath." 2>&1")); + $stmt->BindValue(":info",shell_exec($bin_mediainfo." --Output=JSON ".$shellpath." 2>&1")); $stmt->execute(); $found = 0; } @@ -1005,8 +971,10 @@ foreach ($files as $splFileInfo) { $yes_contents = $dbp->query("SELECT relative_path FROM contents WHERE fid='".$fid."'")->fetch()[0]; $stmt->BindValue(":contents_filename",$yes_contents); - - $fetch_thumb = $dbp->query("SELECT * FROM thumbs WHERE fid='".$fid."'")->fetch(); + + if (!in_array($extension, $t_skip)) { + $fetch_thumb = $dbp->query("SELECT * FROM thumbs WHERE fid='".$fid."'")->fetch(); + } if (@$fetch_thumb['relative_path']) { $stmt->BindValue(":thumb_filename",$fetch_thumb['relative_path']); $stmt->BindValue(":thumb_width",$fetch_thumb['width']); @@ -1137,14 +1105,18 @@ while ($row_a = $loop->fetch()) { if (isset($row_a['has_mediainfo'])) { $row_d = $dbp->query("SELECT * FROM mediainfo WHERE (rowid='".$row_a['has_mediainfo']."')")->fetch(); - $decoded = @json_decode(json_encode(simplexml_load_string($row_d['info'])),true); - $m['m'] = $decoded['File']['track'][0]; + if (substr($row_d['info'],0,5) == " \ No newline at end of file diff --git a/functions.php b/functions.php index 36e9afe..a6276dd 100755 --- a/functions.php +++ b/functions.php @@ -59,7 +59,7 @@ class ProgressBar { global $wopt_currstep; $wopt_currstep++; self::$done = 0; - return "\n"; + return "\nREFRESH\n"; } } @@ -67,17 +67,6 @@ class ProgressBar { // Functions ////////////////////////////////////////// -/* -function getParents($zpath, $pathname) { - $path = dirname($pathname); - $parts = explode("/",trim(substr($path,strlen(basename($zpath))),"/")); - foreach ($parts as $index => $part) { - $parents[] = array($part, md5($zpath."/".implode("/",array_slice($parts, 0, $index+1)))); - } - return $parents; - } -*/ - function msg($string) { global $messages_log_file; $logstring = "[".date('Y-m-d h:i:s')."] ".$string."\n"; @@ -185,7 +174,7 @@ class plistParser extends XMLReader { case 'false': return false; break; case 'array': return $this->parse_array(); break; case 'dict': return $this->parse_dict(); break; - // why the fuck cant this plist parser handle the plist generated from an hdiutil list with no dimgs attached? + // why the can't this plist parser handle the plist generated from an hdiutil list with no dimgs attached? //default: throw new Exception(sprintf("Not a valid plist. %s is not a valid type", $this->name), 4); } } diff --git a/version.txt b/version.txt new file mode 100755 index 0000000..66fc364 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.7.11.5 \ No newline at end of file diff --git a/web/rtc.php b/web/rtc.php index 988b31b..a4f4974 100644 --- a/web/rtc.php +++ b/web/rtc.php @@ -4,14 +4,18 @@ // Yuba RTC Browser ///////////////////////////////////////////////////////////////// -$browser_version = "0.7.10.3"; +$browser_version = "0.7.10.5"; require "togggle.php"; -require "lib/debug.php"; +require "lib/ref/ref.php"; + +ref::config('expLvl', 1); +ref::config('validHtml', TRUE); $db_dir = "data/skim"; $icon_size = 96; $pad = 40; +$overlay_exts = array("txt","php","inc","sh","md","json","cmd"); ?> @@ -22,6 +26,9 @@ $pad = 40; html { font-family: Helvetica; word-wrap: break-word; } +table.file td { width: 33%; } +table.dir td:last-of-type { width: 22%; } + div#exectime { position: absolute; right: 8px; top: 8px; } div.container { display: flex; flex-flow: row wrap; justify-content: center; } @@ -61,7 +68,11 @@ div.size { color: grey; margin-top: 3px; } img#thumb { padding: 6px; border: 1px solid gainsboro; } img#item { float: left; margin-right: 10px; width: 32px; height: 32px; } img { margin-bottom: 8px; } - +img#thumb.overlay { border: 0px !important; + padding: 6px !important; + -webkit-mask-image: url('/icons/mask.png'); + -webkit-mask-size: 100%; + outline: 1px solid black; } div.fileinfo { font-size: 12px; } div.fileinfo span.title { display: table-cell; font-weight: bold; width: 200px; } div.fileinfo span.value { display: table-cell; } @@ -76,10 +87,12 @@ div.dblist { display: none; } + +