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