]>
Commit | Line | Data |
---|---|---|
23c9e63c | 1 | /* |
eb20d728 | 2 | * Copyright (c) 2000,2007 Red Hat, Inc. |
23c9e63c DD |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by DJ Delorie <dj@cygnus.com> | |
13 | * | |
14 | */ | |
15 | ||
16 | /* The purpose of this file is to get and parse the setup.ini file | |
17 | from the mirror site. A few support routines for the bison and | |
18 | flex parsers are provided also. We check to see if this setup.ini | |
19 | is older than the one we used last time, and if so, warn the user. */ | |
20 | ||
2b734ec7 MB |
21 | #include "ini.h" |
22 | ||
946198be | 23 | #include "csu_util/rfc1738.h" |
2b734ec7 MB |
24 | #include "csu_util/version_compare.h" |
25 | ||
26 | #include "setup_version.h" | |
23c9e63c | 27 | #include "win32.h" |
5fa64c3c | 28 | #include "LogFile.h" |
23c9e63c DD |
29 | |
30 | #include <stdio.h> | |
b11b49f3 | 31 | #include <stdlib.h> |
23c9e63c | 32 | #include <stdarg.h> |
ab57ceaa | 33 | #include <process.h> |
23c9e63c | 34 | |
23c9e63c | 35 | #include "resource.h" |
23c9e63c DD |
36 | #include "state.h" |
37 | #include "geturl.h" | |
38 | #include "dialog.h" | |
a351e48c | 39 | #include "mount.h" |
de6a1a64 | 40 | #include "site.h" |
de6a1a64 | 41 | #include "find.h" |
67829ce0 | 42 | #include "IniParseFeedback.h" |
23c9e63c | 43 | |
b24c88b3 | 44 | #include "io_stream.h" |
eb20d728 | 45 | #include "io_stream_memory.h" |
b24c88b3 | 46 | |
ab57ceaa | 47 | #include "threebar.h" |
58ee6135 | 48 | |
dbfe3c19 | 49 | #include "getopt++/BoolOption.h" |
076654e7 | 50 | #include "IniDBBuilderPackage.h" |
3272d625 | 51 | #include "compress.h" |
072fb49a | 52 | #include "Exception.h" |
dbfe3c19 | 53 | #include "crypto.h" |
f6d6c600 | 54 | #include "package_db.h" |
30718d6f | 55 | |
ab57ceaa RC |
56 | extern ThreeBarProgressPage Progress; |
57 | ||
23c9e63c | 58 | unsigned int setup_timestamp = 0; |
fd93eff9 | 59 | std::string ini_setup_version; |
18837afa AG |
60 | // TODO: use C++11x initializer lists instead and drop the literal array |
61 | IniList setup_ext_list (setup_exts, | |
62 | setup_exts + (sizeof(setup_exts) / sizeof(*setup_exts))); | |
dbfe3c19 | 63 | |
20f237b4 JT |
64 | static BoolOption NoVerifyOption (false, 'X', "no-verify", IDS_HELPTEXT_NO_VERIFY); |
65 | static BoolOption NoVersionCheckOption (false, '\0', "no-version-check", IDS_HELPTEXT_NO_VERSION_CHECK); | |
23c9e63c | 66 | |
67829ce0 RC |
67 | class GuiParseFeedback : public IniParseFeedback |
68 | { | |
69 | public: | |
30718d6f | 70 | GuiParseFeedback () : lastpct (0) |
aa1e3b4d | 71 | { |
489d9c53 | 72 | Progress.SetText1 (IDS_PROGRESS_PARSING); |
aa1e3b4d RC |
73 | Progress.SetText2 (""); |
74 | Progress.SetText3 (""); | |
489d9c53 | 75 | Progress.SetText4 (IDS_PROGRESS_PROGRESS); |
80811841 JT |
76 | |
77 | yyerror_count = 0; | |
78 | yyerror_messages.clear (); | |
aa1e3b4d | 79 | } |
18837afa | 80 | virtual void progress (unsigned long const pos, unsigned long const max) |
aa1e3b4d RC |
81 | { |
82 | if (!max) | |
83 | /* length not known or eof */ | |
84 | return; | |
85 | if (lastpct == 100) | |
86 | /* rounding down should mean this only ever fires once */ | |
87 | lastpct = 0; | |
88 | if (pos * 100 / max > lastpct) | |
89 | { | |
90 | lastpct = pos * 100 / max; | |
157dc2b8 | 91 | /* Log (LOG_BABBLE) << lastpct << "% (" << pos << " of " << max |
30718d6f | 92 | << " bytes of ini file read)" << endLog; */ |
aa1e3b4d | 93 | } |
18837afa | 94 | Progress.SetBar1 (pos, max); |
10087ff4 JT |
95 | |
96 | static char buf[100]; | |
18837afa AG |
97 | sprintf (buf, "%d %% (%ldk/%ldk)", lastpct, pos/1000, max/1000); |
98 | Progress.SetText3 (buf); | |
aa1e3b4d | 99 | } |
fd93eff9 | 100 | virtual void iniName (const std::string& name) |
aa1e3b4d | 101 | { |
18837afa | 102 | Progress.SetText2 (name.c_str ()); |
10087ff4 | 103 | Progress.SetText3 (""); |
80811841 | 104 | filename = name; |
aa1e3b4d | 105 | } |
18837afa | 106 | virtual void babble (const std::string& message)const |
67829ce0 | 107 | { |
157dc2b8 | 108 | Log (LOG_BABBLE) << message << endLog; |
67829ce0 | 109 | } |
fd93eff9 | 110 | virtual void warning (const std::string& message)const |
67829ce0 | 111 | { |
b7bcb7e6 | 112 | mbox (Progress.GetHWND(), message.c_str (), "Warning", 0); |
67829ce0 | 113 | } |
80811841 JT |
114 | virtual void note_error(int lineno, const std::string &error) |
115 | { | |
116 | char tmp[16]; | |
117 | sprintf (tmp, "%d", lineno); | |
118 | ||
119 | std::string e = filename + " line " + tmp + ": " + error; | |
120 | ||
121 | if (!yyerror_messages.empty ()) | |
122 | yyerror_messages += "\n"; | |
123 | ||
124 | yyerror_messages += e; | |
125 | yyerror_count++; | |
126 | } | |
127 | virtual bool has_errors () const | |
128 | { | |
129 | return (yyerror_count > 0); | |
130 | } | |
131 | virtual void show_errors () const | |
67829ce0 | 132 | { |
80811841 | 133 | mbox (Progress.GetHWND(), yyerror_messages.c_str (), "Parse Errors", 0); |
67829ce0 RC |
134 | } |
135 | virtual ~ GuiParseFeedback () | |
136 | { | |
489d9c53 JT |
137 | Progress.SetText2 (""); |
138 | Progress.SetText3 (""); | |
b3cc8ab6 | 139 | Progress.SetText4 (IDS_PROGRESS_PACKAGE); |
489d9c53 | 140 | Progress.SetBar1 (0); |
67829ce0 | 141 | } |
aa1e3b4d | 142 | private: |
b4cf6208 | 143 | unsigned int lastpct; |
80811841 JT |
144 | std::string filename; |
145 | std::string yyerror_messages; | |
146 | int yyerror_count; | |
67829ce0 RC |
147 | }; |
148 | ||
18837afa | 149 | static io_stream* |
80811841 | 150 | decompress_ini (io_stream *ini_file, std::string ¤t_ini_name) |
de6a1a64 | 151 | { |
18837afa AG |
152 | // Replace the current compressed setup stream with its decompressed |
153 | // version. Which decompressor to use is determined by file magic. | |
154 | io_stream *compressed_stream = compress::decompress (ini_file); | |
155 | if (!compressed_stream) | |
156 | { | |
157 | /* This isn't a known compression format or an uncompressed file | |
158 | stream. Pass it on in case it was uncompressed, it will | |
159 | generate a parser error if it was some unknown format. */ | |
160 | delete compressed_stream; | |
161 | } | |
162 | else | |
163 | { | |
164 | /* Decompress the entire file in memory. This has the advantage | |
165 | that input_stream->get_size () will work during parsing and | |
166 | we'll have an accurate status bar. Also, we can't seek | |
167 | compressed streams, so when we write out a local cached copy | |
168 | of the .ini file below, we'd otherwise have to delete this | |
169 | stream and uncompress it again from the start, which is | |
170 | wasteful. The current uncompressed size of the setup.ini | |
171 | file as of 2015 is about 5 MiB, so this is not a great deal | |
172 | of memory. */ | |
173 | io_stream *uncompressed = new io_stream_memory (); | |
174 | /* Note that the decompress io_stream now "owns" the underlying | |
175 | compressed io_stream instance, so it need not be deleted | |
176 | explicitly. */ | |
177 | if ((io_stream::copy (compressed_stream, uncompressed) != 0) || | |
178 | (compressed_stream->error () != 0)) | |
179 | { | |
180 | /* There was a problem decompressing compressed_stream. */ | |
181 | Log (LOG_PLAIN) << | |
182 | "Warning: Error code " << compressed_stream->error () << | |
183 | " occurred while uncompressing " << current_ini_name << | |
184 | " - possibly truncated or corrupt file. " << endLog; | |
185 | delete uncompressed; | |
186 | ini_file = NULL; | |
187 | } | |
188 | else | |
189 | { | |
190 | ini_file = uncompressed; | |
191 | ini_file->seek (0, IO_SEEK_SET); | |
192 | } | |
193 | } | |
194 | return ini_file; | |
de6a1a64 RC |
195 | } |
196 | ||
18837afa AG |
197 | static io_stream* |
198 | check_ini_sig (io_stream* ini_file, io_stream* ini_sig_file, | |
199 | bool& sig_fail, const char* site, const char* sig_name, HWND owner) | |
de6a1a64 | 200 | { |
18837afa AG |
201 | /* Unless the NoVerifyOption is set, check the signature for the |
202 | current setup and record the result. On a failed signature check | |
203 | the streams are invalidated so even if we tried to read in the | |
204 | setup anyway there's be nothing to parse. */ | |
205 | if (!NoVerifyOption && ini_file) | |
dd3f7f9b | 206 | { |
679f6a2e AG |
207 | if (!ini_sig_file) { |
208 | // don't complain if the user installs from localdir and no | |
209 | // signature file is present | |
210 | // TODO: download the ini + signature file instead | |
211 | if (casecompare (site, "localdir")) | |
212 | { | |
213 | note (owner, IDS_SETUPINI_MISSING, sig_name, site); | |
214 | delete ini_file; | |
215 | ini_file = NULL; | |
216 | sig_fail = true; | |
217 | } | |
218 | } | |
18837afa | 219 | else if (!verify_ini_file_sig (ini_file, ini_sig_file, owner)) |
dbfe3c19 | 220 | { |
18837afa | 221 | note (owner, IDS_SIG_INVALID, sig_name, site); |
dbfe3c19 DK |
222 | delete ini_sig_file; |
223 | ini_sig_file = NULL; | |
18837afa AG |
224 | delete ini_file; |
225 | ini_file = NULL; | |
dbfe3c19 DK |
226 | sig_fail = true; |
227 | } | |
18837afa AG |
228 | } |
229 | return ini_file; | |
230 | } | |
231 | ||
bd185b7b | 232 | static bool |
18837afa AG |
233 | do_local_ini (HWND owner) |
234 | { | |
bd185b7b | 235 | bool ini_error = false; |
18837afa AG |
236 | io_stream *ini_file, *ini_sig_file; |
237 | // iterate over all setup files found in do_from_local_dir | |
238 | for (IniList::const_iterator n = found_ini_list.begin (); | |
239 | n != found_ini_list.end (); ++n) | |
240 | { | |
463042fe KB |
241 | GuiParseFeedback myFeedback; |
242 | IniDBBuilderPackage aBuilder (myFeedback); | |
18837afa AG |
243 | bool sig_fail = false; |
244 | std::string current_ini_ext, current_ini_name, current_ini_sig_name; | |
245 | ||
246 | current_ini_name = *n; | |
247 | current_ini_sig_name = current_ini_name + ".sig"; | |
248 | current_ini_ext = current_ini_name.substr (current_ini_name.rfind (".") + 1); | |
249 | ini_sig_file = io_stream::open ("file://" + current_ini_sig_name, "rb", 0); | |
250 | ini_file = io_stream::open ("file://" + current_ini_name, "rb", 0); | |
251 | ini_file = check_ini_sig (ini_file, ini_sig_file, sig_fail, | |
252 | "localdir", current_ini_sig_name.c_str (), owner); | |
dbfe3c19 | 253 | if (ini_file) |
80811841 | 254 | ini_file = decompress_ini (ini_file, current_ini_name); |
18837afa | 255 | if (!ini_file || sig_fail) |
3272d625 | 256 | { |
18837afa AG |
257 | // no setup found or signature invalid |
258 | note (owner, IDS_SETUPINI_MISSING, SetupBaseName.c_str (), | |
259 | "localdir"); | |
bd185b7b | 260 | ini_error = true; |
18837afa AG |
261 | } |
262 | else | |
263 | { | |
264 | // grok information from setup | |
265 | myFeedback.babble ("Found ini file - " + current_ini_name); | |
266 | myFeedback.iniName (current_ini_name); | |
679f6a2e AG |
267 | int ldl = local_dir.length () + 1; |
268 | int cap = current_ini_name.rfind ("/" + SetupArch); | |
269 | aBuilder.parse_mirror = | |
270 | rfc1738_unescape (current_ini_name.substr (ldl, cap - ldl)); | |
18837afa AG |
271 | ini_init (ini_file, &aBuilder, myFeedback); |
272 | ||
80811841 | 273 | if (yyparse () || myFeedback.has_errors()) |
bd185b7b | 274 | { |
80811841 | 275 | myFeedback.show_errors (); |
bd185b7b JT |
276 | ini_error = true; |
277 | } | |
eb20d728 | 278 | |
18837afa AG |
279 | if (aBuilder.timestamp > setup_timestamp) |
280 | { | |
281 | setup_timestamp = aBuilder.timestamp; | |
282 | ini_setup_version = aBuilder.version; | |
30718d6f | 283 | } |
18837afa AG |
284 | delete ini_file; |
285 | ini_file = NULL; | |
eb20d728 | 286 | } |
18837afa | 287 | } |
bd185b7b | 288 | return ini_error; |
18837afa | 289 | } |
30718d6f | 290 | |
bd185b7b | 291 | static bool |
18837afa AG |
292 | do_remote_ini (HWND owner) |
293 | { | |
bd185b7b | 294 | bool ini_error = false; |
abad8c0c | 295 | io_stream *ini_file = NULL, *ini_sig_file; |
18837afa AG |
296 | |
297 | /* FIXME: Get rid of this io_stream pointer travesty. The need to | |
298 | explicitly delete these things is ridiculous. */ | |
299 | ||
300 | // iterate over all sites | |
301 | for (SiteList::const_iterator n = site_list.begin (); | |
302 | n != site_list.end (); ++n) | |
303 | { | |
463042fe KB |
304 | GuiParseFeedback myFeedback; |
305 | IniDBBuilderPackage aBuilder (myFeedback); | |
18837afa AG |
306 | bool sig_fail = false; |
307 | std::string current_ini_ext, current_ini_name, current_ini_sig_name; | |
308 | // iterate over known extensions for setup | |
309 | for (IniList::const_iterator ext = setup_ext_list.begin (); | |
310 | ext != setup_ext_list.end (); | |
311 | ext++) | |
30718d6f | 312 | { |
18837afa AG |
313 | current_ini_ext = *ext; |
314 | current_ini_name = n->url + SetupIniDir + SetupBaseName + "." + current_ini_ext; | |
315 | current_ini_sig_name = current_ini_name + ".sig"; | |
316 | ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner); | |
eb20d728 | 317 | ini_file = get_url_to_membuf (current_ini_name, owner); |
18837afa AG |
318 | ini_file = check_ini_sig (ini_file, ini_sig_file, sig_fail, |
319 | n->url.c_str (), current_ini_sig_name.c_str (), owner); | |
320 | // stop searching as soon as we find a setup file | |
321 | if (ini_file) | |
322 | break; | |
3272d625 | 323 | } |
18837afa | 324 | if (ini_file) |
80811841 | 325 | ini_file = decompress_ini (ini_file, current_ini_name); |
18837afa | 326 | if (!ini_file || sig_fail) |
de6a1a64 | 327 | { |
18837afa AG |
328 | // no setup found or signature invalid |
329 | note (owner, IDS_SETUPINI_MISSING, SetupBaseName.c_str (), n->url.c_str ()); | |
bd185b7b | 330 | ini_error = true; |
de6a1a64 | 331 | } |
de6a1a64 RC |
332 | else |
333 | { | |
18837afa AG |
334 | // grok information from setup |
335 | myFeedback.iniName (current_ini_name); | |
336 | aBuilder.parse_mirror = n->url; | |
337 | ini_init (ini_file, &aBuilder, myFeedback); | |
338 | ||
80811841 | 339 | if (yyparse () || myFeedback.has_errors()) |
bd185b7b | 340 | { |
80811841 | 341 | myFeedback.show_errors (); |
bd185b7b JT |
342 | ini_error = true; |
343 | } | |
18837afa | 344 | else |
de6a1a64 | 345 | { |
18837afa AG |
346 | /* save known-good setup.ini locally */ |
347 | const std::string fp = "file://" + local_dir + "/" + | |
348 | rfc1738_escape_part (n->url) + | |
349 | "/" + SetupIniDir + SetupBaseName + ".ini"; | |
350 | io_stream::mkpath_p (PATH_TO_FILE, fp, 0); | |
351 | if (io_stream *out = io_stream::open (fp, "wb", 0)) | |
352 | { | |
353 | ini_file->seek (0, IO_SEEK_SET); | |
354 | if (io_stream::copy (ini_file, out) != 0) | |
355 | io_stream::remove (fp); | |
356 | delete out; | |
357 | } | |
de6a1a64 | 358 | } |
18837afa AG |
359 | if (aBuilder.timestamp > setup_timestamp) |
360 | { | |
361 | setup_timestamp = aBuilder.timestamp; | |
362 | ini_setup_version = aBuilder.version; | |
363 | } | |
364 | delete ini_file; | |
365 | ini_file = NULL; | |
b92028a4 | 366 | } |
dd3f7f9b | 367 | } |
bd185b7b | 368 | return ini_error; |
de6a1a64 RC |
369 | } |
370 | ||
d9f4a2ba | 371 | static bool |
ab57ceaa | 372 | do_ini_thread (HINSTANCE h, HWND owner) |
de6a1a64 | 373 | { |
ab67dafa JT |
374 | packagedb db; |
375 | db.init(); | |
376 | ||
bd185b7b | 377 | bool ini_error = true; |
b0cccc59 | 378 | |
7cc4d95e | 379 | if (source == IDC_SOURCE_LOCALDIR) |
bd185b7b | 380 | ini_error = do_local_ini (owner); |
de6a1a64 | 381 | else |
bd185b7b | 382 | ini_error = do_remote_ini (owner); |
de6a1a64 | 383 | |
bd185b7b | 384 | if (ini_error) |
d9f4a2ba | 385 | return false; |
23c9e63c | 386 | |
18837afa | 387 | if (get_root_dir ().c_str ()) |
23c9e63c | 388 | { |
b41c2908 | 389 | io_stream::mkpath_p (PATH_TO_DIR, "cygfile:///etc/setup", 0755); |
04d6e06b DD |
390 | |
391 | unsigned int old_timestamp = 0; | |
b24c88b3 | 392 | io_stream *ots = |
26922cd2 | 393 | io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0); |
04d6e06b | 394 | if (ots) |
23c9e63c | 395 | { |
b24c88b3 RC |
396 | char temp[20]; |
397 | memset (temp, '\0', 20); | |
398 | if (ots->read (temp, 19)) | |
399 | sscanf (temp, "%u", &old_timestamp); | |
400 | delete ots; | |
04d6e06b DD |
401 | if (old_timestamp && setup_timestamp |
402 | && (old_timestamp > setup_timestamp)) | |
403 | { | |
ab57ceaa | 404 | int yn = yesno (owner, IDS_OLD_SETUPINI); |
04d6e06b | 405 | if (yn == IDNO) |
5fa64c3c | 406 | Logger ().exit (1); |
04d6e06b | 407 | } |
23c9e63c | 408 | } |
04d6e06b | 409 | if (setup_timestamp) |
23c9e63c | 410 | { |
b24c88b3 | 411 | io_stream *nts = |
26922cd2 | 412 | io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0); |
04d6e06b DD |
413 | if (nts) |
414 | { | |
b24c88b3 RC |
415 | char temp[20]; |
416 | sprintf (temp, "%u", setup_timestamp); | |
417 | nts->write (temp, strlen (temp)); | |
418 | delete nts; | |
04d6e06b | 419 | } |
23c9e63c DD |
420 | } |
421 | } | |
422 | ||
18837afa | 423 | LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version.size () ? |
b3edf57f | 424 | ini_setup_version.c_str () : "(null)", |
2b734ec7 | 425 | setup_version); |
b3edf57f | 426 | if (ini_setup_version.size ()) |
13d27274 | 427 | { |
0955c7b0 KB |
428 | if ((version_compare (setup_version, ini_setup_version) < 0) |
429 | && !NoVersionCheckOption) | |
2b734ec7 | 430 | note (owner, IDS_OLD_SETUP_VERSION, setup_version, |
b3edf57f | 431 | ini_setup_version.c_str ()); |
13d27274 DD |
432 | } |
433 | ||
d9f4a2ba | 434 | return true; |
23c9e63c DD |
435 | } |
436 | ||
45e01f23 | 437 | static DWORD WINAPI |
18837afa | 438 | do_ini_thread_reflector (void* p) |
ab57ceaa | 439 | { |
072fb49a MB |
440 | HANDLE *context; |
441 | context = (HANDLE*)p; | |
ab57ceaa | 442 | |
069cfbb4 JT |
443 | SetThreadUILanguage(langid); |
444 | ||
072fb49a MB |
445 | try |
446 | { | |
18837afa | 447 | bool succeeded = do_ini_thread ((HINSTANCE)context[0], (HWND)context[1]); |
ab57ceaa | 448 | |
072fb49a | 449 | // Tell the progress page that we're done downloading |
18837afa | 450 | Progress.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE, 0, succeeded); |
072fb49a | 451 | } |
703f1a44 | 452 | TOPLEVEL_CATCH ((HWND) context[1], "ini"); |
ab57ceaa | 453 | |
18837afa | 454 | ExitThread (0); |
ab57ceaa RC |
455 | } |
456 | ||
457 | static HANDLE context[2]; | |
458 | ||
459 | void | |
460 | do_ini (HINSTANCE h, HWND owner) | |
461 | { | |
45e01f23 RC |
462 | context[0] = h; |
463 | context[1] = owner; | |
30718d6f CF |
464 | |
465 | DWORD threadID; | |
45e01f23 | 466 | CreateThread (NULL, 0, do_ini_thread_reflector, context, 0, &threadID); |
ab57ceaa | 467 | } |