2 * Copyright (c) 2000, Red Hat, Inc.
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.
9 * A copy of the GNU General Public License can be found at
12 * Written by DJ Delorie <dj@cygnus.com>
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. */
33 #include "LogSingleton.h"
34 #include "io_stream.h"
37 #include "propsheet.h"
40 #include "ControlAdjuster.h"
41 #include "Exception.h"
44 extern ThreeBarProgressPage Progress
;
47 What to do if dropped mirrors are selected.
51 CACHE_REJECT
, // Go back to re-select mirrors.
52 CACHE_ACCEPT_WARN
, // Go on. Warn again next time.
53 CACHE_ACCEPT_NOWARN
// Go on. Don't warn again.
59 static ControlAdjuster::ControlInfo SiteControlsInfo
[] = {
60 {IDC_URL_LIST
, CP_STRETCH
, CP_STRETCH
},
61 {IDC_EDIT_USER_URL
, CP_STRETCH
, CP_BOTTOM
},
62 {IDC_BUTTON_ADD_URL
, CP_RIGHT
, CP_BOTTOM
},
63 {IDC_SITE_USERURL
, CP_LEFT
, CP_BOTTOM
},
69 sizeProcessor
.AddControlInfo (SiteControlsInfo
);
72 #include "getopt++/StringArrayOption.h"
73 #include "getopt++/BoolOption.h"
74 #include "UserSettings.h"
77 bool cache_needs_writing
;
78 std::string cache_warn_urls
;
83 /* Fresh mirrors + selected sites */
84 SiteList all_site_list
;
86 /* Previously fresh + cached before */
87 SiteList cached_site_list
;
89 /* Stale selected sites to warn about and add to cache */
90 SiteList dropped_site_list
;
92 StringArrayOption
SiteOption('s', "site", "Download site URL");
94 BoolOption
OnlySiteOption(false, 'O', "only-site", "Do not download mirror list. Only use sites specified with -s.");
95 extern BoolOption UnsupportedOption
;
97 SiteSetting::SiteSetting (): saved (false)
99 std::vector
<std::string
> SiteOptionStrings
= SiteOption
;
100 if (SiteOptionStrings
.size())
102 for (std::vector
<std::string
>::const_iterator n
= SiteOptionStrings
.begin ();
103 n
!= SiteOptionStrings
.end (); ++n
)
104 registerSavedSite (n
->c_str ());
111 SiteSetting::lastMirrorKey ()
113 if (UnsupportedOption
)
114 return "last-mirror-unsupported";
116 return "last-mirror";
122 io_stream
*f
= UserSettings::instance().open (lastMirrorKey ());
125 for (SiteList::const_iterator n
= site_list
.begin ();
126 n
!= site_list
.end (); ++n
)
133 SiteSetting::~SiteSetting ()
139 site_list_type::site_list_type (const std::string
&_url
,
140 const std::string
&_servername
,
141 const std::string
&_area
,
142 const std::string
&_location
,
143 bool _from_mirrors_lst
)
146 servername
= _servername
;
148 location
= _location
;
149 from_mirrors_lst
= _from_mirrors_lst
;
151 /* Canonicalize URL to ensure it ends with a '/' */
152 if (url
.at(url
.length()-1) != '/')
155 /* displayed_url is protocol and site name part of url */
156 std::string::size_type path_offset
= url
.find ("/", url
.find ("//") + 2);
157 displayed_url
= url
.substr(0, path_offset
);
159 /* the sorting key is hostname components in reverse order (to sort by country code)
160 plus the url (to ensure uniqueness) */
162 std::string::size_type last_idx
= displayed_url
.length () - 1;
163 std::string::size_type idx
= url
.find_last_of("./", last_idx
);
164 if (last_idx
- idx
== 3)
166 /* Sort non-country TLDs (.com, .net, ...) together. */
171 key
+= url
.substr(idx
+ 1, last_idx
- idx
);
174 idx
= url
.find_last_of("./", last_idx
);
175 if (idx
== std::string::npos
)
181 site_list_type::site_list_type (site_list_type
const &rhs
)
185 servername
= rhs
.servername
;
187 location
= rhs
.location
;
188 from_mirrors_lst
= rhs
.from_mirrors_lst
;
189 displayed_url
= rhs
.displayed_url
;
193 site_list_type::operator= (site_list_type
const &rhs
)
197 servername
= rhs
.servername
;
199 location
= rhs
.location
;
200 from_mirrors_lst
= rhs
.from_mirrors_lst
;
201 displayed_url
= rhs
.displayed_url
;
206 site_list_type::operator == (site_list_type
const &rhs
) const
208 return stricmp (key
.c_str(), rhs
.key
.c_str()) == 0;
212 site_list_type::operator < (site_list_type
const &rhs
) const
214 return stricmp (key
.c_str(), rhs
.key
.c_str()) < 0;
220 // Remove anything that was previously in the selected site list.
223 HWND listbox
= GetDlgItem (h
, IDC_URL_LIST
);
224 int sel_count
= SendMessage (listbox
, LB_GETSELCOUNT
, 0, 0);
227 int sel_buffer
[sel_count
];
228 SendMessage (listbox
, LB_GETSELITEMS
, sel_count
, (LPARAM
) sel_buffer
);
229 for (int n
= 0; n
< sel_count
; n
++)
232 SendMessage (listbox
, LB_GETITEMDATA
, sel_buffer
[n
], 0);
233 site_list
.push_back (all_site_list
[mirror
]);
238 // This is called only for lists of mirrors that came (now or in a
239 // previous setup run) from mirrors.lst.
241 load_site_list (SiteList
& theSites
, char *theString
)
243 char *bol
, *eol
, *nl
;
249 for (eol
= bol
; *eol
&& *eol
!= '\n'; eol
++);
254 while (eol
> bol
&& eol
[-1] == '\r')
257 if (*bol
== '#' || !*bol
)
259 /* Accept only the URL schemes we can understand. */
260 if (strncmp(bol
, "http://", 7) == 0 ||
261 strncmp(bol
, "https://", 8) == 0 ||
262 strncmp(bol
, "ftp://", 6) == 0 ||
263 strncmp(bol
, "ftps://", 7) == 0)
265 char *semi
= strchr (bol
, ';');
272 semi2
= strchr (semi
, ';');
277 semi3
= strchr (semi2
, ';');
286 /* Ignore malformed lines */
287 if (!semi
|| !semi2
|| !semi3
)
290 site_list_type
newsite (bol
, semi
, semi2
, semi3
, true);
291 SiteList::iterator i
= find (theSites
.begin(),
292 theSites
.end(), newsite
);
293 if (i
== theSites
.end())
296 merge (theSites
.begin(), theSites
.end(),
297 &newsite
, &newsite
+ 1,
298 inserter (result
, result
.begin()));
302 //TODO: remove and remerge
307 Log (LOG_BABBLE
) << "Discarding line '" << bol
<< "' due to unknown protocol" << endLog
;
313 get_site_list (HINSTANCE h
, HWND owner
)
315 char mirror_url
[1000];
317 char *theMirrorString
, *theCachedString
;
319 if (UnsupportedOption
)
322 const char *cached_mirrors
= OnlySiteOption
? NULL
: UserSettings::instance().get ("mirrors-lst");
325 Log (LOG_BABBLE
) << "Loaded cached mirror list" << endLog
;
326 cache_is_usable
= true;
330 Log (LOG_BABBLE
) << "Cached mirror list unavailable" << endLog
;
331 cache_is_usable
= false;
335 if (LoadString (h
, IDS_MIRROR_LST
, mirror_url
, sizeof (mirror_url
)) <= 0)
338 std::string mirrors
= OnlySiteOption
? std::string ("") : get_url_to_string (mirror_url
, owner
);
340 cache_needs_writing
= true;
343 if (!cached_mirrors
[0])
346 note(owner
, IDS_NO_MIRROR_LST
);
347 Log (LOG_BABBLE
) << "Defaulting to empty mirror list" << endLog
;
351 mirrors
= cached_mirrors
;
352 Log (LOG_BABBLE
) << "Using cached mirror list" << endLog
;
354 cache_is_usable
= false;
355 cache_needs_writing
= false;
357 theMirrorString
= new_cstr_char_array (mirrors
);
358 theCachedString
= new_cstr_char_array (cached_mirrors
);
360 load_site_list (all_site_list
, theMirrorString
);
361 load_site_list (cached_site_list
, theCachedString
);
363 delete[] theMirrorString
;
364 delete[] theCachedString
;
369 /* List of machines that should not be used by default when saved
371 #define NOSAVE1 "ftp://sourceware.org/"
372 #define NOSAVE1_LEN (sizeof (NOSAVE2) - 1)
373 #define NOSAVE2 "ftp://sources.redhat.com/"
374 #define NOSAVE2_LEN (sizeof (NOSAVE1) - 1)
375 #define NOSAVE3 "ftp://gcc.gnu.org/"
376 #define NOSAVE3_LEN (sizeof (NOSAVE3) - 1)
379 SiteSetting::registerSavedSite (const char * site
)
381 site_list_type
tempSite(site
, "", "", "", false);
382 SiteList::iterator i
= find (all_site_list
.begin(),
383 all_site_list
.end(), tempSite
);
384 if (i
== all_site_list
.end())
386 /* Don't default to certain machines if they suffer
387 from bandwidth limitations. */
388 if (strnicmp (site
, NOSAVE1
, NOSAVE1_LEN
) == 0
389 || strnicmp (site
, NOSAVE2
, NOSAVE2_LEN
) == 0
390 || strnicmp (site
, NOSAVE3
, NOSAVE3_LEN
) == 0)
393 merge (all_site_list
.begin(), all_site_list
.end(),
394 &tempSite
, &tempSite
+ 1,
395 inserter (result
, result
.begin()));
396 all_site_list
= result
;
397 site_list
.push_back (tempSite
);
400 site_list
.push_back (*i
);
404 SiteSetting::getSavedSites ()
406 const char *buf
= UserSettings::instance().get (lastMirrorKey ());
409 char *fg_ret
= strdup (buf
);
410 for (char *site
= strtok (fg_ret
, "\n"); site
; site
= strtok (NULL
, "\n"))
411 registerSavedSite (site
);
416 do_download_site_info_thread (void *p
)
421 context
= (HANDLE
*) p
;
425 hinst
= (HINSTANCE
) (context
[0]);
426 h
= (HWND
) (context
[1]);
427 static bool downloaded
= false;
428 if (!downloaded
&& get_site_list (hinst
, h
))
430 // Error: Couldn't download the site info.
431 // Go back to the Net setup page.
432 mbox (h
, TEXT ("Can't get list of download sites.\n")
433 TEXT("Make sure your network settings are correct and try again."),
436 // Tell the progress page that we're done downloading
437 Progress
.PostMessageNow (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE
, 0, IDD_NET
);
442 // Everything worked, go to the site select page
443 // Tell the progress page that we're done downloading
444 Progress
.PostMessageNow (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE
, 0, IDD_SITE
);
447 TOPLEVEL_CATCH((HWND
) context
[1], "site");
452 static HANDLE context
[2];
455 do_download_site_info (HINSTANCE hinst
, HWND owner
)
462 CreateThread (NULL
, 0, do_download_site_info_thread
, context
, 0, &threadID
);
465 static INT_PTR CALLBACK
466 drop_proc (HWND h
, UINT message
, WPARAM wParam
, LPARAM lParam
)
471 eset(h
, IDC_DROP_MIRRORS
, cache_warn_urls
);
472 /* Should this be set by default? */
473 // CheckDlgButton (h, IDC_DROP_NOWARN, BST_CHECKED);
474 SetFocus (GetDlgItem(h
, IDC_DROP_NOWARN
));
478 switch (LOWORD (wParam
))
481 if (IsDlgButtonChecked (h
, IDC_DROP_NOWARN
) == BST_CHECKED
)
482 EndDialog (h
, CACHE_ACCEPT_NOWARN
);
484 EndDialog (h
, CACHE_ACCEPT_WARN
);
488 EndDialog (h
, CACHE_REJECT
);
501 int check_dropped_mirrors (HWND h
)
503 cache_warn_urls
= "";
504 dropped_site_list
.clear ();
506 for (SiteList::const_iterator n
= site_list
.begin ();
507 n
!= site_list
.end (); ++n
)
509 SiteList::iterator i
= find (all_site_list
.begin(), all_site_list
.end(),
511 if (i
== all_site_list
.end() || !i
->from_mirrors_lst
)
513 SiteList::iterator j
= find (cached_site_list
.begin(),
514 cached_site_list
.end(), *n
);
515 if (j
!= cached_site_list
.end())
517 Log (LOG_PLAIN
) << "Dropped selected mirror: " << n
->url
519 dropped_site_list
.push_back (*j
);
520 if (cache_warn_urls
.size())
521 cache_warn_urls
+= "\r\n";
522 cache_warn_urls
+= i
->url
;
526 if (cache_warn_urls
.size())
529 return CACHE_ACCEPT_WARN
;
530 return DialogBox (hinstance
, MAKEINTRESOURCE (IDD_DROPPED
), h
,
533 return CACHE_ACCEPT_NOWARN
;
536 void write_cache_list (io_stream
*f
, const SiteList
& theSites
)
538 for (SiteList::const_iterator n
= theSites
.begin ();
539 n
!= theSites
.end (); ++n
)
540 if (n
->from_mirrors_lst
)
541 *f
<< (n
->url
+ ";" + n
->servername
+ ";" + n
->area
+ ";"
545 void save_cache_file (int cache_action
)
547 io_stream
*f
= UserSettings::instance().open ("mirrors-lst");
550 write_cache_list (f
, all_site_list
);
551 if (cache_action
== CACHE_ACCEPT_WARN
)
553 Log (LOG_PLAIN
) << "Adding dropped mirrors to cache to warn again."
555 *f
<< "# Following mirrors re-added by setup.exe to warn again about dropped urls.";
556 write_cache_list (f
, dropped_site_list
);
562 bool SitePage::Create ()
564 return PropertyPage::Create (IDD_SITE
);
571 int cache_action
= CACHE_ACCEPT_NOWARN
;
575 if (cache_is_usable
&& !(cache_action
= check_dropped_mirrors (h
)))
578 if (cache_needs_writing
)
579 save_cache_file (cache_action
);
581 // Log all the selected URLs from the list.
582 for (SiteList::const_iterator n
= site_list
.begin ();
583 n
!= site_list
.end (); ++n
)
584 Log (LOG_PLAIN
) << "site: " << n
->url
<< endLog
;
586 Progress
.SetActivateTask (WM_APP_START_SETUP_INI_DOWNLOAD
);
599 // Go back to the net connection type page
604 SitePage::OnActivate ()
606 // Fill the list box with all known sites.
609 // Load the user URL box with nothing - it is in the list already.
610 eset (GetHWND (), IDC_EDIT_USER_URL
, "");
612 // Get the enabled/disabled states of the controls set accordingly.
613 CheckControlsAndDisableAccordingly ();
617 SitePage::OnUnattended ()
619 if (SendMessage (GetDlgItem (IDC_URL_LIST
), LB_GETSELCOUNT
, 0, 0) > 0)
626 SitePage::CheckControlsAndDisableAccordingly () const
628 DWORD ButtonFlags
= PSWIZB_BACK
;
630 // Check that at least one download site is selected.
631 if (SendMessage (GetDlgItem (IDC_URL_LIST
), LB_GETSELCOUNT
, 0, 0) > 0)
633 // At least one site selected, enable "Next".
634 ButtonFlags
|= PSWIZB_NEXT
;
636 GetOwner ()->SetButtons (ButtonFlags
);
640 SitePage::PopulateListBox ()
643 HWND listbox
= GetDlgItem (IDC_URL_LIST
);
645 // Populate the list box with the URLs.
646 SendMessage (listbox
, LB_RESETCONTENT
, 0, 0);
647 for (SiteList::const_iterator i
= all_site_list
.begin ();
648 i
!= all_site_list
.end (); ++i
)
650 j
= SendMessage (listbox
, LB_ADDSTRING
, 0,
651 (LPARAM
) i
->displayed_url
.c_str());
652 SendMessage (listbox
, LB_SETITEMDATA
, j
, j
);
655 // Select the selected ones.
656 for (SiteList::const_iterator n
= site_list
.begin ();
657 n
!= site_list
.end (); ++n
)
659 SiteList::iterator i
= find (all_site_list
.begin(),
660 all_site_list
.end(), *n
);
661 if (i
!= all_site_list
.end())
663 int index
= i
- all_site_list
.begin();
665 // Highlight the selected item
666 SendMessage (listbox
, LB_SELITEMRANGE
, TRUE
, (index
<< 16) | index
);
667 // Make sure it's fully visible
668 SendMessage (listbox
, LB_SETCARETINDEX
, index
, FALSE
);
673 bool SitePage::OnMessageCmd (int id
, HWND hwndctl
, UINT code
)
677 case IDC_EDIT_USER_URL
:
679 // Set the default pushbutton to ADD if the user is entering text.
680 if (code
== EN_CHANGE
)
681 SendMessage (GetHWND (), DM_SETDEFID
, (WPARAM
) IDC_BUTTON_ADD_URL
, 0);
686 if (code
== LBN_SELCHANGE
)
688 CheckControlsAndDisableAccordingly ();
689 save_dialog (GetHWND ());
693 case IDC_BUTTON_ADD_URL
:
695 if (code
== BN_CLICKED
)
697 // User pushed the Add button.
698 std::string other_url
= egetString (GetHWND (), IDC_EDIT_USER_URL
);
699 if (other_url
.size())
701 site_list_type
newsite (other_url
, "", "", "", false);
702 SiteList::iterator i
= find (all_site_list
.begin(),
703 all_site_list
.end(), newsite
);
704 if (i
== all_site_list
.end())
706 all_site_list
.push_back (newsite
);
707 Log (LOG_BABBLE
) << "Adding site: " << other_url
<< endLog
;
708 site_list
.push_back (newsite
);
711 site_list
.push_back (*i
);
713 // Update the list box.
715 // And allow the user to continue
716 CheckControlsAndDisableAccordingly ();
717 eset (GetHWND (), IDC_EDIT_USER_URL
, "");
723 // Wasn't recognized or handled.
727 // Was handled since we never got to default above.