]> cygwin.com Git - cygwin-apps/setup.git/blob - ini.cc
Make removeEmptyCategories() explicit, rather than a side effect of defaultTrust()
[cygwin-apps/setup.git] / ini.cc
1 /*
2 * Copyright (c) 2000,2007 Red Hat, Inc.
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
21 #include "ini.h"
22
23 #include "csu_util/rfc1738.h"
24 #include "csu_util/version_compare.h"
25
26 #include "setup_version.h"
27 #include "win32.h"
28 #include "LogFile.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <process.h>
34
35 #include "resource.h"
36 #include "state.h"
37 #include "geturl.h"
38 #include "dialog.h"
39 #include "mount.h"
40 #include "site.h"
41 #include "find.h"
42 #include "IniParseFeedback.h"
43
44 #include "io_stream.h"
45 #include "io_stream_memory.h"
46
47 #include "threebar.h"
48
49 #include "getopt++/BoolOption.h"
50 #include "IniDBBuilderPackage.h"
51 #include "compress.h"
52 #include "Exception.h"
53 #include "crypto.h"
54 #include "package_db.h"
55
56 extern ThreeBarProgressPage Progress;
57
58 unsigned int setup_timestamp = 0;
59 std::string ini_setup_version;
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)));
63
64 static BoolOption NoVerifyOption (false, 'X', "no-verify", "Don't verify setup.ini signatures");
65
66 extern int yyparse ();
67
68 /*extern int yydebug;*/
69
70 class GuiParseFeedback : public IniParseFeedback
71 {
72 public:
73 GuiParseFeedback () : lastpct (0)
74 {
75 Progress.SetText2 ("");
76 Progress.SetText3 ("");
77 Progress.SetText4 ("Progress:");
78 }
79 virtual void progress (unsigned long const pos, unsigned long const max)
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;
90 /* Log (LOG_BABBLE) << lastpct << "% (" << pos << " of " << max
91 << " bytes of ini file read)" << endLog; */
92 }
93 Progress.SetBar1 (pos, max);
94
95 static char buf[100];
96 sprintf (buf, "%d %% (%ldk/%ldk)", lastpct, pos/1000, max/1000);
97 Progress.SetText3 (buf);
98 }
99 virtual void iniName (const std::string& name)
100 {
101 Progress.SetText1 ("Parsing...");
102 Progress.SetText2 (name.c_str ());
103 Progress.SetText3 ("");
104 }
105 virtual void babble (const std::string& message)const
106 {
107 Log (LOG_BABBLE) << message << endLog;
108 }
109 virtual void warning (const std::string& message)const
110 {
111 mbox (0, message.c_str (), "Warning", 0);
112 }
113 virtual void error (const std::string& message)const
114 {
115 mbox (0, message.c_str (), "Parse Errors", 0);
116 }
117 virtual ~ GuiParseFeedback ()
118 {
119 Progress.SetText4 ("Package:");
120 }
121 private:
122 unsigned int lastpct;
123 };
124
125 static io_stream*
126 decompress_ini (io_stream *ini_file)
127 {
128 // Replace the current compressed setup stream with its decompressed
129 // version. Which decompressor to use is determined by file magic.
130 io_stream *compressed_stream = compress::decompress (ini_file);
131 if (!compressed_stream)
132 {
133 /* This isn't a known compression format or an uncompressed file
134 stream. Pass it on in case it was uncompressed, it will
135 generate a parser error if it was some unknown format. */
136 delete compressed_stream;
137 }
138 else
139 {
140 /* Decompress the entire file in memory. This has the advantage
141 that input_stream->get_size () will work during parsing and
142 we'll have an accurate status bar. Also, we can't seek
143 compressed streams, so when we write out a local cached copy
144 of the .ini file below, we'd otherwise have to delete this
145 stream and uncompress it again from the start, which is
146 wasteful. The current uncompressed size of the setup.ini
147 file as of 2015 is about 5 MiB, so this is not a great deal
148 of memory. */
149 io_stream *uncompressed = new io_stream_memory ();
150 /* Note that the decompress io_stream now "owns" the underlying
151 compressed io_stream instance, so it need not be deleted
152 explicitly. */
153 if ((io_stream::copy (compressed_stream, uncompressed) != 0) ||
154 (compressed_stream->error () != 0))
155 {
156 /* There was a problem decompressing compressed_stream. */
157 Log (LOG_PLAIN) <<
158 "Warning: Error code " << compressed_stream->error () <<
159 " occurred while uncompressing " << current_ini_name <<
160 " - possibly truncated or corrupt file. " << endLog;
161 delete uncompressed;
162 ini_file = NULL;
163 }
164 else
165 {
166 ini_file = uncompressed;
167 ini_file->seek (0, IO_SEEK_SET);
168 }
169 }
170 return ini_file;
171 }
172
173 static io_stream*
174 check_ini_sig (io_stream* ini_file, io_stream* ini_sig_file,
175 bool& sig_fail, const char* site, const char* sig_name, HWND owner)
176 {
177 /* Unless the NoVerifyOption is set, check the signature for the
178 current setup and record the result. On a failed signature check
179 the streams are invalidated so even if we tried to read in the
180 setup anyway there's be nothing to parse. */
181 if (!NoVerifyOption && ini_file)
182 {
183 if (!ini_sig_file) {
184 // don't complain if the user installs from localdir and no
185 // signature file is present
186 // TODO: download the ini + signature file instead
187 if (casecompare (site, "localdir"))
188 {
189 note (owner, IDS_SETUPINI_MISSING, sig_name, site);
190 delete ini_file;
191 ini_file = NULL;
192 sig_fail = true;
193 }
194 }
195 else if (!verify_ini_file_sig (ini_file, ini_sig_file, owner))
196 {
197 note (owner, IDS_SIG_INVALID, sig_name, site);
198 delete ini_sig_file;
199 ini_sig_file = NULL;
200 delete ini_file;
201 ini_file = NULL;
202 sig_fail = true;
203 }
204 }
205 return ini_file;
206 }
207
208 static int
209 do_local_ini (HWND owner)
210 {
211 size_t ini_count = 0;
212 GuiParseFeedback myFeedback;
213 IniDBBuilderPackage aBuilder (myFeedback);
214 io_stream *ini_file, *ini_sig_file;
215 // iterate over all setup files found in do_from_local_dir
216 for (IniList::const_iterator n = found_ini_list.begin ();
217 n != found_ini_list.end (); ++n)
218 {
219 bool sig_fail = false;
220 std::string current_ini_ext, current_ini_name, current_ini_sig_name;
221
222 current_ini_name = *n;
223 current_ini_sig_name = current_ini_name + ".sig";
224 current_ini_ext = current_ini_name.substr (current_ini_name.rfind (".") + 1);
225 ini_sig_file = io_stream::open ("file://" + current_ini_sig_name, "rb", 0);
226 ini_file = io_stream::open ("file://" + current_ini_name, "rb", 0);
227 ini_file = check_ini_sig (ini_file, ini_sig_file, sig_fail,
228 "localdir", current_ini_sig_name.c_str (), owner);
229 if (ini_file)
230 ini_file = decompress_ini (ini_file);
231 if (!ini_file || sig_fail)
232 {
233 // no setup found or signature invalid
234 note (owner, IDS_SETUPINI_MISSING, SetupBaseName.c_str (),
235 "localdir");
236 }
237 else
238 {
239 // grok information from setup
240 myFeedback.babble ("Found ini file - " + current_ini_name);
241 myFeedback.iniName (current_ini_name);
242 int ldl = local_dir.length () + 1;
243 int cap = current_ini_name.rfind ("/" + SetupArch);
244 aBuilder.parse_mirror =
245 rfc1738_unescape (current_ini_name.substr (ldl, cap - ldl));
246 ini_init (ini_file, &aBuilder, myFeedback);
247
248 /*yydebug = 1; */
249
250 if (yyparse () || yyerror_count > 0)
251 myFeedback.error (yyerror_messages);
252 else
253 ++ini_count;
254
255 if (aBuilder.timestamp > setup_timestamp)
256 {
257 setup_timestamp = aBuilder.timestamp;
258 ini_setup_version = aBuilder.version;
259 }
260 delete ini_file;
261 ini_file = NULL;
262 }
263 }
264 return ini_count;
265 }
266
267 static int
268 do_remote_ini (HWND owner)
269 {
270 size_t ini_count = 0;
271 GuiParseFeedback myFeedback;
272 IniDBBuilderPackage aBuilder (myFeedback);
273 io_stream *ini_file = NULL, *ini_sig_file;
274
275 /* FIXME: Get rid of this io_stream pointer travesty. The need to
276 explicitly delete these things is ridiculous. */
277
278 // iterate over all sites
279 for (SiteList::const_iterator n = site_list.begin ();
280 n != site_list.end (); ++n)
281 {
282 bool sig_fail = false;
283 std::string current_ini_ext, current_ini_name, current_ini_sig_name;
284 // iterate over known extensions for setup
285 for (IniList::const_iterator ext = setup_ext_list.begin ();
286 ext != setup_ext_list.end ();
287 ext++)
288 {
289 current_ini_ext = *ext;
290 current_ini_name = n->url + SetupIniDir + SetupBaseName + "." + current_ini_ext;
291 current_ini_sig_name = current_ini_name + ".sig";
292 ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner);
293 ini_file = get_url_to_membuf (current_ini_name, owner);
294 ini_file = check_ini_sig (ini_file, ini_sig_file, sig_fail,
295 n->url.c_str (), current_ini_sig_name.c_str (), owner);
296 // stop searching as soon as we find a setup file
297 if (ini_file)
298 break;
299 }
300 if (ini_file)
301 ini_file = decompress_ini (ini_file);
302 if (!ini_file || sig_fail)
303 {
304 // no setup found or signature invalid
305 note (owner, IDS_SETUPINI_MISSING, SetupBaseName.c_str (), n->url.c_str ());
306 }
307 else
308 {
309 // grok information from setup
310 myFeedback.iniName (current_ini_name);
311 aBuilder.parse_mirror = n->url;
312 ini_init (ini_file, &aBuilder, myFeedback);
313
314 /*yydebug = 1; */
315
316 if (yyparse () || yyerror_count > 0)
317 myFeedback.error (yyerror_messages);
318 else
319 {
320 /* save known-good setup.ini locally */
321 const std::string fp = "file://" + local_dir + "/" +
322 rfc1738_escape_part (n->url) +
323 "/" + SetupIniDir + SetupBaseName + ".ini";
324 io_stream::mkpath_p (PATH_TO_FILE, fp, 0);
325 if (io_stream *out = io_stream::open (fp, "wb", 0))
326 {
327 ini_file->seek (0, IO_SEEK_SET);
328 if (io_stream::copy (ini_file, out) != 0)
329 io_stream::remove (fp);
330 delete out;
331 }
332 ++ini_count;
333 }
334 if (aBuilder.timestamp > setup_timestamp)
335 {
336 setup_timestamp = aBuilder.timestamp;
337 ini_setup_version = aBuilder.version;
338 }
339 delete ini_file;
340 ini_file = NULL;
341 }
342 }
343 return ini_count;
344 }
345
346 static bool
347 do_ini_thread (HINSTANCE h, HWND owner)
348 {
349 size_t ini_count = 0;
350 if (source == IDC_SOURCE_LOCALDIR)
351 ini_count = do_local_ini (owner);
352 else
353 ini_count = do_remote_ini (owner);
354
355 packagedb db;
356 db.upgrade();
357 db.removeEmptyCategories();
358
359 if (ini_count == 0)
360 return false;
361
362 if (get_root_dir ().c_str ())
363 {
364 io_stream::mkpath_p (PATH_TO_DIR, "cygfile:///etc/setup", 0755);
365
366 unsigned int old_timestamp = 0;
367 io_stream *ots =
368 io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0);
369 if (ots)
370 {
371 char temp[20];
372 memset (temp, '\0', 20);
373 if (ots->read (temp, 19))
374 sscanf (temp, "%u", &old_timestamp);
375 delete ots;
376 if (old_timestamp && setup_timestamp
377 && (old_timestamp > setup_timestamp))
378 {
379 int yn = yesno (owner, IDS_OLD_SETUPINI);
380 if (yn == IDNO)
381 Logger ().exit (1);
382 }
383 }
384 if (setup_timestamp)
385 {
386 io_stream *nts =
387 io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0);
388 if (nts)
389 {
390 char temp[20];
391 sprintf (temp, "%u", setup_timestamp);
392 nts->write (temp, strlen (temp));
393 delete nts;
394 }
395 }
396 }
397
398 LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version.size () ?
399 ini_setup_version.c_str () : "(null)",
400 setup_version);
401 if (ini_setup_version.size ())
402 {
403 if (version_compare (setup_version, ini_setup_version) < 0)
404 note (owner, IDS_OLD_SETUP_VERSION, setup_version,
405 ini_setup_version.c_str ());
406 }
407
408 return true;
409 }
410
411 static DWORD WINAPI
412 do_ini_thread_reflector (void* p)
413 {
414 HANDLE *context;
415 context = (HANDLE*)p;
416
417 try
418 {
419 bool succeeded = do_ini_thread ((HINSTANCE)context[0], (HWND)context[1]);
420
421 // Tell the progress page that we're done downloading
422 Progress.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE, 0, succeeded);
423 }
424 TOPLEVEL_CATCH ("ini");
425
426 ExitThread (0);
427 }
428
429 static HANDLE context[2];
430
431 void
432 do_ini (HINSTANCE h, HWND owner)
433 {
434 context[0] = h;
435 context[1] = owner;
436
437 DWORD threadID;
438 CreateThread (NULL, 0, do_ini_thread_reflector, context, 0, &threadID);
439 }
This page took 0.05664 seconds and 6 git commands to generate.