]> cygwin.com Git - cygwin-apps/setup.git/blob - ini.cc
5ba481eb5a02deb3a58280a956778be32350f87d
[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 #if 0
22 static const char *cvsid =
23 "\n%%% $Id$\n";
24 #endif
25
26 #include "ini.h"
27
28 #include "csu_util/rfc1738.h"
29 #include "csu_util/version_compare.h"
30
31 #include "setup_version.h"
32 #include "win32.h"
33 #include "LogSingleton.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <process.h>
39
40 #include "resource.h"
41 #include "state.h"
42 #include "geturl.h"
43 #include "dialog.h"
44 #include "msg.h"
45 #include "mount.h"
46 #include "site.h"
47 #include "find.h"
48 #include "IniParseFindVisitor.h"
49 #include "IniParseFeedback.h"
50
51 #include "io_stream.h"
52 #include "io_stream_memory.h"
53
54 #include "threebar.h"
55
56 #include "getopt++/BoolOption.h"
57 #include "IniDBBuilderPackage.h"
58 #include "compress.h"
59 #include "Exception.h"
60 #include "crypto.h"
61
62 extern ThreeBarProgressPage Progress;
63
64 unsigned int setup_timestamp = 0;
65 std::string ini_setup_version;
66 std::string current_ini_sig_name;
67
68 static BoolOption NoVerifyOption (false, 'X', "no-verify", "Don't verify setup.ini signatures");
69
70 extern int yyparse ();
71 /*extern int yydebug;*/
72
73 class GuiParseFeedback : public IniParseFeedback
74 {
75 public:
76 GuiParseFeedback () : lastpct (0)
77 {
78 Progress.SetText2 ("");
79 Progress.SetText3 ("");
80 Progress.SetText4 ("Progress:");
81 }
82 virtual void progress(unsigned long const pos, unsigned long const max)
83 {
84 if (!max)
85 /* length not known or eof */
86 return;
87 if (lastpct == 100)
88 /* rounding down should mean this only ever fires once */
89 lastpct = 0;
90 if (pos * 100 / max > lastpct)
91 {
92 lastpct = pos * 100 / max;
93 /* log (LOG_BABBLE) << lastpct << "% (" << pos << " of " << max
94 << " bytes of ini file read)" << endLog; */
95 }
96 Progress.SetBar1(pos, max);
97 }
98 virtual void iniName (const std::string& name)
99 {
100 Progress.SetText1 (("Parsing ini file \"" + name + "\"").c_str());
101 }
102 virtual void babble(const std::string& message)const
103 {
104 log (LOG_BABBLE) << message << endLog;
105 }
106 virtual void warning (const std::string& message)const
107 {
108 MessageBox (0, message.c_str(), "Warning", 0);
109 }
110 virtual void error(const std::string& message)const
111 {
112 MessageBox (0, message.c_str(), "Parse Errors", 0);
113 }
114 virtual ~ GuiParseFeedback ()
115 {
116 Progress.SetText4("Package:");
117 }
118 private:
119 unsigned int lastpct;
120 };
121
122 static int
123 do_local_ini (HWND owner)
124 {
125 GuiParseFeedback myFeedback;
126 IniDBBuilderPackage findBuilder(myFeedback);
127 IniParseFindVisitor myVisitor (findBuilder, local_dir, myFeedback);
128 Find (local_dir).accept(myVisitor);
129 setup_timestamp = myVisitor.timeStamp();
130 ini_setup_version = myVisitor.version();
131 return myVisitor.iniCount();
132 }
133
134 static int
135 do_remote_ini (HWND owner)
136 {
137 size_t ini_count = 0;
138 GuiParseFeedback myFeedback;
139 IniDBBuilderPackage aBuilder(myFeedback);
140 io_stream *ini_file, *ini_sig_file;
141
142 /* FIXME: Get rid of this io_stream pointer travesty. The need to
143 explicitly delete these things is ridiculous. Note that the
144 decompress io_stream "owns" the underlying compressed io_stream
145 instance, so it should not be deleted explicitly. */
146
147 for (SiteList::const_iterator n = site_list.begin();
148 n != site_list.end(); ++n)
149 {
150 bool sig_fail = false;
151 /* First try to fetch the .bz2 compressed ini file. */
152 current_ini_name = n->url + "/" + SETUP_BZ2_FILENAME;
153 current_ini_sig_name = n->url + "/" + SETUP_BZ2_FILENAME + ".sig";
154 ini_file = get_url_to_membuf (current_ini_name, owner);
155 if (!NoVerifyOption)
156 ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner);
157 if (!NoVerifyOption && ini_file && !ini_sig_file)
158 {
159 note (owner, IDS_SETUPINI_MISSING, current_ini_sig_name.c_str(), n->url.c_str());
160 delete ini_file;
161 ini_file = NULL;
162 sig_fail = true;
163 }
164 else if (!NoVerifyOption && ini_file && !verify_ini_file_sig (ini_file, ini_sig_file, owner))
165 {
166 note (owner, IDS_SIG_INVALID, current_ini_sig_name.c_str(), n->url.c_str());
167 delete ini_file;
168 ini_file = NULL;
169 delete ini_sig_file;
170 ini_sig_file = NULL;
171 sig_fail = true;
172 }
173 if (ini_file)
174 {
175 /* Decompress the entire file in memory right now. This has the
176 advantage that input_stream->get_size() will work during parsing
177 and we'll have an accurate status bar. Also, we can't seek
178 bz2 streams, so when it comes time to write out a local cached
179 copy of the .ini file below, we'd otherwise have to delete this
180 stream and uncompress it again from the start, which is wasteful.
181 The current uncompresed size of the .ini file as of 2007 is less
182 than 600 kB, so this is not a great deal of memory. */
183 io_stream *bz2_stream = compress::decompress (ini_file);
184 if (!bz2_stream)
185 {
186 /* This isn't a valid bz2 file. */
187 delete ini_file;
188 ini_file = NULL;
189 }
190 else
191 {
192 io_stream *uncompressed = new io_stream_memory ();
193
194 if (io_stream::copy (bz2_stream, uncompressed) != 0 ||
195 bz2_stream->error () == EIO)
196 {
197 /* There was a problem decompressing bz2. */
198 delete bz2_stream;
199 delete uncompressed;
200 ini_file = NULL;
201 log (LOG_PLAIN) <<
202 "Warning: Problem encountered while uncompressing " <<
203 current_ini_name << " - possibly truncated or corrupt bzip2"
204 " file. Retrying with uncompressed version." << endLog;
205 }
206 else
207 {
208 delete bz2_stream;
209 ini_file = uncompressed;
210 ini_file->seek (0, IO_SEEK_SET);
211 }
212 }
213 }
214
215 if (!ini_file)
216 {
217 /* Try to look for a plain .ini file because one of the following
218 happened above:
219 - there was no .bz2 file found on the mirror.
220 - the .bz2 file didn't look like a valid bzip2 file.
221 - there was an error during bzip2 decompression. */
222 current_ini_name = n->url + "/" + SETUP_INI_FILENAME;
223 current_ini_sig_name = n->url + "/" + SETUP_INI_FILENAME + ".sig";
224 ini_file = get_url_to_membuf (current_ini_name, owner);
225 if (!NoVerifyOption)
226 ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner);
227
228 if (!NoVerifyOption && ini_file && !ini_sig_file)
229 {
230 note (owner, IDS_SETUPINI_MISSING, current_ini_sig_name.c_str(), n->url.c_str());
231 delete ini_file;
232 ini_file = NULL;
233 sig_fail = true;
234 }
235 else if (!NoVerifyOption && ini_file && !verify_ini_file_sig (ini_file, ini_sig_file, owner))
236 {
237 note (owner, IDS_SIG_INVALID, current_ini_sig_name.c_str(), n->url.c_str());
238 delete ini_file;
239 ini_file = NULL;
240 delete ini_sig_file;
241 ini_sig_file = NULL;
242 sig_fail = true;
243 }
244 }
245
246 if (!ini_file)
247 {
248 if (!sig_fail)
249 note (owner, IDS_SETUPINI_MISSING, SETUP_INI_FILENAME, n->url.c_str());
250 continue;
251 }
252
253 myFeedback.iniName (current_ini_name);
254 aBuilder.parse_mirror = n->url;
255 ini_init (ini_file, &aBuilder, myFeedback);
256
257 /*yydebug = 1; */
258
259 if (yyparse () || yyerror_count > 0)
260 myFeedback.error (yyerror_messages);
261 else
262 {
263 /* save known-good setup.ini locally */
264 const std::string fp = "file://" + local_dir + "/" +
265 rfc1738_escape_part (n->url) +
266 "/" + SETUP_INI_FILENAME;
267 io_stream::mkpath_p (PATH_TO_FILE, fp);
268 if (io_stream *out = io_stream::open (fp, "wb"))
269 {
270 ini_file->seek (0, IO_SEEK_SET);
271 if (io_stream::copy (ini_file, out) != 0)
272 io_stream::remove (fp);
273 delete out;
274 }
275 ++ini_count;
276 }
277 if (aBuilder.timestamp > setup_timestamp)
278 {
279 setup_timestamp = aBuilder.timestamp;
280 ini_setup_version = aBuilder.version;
281 }
282 delete ini_file;
283 }
284 return ini_count;
285 }
286
287 static bool
288 do_ini_thread (HINSTANCE h, HWND owner)
289 {
290 size_t ini_count = 0;
291 if (source == IDC_SOURCE_CWD)
292 ini_count = do_local_ini (owner);
293 else
294 ini_count = do_remote_ini (owner);
295
296 if (ini_count == 0)
297 return false;
298
299 if (get_root_dir ().c_str())
300 {
301 io_stream::mkpath_p (PATH_TO_DIR, "cygfile:///etc/setup");
302
303 unsigned int old_timestamp = 0;
304 io_stream *ots =
305 io_stream::open ("cygfile:///etc/setup/timestamp", "rt");
306 if (ots)
307 {
308 char temp[20];
309 memset (temp, '\0', 20);
310 if (ots->read (temp, 19))
311 sscanf (temp, "%u", &old_timestamp);
312 delete ots;
313 if (old_timestamp && setup_timestamp
314 && (old_timestamp > setup_timestamp))
315 {
316 int yn = yesno (owner, IDS_OLD_SETUPINI);
317 if (yn == IDNO)
318 LogSingleton::GetInstance().exit (1);
319 }
320 }
321 if (setup_timestamp)
322 {
323 io_stream *nts =
324 io_stream::open ("cygfile:///etc/setup/timestamp", "wt");
325 if (nts)
326 {
327 char temp[20];
328 sprintf (temp, "%u", setup_timestamp);
329 nts->write (temp, strlen (temp));
330 delete nts;
331 }
332 }
333 }
334
335 msg (".ini setup_version is %s, our setup_version is %s", ini_setup_version.size() ?
336 ini_setup_version.c_str() : "(null)",
337 setup_version);
338 if (ini_setup_version.size())
339 {
340 if (version_compare(setup_version, ini_setup_version) < 0)
341 note (owner, IDS_OLD_SETUP_VERSION, setup_version,
342 ini_setup_version.c_str());
343 }
344
345 return true;
346 }
347
348 static DWORD WINAPI
349 do_ini_thread_reflector(void* p)
350 {
351 HANDLE *context;
352 context = (HANDLE*)p;
353
354 try
355 {
356 bool succeeded = do_ini_thread((HINSTANCE)context[0], (HWND)context[1]);
357
358 // Tell the progress page that we're done downloading
359 Progress.PostMessage(WM_APP_SETUP_INI_DOWNLOAD_COMPLETE, 0, succeeded);
360 }
361 TOPLEVEL_CATCH("ini");
362
363 ExitThread(0);
364 }
365
366 static HANDLE context[2];
367
368 void
369 do_ini (HINSTANCE h, HWND owner)
370 {
371 context[0] = h;
372 context[1] = owner;
373
374 DWORD threadID;
375 CreateThread (NULL, 0, do_ini_thread_reflector, context, 0, &threadID);
376 }
377
This page took 0.049242 seconds and 5 git commands to generate.