]> cygwin.com Git - cygwin-apps/setup.git/blob - site.cc
* Makefile.am: Treat libgetopt++ as full-fledged SUBDIRS.
[cygwin-apps/setup.git] / site.cc
1 /*
2 * Copyright (c) 2000, 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 the list of mirror sites and ask
17 the user which mirror site they want to download from. */
18
19 #if 0
20 static const char *cvsid =
21 "\n%%% $Id$\n";
22 #endif
23
24 #include <string>
25 #include <algorithm>
26
27 #include "site.h"
28 #include "win32.h"
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <process.h>
32
33 #include "dialog.h"
34 #include "resource.h"
35 #include "state.h"
36 #include "geturl.h"
37 #include "msg.h"
38 #include "LogSingleton.h"
39 #include "io_stream.h"
40 #include "site.h"
41
42 #include "propsheet.h"
43
44 #include "threebar.h"
45 #include "ControlAdjuster.h"
46 #include "Exception.h"
47 #include "String++.h"
48
49 using namespace std;
50
51 extern ThreeBarProgressPage Progress;
52
53
54 /*
55 What to do if dropped mirrors are selected.
56 */
57 enum
58 {
59 CACHE_REJECT, // Go back to re-select mirrors.
60 CACHE_ACCEPT_WARN, // Go on. Warn again next time.
61 CACHE_ACCEPT_NOWARN // Go on. Don't warn again.
62 };
63
64 /*
65 Sizing information.
66 */
67 static ControlAdjuster::ControlInfo SiteControlsInfo[] = {
68 {IDC_URL_LIST, CP_STRETCH, CP_STRETCH},
69 {IDC_EDIT_USER_URL, CP_STRETCH, CP_BOTTOM},
70 {IDC_BUTTON_ADD_URL, CP_RIGHT, CP_BOTTOM},
71 {IDC_SITE_USERURL, CP_LEFT, CP_BOTTOM},
72 {0, CP_LEFT, CP_TOP}
73 };
74
75 SitePage::SitePage ()
76 {
77 sizeProcessor.AddControlInfo (SiteControlsInfo);
78 }
79
80 #include "getopt++/StringOption.h"
81 #include "getopt++/BoolOption.h"
82 #include "UserSettings.h"
83
84 using namespace std;
85
86 bool cache_is_usable;
87 bool cache_needs_writing;
88 string cache_warn_urls;
89
90 /* Selected sites */
91 SiteList site_list;
92
93 /* Fresh mirrors + selected sites */
94 SiteList all_site_list;
95
96 /* Previously fresh + cached before */
97 SiteList cached_site_list;
98
99 /* Stale selected sites to warn about and add to cache */
100 SiteList dropped_site_list;
101
102 StringOption SiteOption("", 's', "site", "Download site", false);
103
104 BoolOption OnlySiteOption(false, 'O', "only-site", "Ignore all sites except for -s");
105
106 SiteSetting::SiteSetting (): saved (false)
107 {
108 string SiteOptionString = SiteOption;
109 if (SiteOptionString.size())
110 registerSavedSite (SiteOptionString.c_str());
111 else
112 getSavedSites ();
113 }
114
115 void
116 SiteSetting::save()
117 {
118 io_stream *f = UserSettings::instance().open ("last-mirror");
119 if (f)
120 {
121 for (SiteList::const_iterator n = site_list.begin ();
122 n != site_list.end (); ++n)
123 *f << n->url;
124 delete f;
125 }
126 saved = true;
127 }
128
129 SiteSetting::~SiteSetting ()
130 {
131 if (!saved)
132 save ();
133 }
134
135 void
136 site_list_type::init (const string &_url, const string &_servername,
137 const string &_area, const string &_location)
138 {
139 url = _url;
140 servername = _servername;
141 area = _area;
142 location = _location;
143 displayed_url = url.substr (0, url.find ("/", url.find (".")));
144
145 key = string();
146 string::size_type last_idx = displayed_url.length () - 1;
147 string::size_type idx = url.find_last_of("./", last_idx);
148 if (last_idx - idx == 3)
149 {
150 /* Sort non-country TLDs (.com, .net, ...) together. */
151 key += " ";
152 }
153 do
154 {
155 key += url.substr(idx + 1, last_idx - idx);
156 key += " ";
157 last_idx = idx - 1;
158 idx = url.find_last_of("./", last_idx);
159 if (idx == string::npos)
160 idx = 0;
161 } while (idx > 0);
162 key += url;
163 }
164
165 site_list_type::site_list_type (const string &_url,
166 const string &_servername,
167 const string &_area,
168 const string &_location)
169 {
170 init (_url, _servername, _area, _location);
171 }
172
173 site_list_type::site_list_type (site_list_type const &rhs)
174 {
175 key = rhs.key;
176 url = rhs.url;
177 servername = rhs.servername;
178 area = rhs.area;
179 location = rhs.location;
180 displayed_url = rhs.displayed_url;
181 }
182
183 site_list_type &
184 site_list_type::operator= (site_list_type const &rhs)
185 {
186 key = rhs.key;
187 url = rhs.url;
188 servername = rhs.servername;
189 area = rhs.area;
190 location = rhs.location;
191 displayed_url = rhs.displayed_url;
192 return *this;
193 }
194
195 bool
196 site_list_type::operator == (site_list_type const &rhs) const
197 {
198 return stricmp (key.c_str(), rhs.key.c_str()) == 0;
199 }
200
201 bool
202 site_list_type::operator < (site_list_type const &rhs) const
203 {
204 return stricmp (key.c_str(), rhs.key.c_str()) < 0;
205 }
206
207 static void
208 save_dialog (HWND h)
209 {
210 // Remove anything that was previously in the selected site list.
211 site_list.clear ();
212
213 HWND listbox = GetDlgItem (h, IDC_URL_LIST);
214 int sel_count = SendMessage (listbox, LB_GETSELCOUNT, 0, 0);
215 if (sel_count > 0)
216 {
217 int sel_buffer[sel_count];
218 SendMessage (listbox, LB_GETSELITEMS, sel_count, (LPARAM) sel_buffer);
219 for (int n = 0; n < sel_count; n++)
220 {
221 int mirror =
222 SendMessage (listbox, LB_GETITEMDATA, sel_buffer[n], 0);
223 site_list.push_back (all_site_list[mirror]);
224 }
225 }
226 }
227
228 void
229 load_site_list (SiteList& theSites, char *theString)
230 {
231 char *bol, *eol, *nl;
232
233 nl = theString;
234 while (*nl)
235 {
236 bol = nl;
237 for (eol = bol; *eol && *eol != '\n'; eol++);
238 if (*eol)
239 nl = eol + 1;
240 else
241 nl = eol;
242 while (eol > bol && eol[-1] == '\r')
243 eol--;
244 *eol = 0;
245 if (*bol == '#' || !*bol)
246 continue;
247 /* Accept only the URL schemes we can understand. */
248 if (strncmp(bol, "http://", 7) == 0 ||
249 strncmp(bol, "ftp://", 6) == 0)
250 {
251 char *semi = strchr (bol, ';');
252 char *semi2 = NULL;
253 char *semi3 = NULL;
254 if (semi)
255 {
256 *semi = 0;
257 semi++;
258 semi2 = strchr (semi, ';');
259 if (semi2)
260 {
261 *semi2 = 0;
262 semi2++;
263 semi3 = strchr (semi2, ';');
264 if (semi3)
265 {
266 *semi3 = 0;
267 semi3++;
268 }
269 }
270 }
271 site_list_type newsite (bol, semi, semi2, semi3);
272 SiteList::iterator i = find (theSites.begin(),
273 theSites.end(), newsite);
274 if (i == theSites.end())
275 {
276 SiteList result;
277 merge (theSites.begin(), theSites.end(),
278 &newsite, &newsite + 1,
279 inserter (result, result.begin()));
280 theSites = result;
281 }
282 else
283 //TODO: remove and remerge
284 *i = newsite;
285 }
286 }
287 }
288
289 static int
290 get_site_list (HINSTANCE h, HWND owner)
291 {
292 char mirror_url[1000];
293
294 char *theMirrorString, *theCachedString;
295 const char *cached_mirrors = OnlySiteOption ? NULL : UserSettings::instance().get ("mirrors-lst");
296 if (cached_mirrors)
297 {
298 log (LOG_BABBLE) << "Loaded cached mirror list" << endLog;
299 cache_is_usable = true;
300 }
301 else
302 {
303 log (LOG_BABBLE) << "Cached mirror list unavailable" << endLog;
304 cache_is_usable = false;
305 cached_mirrors = "";
306 }
307
308 if (LoadString (h, IDS_MIRROR_LST, mirror_url, sizeof (mirror_url)) <= 0)
309 return 1;
310
311 string mirrors = OnlySiteOption ? string ("") : get_url_to_string (mirror_url, owner);
312 if (mirrors.size())
313 cache_needs_writing = true;
314 else
315 {
316 if (!cached_mirrors[0])
317 log (LOG_BABBLE) << "Defaulting to empty mirror list" << endLog;
318 else
319 {
320 mirrors = cached_mirrors;
321 log (LOG_BABBLE) << "Using cached mirror list" << endLog;
322 }
323 cache_is_usable = false;
324 cache_needs_writing = false;
325 }
326 theMirrorString = new_cstr_char_array (mirrors);
327 theCachedString = new_cstr_char_array (cached_mirrors);
328
329 load_site_list (all_site_list, theMirrorString);
330 load_site_list (cached_site_list, theCachedString);
331
332 delete[] theMirrorString;
333 delete[] theCachedString;
334
335 return 0;
336 }
337
338 /* List of machines that should not be used by default when saved
339 in "last-mirror". */
340 #define NOSAVE1 "ftp://sourceware.org/"
341 #define NOSAVE1_LEN (sizeof (NOSAVE2) - 1)
342 #define NOSAVE2 "ftp://sources.redhat.com/"
343 #define NOSAVE2_LEN (sizeof (NOSAVE1) - 1)
344 #define NOSAVE3 "ftp://gcc.gnu.org/"
345 #define NOSAVE3_LEN (sizeof (NOSAVE3) - 1)
346
347 void
348 SiteSetting::registerSavedSite (const char * site)
349 {
350 site_list_type tempSite(site, "", "", "");
351 SiteList::iterator i = find (all_site_list.begin(),
352 all_site_list.end(), tempSite);
353 if (i == all_site_list.end())
354 {
355 /* Don't default to certain machines if they suffer
356 from bandwidth limitations. */
357 if (strnicmp (site, NOSAVE1, NOSAVE1_LEN) == 0
358 || strnicmp (site, NOSAVE2, NOSAVE2_LEN) == 0
359 || strnicmp (site, NOSAVE3, NOSAVE3_LEN) == 0)
360 return;
361 SiteList result;
362 merge (all_site_list.begin(), all_site_list.end(),
363 &tempSite, &tempSite + 1,
364 inserter (result, result.begin()));
365 all_site_list = result;
366 site_list.push_back (tempSite);
367 }
368 else
369 site_list.push_back (tempSite);
370 }
371
372 void
373 SiteSetting::getSavedSites ()
374 {
375 const char *buf = UserSettings::instance().get ("last-mirror");
376 if (!buf)
377 return;
378 char *fg_ret = strdup (buf);
379 for (char *site = strtok (fg_ret, "\n"); site; site = strtok (NULL, "\n"))
380 registerSavedSite (site);
381 free (fg_ret);
382 }
383
384 static DWORD WINAPI
385 do_download_site_info_thread (void *p)
386 {
387 HANDLE *context;
388 HINSTANCE hinst;
389 HWND h;
390 context = (HANDLE *) p;
391
392 try
393 {
394 hinst = (HINSTANCE) (context[0]);
395 h = (HWND) (context[1]);
396 static bool downloaded = false;
397 if (!downloaded && get_site_list (hinst, h))
398 {
399 // Error: Couldn't download the site info.
400 // Go back to the Net setup page.
401 MessageBox (h, TEXT ("Can't get list of download sites.\n")
402 TEXT("Make sure your network settings are correct and try again."),
403 NULL, MB_OK);
404
405 // Tell the progress page that we're done downloading
406 Progress.PostMessage (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE, 0, IDD_NET);
407 }
408 else
409 {
410 downloaded = true;
411 // Everything worked, go to the site select page
412 // Tell the progress page that we're done downloading
413 Progress.PostMessage (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE, 0, IDD_SITE);
414 }
415 }
416 TOPLEVEL_CATCH("site");
417
418 ExitThread(0);
419 }
420
421 static HANDLE context[2];
422
423 void
424 do_download_site_info (HINSTANCE hinst, HWND owner)
425 {
426
427 context[0] = hinst;
428 context[1] = owner;
429
430 DWORD threadID;
431 CreateThread (NULL, 0, do_download_site_info_thread, context, 0, &threadID);
432 }
433
434 static BOOL CALLBACK
435 drop_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
436 {
437 switch (message)
438 {
439 case WM_INITDIALOG:
440 eset(h, IDC_DROP_MIRRORS, cache_warn_urls);
441 /* Should this be set by default? */
442 // CheckDlgButton (h, IDC_DROP_NOWARN, BST_CHECKED);
443 SetFocus (GetDlgItem(h, IDC_DROP_NOWARN));
444 return FALSE;
445 break;
446 case WM_COMMAND:
447 switch (LOWORD (wParam))
448 {
449 case IDYES:
450 if (IsDlgButtonChecked (h, IDC_DROP_NOWARN) == BST_CHECKED)
451 EndDialog (h, CACHE_ACCEPT_NOWARN);
452 else
453 EndDialog (h, CACHE_ACCEPT_WARN);
454 break;
455
456 case IDNO:
457 EndDialog (h, CACHE_REJECT);
458 break;
459
460 default:
461 return 0;
462 }
463 return TRUE;
464 break;
465 default:
466 return FALSE;
467 }
468 }
469
470 int check_dropped_mirrors (HWND h)
471 {
472 cache_warn_urls = "";
473 dropped_site_list.clear ();
474
475 for (SiteList::const_iterator n = site_list.begin ();
476 n != site_list.end (); ++n)
477 {
478 SiteList::iterator i = find (all_site_list.begin(), all_site_list.end(),
479 *n);
480 if (i == all_site_list.end() || !i->servername.size())
481 {
482 SiteList::iterator j = find (cached_site_list.begin(),
483 cached_site_list.end(), *n);
484 if (j != cached_site_list.end())
485 {
486 log (LOG_PLAIN) << "Dropped selected mirror: " << n->url
487 << endLog;
488 dropped_site_list.push_back (*j);
489 if (cache_warn_urls.size())
490 cache_warn_urls += "\r\n";
491 cache_warn_urls += i->url;
492 }
493 }
494 }
495 if (cache_warn_urls.size())
496 {
497 if (unattended_mode)
498 return CACHE_ACCEPT_WARN;
499 return DialogBox (hinstance, MAKEINTRESOURCE (IDD_DROPPED), h,
500 drop_proc);
501 }
502 return CACHE_ACCEPT_NOWARN;
503 }
504
505 void write_cache_list (io_stream *f, const SiteList& theSites)
506 {
507 string s;
508 for (SiteList::const_iterator n = theSites.begin ();
509 n != theSites.end (); ++n)
510 if (n->servername.size())
511 *f << (n->url + ";" + n->servername + ";" + n->area + ";"
512 + n->location);
513 }
514
515 void save_cache_file (int cache_action)
516 {
517 string s;
518 io_stream *f = UserSettings::instance().open ("mirrors-lst");
519 if (f)
520 {
521 write_cache_list (f, all_site_list);
522 if (cache_action == CACHE_ACCEPT_WARN)
523 {
524 log (LOG_PLAIN) << "Adding dropped mirrors to cache to warn again."
525 << endLog;
526 *f << "# Following mirrors re-added by setup.exe to warn again about dropped urls.";
527 write_cache_list (f, dropped_site_list);
528 }
529 delete f;
530 }
531 }
532
533 bool SitePage::Create ()
534 {
535 return PropertyPage::Create (IDD_SITE);
536 }
537
538 long
539 SitePage::OnNext ()
540 {
541 HWND h = GetHWND ();
542 int cache_action = CACHE_ACCEPT_NOWARN;
543
544 save_dialog (h);
545
546 if (cache_is_usable && !(cache_action = check_dropped_mirrors (h)))
547 return -1;
548
549 if (cache_needs_writing)
550 save_cache_file (cache_action);
551
552 // Log all the selected URLs from the list.
553 for (SiteList::const_iterator n = site_list.begin ();
554 n != site_list.end (); ++n)
555 log (LOG_PLAIN) << "site: " << n->url << endLog;
556
557 Progress.SetActivateTask (WM_APP_START_SETUP_INI_DOWNLOAD);
558 return IDD_INSTATUS;
559
560 return 0;
561 }
562
563 long
564 SitePage::OnBack ()
565 {
566 HWND h = GetHWND ();
567
568 save_dialog (h);
569
570 // Go back to the net connection type page
571 return 0;
572 }
573
574 void
575 SitePage::OnActivate ()
576 {
577 // Fill the list box with all known sites.
578 PopulateListBox ();
579
580 // Load the user URL box with nothing - it is in the list already.
581 eset (GetHWND (), IDC_EDIT_USER_URL, "");
582
583 // Get the enabled/disabled states of the controls set accordingly.
584 CheckControlsAndDisableAccordingly ();
585 }
586
587 long
588 SitePage::OnUnattended ()
589 {
590 if (SendMessage (GetDlgItem (IDC_URL_LIST), LB_GETSELCOUNT, 0, 0) > 0)
591 return OnNext ();
592 else
593 return -2;
594 }
595
596 void
597 SitePage::CheckControlsAndDisableAccordingly () const
598 {
599 DWORD ButtonFlags = PSWIZB_BACK;
600
601 // Check that at least one download site is selected.
602 if (SendMessage (GetDlgItem (IDC_URL_LIST), LB_GETSELCOUNT, 0, 0) > 0)
603 {
604 // At least one site selected, enable "Next".
605 ButtonFlags |= PSWIZB_NEXT;
606 }
607 GetOwner ()->SetButtons (ButtonFlags);
608 }
609
610 void
611 SitePage::PopulateListBox ()
612 {
613 int j;
614 HWND listbox = GetDlgItem (IDC_URL_LIST);
615
616 // Populate the list box with the URLs.
617 SendMessage (listbox, LB_RESETCONTENT, 0, 0);
618 for (SiteList::const_iterator i = all_site_list.begin ();
619 i != all_site_list.end (); ++i)
620 {
621 j = SendMessage (listbox, LB_ADDSTRING, 0,
622 (LPARAM) i->displayed_url.c_str());
623 SendMessage (listbox, LB_SETITEMDATA, j, j);
624 }
625
626 // Select the selected ones.
627 for (SiteList::const_iterator n = site_list.begin ();
628 n != site_list.end (); ++n)
629 {
630 int index = SendMessage (listbox, LB_FINDSTRING, (WPARAM) - 1,
631 (LPARAM) n->displayed_url.c_str());
632 if (index != LB_ERR)
633 {
634 // Highlight the selected item
635 SendMessage (listbox, LB_SELITEMRANGE, TRUE, (index << 16) | index);
636 // Make sure it's fully visible
637 SendMessage (listbox, LB_SETCARETINDEX, index, FALSE);
638 }
639 }
640 }
641
642 bool SitePage::OnMessageCmd (int id, HWND hwndctl, UINT code)
643 {
644 switch (id)
645 {
646 case IDC_EDIT_USER_URL:
647 {
648 // FIXME: Make Enter here cause an ADD, not a NEXT.
649 break;
650 }
651 case IDC_URL_LIST:
652 {
653 if (code == LBN_SELCHANGE)
654 {
655 CheckControlsAndDisableAccordingly ();
656 save_dialog (GetHWND ());
657 }
658 break;
659 }
660 case IDC_BUTTON_ADD_URL:
661 {
662 if (code == BN_CLICKED)
663 {
664 // User pushed the Add button.
665 std::string other_url = egetString (GetHWND (), IDC_EDIT_USER_URL);
666 if (other_url.size())
667 {
668 site_list_type newsite (other_url, "", "", "");
669 SiteList::iterator i = find (all_site_list.begin(),
670 all_site_list.end(), newsite);
671 if (i == all_site_list.end())
672 {
673 all_site_list.push_back (newsite);
674 log (LOG_BABBLE) << "Adding site: " << other_url << endLog;
675 }
676 else
677 {
678 *i = newsite;
679 log (LOG_BABBLE) << "Replacing site: " << other_url << endLog;
680 }
681
682 // Assume the user wants to use it and select it for him.
683 site_list.push_back (newsite);
684
685 // Update the list box.
686 PopulateListBox ();
687 // And allow the user to continue
688 CheckControlsAndDisableAccordingly ();
689 eset (GetHWND (), IDC_EDIT_USER_URL, "");
690 }
691 }
692 break;
693 }
694 default:
695 // Wasn't recognized or handled.
696 return false;
697 }
698
699 // Was handled since we never got to default above.
700 return true;
701 }
This page took 0.075545 seconds and 5 git commands to generate.