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");
66 extern int yyparse ();
68 /*extern int yydebug;*/
70 class GuiParseFeedback
: public IniParseFeedback
73 GuiParseFeedback () : lastpct (0)
75 Progress
.SetText2 ("");
76 Progress
.SetText3 ("");
77 Progress
.SetText4 ("Progress:");
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 ("Parsing...");
102 Progress
.SetText2 (name
.c_str ());
103 Progress
.SetText3 ("");
105 virtual void babble (const std::string
& message
)const
107 Log (LOG_BABBLE
) << message
<< endLog
;
109 virtual void warning (const std::string
& message
)const
111 mbox (0, message
.c_str (), "Warning", 0);
113 virtual void error (const std::string
& message
)const
115 mbox (0, message
.c_str (), "Parse Errors", 0);
117 virtual ~ GuiParseFeedback ()
119 Progress
.SetText4 ("Package:");
122 unsigned int lastpct
;
126 decompress_ini (io_stream
*ini_file
)
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
)
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
;
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
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
153 if ((io_stream::copy (compressed_stream
, uncompressed
) != 0) ||
154 (compressed_stream
->error () != 0))
156 /* There was a problem decompressing compressed_stream. */
158 "Warning: Error code " << compressed_stream
->error () <<
159 " occurred while uncompressing " << current_ini_name
<<
160 " - possibly truncated or corrupt file. " << endLog
;
166 ini_file
= uncompressed
;
167 ini_file
->seek (0, IO_SEEK_SET
);
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
)
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
)
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"))
189 note (owner
, IDS_SETUPINI_MISSING
, sig_name
, site
);
195 else if (!verify_ini_file_sig (ini_file
, ini_sig_file
, owner
))
197 note (owner
, IDS_SIG_INVALID
, sig_name
, site
);
209 do_local_ini (HWND owner
)
211 size_t ini_count
= 0;
212 GuiParseFeedback myFeedback
;
213 IniDBBuilderPackage
aBuilder (myFeedback
);
214 io_stream
*ini_file
, *ini_sig_file
;
215 // iterate over all setup files found in do_from_local_dir
216 for (IniList::const_iterator n
= found_ini_list
.begin ();
217 n
!= found_ini_list
.end (); ++n
)
219 bool sig_fail
= false;
220 std::string current_ini_ext
, current_ini_name
, current_ini_sig_name
;
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
);
230 ini_file
= decompress_ini (ini_file
);
231 if (!ini_file
|| sig_fail
)
233 // no setup found or signature invalid
234 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (),
239 // grok information from setup
240 myFeedback
.babble ("Found ini file - " + current_ini_name
);
241 myFeedback
.iniName (current_ini_name
);
242 int ldl
= local_dir
.length () + 1;
243 int cap
= current_ini_name
.rfind ("/" + SetupArch
);
244 aBuilder
.parse_mirror
=
245 rfc1738_unescape (current_ini_name
.substr (ldl
, cap
- ldl
));
246 ini_init (ini_file
, &aBuilder
, myFeedback
);
250 if (yyparse () || yyerror_count
> 0)
251 myFeedback
.error (yyerror_messages
);
255 if (aBuilder
.timestamp
> setup_timestamp
)
257 setup_timestamp
= aBuilder
.timestamp
;
258 ini_setup_version
= aBuilder
.version
;
268 do_remote_ini (HWND owner
)
270 size_t ini_count
= 0;
271 GuiParseFeedback myFeedback
;
272 IniDBBuilderPackage
aBuilder (myFeedback
);
273 io_stream
*ini_file
= NULL
, *ini_sig_file
;
275 /* FIXME: Get rid of this io_stream pointer travesty. The need to
276 explicitly delete these things is ridiculous. */
278 // iterate over all sites
279 for (SiteList::const_iterator n
= site_list
.begin ();
280 n
!= site_list
.end (); ++n
)
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 ();
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
301 ini_file
= decompress_ini (ini_file
);
302 if (!ini_file
|| sig_fail
)
304 // no setup found or signature invalid
305 note (owner
, IDS_SETUPINI_MISSING
, SetupBaseName
.c_str (), n
->url
.c_str ());
309 // grok information from setup
310 myFeedback
.iniName (current_ini_name
);
311 aBuilder
.parse_mirror
= n
->url
;
312 ini_init (ini_file
, &aBuilder
, myFeedback
);
316 if (yyparse () || yyerror_count
> 0)
317 myFeedback
.error (yyerror_messages
);
320 /* save known-good setup.ini locally */
321 const std::string fp
= "file://" + local_dir
+ "/" +
322 rfc1738_escape_part (n
->url
) +
323 "/" + SetupIniDir
+ SetupBaseName
+ ".ini";
324 io_stream::mkpath_p (PATH_TO_FILE
, fp
, 0);
325 if (io_stream
*out
= io_stream::open (fp
, "wb", 0))
327 ini_file
->seek (0, IO_SEEK_SET
);
328 if (io_stream::copy (ini_file
, out
) != 0)
329 io_stream::remove (fp
);
334 if (aBuilder
.timestamp
> setup_timestamp
)
336 setup_timestamp
= aBuilder
.timestamp
;
337 ini_setup_version
= aBuilder
.version
;
347 do_ini_thread (HINSTANCE h
, HWND owner
)
349 size_t ini_count
= 0;
350 if (source
== IDC_SOURCE_LOCALDIR
)
351 ini_count
= do_local_ini (owner
);
353 ini_count
= do_remote_ini (owner
);
357 db
.removeEmptyCategories();
362 if (get_root_dir ().c_str ())
364 io_stream::mkpath_p (PATH_TO_DIR
, "cygfile:///etc/setup", 0755);
366 unsigned int old_timestamp
= 0;
368 io_stream::open ("cygfile:///etc/setup/timestamp", "rt", 0);
372 memset (temp
, '\0', 20);
373 if (ots
->read (temp
, 19))
374 sscanf (temp
, "%u", &old_timestamp
);
376 if (old_timestamp
&& setup_timestamp
377 && (old_timestamp
> setup_timestamp
))
379 int yn
= yesno (owner
, IDS_OLD_SETUPINI
);
387 io_stream::open ("cygfile:///etc/setup/timestamp", "wt", 0);
391 sprintf (temp
, "%u", setup_timestamp
);
392 nts
->write (temp
, strlen (temp
));
398 LogBabblePrintf (".ini setup_version is %s, our setup_version is %s", ini_setup_version
.size () ?
399 ini_setup_version
.c_str () : "(null)",
401 if (ini_setup_version
.size ())
403 if (version_compare (setup_version
, ini_setup_version
) < 0)
404 note (owner
, IDS_OLD_SETUP_VERSION
, setup_version
,
405 ini_setup_version
.c_str ());
412 do_ini_thread_reflector (void* p
)
415 context
= (HANDLE
*)p
;
419 bool succeeded
= do_ini_thread ((HINSTANCE
)context
[0], (HWND
)context
[1]);
421 // Tell the progress page that we're done downloading
422 Progress
.PostMessageNow (WM_APP_SETUP_INI_DOWNLOAD_COMPLETE
, 0, succeeded
);
424 TOPLEVEL_CATCH ("ini");
429 static HANDLE context
[2];
432 do_ini (HINSTANCE h
, HWND owner
)
438 CreateThread (NULL
, 0, do_ini_thread_reflector
, context
, 0, &threadID
);