]> cygwin.com Git - cygwin-apps/setup.git/blame - ini.cc
Make inilint do something slightly useful
[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
DK
63
64static BoolOption NoVerifyOption (false, 'X', "no-verify", "Don't verify setup.ini signatures");
0955c7b0 65static BoolOption NoVersionCheckOption (false, '\0', "no-version-check", "Suppress checking if a newer version of setup is available");
23c9e63c 66
67829ce0
RC
67class GuiParseFeedback : public IniParseFeedback
68{
69public:
30718d6f 70 GuiParseFeedback () : lastpct (0)
aa1e3b4d
RC
71 {
72 Progress.SetText2 ("");
73 Progress.SetText3 ("");
74 Progress.SetText4 ("Progress:");
80811841
JT
75
76 yyerror_count = 0;
77 yyerror_messages.clear ();
aa1e3b4d 78 }
18837afa 79 virtual void progress (unsigned long const pos, unsigned long const max)
aa1e3b4d
RC
80 {
81 if (!max)
82 /* length not known or eof */
83 return;
84 if (lastpct == 100)
85 /* rounding down should mean this only ever fires once */
86 lastpct = 0;
87 if (pos * 100 / max > lastpct)
88 {
89 lastpct = pos * 100 / max;
157dc2b8 90 /* Log (LOG_BABBLE) << lastpct << "% (" << pos << " of " << max
30718d6f 91 << " bytes of ini file read)" << endLog; */
aa1e3b4d 92 }
18837afa 93 Progress.SetBar1 (pos, max);
10087ff4
JT
94
95 static char buf[100];
18837afa
AG
96 sprintf (buf, "%d %% (%ldk/%ldk)", lastpct, pos/1000, max/1000);
97 Progress.SetText3 (buf);
aa1e3b4d 98 }
fd93eff9 99 virtual void iniName (const std::string& name)
aa1e3b4d 100 {
10087ff4 101 Progress.SetText1 ("Parsing...");
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 {
18837afa 137 Progress.SetText4 ("Package:");
67829ce0 138 }
aa1e3b4d 139private:
b4cf6208 140 unsigned int lastpct;
80811841
JT
141 std::string filename;
142 std::string yyerror_messages;
143 int yyerror_count;
67829ce0
RC
144};
145
18837afa 146static io_stream*
80811841 147decompress_ini (io_stream *ini_file, std::string &current_ini_name)
de6a1a64 148{
18837afa
AG
149 // Replace the current compressed setup stream with its decompressed
150 // version. Which decompressor to use is determined by file magic.
151 io_stream *compressed_stream = compress::decompress (ini_file);
152 if (!compressed_stream)
153 {
154 /* This isn't a known compression format or an uncompressed file
155 stream. Pass it on in case it was uncompressed, it will
156 generate a parser error if it was some unknown format. */
157 delete compressed_stream;
158 }
159 else
160 {
161 /* Decompress the entire file in memory. This has the advantage
162 that input_stream->get_size () will work during parsing and
163 we'll have an accurate status bar. Also, we can't seek
164 compressed streams, so when we write out a local cached copy
165 of the .ini file below, we'd otherwise have to delete this
166 stream and uncompress it again from the start, which is
167 wasteful. The current uncompressed size of the setup.ini
168 file as of 2015 is about 5 MiB, so this is not a great deal
169 of memory. */
170 io_stream *uncompressed = new io_stream_memory ();
171 /* Note that the decompress io_stream now "owns" the underlying
172 compressed io_stream instance, so it need not be deleted
173 explicitly. */
174 if ((io_stream::copy (compressed_stream, uncompressed) != 0) ||
175 (compressed_stream->error () != 0))
176 {
177 /* There was a problem decompressing compressed_stream. */
178 Log (LOG_PLAIN) <<
179 "Warning: Error code " << compressed_stream->error () <<
180 " occurred while uncompressing " << current_ini_name <<
181 " - possibly truncated or corrupt file. " << endLog;
182 delete uncompressed;
183 ini_file = NULL;
184 }
185 else
186 {
187 ini_file = uncompressed;
188 ini_file->seek (0, IO_SEEK_SET);
189 }
190 }
191 return ini_file;
de6a1a64
RC
192}
193
18837afa
AG
194static io_stream*
195check_ini_sig (io_stream* ini_file, io_stream* ini_sig_file,
196 bool& sig_fail, const char* site, const char* sig_name, HWND owner)
de6a1a64 197{
18837afa
AG
198 /* Unless the NoVerifyOption is set, check the signature for the
199 current setup and record the result. On a failed signature check
200 the streams are invalidated so even if we tried to read in the
201 setup anyway there's be nothing to parse. */
202 if (!NoVerifyOption && ini_file)
dd3f7f9b 203 {
679f6a2e
AG
204 if (!ini_sig_file) {
205 // don't complain if the user installs from localdir and no
206 // signature file is present
207 // TODO: download the ini + signature file instead
208 if (casecompare (site, "localdir"))
209 {
210 note (owner, IDS_SETUPINI_MISSING, sig_name, site);
211 delete ini_file;
212 ini_file = NULL;
213 sig_fail = true;
214 }
215 }
18837afa 216 else if (!verify_ini_file_sig (ini_file, ini_sig_file, owner))
dbfe3c19 217 {
18837afa 218 note (owner, IDS_SIG_INVALID, sig_name, site);
dbfe3c19
DK
219 delete ini_sig_file;
220 ini_sig_file = NULL;
18837afa
AG
221 delete ini_file;
222 ini_file = NULL;
dbfe3c19
DK
223 sig_fail = true;
224 }
18837afa
AG
225 }
226 return ini_file;
227}
228
bd185b7b 229static bool
18837afa
AG
230do_local_ini (HWND owner)
231{
bd185b7b 232 bool ini_error = false;
18837afa
AG
233 io_stream *ini_file, *ini_sig_file;
234 // iterate over all setup files found in do_from_local_dir
235 for (IniList::const_iterator n = found_ini_list.begin ();
236 n != found_ini_list.end (); ++n)
237 {
463042fe
KB
238 GuiParseFeedback myFeedback;
239 IniDBBuilderPackage aBuilder (myFeedback);
18837afa
AG
240 bool sig_fail = false;
241 std::string current_ini_ext, current_ini_name, current_ini_sig_name;
242
243 current_ini_name = *n;
244 current_ini_sig_name = current_ini_name + ".sig";
245 current_ini_ext = current_ini_name.substr (current_ini_name.rfind (".") + 1);
246 ini_sig_file = io_stream::open ("file://" + current_ini_sig_name, "rb", 0);
247 ini_file = io_stream::open ("file://" + current_ini_name, "rb", 0);
248 ini_file = check_ini_sig (ini_file, ini_sig_file, sig_fail,
249 "localdir", current_ini_sig_name.c_str (), owner);
dbfe3c19 250 if (ini_file)
80811841 251 ini_file = decompress_ini (ini_file, current_ini_name);
18837afa 252 if (!ini_file || sig_fail)
3272d625 253 {
18837afa
AG
254 // no setup found or signature invalid
255 note (owner, IDS_SETUPINI_MISSING, SetupBaseName.c_str (),
256 "localdir");
bd185b7b 257 ini_error = true;
18837afa
AG
258 }
259 else
260 {
261 // grok information from setup
262 myFeedback.babble ("Found ini file - " + current_ini_name);
263 myFeedback.iniName (current_ini_name);
679f6a2e
AG
264 int ldl = local_dir.length () + 1;
265 int cap = current_ini_name.rfind ("/" + SetupArch);
266 aBuilder.parse_mirror =
267 rfc1738_unescape (current_ini_name.substr (ldl, cap - ldl));
18837afa
AG
268 ini_init (ini_file, &aBuilder, myFeedback);
269
80811841 270 if (yyparse () || myFeedback.has_errors())
bd185b7b 271 {
80811841 272 myFeedback.show_errors ();
bd185b7b
JT
273 ini_error = true;
274 }
eb20d728 275
18837afa
AG
276 if (aBuilder.timestamp > setup_timestamp)
277 {
278 setup_timestamp = aBuilder.timestamp;
279 ini_setup_version = aBuilder.version;
30718d6f 280 }
18837afa
AG
281 delete ini_file;
282 ini_file = NULL;
eb20d728 283 }
18837afa 284 }
bd185b7b 285 return ini_error;
18837afa 286}
30718d6f 287
bd185b7b 288static bool
18837afa
AG
289do_remote_ini (HWND owner)
290{
bd185b7b 291 bool ini_error = false;
abad8c0c 292 io_stream *ini_file = NULL, *ini_sig_file;
18837afa
AG
293
294 /* FIXME: Get rid of this io_stream pointer travesty. The need to
295 explicitly delete these things is ridiculous. */
296
297 // iterate over all sites
298 for (SiteList::const_iterator n = site_list.begin ();
299 n != site_list.end (); ++n)
300 {
463042fe
KB
301 GuiParseFeedback myFeedback;
302 IniDBBuilderPackage aBuilder (myFeedback);
18837afa
AG
303 bool sig_fail = false;
304 std::string current_ini_ext, current_ini_name, current_ini_sig_name;
305 // iterate over known extensions for setup
306 for (IniList::const_iterator ext = setup_ext_list.begin ();
307 ext != setup_ext_list.end ();
308 ext++)
30718d6f 309 {
18837afa
AG
310 current_ini_ext = *ext;
311 current_ini_name = n->url + SetupIniDir + SetupBaseName + "." + current_ini_ext;
312 current_ini_sig_name = current_ini_name + ".sig";
313 ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner);
eb20d728 314 ini_file = get_url_to_membuf (current_ini_name, owner);
18837afa
AG
315 ini_file = check_ini_sig (ini_file, ini_sig_file, sig_fail,
316 n->url.c_str (), current_ini_sig_name.c_str (), owner);
317 // stop searching as soon as we find a setup file
318 if (ini_file)
319 break;
3272d625 320 }
18837afa 321 if (ini_file)
80811841 322 ini_file = decompress_ini (ini_file, current_ini_name);
18837afa 323 if (!ini_file || sig_fail)
de6a1a64 324 {
18837afa
AG
325 // no setup found or signature invalid
326 note (owner, IDS_SETUPINI_MISSING, SetupBaseName.c_str (), n->url.c_str ());
bd185b7b 327 ini_error = true;
de6a1a64 328 }
de6a1a64
RC
329 else
330 {
18837afa
AG
331 // grok information from setup
332 myFeedback.iniName (current_ini_name);
333 aBuilder.parse_mirror = n->url;
334 ini_init (ini_file, &aBuilder, myFeedback);
335
80811841 336 if (yyparse () || myFeedback.has_errors())
bd185b7b 337 {
80811841 338 myFeedback.show_errors ();
bd185b7b
JT
339 ini_error = true;
340 }
18837afa 341 else
de6a1a64 342 {
18837afa
AG
343 /* save known-good setup.ini locally */
344 const std::string fp = "file://" + local_dir + "/" +
345 rfc1738_escape_part (n->url) +
346 "/" + SetupIniDir + SetupBaseName + ".ini";
347 io_stream::mkpath_p (PATH_TO_FILE, fp, 0);
348 if (io_stream *out = io_stream::open (fp, "wb", 0))
349 {
350 ini_file->seek (0, IO_SEEK_SET);
351 if (io_stream::copy (ini_file, out) != 0)
352 io_stream::remove (fp);
353 delete out;
354 }
de6a1a64 355 }
18837afa
AG
356 if (aBuilder.timestamp > setup_timestamp)
357 {
358 setup_timestamp = aBuilder.timestamp;
359 ini_setup_version = aBuilder.version;
360 }
361 delete ini_file;
362 ini_file = NULL;
b92028a4 363 }
dd3f7f9b 364 }
bd185b7b 365 return ini_error;
de6a1a64
RC
366}
367
d9f4a2ba 368static bool
ab57ceaa 369do_ini_thread (HINSTANCE h, HWND owner)
de6a1a64 370{
ab67dafa
JT
371 packagedb db;
372 db.init();
373
bd185b7b 374 bool ini_error = true;
b0cccc59 375
7cc4d95e 376 if (source == IDC_SOURCE_LOCALDIR)
bd185b7b 377 ini_error = do_local_ini (owner);
de6a1a64 378 else
bd185b7b 379 ini_error = do_remote_ini (owner);
de6a1a64 380
bd185b7b 381 if (ini_error)
d9f4a2ba 382 return false;
23c9e63c 383
18837afa 384 if (get_root_dir ().c_str ())
23c9e63c 385 {
b41c2908 386 io_stream::mkpath_p (PATH_TO_DIR, "cygfile:///etc/setup", 0755);
04d6e06b
DD
387
388 unsigned int old_timestamp = 0;
b24c88b3 389 io_stream *ots =
26922cd2 390 io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0);
04d6e06b 391 if (ots)
23c9e63c 392 {
b24c88b3
RC
393 char temp[20];
394 memset (temp, '\0', 20);
395 if (ots->read (temp, 19))
396 sscanf (temp, "%u", &old_timestamp);
397 delete ots;
04d6e06b
DD
398 if (old_timestamp && setup_timestamp
399 && (old_timestamp > setup_timestamp))
400 {
ab57ceaa 401 int yn = yesno (owner, IDS_OLD_SETUPINI);
04d6e06b 402 if (yn == IDNO)
5fa64c3c 403 Logger ().exit (1);
04d6e06b 404 }
23c9e63c 405 }
04d6e06b 406 if (setup_timestamp)
23c9e63c 407 {
b24c88b3 408 io_stream *nts =
26922cd2 409 io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0);
04d6e06b
DD
410 if (nts)
411 {
b24c88b3
RC
412 char temp[20];
413 sprintf (temp, "%u", setup_timestamp);
414 nts->write (temp, strlen (temp));
415 delete nts;
04d6e06b 416 }
23c9e63c
DD
417 }
418 }
419
18837afa 420 LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version.size () ?
b3edf57f 421 ini_setup_version.c_str () : "(null)",
2b734ec7 422 setup_version);
b3edf57f 423 if (ini_setup_version.size ())
13d27274 424 {
0955c7b0
KB
425 if ((version_compare (setup_version, ini_setup_version) < 0)
426 && !NoVersionCheckOption)
2b734ec7 427 note (owner, IDS_OLD_SETUP_VERSION, setup_version,
b3edf57f 428 ini_setup_version.c_str ());
13d27274
DD
429 }
430
d9f4a2ba 431 return true;
23c9e63c
DD
432}
433
45e01f23 434static DWORD WINAPI
18837afa 435do_ini_thread_reflector (void* p)
ab57ceaa 436{
072fb49a
MB
437 HANDLE *context;
438 context = (HANDLE*)p;
ab57ceaa 439
072fb49a
MB
440 try
441 {
18837afa 442 bool succeeded = do_ini_thread ((HINSTANCE)context[0], (HWND)context[1]);
ab57ceaa 443
072fb49a 444 // Tell the progress page that we're done downloading
18837afa 445 Progress.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE, 0, succeeded);
072fb49a 446 }
703f1a44 447 TOPLEVEL_CATCH ((HWND) context[1], "ini");
ab57ceaa 448
18837afa 449 ExitThread (0);
ab57ceaa
RC
450}
451
452static HANDLE context[2];
453
454void
455do_ini (HINSTANCE h, HWND owner)
456{
45e01f23
RC
457 context[0] = h;
458 context[1] = owner;
30718d6f
CF
459
460 DWORD threadID;
45e01f23 461 CreateThread (NULL, 0, do_ini_thread_reflector, context, 0, &threadID);
ab57ceaa 462}
This page took 0.165035 seconds and 5 git commands to generate.