/*
- * Copyright (c) 2000, Red Hat, Inc.
+ * Copyright (c) 2000,2007 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
"\n%%% $Id$\n";
#endif
+#include "ini.h"
+
+#include "csu_util/rfc1738.h"
+#include "csu_util/version_compare.h"
+
+#include "setup_version.h"
#include "win32.h"
+#include "LogSingleton.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <process.h>
-#include "ini.h"
#include "resource.h"
-#include "String++.h"
#include "state.h"
#include "geturl.h"
#include "dialog.h"
#include "msg.h"
-#include "log.h"
-#include "version.h"
#include "mount.h"
#include "site.h"
-#include "rfc1738.h"
#include "find.h"
#include "IniParseFindVisitor.h"
#include "IniParseFeedback.h"
-//#include "filemanip.h"
#include "io_stream.h"
+#include "io_stream_memory.h"
#include "threebar.h"
-#include "rfc1738.h"
-
+#include "getopt++/BoolOption.h"
#include "IniDBBuilderPackage.h"
#include "compress.h"
+#include "Exception.h"
+#include "crypto.h"
extern ThreeBarProgressPage Progress;
unsigned int setup_timestamp = 0;
-String setup_version;
+std::string ini_setup_version;
+std::string current_ini_sig_name;
+
+static BoolOption NoVerifyOption (false, 'X', "no-verify", "Don't verify setup.ini signatures");
extern int yyparse ();
/*extern int yydebug;*/
-static char *error_buf = 0;
-static int error_count = 0;
-
-static const char *ini_filename;
-
class GuiParseFeedback : public IniParseFeedback
{
public:
- virtual void babble(String const &message)const
+ GuiParseFeedback () : lastpct (0)
+ {
+ Progress.SetText2 ("");
+ Progress.SetText3 ("");
+ Progress.SetText4 ("Progress:");
+ }
+ virtual void progress(unsigned long const pos, unsigned long const max)
+ {
+ if (!max)
+ /* length not known or eof */
+ return;
+ if (lastpct == 100)
+ /* rounding down should mean this only ever fires once */
+ lastpct = 0;
+ if (pos * 100 / max > lastpct)
+ {
+ lastpct = pos * 100 / max;
+ /* log (LOG_BABBLE) << lastpct << "% (" << pos << " of " << max
+ << " bytes of ini file read)" << endLog; */
+ }
+ Progress.SetBar1(pos, max);
+ }
+ virtual void iniName (const std::string& name)
{
- log (LOG_BABBLE, message);
+ Progress.SetText1 (("Parsing ini file \"" + name + "\"").c_str());
}
- virtual void warning (String const &message)const
+ virtual void babble(const std::string& message)const
{
- MessageBox (0, message.cstr_oneuse(), "Warning", 0);
+ log (LOG_BABBLE) << message << endLog;
}
- virtual void error(String const &message)const
+ virtual void warning (const std::string& message)const
{
- MessageBox (0, message.cstr_oneuse(), "Error parsing", 0);
+ MessageBox (0, message.c_str(), "Warning", 0);
+ }
+ virtual void error(const std::string& message)const
+ {
+ MessageBox (0, message.c_str(), "Parse Errors", 0);
}
virtual ~ GuiParseFeedback ()
{
+ Progress.SetText4("Package:");
}
+private:
+ unsigned int lastpct;
};
static int
IniParseFindVisitor myVisitor (findBuilder, local_dir, myFeedback);
Find (local_dir).accept(myVisitor);
setup_timestamp = myVisitor.timeStamp();
- setup_version = myVisitor.version();
+ ini_setup_version = myVisitor.version();
return myVisitor.iniCount();
}
size_t ini_count = 0;
GuiParseFeedback myFeedback;
IniDBBuilderPackage aBuilder(myFeedback);
-
- for (size_t n = 1; n <= site_list.number (); n++)
+ io_stream *ini_file, *ini_sig_file;
+
+ /* FIXME: Get rid of this io_stream pointer travesty. The need to
+ explicitly delete these things is ridiculous. Note that the
+ decompress io_stream "owns" the underlying compressed io_stream
+ instance, so it should not be deleted explicitly. */
+
+ for (SiteList::const_iterator n = site_list.begin();
+ n != site_list.end(); ++n)
{
- io_stream *compressed_ini_file =
- get_url_to_membuf (site_list[n]->url + "/setup.bz2", owner);
- io_stream *ini_file = 0;
- if (!compressed_ini_file)
- ini_file = get_url_to_membuf (site_list[n]->url + "/setup.ini", owner);
- else
+ bool sig_fail = false;
+ /* First try to fetch the .bz2 compressed ini file. */
+ current_ini_name = n->url + "/" + SETUP_BZ2_FILENAME;
+ current_ini_sig_name = n->url + "/" + SETUP_BZ2_FILENAME + ".sig";
+ ini_file = get_url_to_membuf (current_ini_name, owner);
+ if (!NoVerifyOption)
+ ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner);
+ if (!NoVerifyOption && ini_file && !ini_sig_file)
+ {
+ note (owner, IDS_SETUPINI_MISSING, current_ini_sig_name.c_str(), n->url.c_str());
+ delete ini_file;
+ ini_file = NULL;
+ sig_fail = true;
+ }
+ else if (!NoVerifyOption && ini_file && !verify_ini_file_sig (ini_file, ini_sig_file, owner))
+ {
+ note (owner, IDS_SIG_INVALID, current_ini_sig_name.c_str(), n->url.c_str());
+ delete ini_file;
+ ini_file = NULL;
+ delete ini_sig_file;
+ ini_sig_file = NULL;
+ sig_fail = true;
+ }
+ if (ini_file)
{
- ini_file = compress::decompress (compressed_ini_file);
+ /* Decompress the entire file in memory right now. This has the
+ advantage that input_stream->get_size() will work during parsing
+ and we'll have an accurate status bar. Also, we can't seek
+ bz2 streams, so when it comes time to write out a local cached
+ copy of the .ini file below, we'd otherwise have to delete this
+ stream and uncompress it again from the start, which is wasteful.
+ The current uncompresed size of the .ini file as of 2007 is less
+ than 600 kB, so this is not a great deal of memory. */
+ io_stream *bz2_stream = compress::decompress (ini_file);
+ if (!bz2_stream)
+ {
+ /* This isn't a valid bz2 file. */
+ delete ini_file;
+ ini_file = NULL;
+ }
+ else
+ {
+ io_stream *uncompressed = new io_stream_memory ();
+
+ if (io_stream::copy (bz2_stream, uncompressed) != 0 ||
+ bz2_stream->error () == EIO)
+ {
+ /* There was a problem decompressing bz2. */
+ delete bz2_stream;
+ delete uncompressed;
+ ini_file = NULL;
+ log (LOG_PLAIN) <<
+ "Warning: Problem encountered while uncompressing " <<
+ current_ini_name << " - possibly truncated or corrupt bzip2"
+ " file. Retrying with uncompressed version." << endLog;
+ }
+ else
+ {
+ delete bz2_stream;
+ ini_file = uncompressed;
+ ini_file->seek (0, IO_SEEK_SET);
+ }
+ }
+ }
+
+ if (!ini_file)
+ {
+ /* Try to look for a plain .ini file because one of the following
+ happened above:
+ - there was no .bz2 file found on the mirror.
+ - the .bz2 file didn't look like a valid bzip2 file.
+ - there was an error during bzip2 decompression. */
+ current_ini_name = n->url + "/" + SETUP_INI_FILENAME;
+ current_ini_sig_name = n->url + "/" + SETUP_INI_FILENAME + ".sig";
+ ini_file = get_url_to_membuf (current_ini_name, owner);
+ if (!NoVerifyOption)
+ ini_sig_file = get_url_to_membuf (current_ini_sig_name, owner);
+
+ if (!NoVerifyOption && ini_file && !ini_sig_file)
+ {
+ note (owner, IDS_SETUPINI_MISSING, current_ini_sig_name.c_str(), n->url.c_str());
+ delete ini_file;
+ ini_file = NULL;
+ sig_fail = true;
+ }
+ else if (!NoVerifyOption && ini_file && !verify_ini_file_sig (ini_file, ini_sig_file, owner))
+ {
+ note (owner, IDS_SIG_INVALID, current_ini_sig_name.c_str(), n->url.c_str());
+ delete ini_file;
+ ini_file = NULL;
+ delete ini_sig_file;
+ ini_sig_file = NULL;
+ sig_fail = true;
+ }
}
if (!ini_file)
{
- note (owner, IDS_SETUPINI_MISSING, site_list[n]->url.cstr_oneuse());
+ if (!sig_fail)
+ note (owner, IDS_SETUPINI_MISSING, SETUP_INI_FILENAME, n->url.c_str());
continue;
}
- aBuilder.parse_mirror = site_list[n]->url;
- ini_init (ini_file, &aBuilder);
+ myFeedback.iniName (current_ini_name);
+ aBuilder.parse_mirror = n->url;
+ ini_init (ini_file, &aBuilder, myFeedback);
/*yydebug = 1; */
- if (yyparse () || error_count > 0)
- MessageBox (0, error_buf,
- error_count == 1 ? "Parse Error" : "Parse Errors", 0);
+ if (yyparse () || yyerror_count > 0)
+ myFeedback.error (yyerror_messages);
else
{
/* save known-good setup.ini locally */
- String const fp = String ("file://") + local_dir + "/" +
- rfc1738_escape_part (site_list[n]->url) +
- "/setup.ini";
+ const std::string fp = "file://" + local_dir + "/" +
+ rfc1738_escape_part (n->url) +
+ "/" + SETUP_INI_FILENAME;
io_stream::mkpath_p (PATH_TO_FILE, fp);
- io_stream *inistream = io_stream::open (fp, "wb");
- if (inistream)
+ if (io_stream *out = io_stream::open (fp, "wb"))
{
- if (compressed_ini_file)
- {
- delete ini_file;
- compressed_ini_file->seek (0, IO_SEEK_SET);
- ini_file = compress::decompress (compressed_ini_file);
- }
- else
- ini_file->seek (0, IO_SEEK_SET);
- if (io_stream::copy (ini_file, inistream))
+ ini_file->seek (0, IO_SEEK_SET);
+ if (io_stream::copy (ini_file, out) != 0)
io_stream::remove (fp);
- delete inistream;
+ delete out;
}
++ini_count;
}
if (aBuilder.timestamp > setup_timestamp)
{
setup_timestamp = aBuilder.timestamp;
- setup_version = aBuilder.version;
+ ini_setup_version = aBuilder.version;
}
delete ini_file;
- delete compressed_ini_file;
}
return ini_count;
}
-static void
+static bool
do_ini_thread (HINSTANCE h, HWND owner)
{
size_t ini_count = 0;
ini_count = do_remote_ini (owner);
if (ini_count == 0)
- {
- next_dialog = source == IDC_SOURCE_CWD ? IDD_S_FROM_CWD : IDD_SITE;
- return;
- }
+ return false;
- if (get_root_dir ().cstr_oneuse())
+ if (get_root_dir ().c_str())
{
io_stream::mkpath_p (PATH_TO_DIR, "cygfile:///etc/setup");
}
}
- msg ("setup_version is %s, our_version is %s", setup_version.size() ?
- setup_version.cstr_oneuse() : "(null)",
- version);
- if (setup_version.size())
+ msg (".ini setup_version is %s, our setup_version is %s", ini_setup_version.size() ?
+ ini_setup_version.c_str() : "(null)",
+ setup_version);
+ if (ini_setup_version.size())
{
- String ini_version = canonicalize_version (setup_version);
- String our_version = canonicalize_version (version);
- // XXX useversion < operator
- if (our_version.compare (ini_version) < 0)
- note (owner, IDS_OLD_SETUP_VERSION, version, setup_version.cstr_oneuse());
+ if (version_compare(setup_version, ini_setup_version) < 0)
+ note (owner, IDS_OLD_SETUP_VERSION, setup_version,
+ ini_setup_version.c_str());
}
- next_dialog = IDD_CHOOSE;
+ return true;
}
static DWORD WINAPI
do_ini_thread_reflector(void* p)
{
- HANDLE *context;
- context = (HANDLE*)p;
+ HANDLE *context;
+ context = (HANDLE*)p;
- do_ini_thread((HINSTANCE)context[0], (HWND)context[1]);
+ try
+ {
+ bool succeeded = do_ini_thread((HINSTANCE)context[0], (HWND)context[1]);
- // Tell the progress page that we're done downloading
- Progress.PostMessage(WM_APP_SETUP_INI_DOWNLOAD_COMPLETE, 0, next_dialog);
+ // Tell the progress page that we're done downloading
+ Progress.PostMessage(WM_APP_SETUP_INI_DOWNLOAD_COMPLETE, 0, succeeded);
+ }
+ TOPLEVEL_CATCH("ini");
- ExitThread(0);
+ ExitThread(0);
}
static HANDLE context[2];
CreateThread (NULL, 0, do_ini_thread_reflector, context, 0, &threadID);
}
-
-extern int yylineno;
-extern int yybol ();
-
-extern int
-yyerror (String const &s)
-{
- char buf[MAX_PATH + 1000];
- int len;
- sprintf (buf, "%s line %d: ", ini_filename, yylineno - yybol ());
- sprintf (buf + strlen (buf), s.cstr_oneuse());
- OutputDebugString (buf);
- if (error_buf)
- {
- strcat (error_buf, "\n");
- len = strlen (error_buf) + strlen (buf) + 5;
- error_buf = (char *) realloc (error_buf, len);
- strcat (error_buf, buf);
- }
- else
- {
- len = strlen (buf) + 5;
- error_buf = (char *) malloc (len);
- strcpy (error_buf, buf);
- }
- error_count++;
- /* TODO: is return 0 correct? */
- return 0;
-}
-
-extern "C" int fprintf (FILE * f, const char *s, ...);
-
-static char stderrbuf[1000];
-
-int
-fprintf (FILE * f, const char *fmt, ...)
-{
- char buf[1000];
- int rv;
- va_list args;
- va_start (args, fmt);
- if (f == stderr)
- {
- rv = vsprintf (buf, fmt, args);
- strcat (stderrbuf, buf);
- if (char *nl = strchr (stderrbuf, '\n'))
- {
- *nl = 0;
- /*OutputDebugString (stderrbuf); */
- MessageBox (0, buf, "Cygwin Setup", 0);
- stderrbuf[0] = 0;
- }
-
- }
- else
- {
- rv = vfprintf (f, fmt, args);
- }
- return rv;
-}