2 * Copyright (c) 2000,2007 Red Hat, Inc.
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.
9 * A copy of the GNU General Public License can be found at
12 * Written by DJ Delorie <dj@cygnus.com>
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. */
23 #include "csu_util/rfc1738.h"
24 #include "csu_util/version_compare.h"
26 #include "setup_version.h"
42 #include "IniParseFeedback.h"
44 #include "io_stream.h"
45 #include "io_stream_memory.h"
49 #include "getopt++/BoolOption.h"
50 #include "IniDBBuilderPackage.h"
52 #include "Exception.h"
54 #include "package_db.h"
56 extern ThreeBarProgressPage Progress
;
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
)));
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");
67 extern int yyparse ();
69 /*extern int yydebug;*/
71 class GuiParseFeedback
: public IniParseFeedback
74 GuiParseFeedback () : lastpct (0)
76 Progress
.SetText2 ("");
77 Progress
.SetText3 ("");
78 Progress
.SetText4 ("Progress:");
80 virtual void progress (unsigned long const pos
, unsigned long const max
)
83 /* length not known or eof */
86 /* rounding down should mean this only ever fires once */
88 if (pos
* 100 / max
> lastpct
)
90 lastpct
= pos
* 100 / max
;
91 /* Log (LOG_BABBLE) << lastpct << "% (" << pos << " of " << max
92 << " bytes of ini file read)" << endLog; */
94 Progress
.SetBar1 (pos
, max
);
97 sprintf (buf
, "%d %% (%ldk/%ldk)", lastpct
, pos
/1000, max
/1000);
98 Progress
.SetText3 (buf
);
100 virtual void iniName (const std::string
& name
)
102 Progress
.SetText1 ("Parsing...");
103 Progress
.SetText2 (name
.c_str ());
104 Progress
.SetText3 ("");
106 virtual void babble (const std::string
& message
)const
108 Log (LOG_BABBLE
) << message
<< endLog
;
110 virtual void warning (const std::string
& message
)const
112 mbox (Progress
.GetHWND(), message
.c_str (), "Warning", 0);
114 virtual void error (const std::string
& message
)const
116 mbox (Progress
.GetHWND(), message
.c_str (), "Parse Errors", 0);
118 virtual ~ GuiParseFeedback ()
120 Progress
.SetText4 ("Package:");
123 unsigned int lastpct
;
127 decompress_ini (io_stream
*ini_file
)
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
)
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
;
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
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
154 if ((io_stream::copy (compressed_stream
, uncompressed
) != 0) ||
155 (compressed_stream
->error () != 0))
157 /* There was a problem decompressing compressed_stream. */
159 "Warning: Error code " << compressed_stream
->error () <<
160 " occurred while uncompressing " << current_ini_name
<<
161 " - possibly truncated or corrupt file. " << endLog
;
167 ini_file
= uncompressed
;
168 ini_file
->seek (0, IO_SEEK_SET
);
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
)
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
)
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"))
190 note (owner
, IDS_SETUPINI_MISSING
, sig_name
, site
);
196 else if (!verify_ini_file_sig (ini_file
, ini_sig_file
, owner
))
198 note (owner
, IDS_SIG_INVALID
, sig_name
, site
);
210 do_local_ini (HWND owner
)
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
)
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
;
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
);
231 ini_file
= decompress_ini (ini_file
);
232 if (!ini_file
|| sig_fail
)
234 // no setup found or signature invalid
235 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (),
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
);
250 if (yyparse () || yyerror_count
> 0)
252 myFeedback
.error (yyerror_messages
);
256 if (aBuilder
.timestamp
> setup_timestamp
)
258 setup_timestamp
= aBuilder
.timestamp
;
259 ini_setup_version
= aBuilder
.version
;
269 do_remote_ini (HWND owner
)
271 bool ini_error
= false;
272 io_stream
*ini_file
= NULL
, *ini_sig_file
;
274 /* FIXME: Get rid of this io_stream pointer travesty. The need to
275 explicitly delete these things is ridiculous. */
277 // iterate over all sites
278 for (SiteList::const_iterator n
= site_list
.begin ();
279 n
!= site_list
.end (); ++n
)
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 ();
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
302 ini_file
= decompress_ini (ini_file
);
303 if (!ini_file
|| sig_fail
)
305 // no setup found or signature invalid
306 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (), n
->url
.c_str ());
311 // grok information from setup
312 myFeedback
.iniName (current_ini_name
);
313 aBuilder
.parse_mirror
= n
->url
;
314 ini_init (ini_file
, &aBuilder
, myFeedback
);
316 if (yyparse () || yyerror_count
> 0)
318 myFeedback
.error (yyerror_messages
);
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))
330 ini_file
->seek (0, IO_SEEK_SET
);
331 if (io_stream::copy (ini_file
, out
) != 0)
332 io_stream::remove (fp
);
336 if (aBuilder
.timestamp
> setup_timestamp
)
338 setup_timestamp
= aBuilder
.timestamp
;
339 ini_setup_version
= aBuilder
.version
;
349 do_ini_thread (HINSTANCE h
, HWND owner
)
354 bool ini_error
= true;
356 if (source
== IDC_SOURCE_LOCALDIR
)
357 ini_error
= do_local_ini (owner
);
359 ini_error
= do_remote_ini (owner
);
364 if (get_root_dir ().c_str ())
366 io_stream::mkpath_p (PATH_TO_DIR
, "cygfile:///etc/setup", 0755);
368 unsigned int old_timestamp
= 0;
370 io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0);
374 memset (temp
, '\0', 20);
375 if (ots
->read (temp
, 19))
376 sscanf (temp
, "%u", &old_timestamp
);
378 if (old_timestamp
&& setup_timestamp
379 && (old_timestamp
> setup_timestamp
))
381 int yn
= yesno (owner
, IDS_OLD_SETUPINI
);
389 io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0);
393 sprintf (temp
, "%u", setup_timestamp
);
394 nts
->write (temp
, strlen (temp
));
400 LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version
.size () ?
401 ini_setup_version
.c_str () : "(null)",
403 if (ini_setup_version
.size ())
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 ());
415 do_ini_thread_reflector (void* p
)
418 context
= (HANDLE
*)p
;
422 bool succeeded
= do_ini_thread ((HINSTANCE
)context
[0], (HWND
)context
[1]);
424 // Tell the progress page that we're done downloading
425 Progress
.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE
, 0, succeeded
);
427 TOPLEVEL_CATCH ((HWND
) context
[1], "ini");
432 static HANDLE context
[2];
435 do_ini (HINSTANCE h
, HWND owner
)
441 CreateThread (NULL
, 0, do_ini_thread_reflector
, context
, 0, &threadID
);