using Posix; static bool version; static bool list_only; [CCode (array_length = false, array_null_terminated = true)] static string[] files; static string? directory = null; private const OptionEntry[] options = { { "version", 0, 0, OptionArg.NONE, ref version, N_("Display version number"), null }, { "directory", 'C', 0, OptionArg.FILENAME, ref directory, N_("Extract to directory"), null }, { "list", 'l', 0, OptionArg.NONE, ref list_only, N_("List files only"), null }, { "", 0, 0, OptionArg.FILENAME_ARRAY, ref files, null, N_("MSI_FILE...") }, { null } }; public string get_long_name (string str) { var names = str.split ("|", 2); if (names.length == 2) return names[1]; return str; } public void extract_cab (Libmsi.Database db, string cab, HashTable cab_to_name) throws GLib.Error { if (cab.has_prefix ("#")) { var name = cab.substring (1); var query = new Libmsi.Query (db, "SELECT `Data` FROM `_Streams` WHERE `Name` = '%s'".printf (name)); query.execute (); var rec = query.fetch (); var cabinet = new GCab.Cabinet (); cabinet.load (rec.get_stream (1)); var path = File.new_for_path (directory); cabinet.extract_simple (path, (current) => { var extname = cab_to_name.lookup (current.get_name ()); if (extname == null) { extname = current.get_name (); warning ("couldn't lookup MSI name, fallback on cab name %s", extname); } current.set_extract_name (extname); GLib.stdout.printf ("%s\n", extname); return true; }, null); } } public string? get_directory_name (Libmsi.Record rec) throws GLib.Error { var name = get_long_name (rec.get_string (3)); // only by intuition... if (rec.get_string (1) == "ProgramFilesFolder") return "Program Files"; else if (name == ".") return ""; else if (name == "SourceDir") return null; return name; } public void extract (string filename) throws GLib.Error { Libmsi.Record? rec; var db = new Libmsi.Database (filename, Libmsi.DbFlags.READONLY, null); var directories = new HashTable (str_hash, str_equal); var query = new Libmsi.Query (db, "SELECT * FROM `Directory`"); query.execute (); while ((rec = query.fetch ()) != null) directories.insert (rec.get_string (1), rec); var components_dir = new HashTable (str_hash, str_equal); query = new Libmsi.Query (db, "SELECT * FROM `Component`"); query.execute (); while ((rec = query.fetch ()) != null) { var dir_id = rec.get_string (3); var dir_rec = directories.lookup (dir_id); var dir = get_directory_name (dir_rec); do { var parent = dir_rec.get_string (2); dir_rec = directories.lookup (parent); if (dir_rec == null) break; parent = get_directory_name (dir_rec); if (parent == null) break; if (parent == "") continue; dir = Path.build_filename (parent, dir); } while (true); components_dir.insert (rec.get_string (1), dir); } var cab_to_name = new HashTable (str_hash, str_equal); query = new Libmsi.Query (db, "SELECT * FROM `File`"); query.execute (); while ((rec = query.fetch ()) != null) { var dir = components_dir.lookup (rec.get_string (2)); var file = Path.build_filename (dir, get_long_name (rec.get_string (3))); if (list_only) GLib.stdout.printf ("%s\n", file); cab_to_name.insert (rec.get_string (1), file); } if (list_only) exit (0); query = new Libmsi.Query (db, "SELECT * FROM `Media`"); query.execute (); while ((rec = query.fetch ()) != null) { var cab = rec.get_string (4); extract_cab (db, cab, cab_to_name); } } public int main (string[] args) { Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR); Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); Intl.textdomain (Config.GETTEXT_PACKAGE); GLib.Environment.set_application_name ("msiextract"); var parameter_string = _("- a msi files extracting tool"); var opt_context = new OptionContext (parameter_string); opt_context.set_help_enabled (true); opt_context.add_main_entries (options, null); try { opt_context.parse (ref args); } catch (OptionError.BAD_VALUE err) { GLib.stdout.printf (opt_context.get_help (true, null)); exit (1); } catch (OptionError error) { warning (error.message); } if (version) { GLib.stdout.printf ("%s\n", Config.PACKAGE_VERSION); exit (0); } if (files.length < 1) { GLib.stderr.printf (_("Please specify input files.\n")); exit (1); } if (directory == null) directory = Environment.get_current_dir (); try { foreach (var file in files) extract (file); } catch (GLib.Error error) { GLib.stderr.printf (error.message); exit (1); } return 0; }