diff --git a/README.md b/README.md index 3c6799a..ea622a5 100755 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ Shows information about applications on the client. Data can be viewed under the Applications tab on the client details page or using the Applications listing view -Database: + +Table Schema +------ * name - varchar(255) - name of the application * path - TEXT - application's path * last_modified - BIGINT - date application was last modified (epoch) @@ -15,3 +17,4 @@ Database: * info - TEXT - info about the application * has64bit - int - 0/1 does application contain 64-bit code * signed_by - varchar(255) - code signing of application +* bundle_version - varchar(255) - the application's bundle version from the CFBundleVersion key \ No newline at end of file diff --git a/applications_controller.php b/applications_controller.php old mode 100644 new mode 100755 index 3a37e84..0100aa0 --- a/applications_controller.php +++ b/applications_controller.php @@ -16,55 +16,46 @@ function __construct() $this->module_path = dirname(__FILE__); } - /** + /** * Default method * @author tuxudo * **/ - function index() + function index() { echo "You've loaded the applications module!"; } - /** - * Retrieve data in json format + /** + * Retrieve data in json format for widget * **/ - public function get_data($serial_number = '') + public function get_32_bit_apps() { - $obj = new View(); + $sql = "SELECT COUNT(CASE WHEN name <> '' AND has64bit = 0 THEN 1 END) AS count, name + FROM applications + LEFT JOIN reportdata USING (serial_number) + WHERE has64bit = 0 + ".get_machine_group_filter('AND')." + GROUP BY name + ORDER BY count DESC"; - if (! $this->authorized()) { - $obj->view('json', array('msg' => 'Not authorized')); - } - $queryobj = new Applications_model(); - - $sql = "SELECT name, path, lastModified, obtained_from, runtime_environment, version, info, signed_by, has64BitIntelCode - FROM applications - WHERE serial_number = '$serial_number'"; - - $applications_tab = $queryobj->query($sql); - - $applications = new Applications_model; - $obj->view('json', array('msg' => current(array('msg' => $applications_tab)))); - } + jsonView($queryobj->query($sql)); + } - /** - * Retrieve data in json format for widget + /** + * Retrieve data in json format * **/ - public function get_32_bit_apps() - { - $obj = new View(); + public function get_data($serial_number = '') + { + $sql = "SELECT name, path, last_modified, obtained_from, runtime_environment, version, bundle_version, info, signed_by, has64bit + FROM applications + WHERE serial_number = '$serial_number'"; - if (! $this->authorized()) { - $obj->view('json', array('msg' => array('error' => 'Not authenticated'))); - return; - } - - $apps_32 = new Applications_model; - $obj->view('json', array('msg' => $apps_32->get_32_bit_apps())); - } + $queryobj = new Applications_model(); + jsonView($queryobj->query($sql)); + } -} // END class Applications_controller +} // End class Applications_controller diff --git a/applications_model.php b/applications_model.php old mode 100644 new mode 100755 index 3e6fc27..e7ba1d5 --- a/applications_model.php +++ b/applications_model.php @@ -4,117 +4,100 @@ class Applications_model extends \Model { - function __construct($serial='') - { - parent::__construct('id', 'applications'); //primary key, tablename - $this->rs['id'] = ''; - $this->rs['serial_number'] = $serial; - $this->rs['name'] = ''; - $this->rs['path'] = ''; - $this->rs['last_modified'] = 0; - $this->rs['obtained_from'] = ''; - $this->rs['runtime_environment'] = ''; - $this->rs['version'] = ''; - $this->rs['info'] = ''; - $this->rs['signed_by'] = ''; - $this->rs['has64bit'] = 0; // True or False + function __construct($serial='') + { + parent::__construct('id', 'applications'); //primary key, tablename + $this->rs['id'] = ''; + $this->rs['serial_number'] = $serial; + $this->rs['name'] = ''; + $this->rs['path'] = ''; + $this->rs['last_modified'] = 0; + $this->rs['obtained_from'] = ''; + $this->rs['runtime_environment'] = ''; + $this->rs['version'] = ''; + $this->rs['info'] = ''; + $this->rs['signed_by'] = ''; + $this->rs['has64bit'] = 0; // True or False + $this->rs['bundle_version'] = ''; - $this->serial_number = $serial; - } + $this->serial_number = $serial; + } - // ------------------------------------------------------------------------ - - /** - * Retrieve data in json format for widget - * - **/ - public function get_32_bit_apps() - { - $out = array(); - $sql = "SELECT COUNT(CASE WHEN name <> '' AND has64bit = 0 THEN 1 END) AS count, name - FROM applications - LEFT JOIN reportdata USING (serial_number) - WHERE has64bit = 0 - ".get_machine_group_filter('AND')." - GROUP BY name - ORDER BY count DESC"; - - $out = $this->query($sql); - return $out; - } - - /** - * Process data sent by postflight - * - * @param string data - * @author tuxudo - **/ - function process($plist) - { - - if ( ! $plist){ - throw new Exception("Error Processing Request: No property list found", 1); - } - - // Delete previous set - $this->deleteWhere('serial_number=?', $this->serial_number); + // ------------------------------------------------------------------------ + + /** + * Process data sent by postflight + * + * @param string data + * @author tuxudo + **/ + function process($plist) + { + // Check if we have data + if ( ! $plist){ + throw new Exception("Error Processing Request: No property list found", 1); + } + + // Delete previous set + $this->deleteWhere('serial_number=?', $this->serial_number); + + $parser = new CFPropertyList(); + $parser->parse($plist, CFPropertyList::FORMAT_XML); + $myList = $parser->toArray(); + + $typeList = array( + 'name' => '', + 'last_modified' => '', + 'obtained_from' => 'unknown', + 'path' => '', + 'runtime_environment' => '', + 'version' => '', + 'info' => '', + 'signed_by' => '', + 'has64bit' => 0, // Yes or No + 'bundle_version' => '' // Yes or No + ); - $parser = new CFPropertyList(); - $parser->parse($plist, CFPropertyList::FORMAT_XML); - $myList = $parser->toArray(); - - $typeList = array( - 'name' => '', - 'last_modified' => '', - 'obtained_from' => 'unknown', - 'path' => '', - 'runtime_environment' => '', - 'version' => '', - 'info' => '', - 'signed_by' => '', - 'has64bit' => 0 // Yes or No - ); - // List of paths to ignore $bundlepath_ignorelist = is_array(conf('bundlepath_ignorelist')) ? conf('bundlepath_ignorelist') : array(); $path_regex = ':^'.implode('|', $bundlepath_ignorelist).'$:'; - - - foreach ($myList as $app) { - // Check if we have a name - if( ! array_key_exists("name", $app)){ - continue; - } - + + // Process each app + foreach ($myList as $app) { + // Check if we have a name + if( ! array_key_exists("name", $app)){ + continue; + } + // Skip path - if (preg_match($path_regex, $app['path'])) { - continue; - } - + if (preg_match($path_regex, $app['path'])) { + continue; + } + // Fix signed_by entries if (array_key_exists("signed_by",$app)) { $app['signed_by'] = str_replace(array('Developer ID Application: '), array(''), $app['signed_by']); } - + // Fix last_modified date if (array_key_exists("last_modified",$app)) { $temptime = $app['last_modified']; $date = new DateTime("@$temptime"); $app['last_modified'] = $date->format('U'); } - + // Process each app for saving - foreach ($typeList as $key => $value) { - $this->rs[$key] = $value; - if(array_key_exists($key, $app)) - { - $this->rs[$key] = $app[$key]; - } - } - - // Save application - $this->id = ''; - $this->save(); - } - } -} + foreach ($typeList as $key => $value) { + $this->rs[$key] = $value; + if(array_key_exists($key, $app)) + { + $this->rs[$key] = $app[$key]; + } + } + + // Save application + $this->id = ''; + $this->save(); + } + } +} \ No newline at end of file diff --git a/locales/en.json b/locales/en.json old mode 100644 new mode 100755 index 004f7c8..bfc86ec --- a/locales/en.json +++ b/locales/en.json @@ -9,5 +9,6 @@ "identified_developer": "Identified Developer", "applications": "Applications", "no_32_bit": "No 32-bit Applications", - "32_bit_apps": "32-bit Applications" + "32_bit_apps": "32-bit Applications", + "bundle_version": "Bundle Version" } diff --git a/migrations/2021_02_26_000001_applications_add_bundle_version.php b/migrations/2021_02_26_000001_applications_add_bundle_version.php new file mode 100755 index 0000000..886d710 --- /dev/null +++ b/migrations/2021_02_26_000001_applications_add_bundle_version.php @@ -0,0 +1,27 @@ +table($this->tableName, function (Blueprint $table) { + $table->string('bundle_version')->nullable(); + + $table->index('bundle_version'); + }); + } + + public function down() + { + $capsule = new Capsule(); + $capsule::schema()->table($this->tableName, function (Blueprint $table) { + $table->dropColumn('bundle_version'); + }); + } +} diff --git a/scripts/applications.py b/scripts/applications.py index dd7a266..a73e407 100755 --- a/scripts/applications.py +++ b/scripts/applications.py @@ -7,6 +7,20 @@ import plistlib import sys +sys.path.insert(0, '/usr/local/munki') +sys.path.insert(0, '/usr/local/munkireport') + +from munkilib import FoundationPlist + +def get_app_bundle_version(app_path): + '''Return the CFBundleVersion of the app based on its path''' + + try: + info_plist = FoundationPlist.readPlist(app_path+"/Contents/Info.plist") + return info_plist['CFBundleVersion'] + + except Exception: + return "" def get_applications_info(): '''Uses system profiler to get applications for this machine.''' @@ -41,6 +55,7 @@ def flatten_applications_info(array): device['obtained_from'] = obj[item] elif item == 'path': device['path'] = obj[item] + device['bundle_version'] = get_app_bundle_version(obj[item]) elif item == 'runtime_environment': device['runtime_environment'] = obj[item] elif item == 'version': @@ -60,17 +75,6 @@ def flatten_applications_info(array): def main(): """Main""" - # Create cache dir if it does not exist - cachedir = '%s/cache' % os.path.dirname(os.path.realpath(__file__)) - if not os.path.exists(cachedir): - os.makedirs(cachedir) - - # Skip manual check - if len(sys.argv) > 1: - if sys.argv[1] == 'manualcheck': - print 'Manual check: skipping' - exit(0) - # Set the encoding reload(sys) sys.setdefaultencoding('utf8') @@ -79,8 +83,9 @@ def main(): result = dict() info = get_applications_info() result = flatten_applications_info(info) - + # Write applications results to cache + cachedir = '%s/cache' % os.path.dirname(os.path.realpath(__file__)) output_plist = os.path.join(cachedir, 'applications.plist') plistlib.writePlist(result, output_plist) #print plistlib.writePlistToString(result) diff --git a/views/applications_32_bit_apps_widget.php b/views/applications_32_bit_apps_widget.php deleted file mode 100644 index ddafa6c..0000000 --- a/views/applications_32_bit_apps_widget.php +++ /dev/null @@ -1,33 +0,0 @@ -
-
- | - | - | - | - | - | - | - | - |
---|