]> cygwin.com Git - cygwin-apps/setup.git/blob - ini.cc
7afeba229913ee97392194e15de1eb1889e1d8f2
[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 (Progress.GetHWND(), message.c_str (), "Warning", 0);
112 }
113 virtual void error (const std::string& message)const
114 {
115 mbox (Progress.GetHWND(), 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 bool
209 do_local_ini (HWND owner)
210 {
211 bool ini_error = false;
212 io_stream *ini_file, *ini_sig_file;
213 // iterate over all setup files found in do_from_local_dir
214 for (IniList::const_iterator n = found_ini_list.begin ();
215 n != found_ini_list.end (); ++n)
216 {
217 GuiParseFeedback myFeedback;
218 IniDBBuilderPackage aBuilder (myFeedback);
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 ini_error = true;
237 }
238 else
239 {
240 // grok information from setup
241 myFeedback.babble ("Found ini file - " + current_ini_name);
242 myFeedback.iniName (current_ini_name);
243 int ldl = local_dir.length () + 1;
244 int cap = current_ini_name.rfind ("/" + SetupArch);
245 aBuilder.parse_mirror =
246 rfc1738_unescape (current_ini_name.substr (ldl, cap - ldl));
247 ini_init (ini_file, &aBuilder, myFeedback);
248
249 if (yyparse () || yyerror_count > 0)
250 {
251 myFeedback.error (yyerror_messages);
252 ini_error = true;
253 }
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_error;
265 }
266
267 static bool
268 do_remote_ini (HWND owner)
269 {
270 bool ini_error = false;
271 io_stream *ini_file = NULL, *ini_sig_file;
272
273 /* FIXME: Get rid of this io_stream pointer travesty. The need to
274 explicitly delete these things is ridiculous. */
275
276 // iterate over all sites
277 for (SiteList::const_iterator n = site_list.begin ();
278 n != site_list.end (); ++n)
279 {
280 GuiParseFeedback myFeedback;
281 IniDBBuilderPackage aBuilder (myFeedback);
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 ini_error = true;
307 }
308 else
309 {
310 // grok information from setup
311 myFeedback.iniName (current_ini_name);
312 aBuilder.parse_mirror = n->url;
313 ini_init (ini_file, &aBuilder, myFeedback);
314
315 if (yyparse () || yyerror_count > 0)
316 {
317 myFeedback.error (yyerror_messages);
318 ini_error = true;
319 }
320 else
321 {
322 /* save known-good setup.ini locally */
323 const std::string fp = "file://" + local_dir + "/" +
324 rfc1738_escape_part (n->url) +
325 "/" + SetupIniDir + SetupBaseName + ".ini";
326 io_stream::mkpath_p (PATH_TO_FILE, fp, 0);
327 if (io_stream *out = io_stream::open (fp, "wb", 0))
328 {
329 ini_file->seek (0, IO_SEEK_SET);
330 if (io_stream::copy (ini_file, out) != 0)
331 io_stream::remove (fp);
332 delete out;
333 }
334 }
335 if (aBuilder.timestamp > setup_timestamp)
336 {
337 setup_timestamp = aBuilder.timestamp;
338 ini_setup_version = aBuilder.version;
339 }
340 delete ini_file;
341 ini_file = NULL;
342 }
343 }
344 return ini_error;
345 }
346
347 static bool
348 do_ini_thread (HINSTANCE h, HWND owner)
349 {
350 packagedb db;
351 db.init();
352
353 bool ini_error = true;
354
355 if (source == IDC_SOURCE_LOCALDIR)
356 ini_error = do_local_ini (owner);
357 else
358 ini_error = do_remote_ini (owner);
359
360 if (ini_error)
361 return false;
362
363 if (get_root_dir ().c_str ())
364 {
365 io_stream::mkpath_p (PATH_TO_DIR, "cygfile:///etc/setup", 0755);
366
367 unsigned int old_timestamp = 0;
368 io_stream *ots =
369 io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0);
370 if (ots)
371 {
372 char temp[20];
373 memset (temp, '\0', 20);
374 if (ots->read (temp, 19))
375 sscanf (temp, "%u", &old_timestamp);
376 delete ots;
377 if (old_timestamp && setup_timestamp
378 && (old_timestamp > setup_timestamp))
379 {
380 int yn = yesno (owner, IDS_OLD_SETUPINI);
381 if (yn == IDNO)
382 Logger ().exit (1);
383 }
384 }
385 if (setup_timestamp)
386 {
387 io_stream *nts =
388 io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0);
389 if (nts)
390 {
391 char temp[20];
392 sprintf (temp, "%u", setup_timestamp);
393 nts->write (temp, strlen (temp));
394 delete nts;
395 }
396 }
397 }
398
399 LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version.size () ?
400 ini_setup_version.c_str () : "(null)",
401 setup_version);
402 if (ini_setup_version.size ())
403 {
404 if (version_compare (setup_version, ini_setup_version) < 0)
405 note (owner, IDS_OLD_SETUP_VERSION, setup_version,
406 ini_setup_version.c_str ());
407 }
408
409 return true;
410 }
411
412 static DWORD WINAPI
413 do_ini_thread_reflector (void* p)
414 {
415 HANDLE *context;
416 context = (HANDLE*)p;
417
418 try
419 {
420 bool succeeded = do_ini_thread ((HINSTANCE)context[0], (HWND)context[1]);
421
422 // Tell the progress page that we're done downloading
423 Progress.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE, 0, succeeded);
424 }
425 TOPLEVEL_CATCH ((HWND) context[1], "ini");
426
427 ExitThread (0);
428 }
429
430 static HANDLE context[2];
431
432 void
433 do_ini (HINSTANCE h, HWND owner)
434 {
435 context[0] = h;
436 context[1] = owner;
437
438 DWORD threadID;
439 CreateThread (NULL, 0, do_ini_thread_reflector, context, 0, &threadID);
440 }
This page took 0.051968 seconds and 4 git commands to generate.