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 class GuiParseFeedback
: public IniParseFeedback
70 GuiParseFeedback () : lastpct (0)
72 Progress
.SetText2 ("");
73 Progress
.SetText3 ("");
74 Progress
.SetText4 (IDS_PROGRESS_PARSING
);
77 yyerror_messages
.clear ();
79 virtual void progress (unsigned long const pos
, unsigned long const max
)
82 /* length not known or eof */
85 /* rounding down should mean this only ever fires once */
87 if (pos
* 100 / max
> lastpct
)
89 lastpct
= pos
* 100 / max
;
90 /* Log (LOG_BABBLE) << lastpct << "% (" << pos << " of " << max
91 << " bytes of ini file read)" << endLog; */
93 Progress
.SetBar1 (pos
, max
);
96 sprintf (buf
, "%d %% (%ldk/%ldk)", lastpct
, pos
/1000, max
/1000);
97 Progress
.SetText3 (buf
);
99 virtual void iniName (const std::string
& name
)
101 Progress
.SetText1 (IDS_PROGRESS_PARSING
);
102 Progress
.SetText2 (name
.c_str ());
103 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 note_error(int lineno
, const std::string
&error
)
117 sprintf (tmp
, "%d", lineno
);
119 std::string e
= filename
+ " line " + tmp
+ ": " + error
;
121 if (!yyerror_messages
.empty ())
122 yyerror_messages
+= "\n";
124 yyerror_messages
+= e
;
127 virtual bool has_errors () const
129 return (yyerror_count
> 0);
131 virtual void show_errors () const
133 mbox (Progress
.GetHWND(), yyerror_messages
.c_str (), "Parse Errors", 0);
135 virtual ~ GuiParseFeedback ()
137 Progress
.SetText4 (IDS_PROGRESS_PACKAGE
);
140 unsigned int lastpct
;
141 std::string filename
;
142 std::string yyerror_messages
;
147 decompress_ini (io_stream
*ini_file
, std::string
¤t_ini_name
)
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
)
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
;
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
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
174 if ((io_stream::copy (compressed_stream
, uncompressed
) != 0) ||
175 (compressed_stream
->error () != 0))
177 /* There was a problem decompressing compressed_stream. */
179 "Warning: Error code " << compressed_stream
->error () <<
180 " occurred while uncompressing " << current_ini_name
<<
181 " - possibly truncated or corrupt file. " << endLog
;
187 ini_file
= uncompressed
;
188 ini_file
->seek (0, IO_SEEK_SET
);
195 check_ini_sig (io_stream
* ini_file
, io_stream
* ini_sig_file
,
196 bool& sig_fail
, const char* site
, const char* sig_name
, HWND owner
)
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
)
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"))
210 note (owner
, IDS_SETUPINI_MISSING
, sig_name
, site
);
216 else if (!verify_ini_file_sig (ini_file
, ini_sig_file
, owner
))
218 note (owner
, IDS_SIG_INVALID
, sig_name
, site
);
230 do_local_ini (HWND owner
)
232 bool ini_error
= false;
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
)
238 GuiParseFeedback myFeedback
;
239 IniDBBuilderPackage
aBuilder (myFeedback
);
240 bool sig_fail
= false;
241 std::string current_ini_ext
, current_ini_name
, current_ini_sig_name
;
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
);
251 ini_file
= decompress_ini (ini_file
, current_ini_name
);
252 if (!ini_file
|| sig_fail
)
254 // no setup found or signature invalid
255 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (),
261 // grok information from setup
262 myFeedback
.babble ("Found ini file - " + current_ini_name
);
263 myFeedback
.iniName (current_ini_name
);
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
));
268 ini_init (ini_file
, &aBuilder
, myFeedback
);
270 if (yyparse () || myFeedback
.has_errors())
272 myFeedback
.show_errors ();
276 if (aBuilder
.timestamp
> setup_timestamp
)
278 setup_timestamp
= aBuilder
.timestamp
;
279 ini_setup_version
= aBuilder
.version
;
289 do_remote_ini (HWND owner
)
291 bool ini_error
= false;
292 io_stream
*ini_file
= NULL
, *ini_sig_file
;
294 /* FIXME: Get rid of this io_stream pointer travesty. The need to
295 explicitly delete these things is ridiculous. */
297 // iterate over all sites
298 for (SiteList::const_iterator n
= site_list
.begin ();
299 n
!= site_list
.end (); ++n
)
301 GuiParseFeedback myFeedback
;
302 IniDBBuilderPackage
aBuilder (myFeedback
);
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 ();
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
);
314 ini_file
= get_url_to_membuf (current_ini_name
, owner
);
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
322 ini_file
= decompress_ini (ini_file
, current_ini_name
);
323 if (!ini_file
|| sig_fail
)
325 // no setup found or signature invalid
326 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (), n
->url
.c_str ());
331 // grok information from setup
332 myFeedback
.iniName (current_ini_name
);
333 aBuilder
.parse_mirror
= n
->url
;
334 ini_init (ini_file
, &aBuilder
, myFeedback
);
336 if (yyparse () || myFeedback
.has_errors())
338 myFeedback
.show_errors ();
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))
350 ini_file
->seek (0, IO_SEEK_SET
);
351 if (io_stream::copy (ini_file
, out
) != 0)
352 io_stream::remove (fp
);
356 if (aBuilder
.timestamp
> setup_timestamp
)
358 setup_timestamp
= aBuilder
.timestamp
;
359 ini_setup_version
= aBuilder
.version
;
369 do_ini_thread (HINSTANCE h
, HWND owner
)
374 bool ini_error
= true;
376 if (source
== IDC_SOURCE_LOCALDIR
)
377 ini_error
= do_local_ini (owner
);
379 ini_error
= do_remote_ini (owner
);
384 if (get_root_dir ().c_str ())
386 io_stream::mkpath_p (PATH_TO_DIR
, "cygfile:///etc/setup", 0755);
388 unsigned int old_timestamp
= 0;
390 io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0);
394 memset (temp
, '\0', 20);
395 if (ots
->read (temp
, 19))
396 sscanf (temp
, "%u", &old_timestamp
);
398 if (old_timestamp
&& setup_timestamp
399 && (old_timestamp
> setup_timestamp
))
401 int yn
= yesno (owner
, IDS_OLD_SETUPINI
);
409 io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0);
413 sprintf (temp
, "%u", setup_timestamp
);
414 nts
->write (temp
, strlen (temp
));
420 LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version
.size () ?
421 ini_setup_version
.c_str () : "(null)",
423 if (ini_setup_version
.size ())
425 if ((version_compare (setup_version
, ini_setup_version
) < 0)
426 && !NoVersionCheckOption
)
427 note (owner
, IDS_OLD_SETUP_VERSION
, setup_version
,
428 ini_setup_version
.c_str ());
435 do_ini_thread_reflector (void* p
)
438 context
= (HANDLE
*)p
;
440 SetThreadUILanguage(langid
);
444 bool succeeded
= do_ini_thread ((HINSTANCE
)context
[0], (HWND
)context
[1]);
446 // Tell the progress page that we're done downloading
447 Progress
.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE
, 0, succeeded
);
449 TOPLEVEL_CATCH ((HWND
) context
[1], "ini");
454 static HANDLE context
[2];
457 do_ini (HINSTANCE h
, HWND owner
)
463 CreateThread (NULL
, 0, do_ini_thread_reflector
, context
, 0, &threadID
);