]> cygwin.com Git - cygwin-apps/setup.git/blob - download.cc
59af2d61b9dd22bbefe2d11562fe845aa0a09d2f
[cygwin-apps/setup.git] / download.cc
1 /*
2 * Copyright (c) 2000, 2001, 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 download all the files we need to
17 do the installation. */
18
19 #include "csu_util/rfc1738.h"
20
21 #include "download.h"
22
23 #include "win32.h"
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <process.h>
28
29 #include "resource.h"
30 #include "msg.h"
31 #include "dialog.h"
32 #include "geturl.h"
33 #include "state.h"
34 #include "LogFile.h"
35 #include "filemanip.h"
36
37 #include "io_stream.h"
38
39 #include "package_db.h"
40 #include "package_meta.h"
41 #include "package_version.h"
42 #include "package_source.h"
43
44 #include "threebar.h"
45
46 #include "Exception.h"
47
48 #include "getopt++/BoolOption.h"
49
50 using namespace std;
51
52 extern ThreeBarProgressPage Progress;
53
54 BoolOption IncludeSource (false, 'I', "include-source", "Automatically include source download");
55
56 static bool
57 validateCachedPackage (const std::string& fullname, packagesource & pkgsource)
58 {
59 DWORD size = get_file_size(fullname);
60 if (size != pkgsource.size)
61 {
62 Log (LOG_BABBLE) << "INVALID PACKAGE: " << fullname
63 << " - Size mismatch: Ini-file: " << pkgsource.size
64 << " != On-disk: " << size << endLog;
65 return false;
66 }
67 return true;
68 }
69
70 /* 0 on failure
71 */
72 int
73 check_for_cached (packagesource & pkgsource, bool mirror_mode)
74 {
75 // Already found one.
76 if (pkgsource.Cached())
77 return 1;
78
79 /* Note that the cache dir is represented by a mirror site of file://local_dir */
80 std::string prefix = "file://" + local_dir + "/";
81 std::string fullname = prefix + pkgsource.Canonical();
82
83 if (mirror_mode)
84 {
85 /* Just assume correctness of mirror. */
86 pkgsource.set_cached (fullname);
87 return 1;
88 }
89
90 /*
91 1) is there a legacy version in the cache dir available.
92 */
93 if (io_stream::exists (fullname))
94 {
95 if (validateCachedPackage (fullname, pkgsource))
96 pkgsource.set_cached (fullname);
97 else
98 throw new Exception (TOSTRING(__LINE__) " " __FILE__,
99 "Package validation failure for " + fullname,
100 APPERR_CORRUPT_PACKAGE);
101 return 1;
102 }
103
104 /*
105 2) is there a version from one of the selected mirror sites available ?
106 */
107 for (packagesource::sitestype::const_iterator n = pkgsource.sites.begin();
108 n != pkgsource.sites.end(); ++n)
109 {
110 std::string fullname = prefix + rfc1738_escape_part (n->key) + "/" +
111 pkgsource.Canonical ();
112 if (io_stream::exists(fullname))
113 {
114 if (validateCachedPackage (fullname, pkgsource))
115 pkgsource.set_cached (fullname);
116 else
117 throw new Exception (TOSTRING(__LINE__) " " __FILE__,
118 "Package validation failure for " + fullname,
119 APPERR_CORRUPT_PACKAGE);
120 return 1;
121 }
122 }
123 return 0;
124 }
125
126 /* download a file from a mirror site to the local cache. */
127 static int
128 download_one (packagesource & pkgsource, HWND owner)
129 {
130 try
131 {
132 if (check_for_cached (pkgsource))
133 return 0;
134 }
135 catch (Exception * e)
136 {
137 // We know what to do with these..
138 if (e->errNo() == APPERR_CORRUPT_PACKAGE)
139 {
140 fatal (owner, IDS_CORRUPT_PACKAGE, pkgsource.Canonical());
141 return 1;
142 }
143 // Unexpected exception.
144 throw e;
145 }
146 /* try the download sites one after another */
147
148 int success = 0;
149 for (packagesource::sitestype::const_iterator n = pkgsource.sites.begin();
150 n != pkgsource.sites.end() && !success; ++n)
151 {
152 const std::string local = local_dir + "/" +
153 rfc1738_escape_part (n->key) + "/" +
154 pkgsource.Canonical ();
155 io_stream::mkpath_p (PATH_TO_FILE, "file://" + local, 0);
156
157 if (get_url_to_file(n->key + pkgsource.Canonical (),
158 local + ".tmp", pkgsource.size, owner))
159 {
160 /* FIXME: note new source ? */
161 continue;
162 }
163 else
164 {
165 size_t size = get_file_size ("file://" + local + ".tmp");
166 if (size == pkgsource.size)
167 {
168 Log (LOG_PLAIN) << "Downloaded " << local << endLog;
169 if (_access (local.c_str(), 0) == 0)
170 remove (local.c_str());
171 rename ((local + ".tmp").c_str(), local.c_str());
172 success = 1;
173 pkgsource.set_cached ("file://" + local);
174 // FIXME: move the downloaded file to the
175 // original locations - without the mirror site dir in the way
176 continue;
177 }
178 else
179 {
180 Log (LOG_PLAIN) << "Download " << local << " wrong size (" <<
181 size << " actual vs " << pkgsource.size << " expected)" <<
182 endLog;
183 remove ((local + ".tmp").c_str());
184 continue;
185 }
186 }
187 }
188 if (success)
189 return 0;
190 /* FIXME: Do we want to note this? if so how? */
191 return 1;
192 }
193
194 static int
195 do_download_thread (HINSTANCE h, HWND owner)
196 {
197 int errors = 0;
198 total_download_bytes = 0;
199 total_download_bytes_sofar = 0;
200
201 Progress.SetText1 ("Checking for packages to download...");
202 Progress.SetText2 ("");
203 Progress.SetText3 ("");
204
205 packagedb db;
206 /* calculate the amount needed */
207 for (packagedb::packagecollection::iterator i = db.packages.begin ();
208 i != db.packages.end (); ++i)
209 {
210 packagemeta & pkg = *(i->second);
211 if (pkg.desired.picked () || pkg.desired.sourcePackage ().picked ())
212 {
213 packageversion version = pkg.desired;
214 packageversion sourceversion = version.sourcePackage();
215 try
216 {
217 if (version.picked())
218 {
219 if (!check_for_cached (*version.source()))
220 total_download_bytes += version.source()->size;
221 }
222 if (sourceversion.picked () || IncludeSource)
223 {
224 if (!check_for_cached (*sourceversion.source()))
225 total_download_bytes += sourceversion.source()->size;
226 }
227 }
228 catch (Exception * e)
229 {
230 // We know what to do with these..
231 if (e->errNo() == APPERR_CORRUPT_PACKAGE)
232 fatal (owner, IDS_CORRUPT_PACKAGE, pkg.name.c_str());
233 // Unexpected exception.
234 throw e;
235 }
236 }
237 }
238
239 /* and do the download. FIXME: This here we assign a new name for the cached version
240 * and check that above.
241 */
242 for (packagedb::packagecollection::iterator i = db.packages.begin ();
243 i != db.packages.end (); ++i)
244 {
245 packagemeta & pkg = *(i->second);
246 if (pkg.desired.picked () || pkg.desired.sourcePackage ().picked ())
247 {
248 int e = 0;
249 packageversion version = pkg.desired;
250 packageversion sourceversion = version.sourcePackage();
251 if (version.picked())
252 {
253 e += download_one (*version.source(), owner);
254 }
255 if (sourceversion && (sourceversion.picked() || IncludeSource))
256 {
257 e += download_one (*sourceversion.source (), owner);
258 }
259 errors += e;
260 #if 0
261 if (e)
262 pkg->action = ACTION_ERROR;
263 #endif
264 }
265 }
266
267 if (errors)
268 {
269 /* In unattended mode, all dialog boxes automatically get
270 answered with a Yes/OK/other positive response. This
271 means that if there's a download problem, setup will
272 potentially retry forever if we don't take care to give
273 up at some finite point. */
274 static int retries = 4;
275 if (unattended_mode && retries-- <= 0)
276 {
277 Log (LOG_PLAIN) << "download error in unattended_mode: out of retries" << endLog;
278 Logger ().setExitMsg (IDS_INSTALL_INCOMPLETE);
279 Logger ().exit (1);
280 }
281 else if (unattended_mode)
282 {
283 Log (LOG_PLAIN) << "download error in unattended_mode: " << retries
284 << (retries > 1 ? " retries" : " retry") << " remaining." << endLog;
285 return IDD_SITE;
286 }
287 else if (yesno (owner, IDS_DOWNLOAD_INCOMPLETE) == IDYES)
288 return IDD_SITE;
289 }
290
291 if (source == IDC_SOURCE_DOWNLOAD)
292 {
293 if (errors)
294 Logger ().setExitMsg (IDS_DOWNLOAD_INCOMPLETE);
295 else if (!unattended_mode)
296 Logger ().setExitMsg (IDS_DOWNLOAD_COMPLETE);
297 return IDD_DESKTOP;
298 }
299 else
300 return IDD_S_INSTALL;
301 }
302
303 static DWORD WINAPI
304 do_download_reflector (void *p)
305 {
306 HANDLE *context;
307 context = (HANDLE *) p;
308
309 try
310 {
311 int next_dialog =
312 do_download_thread ((HINSTANCE) context[0], (HWND) context[1]);
313
314 // Tell the progress page that we're done downloading
315 Progress.PostMessageNow (WM_APP_DOWNLOAD_THREAD_COMPLETE, 0, next_dialog);
316 }
317 TOPLEVEL_CATCH("download");
318
319 ExitThread(0);
320 }
321
322 static HANDLE context[2];
323
324 void
325 do_download (HINSTANCE h, HWND owner)
326 {
327 context[0] = h;
328 context[1] = owner;
329
330 DWORD threadID;
331 CreateThread (NULL, 0, do_download_reflector, context, 0, &threadID);
332 }
This page took 0.049986 seconds and 5 git commands to generate.