Skip to content

Commit ceb4e1d

Browse files
committed
Start a msiextract tool
1 parent e5ceb0c commit ceb4e1d

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

Makefile.am

+23
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,29 @@ msiinfo_SOURCES = tools/msiinfo.c
2929
msiinfo_LDADD = -lmsi $(GLIB_LIBS) $(GSF_LIBS)
3030
msiinfo_DEPENDENCIES = libmsi/libmsi.la
3131

32+
# msiextract, something on the TODO list...
33+
34+
noinst_PROGRAMS = msiextract
35+
36+
msiextract_CFLAGS = -w
37+
38+
msiextract_VALAFLAGS = \
39+
--enable-experimental \
40+
--pkg config \
41+
--pkg gio-unix-2.0 \
42+
--pkg libgcab-1.0 \
43+
--pkg libmsi-1.0 \
44+
--pkg posix \
45+
--vapidir=. \
46+
$(NULL)
47+
48+
msiextract_SOURCES = \
49+
tools/msiextract.vala \
50+
$(NULL)
51+
52+
msiextract_CPPFLAGS = $(wixl_CPPFLAGS)
53+
msiextract_LDADD = $(wixl_LDADD)
54+
3255
# Wixl
3356

3457
bin_PROGRAMS += wixl

tools/msiextract.vala

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
using Posix;
2+
3+
static bool version;
4+
static bool list_only;
5+
[CCode (array_length = false, array_null_terminated = true)]
6+
static string[] files;
7+
8+
private const OptionEntry[] options = {
9+
{ "version", 0, 0, OptionArg.NONE, ref version, N_("Display version number"), null },
10+
{ "list", 0, 0, OptionArg.NONE, ref list_only, N_("List files only"), null },
11+
{ "", 0, 0, OptionArg.FILENAME_ARRAY, ref files, null, N_("MSI_FILE...") },
12+
{ null }
13+
};
14+
15+
public string get_long_name (string str) {
16+
var names = str.split ("|", 2);
17+
if (names.length == 2)
18+
return names[1];
19+
20+
return str;
21+
}
22+
23+
public void extract_cab (Libmsi.Database db, string cab,
24+
HashTable<string, string> cab_to_name) throws GLib.Error
25+
{
26+
if (cab.has_prefix ("#")) {
27+
var name = cab.substring (1);
28+
var query = new Libmsi.Query (db, "SELECT `Data` FROM `_Streams` WHERE `Name` = '%s'".printf (name));
29+
query.execute (null);
30+
var rec = query.fetch ();
31+
var cabinet = new GCab.Cabinet ();
32+
cabinet.load (rec.get_stream (1));
33+
}
34+
}
35+
36+
public int main (string[] args) {
37+
Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
38+
Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
39+
Intl.textdomain (Config.GETTEXT_PACKAGE);
40+
GLib.Environment.set_application_name ("msiextract");
41+
42+
var parameter_string = _("- a msi files extracting tool");
43+
var opt_context = new OptionContext (parameter_string);
44+
opt_context.set_help_enabled (true);
45+
opt_context.add_main_entries (options, null);
46+
47+
try {
48+
opt_context.parse (ref args);
49+
} catch (OptionError.BAD_VALUE err) {
50+
GLib.stdout.printf (opt_context.get_help (true, null));
51+
return 1;
52+
} catch (OptionError error) {
53+
warning (error.message);
54+
}
55+
56+
if (version) {
57+
GLib.stdout.printf ("%s\n", Config.PACKAGE_VERSION);
58+
exit (0);
59+
}
60+
61+
if (files.length < 1) {
62+
GLib.stderr.printf (_("Please specify input files.\n"));
63+
exit (1);
64+
}
65+
66+
Libmsi.Database? db = null;
67+
try {
68+
db = new Libmsi.Database (files[0], null);
69+
} catch (GLib.Error error) {
70+
GLib.stderr.printf (error.message);
71+
exit (1);
72+
}
73+
74+
var directories = new HashTable<string, Libmsi.Record> (str_hash, str_equal);
75+
try {
76+
var query = new Libmsi.Query (db, "SELECT * FROM `Directory`");
77+
query.execute (null);
78+
do {
79+
var rec = query.fetch ();
80+
directories.insert (rec.get_string (1), rec);
81+
} while (true);
82+
} catch (GLib.Error error) {
83+
}
84+
85+
var components_dir = new HashTable<string, string> (str_hash, str_equal);
86+
try {
87+
var query = new Libmsi.Query (db, "SELECT * FROM `Component`");
88+
query.execute (null);
89+
do {
90+
var rec = query.fetch ();
91+
var dir_id = rec.get_string (3);
92+
var dir_rec = directories.lookup (dir_id);
93+
var dir = get_long_name (dir_rec.get_string (3));
94+
do {
95+
var parent = dir_rec.get_string (2);
96+
dir_rec = directories.lookup (parent);
97+
if (dir_rec == null)
98+
break;
99+
parent = get_long_name (dir_rec.get_string (3));
100+
// only by intuition...
101+
if (dir_rec.get_string (1) == "ProgramFilesFolder")
102+
parent = "Program Files";
103+
else if (parent == ".")
104+
continue;
105+
else if (parent == "SourceDir")
106+
break;
107+
dir = Path.build_filename (parent, dir);
108+
} while (true);
109+
110+
components_dir.insert (rec.get_string (1), dir);
111+
} while (true);
112+
} catch (GLib.Error error) {
113+
}
114+
115+
var cab_to_name = new HashTable<string, string> (str_hash, str_equal);
116+
try {
117+
var query = new Libmsi.Query (db, "SELECT * FROM `File`");
118+
query.execute (null);
119+
do {
120+
var rec = query.fetch ();
121+
var dir = components_dir.lookup (rec.get_string (2));
122+
var file = Path.build_filename (dir, get_long_name (rec.get_string (3)));
123+
if (list_only)
124+
GLib.stdout.printf ("%s\n", file);
125+
cab_to_name.insert (rec.get_string (1), file);
126+
} while (true);
127+
} catch (GLib.Error error) {
128+
}
129+
130+
if (list_only)
131+
exit (0);
132+
133+
message ("FIXME: gcab doesn't support extraction yet!");
134+
try {
135+
var query = new Libmsi.Query (db, "SELECT * FROM `Media`");
136+
query.execute (null);
137+
do {
138+
var rec = query.fetch ();
139+
var cab = rec.get_string (4);
140+
extract_cab (db, cab, cab_to_name);
141+
} while (true);
142+
} catch (GLib.Error error) {
143+
}
144+
145+
return 0;
146+
}

0 commit comments

Comments
 (0)