]> cygwin.com Git - cygwin-apps/setup.git/blob - download.cc
Add an option to set the GUI language
[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 #include <vector>
29
30 #include "resource.h"
31 #include "msg.h"
32 #include "dialog.h"
33 #include "geturl.h"
34 #include "state.h"
35 #include "LogFile.h"
36 #include "filemanip.h"
37
38 #include "io_stream.h"
39
40 #include "package_db.h"
41 #include "package_meta.h"
42 #include "package_source.h"
43
44 #include "threebar.h"
45
46 #include "Exception.h"
47
48 extern ThreeBarProgressPage Progress;
49
50 // Return true if selected checks pass, false if they don't and the
51 // user chooses to delete the file; otherwise throw an exception.
52 static bool
53 validateCachedPackage (const std::string& fullname, packagesource & pkgsource,
54 HWND owner, bool check_hash, bool check_size)
55 {
56 try
57 {
58 if (check_size)
59 pkgsource.check_size_and_cache (fullname);
60 if (check_hash)
61 pkgsource.check_hash ();
62 return true;
63 }
64 catch (Exception *e)
65 {
66 pkgsource.set_cached ("");
67 const char *filename = fullname.c_str ();
68 if (strncmp (filename, "file://", 7) == 0)
69 filename += 7;
70 if (e->errNo() == APPERR_CORRUPT_PACKAGE
71 && yesno (owner, IDS_QUERY_CORRUPT, filename) == IDYES)
72 remove (filename);
73 else
74 throw e;
75 }
76 return false;
77 }
78
79 /* 0 if not cached; may throw exception if validation fails.
80 */
81 int
82 check_for_cached (packagesource & pkgsource, HWND owner, bool mirror_mode,
83 bool check_hash)
84 {
85 /* If the packagesource doesn't have a filename, it can't possibly be in the
86 cache */
87 if (!pkgsource.Canonical())
88 {
89 return 0;
90 }
91
92 /* Note that the cache dir is represented by a mirror site of file://local_dir */
93 std::string prefix = "file://" + local_dir + "/";
94 std::string fullname = prefix + pkgsource.Canonical();
95
96 if (mirror_mode)
97 {
98 /* Just assume correctness of mirror. */
99 if (!pkgsource.Cached())
100 pkgsource.set_cached (fullname);
101 return 1;
102 }
103
104 // Already found one, which we can assume to have the right size.
105 if (pkgsource.Cached())
106 {
107 if (validateCachedPackage (pkgsource.Cached(), pkgsource, owner,
108 check_hash, false))
109 return 1;
110 // If we get here, pkgsource.Cached() was corrupt and deleted.
111 pkgsource.set_cached ("");
112 }
113
114 /*
115 1) is there a legacy version in the cache dir available.
116 */
117 if (io_stream::exists (fullname))
118 {
119 if (validateCachedPackage (fullname, pkgsource, owner, check_hash, true))
120 return 1;
121 // If we get here, fullname was corrupt and deleted, but it
122 // might have been cached.
123 pkgsource.set_cached ("");
124 }
125
126 /*
127 2) is there a version from one of the selected mirror sites available ?
128 */
129 for (packagesource::sitestype::const_iterator n = pkgsource.sites.begin();
130 n != pkgsource.sites.end(); ++n)
131 {
132 std::string fullname = prefix + rfc1738_escape_part (n->key) + "/" +
133 pkgsource.Canonical ();
134 if (io_stream::exists(fullname))
135 {
136 if (validateCachedPackage (fullname, pkgsource, owner, check_hash,
137 true))
138 return 1;
139 // If we get here, fullname was corrupt and deleted, but it
140 // might have been cached.
141 pkgsource.set_cached ("");
142 }
143 }
144 return 0;
145 }
146
147 /* download a file from a mirror site to the local cache. */
148 static int
149 download_one (packagesource & pkgsource, HWND owner)
150 {
151 try
152 {
153 if (check_for_cached (pkgsource, owner))
154 return 0;
155 }
156 catch (Exception * e)
157 {
158 // We know what to do with these..
159 if (e->errNo() == APPERR_CORRUPT_PACKAGE)
160 {
161 fatal (owner, IDS_CORRUPT_PACKAGE, pkgsource.Canonical());
162 return 1;
163 }
164 // Unexpected exception.
165 throw e;
166 }
167 /* try the download sites one after another */
168
169 int success = 0;
170 for (packagesource::sitestype::const_iterator n = pkgsource.sites.begin();
171 n != pkgsource.sites.end() && !success; ++n)
172 {
173 const std::string local = local_dir + "/" +
174 rfc1738_escape_part (n->key) + "/" +
175 pkgsource.Canonical ();
176 io_stream::mkpath_p (PATH_TO_FILE, "file://" + local, 0);
177
178 if (get_url_to_file(n->key + pkgsource.Canonical (),
179 local + ".tmp", pkgsource.size, owner))
180 {
181 /* FIXME: note new source ? */
182 continue;
183 }
184 else
185 {
186 try
187 {
188 if (_access (local.c_str(), 0) == 0)
189 remove (local.c_str());
190 rename ((local + ".tmp").c_str(), local.c_str());
191 pkgsource.check_size_and_cache ("file://" + local);
192 pkgsource.check_hash ();
193 Log (LOG_PLAIN) << "Downloaded " << local << endLog;
194 success = 1;
195 // FIXME: move the downloaded file to the
196 // original locations - without the mirror site dir in the way
197 continue;
198 }
199 catch (Exception *e)
200 {
201 remove (local.c_str());
202 pkgsource.set_cached ("");
203 if (e->errNo() == APPERR_CORRUPT_PACKAGE)
204 {
205 Log (LOG_PLAIN) << "Downloaded file " << local
206 << " is corrupt; deleting." << endLog;
207 continue;
208 }
209 else
210 {
211 Log (LOG_PLAIN) << "Unexpected exception while validating "
212 << "downloaded file " << local
213 << "; deleting." << endLog;
214 throw e;
215 }
216 }
217 }
218 }
219 if (success)
220 return 0;
221 return 1;
222 }
223
224 static std::vector <packageversion> download_failures;
225 static std::string download_warn_pkgs;
226
227 static INT_PTR CALLBACK
228 download_error_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
229 {
230 switch (message)
231 {
232 case WM_INITDIALOG:
233 eset (h, IDC_DOWNLOAD_EDIT, download_warn_pkgs);
234 SetFocus (GetDlgItem(h, IDRETRY));
235 return FALSE;
236
237 case WM_COMMAND:
238 switch (LOWORD (wParam))
239 {
240 case IDRETRY:
241 case IDC_BACK:
242 case IDIGNORE:
243 case IDABORT:
244 EndDialog (h, LOWORD (wParam));
245 default:
246 // Not reached.
247 return 0;
248 }
249
250 default:
251 // Not handled.
252 return FALSE;
253 }
254 return TRUE;
255 }
256
257 static int
258 query_download_errors (HINSTANCE h, HWND owner)
259 {
260 download_warn_pkgs = "";
261 Log (LOG_PLAIN) << "The following package(s) had download errors:" << endLog;
262 for (std::vector <packageversion>::const_iterator i = download_failures.begin (); i != download_failures.end (); i++)
263 {
264 packageversion pv = *i;
265 std::string pvs = pv.Name () + "-" + pv.Canonical_version ();
266 Log (LOG_PLAIN) << " " << pvs << endLog;
267 download_warn_pkgs += pvs + "\r\n";
268 }
269 return DialogBox (h, MAKEINTRESOURCE (IDD_DOWNLOAD_ERROR), owner,
270 download_error_proc);
271 }
272
273 static int
274 do_download_thread (HINSTANCE h, HWND owner)
275 {
276 int errors = 0;
277 total_download_bytes = 0;
278 total_download_bytes_sofar = 0;
279 download_failures.clear ();
280
281 Progress.SetText1 (IDS_PROGRESS_CHECKING);
282 Progress.SetText2 ("");
283 Progress.SetText3 ("");
284
285 packagedb db;
286 const SolverTransactionList &t = db.solution.transactions();
287
288 /* calculate the total size of the download */
289 for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
290 {
291 if (i->type != SolverTransaction::transInstall)
292 continue;
293 packageversion version = i->version;
294
295 try
296 {
297 if (!check_for_cached (*version.source(), owner))
298 total_download_bytes += version.source()->size;
299 }
300 catch (Exception * e)
301 {
302 // We know what to do with these..
303 if (e->errNo() == APPERR_CORRUPT_PACKAGE)
304 fatal (owner, IDS_CORRUPT_PACKAGE, version.Name().c_str());
305 // Unexpected exception.
306 throw e;
307 }
308 }
309
310 /* and do the download. FIXME: This here we assign a new name for the cached version
311 * and check that above.
312 */
313 for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
314 {
315 if (i->type != SolverTransaction::transInstall)
316 continue;
317 packageversion version = i->version;
318
319 {
320 int e = 0;
321 e += download_one (*version.source(), owner);
322 errors += e;
323 if (e)
324 download_failures.push_back (version);
325 #if 0
326 if (e)
327 pkg->action = ACTION_ERROR;
328 #endif
329 }
330 }
331
332 if (errors)
333 {
334 // In unattended mode we retry the download, but not forever.
335 static int retries = 5;
336 int rc;
337 if (unattended_mode && --retries <= 0)
338 {
339 Log (LOG_PLAIN) << "download error in unattended_mode: out of retries" << endLog;
340 rc = IDABORT;
341 }
342 else if (unattended_mode)
343 {
344 Log (LOG_PLAIN) << "download error in unattended_mode: " << retries
345 << (retries > 1 ? " retries" : " retry") << " remaining." << endLog;
346 rc = IDRETRY;
347 }
348 else
349 rc = query_download_errors (h, owner);
350 switch (rc)
351 {
352 case IDRETRY:
353 Progress.SetActivateTask (WM_APP_START_DOWNLOAD);
354 return IDD_INSTATUS;
355 case IDC_BACK:
356 return IDD_CHOOSE;
357 case IDABORT:
358 Logger ().setExitMsg (IDS_DOWNLOAD_INCOMPLETE_EXIT);
359 Logger ().exit (1);
360 case IDIGNORE:
361 break;
362 default:
363 break;
364 }
365 }
366
367 if (source == IDC_SOURCE_DOWNLOAD)
368 {
369 if (errors)
370 Logger ().setExitMsg (IDS_DOWNLOAD_INCOMPLETE_EXIT);
371 else if (!unattended_mode)
372 Logger ().setExitMsg (IDS_DOWNLOAD_COMPLETE);
373 return IDD_DESKTOP;
374 }
375 else
376 return IDD_S_INSTALL;
377 }
378
379 static DWORD WINAPI
380 do_download_reflector (void *p)
381 {
382 HANDLE *context;
383 context = (HANDLE *) p;
384
385 SetThreadUILanguage(langid);
386
387 try
388 {
389 int next_dialog =
390 do_download_thread ((HINSTANCE) context[0], (HWND) context[1]);
391
392 // Tell the progress page that we're done downloading
393 Progress.PostMessageNow (WM_APP_DOWNLOAD_THREAD_COMPLETE, 0, next_dialog);
394 }
395 TOPLEVEL_CATCH((HWND) context[1], "download");
396
397 ExitThread(0);
398 }
399
400 static HANDLE context[2];
401
402 void
403 do_download (HINSTANCE h, HWND owner)
404 {
405 context[0] = h;
406 context[1] = owner;
407
408 DWORD threadID;
409 CreateThread (NULL, 0, do_download_reflector, context, 0, &threadID);
410 }
This page took 0.079914 seconds and 6 git commands to generate.