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", IDS_HELPTEXT_NO_VERIFY
);
65 static BoolOption
NoVersionCheckOption (false, '\0', "no-version-check", IDS_HELPTEXT_NO_VERSION_CHECK
);
67 class GuiParseFeedback
: public IniParseFeedback
70 GuiParseFeedback () : lastpct (0)
72 Progress
.SetText1 (IDS_PROGRESS_PARSING
);
73 Progress
.SetText2 ("");
74 Progress
.SetText3 ("");
75 Progress
.SetText4 (IDS_PROGRESS_PROGRESS
);
78 yyerror_messages
.clear ();
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
.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
.SetText2 ("");
138 Progress
.SetText3 ("");
139 Progress
.SetText4 (IDS_PROGRESS_PACKAGE
);
140 Progress
.SetBar1 (0);
143 unsigned int lastpct
;
144 std::string filename
;
145 std::string yyerror_messages
;
150 decompress_ini (io_stream
*ini_file
, std::string
¤t_ini_name
)
152 // Replace the current compressed setup stream with its decompressed
153 // version. Which decompressor to use is determined by file magic.
154 io_stream
*compressed_stream
= compress::decompress (ini_file
);
155 if (!compressed_stream
)
157 /* This isn't a known compression format or an uncompressed file
158 stream. Pass it on in case it was uncompressed, it will
159 generate a parser error if it was some unknown format. */
160 delete compressed_stream
;
164 /* Decompress the entire file in memory. This has the advantage
165 that input_stream->get_size () will work during parsing and
166 we'll have an accurate status bar. Also, we can't seek
167 compressed streams, so when we write out a local cached copy
168 of the .ini file below, we'd otherwise have to delete this
169 stream and uncompress it again from the start, which is
170 wasteful. The current uncompressed size of the setup.ini
171 file as of 2015 is about 5 MiB, so this is not a great deal
173 io_stream
*uncompressed
= new io_stream_memory ();
174 /* Note that the decompress io_stream now "owns" the underlying
175 compressed io_stream instance, so it need not be deleted
177 if ((io_stream::copy (compressed_stream
, uncompressed
) != 0) ||
178 (compressed_stream
->error () != 0))
180 /* There was a problem decompressing compressed_stream. */
182 "Warning: Error code " << compressed_stream
->error () <<
183 " occurred while uncompressing " << current_ini_name
<<
184 " - possibly truncated or corrupt file. " << endLog
;
190 ini_file
= uncompressed
;
191 ini_file
->seek (0, IO_SEEK_SET
);
198 check_ini_sig (io_stream
* ini_file
, io_stream
* ini_sig_file
,
199 bool& sig_fail
, const char* site
, const char* sig_name
, HWND owner
)
201 /* Unless the NoVerifyOption is set, check the signature for the
202 current setup and record the result. On a failed signature check
203 the streams are invalidated so even if we tried to read in the
204 setup anyway there's be nothing to parse. */
205 if (!NoVerifyOption
&& ini_file
)
208 // don't complain if the user installs from localdir and no
209 // signature file is present
210 // TODO: download the ini + signature file instead
211 if (casecompare (site
, "localdir"))
213 note (owner
, IDS_SETUPINI_MISSING
, sig_name
, site
);
219 else if (!verify_ini_file_sig (ini_file
, ini_sig_file
, owner
))
221 note (owner
, IDS_SIG_INVALID
, sig_name
, site
);
233 do_local_ini (HWND owner
)
235 bool ini_error
= false;
236 io_stream
*ini_file
, *ini_sig_file
;
237 // iterate over all setup files found in do_from_local_dir
238 for (IniList::const_iterator n
= found_ini_list
.begin ();
239 n
!= found_ini_list
.end (); ++n
)
241 GuiParseFeedback myFeedback
;
242 IniDBBuilderPackage
aBuilder (myFeedback
);
243 bool sig_fail
= false;
244 std::string current_ini_ext
, current_ini_name
, current_ini_sig_name
;
246 current_ini_name
= *n
;
247 current_ini_sig_name
= current_ini_name
+ ".sig";
248 current_ini_ext
= current_ini_name
.substr (current_ini_name
.rfind (".") + 1);
249 ini_sig_file
= io_stream::open ("file://" + current_ini_sig_name
, "rb", 0);
250 ini_file
= io_stream::open ("file://" + current_ini_name
, "rb", 0);
251 ini_file
= check_ini_sig (ini_file
, ini_sig_file
, sig_fail
,
252 "localdir", current_ini_sig_name
.c_str (), owner
);
254 ini_file
= decompress_ini (ini_file
, current_ini_name
);
255 if (!ini_file
|| sig_fail
)
257 // no setup found or signature invalid
258 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (),
264 // grok information from setup
265 myFeedback
.babble ("Found ini file - " + current_ini_name
);
266 myFeedback
.iniName (current_ini_name
);
267 int ldl
= local_dir
.length () + 1;
268 int cap
= current_ini_name
.rfind ("/" + SetupArch
);
269 aBuilder
.parse_mirror
=
270 rfc1738_unescape (current_ini_name
.substr (ldl
, cap
- ldl
));
271 ini_init (ini_file
, &aBuilder
, myFeedback
);
273 if (yyparse () || myFeedback
.has_errors())
275 myFeedback
.show_errors ();
279 if (aBuilder
.timestamp
> setup_timestamp
)
281 setup_timestamp
= aBuilder
.timestamp
;
282 ini_setup_version
= aBuilder
.version
;
292 do_remote_ini (HWND owner
)
294 bool ini_error
= false;
295 io_stream
*ini_file
= NULL
, *ini_sig_file
;
297 /* FIXME: Get rid of this io_stream pointer travesty. The need to
298 explicitly delete these things is ridiculous. */
300 // iterate over all sites
301 for (SiteList::const_iterator n
= site_list
.begin ();
302 n
!= site_list
.end (); ++n
)
304 GuiParseFeedback myFeedback
;
305 IniDBBuilderPackage
aBuilder (myFeedback
);
306 bool sig_fail
= false;
307 std::string current_ini_ext
, current_ini_name
, current_ini_sig_name
;
308 // iterate over known extensions for setup
309 for (IniList::const_iterator ext
= setup_ext_list
.begin ();
310 ext
!= setup_ext_list
.end ();
313 current_ini_ext
= *ext
;
314 current_ini_name
= n
->url
+ SetupIniDir
+ SetupBaseName
+ "." + current_ini_ext
;
315 current_ini_sig_name
= current_ini_name
+ ".sig";
316 ini_sig_file
= get_url_to_membuf (current_ini_sig_name
, owner
);
317 ini_file
= get_url_to_membuf (current_ini_name
, owner
);
318 ini_file
= check_ini_sig (ini_file
, ini_sig_file
, sig_fail
,
319 n
->url
.c_str (), current_ini_sig_name
.c_str (), owner
);
320 // stop searching as soon as we find a setup file
325 ini_file
= decompress_ini (ini_file
, current_ini_name
);
326 if (!ini_file
|| sig_fail
)
328 // no setup found or signature invalid
329 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (), n
->url
.c_str ());
334 // grok information from setup
335 myFeedback
.iniName (current_ini_name
);
336 aBuilder
.parse_mirror
= n
->url
;
337 ini_init (ini_file
, &aBuilder
, myFeedback
);
339 if (yyparse () || myFeedback
.has_errors())
341 myFeedback
.show_errors ();
346 /* save known-good setup.ini locally */
347 const std::string fp
= "file://" + local_dir
+ "/" +
348 rfc1738_escape_part (n
->url
) +
349 "/" + SetupIniDir
+ SetupBaseName
+ ".ini";
350 io_stream::mkpath_p (PATH_TO_FILE
, fp
, 0);
351 if (io_stream
*out
= io_stream::open (fp
, "wb", 0))
353 ini_file
->seek (0, IO_SEEK_SET
);
354 if (io_stream::copy (ini_file
, out
) != 0)
355 io_stream::remove (fp
);
359 if (aBuilder
.timestamp
> setup_timestamp
)
361 setup_timestamp
= aBuilder
.timestamp
;
362 ini_setup_version
= aBuilder
.version
;
372 do_ini_thread (HINSTANCE h
, HWND owner
)
377 bool ini_error
= true;
379 if (source
== IDC_SOURCE_LOCALDIR
)
380 ini_error
= do_local_ini (owner
);
382 ini_error
= do_remote_ini (owner
);
387 if (get_root_dir ().c_str ())
389 io_stream::mkpath_p (PATH_TO_DIR
, "cygfile:///etc/setup", 0755);
391 unsigned int old_timestamp
= 0;
393 io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0);
397 memset (temp
, '\0', 20);
398 if (ots
->read (temp
, 19))
399 sscanf (temp
, "%u", &old_timestamp
);
401 if (old_timestamp
&& setup_timestamp
402 && (old_timestamp
> setup_timestamp
))
404 int yn
= yesno (owner
, IDS_OLD_SETUPINI
);
412 io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0);
416 sprintf (temp
, "%u", setup_timestamp
);
417 nts
->write (temp
, strlen (temp
));
423 LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version
.size () ?
424 ini_setup_version
.c_str () : "(null)",
426 if (ini_setup_version
.size ())
428 if ((version_compare (setup_version
, ini_setup_version
) < 0)
429 && !NoVersionCheckOption
)
430 note (owner
, IDS_OLD_SETUP_VERSION
, setup_version
,
431 ini_setup_version
.c_str ());
438 do_ini_thread_reflector (void* p
)
441 context
= (HANDLE
*)p
;
443 SetThreadUILanguage(langid
);
447 bool succeeded
= do_ini_thread ((HINSTANCE
)context
[0], (HWND
)context
[1]);
449 // Tell the progress page that we're done downloading
450 Progress
.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE
, 0, succeeded
);
452 TOPLEVEL_CATCH ((HWND
) context
[1], "ini");
457 static HANDLE context
[2];
460 do_ini (HINSTANCE h
, HWND owner
)
466 CreateThread (NULL
, 0, do_ini_thread_reflector
, context
, 0, &threadID
);