]> cygwin.com Git - cygwin-apps/setup.git/blame - ini.cc
Added dpiAwareness element to manifest
[cygwin-apps/setup.git] / ini.cc
CommitLineData
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
56extern ThreeBarProgressPage Progress;
57
23c9e63c 58unsigned int setup_timestamp = 0;
fd93eff9 59std::string ini_setup_version;
18837afa
AG
60// TODO: use C++11x initializer lists instead and drop the literal array
61IniList setup_ext_list (setup_exts,
62 setup_exts + (sizeof(setup_exts) / sizeof(*setup_exts)));
dbfe3c19 63
20f237b4
JT
64static BoolOption NoVerifyOption (false, 'X', "no-verify", IDS_HELPTEXT_NO_VERIFY);
65static BoolOption NoVersionCheckOption (false, '\0', "no-version-check", IDS_HELPTEXT_NO_VERSION_CHECK);
23c9e63c 66
67829ce0
RC
67class GuiParseFeedback : public IniParseFeedback
68{
69public:
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 142private:
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 149static io_stream*
80811841 150decompress_ini (io_stream *ini_file, std::string &current_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
197static io_stream*
198check_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 232static bool
18837afa
AG
233do_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 291static bool
18837afa
AG
292do_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 371static bool
ab57ceaa 372do_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 437static DWORD WINAPI
18837afa 438do_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
457static HANDLE context[2];
458
459void
460do_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}
This page took 0.224389 seconds and 6 git commands to generate.