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 #define MIRROR_LIST_URL "https://cygwin.com/mirrors.lst"
46 extern ThreeBarProgressPage Progress
;
49 What to do if dropped mirrors are selected.
53 CACHE_REJECT
, // Go back to re-select mirrors.
54 CACHE_ACCEPT_WARN
, // Go on. Warn again next time.
55 CACHE_ACCEPT_NOWARN
// Go on. Don't warn again.
61 static ControlAdjuster::ControlInfo SiteControlsInfo
[] = {
62 {IDC_URL_LIST
, CP_STRETCH
, CP_STRETCH
},
63 {IDC_EDIT_USER_URL
, CP_STRETCH
, CP_BOTTOM
},
64 {IDC_BUTTON_ADD_URL
, CP_RIGHT
, CP_BOTTOM
},
65 {IDC_SITE_USERURL
, CP_LEFT
, CP_BOTTOM
},
71 sizeProcessor
.AddControlInfo (SiteControlsInfo
);
74 #include "getopt++/StringArrayOption.h"
75 #include "getopt++/BoolOption.h"
76 #include "UserSettings.h"
79 bool cache_needs_writing
;
80 std::string cache_warn_urls
;
85 /* Fresh mirrors + selected sites */
86 SiteList all_site_list
;
88 /* Previously fresh + cached before */
89 SiteList cached_site_list
;
91 /* Stale selected sites to warn about and add to cache */
92 SiteList dropped_site_list
;
94 StringArrayOption
SiteOption('s', "site", IDS_HELPTEXT_SITE
);
95 BoolOption
OnlySiteOption(false, 'O', "only-site", IDS_HELPTEXT_ONLY_SITE
);
96 extern BoolOption UnsupportedOption
;
98 SiteSetting::SiteSetting (): saved (false)
100 std::vector
<std::string
> SiteOptionStrings
= SiteOption
;
101 if (SiteOptionStrings
.size())
103 for (std::vector
<std::string
>::const_iterator n
= SiteOptionStrings
.begin ();
104 n
!= SiteOptionStrings
.end (); ++n
)
105 registerSavedSite (n
->c_str ());
112 SiteSetting::lastMirrorKey ()
114 if (UnsupportedOption
)
115 return "last-mirror-unsupported";
117 return "last-mirror";
123 io_stream
*f
= UserSettings::instance().open (lastMirrorKey ());
126 for (SiteList::const_iterator n
= site_list
.begin ();
127 n
!= site_list
.end (); ++n
)
134 SiteSetting::~SiteSetting ()
140 site_list_type::site_list_type (const std::string
&_url
,
141 const std::string
&_servername
,
142 const std::string
&_area
,
143 const std::string
&_location
,
144 bool _from_mirrors_lst
,
145 bool _noshow
= false,
146 const std::string
&_redir
= "")
149 servername
= _servername
;
151 location
= _location
;
152 from_mirrors_lst
= _from_mirrors_lst
;
156 /* Canonicalize URL to ensure it ends with a '/' */
157 if (url
.at(url
.length()-1) != '/')
160 /* displayed_url is protocol and site name part of url */
161 std::string::size_type path_offset
= url
.find ("/", url
.find ("//") + 2);
162 displayed_url
= url
.substr(0, path_offset
);
164 /* the sorting key is hostname components in reverse order (to sort by country code)
165 plus the url (to ensure uniqueness) */
167 std::string::size_type last_idx
= displayed_url
.length () - 1;
168 std::string::size_type idx
= url
.find_last_of("./", last_idx
);
169 if (last_idx
- idx
== 3)
171 /* Sort non-country TLDs (.com, .net, ...) together. */
176 key
+= url
.substr(idx
+ 1, last_idx
- idx
);
179 idx
= url
.find_last_of("./", last_idx
);
180 if (idx
== std::string::npos
)
187 site_list_type::operator == (site_list_type
const &rhs
) const
189 return stricmp (key
.c_str(), rhs
.key
.c_str()) == 0;
193 site_list_type::operator < (site_list_type
const &rhs
) const
195 return stricmp (key
.c_str(), rhs
.key
.c_str()) < 0;
199 A SiteList is maintained as an in-order std::vector of site_list_type, by
200 replacing it with a new object with the new item inserted in the correct
203 Yes, we could just use an ordered container, instead.
206 site_list_insert(SiteList
&site_list
, site_list_type newsite
)
208 SiteList::iterator i
= find (site_list
.begin(), site_list
.end(), newsite
);
209 if (i
== site_list
.end())
212 merge (site_list
.begin(), site_list
.end(),
213 &newsite
, &newsite
+ 1,
214 inserter (result
, result
.begin()));
224 // Remove anything that was previously in the selected site list.
227 HWND listbox
= GetDlgItem (h
, IDC_URL_LIST
);
228 int sel_count
= SendMessage (listbox
, LB_GETSELCOUNT
, 0, 0);
231 int sel_buffer
[sel_count
];
232 SendMessage (listbox
, LB_GETSELITEMS
, sel_count
, (LPARAM
) sel_buffer
);
233 for (int n
= 0; n
< sel_count
; n
++)
236 SendMessage (listbox
, LB_GETITEMDATA
, sel_buffer
[n
], 0);
237 site_list
.push_back (all_site_list
[mirror
]);
242 // This is called only for lists of mirrors that came (now or in a
243 // previous setup run) from mirrors.lst.
245 load_site_list (SiteList
& theSites
, char *theString
)
247 char *bol
, *eol
, *nl
;
253 for (eol
= bol
; *eol
&& *eol
!= '\n'; eol
++);
258 while (eol
> bol
&& eol
[-1] == '\r')
261 if (*bol
== '#' || !*bol
)
263 /* Accept only the URL schemes we can understand. */
264 if (strncmp(bol
, "http://", 7) == 0 ||
265 strncmp(bol
, "https://", 8) == 0 ||
266 strncmp(bol
, "ftp://", 6) == 0 ||
267 strncmp(bol
, "ftps://", 7) == 0)
272 /* split into up to 4 semicolon-delimited parts */
273 for (i
= 0; i
< 4; i
++)
277 for (i
= 0; i
< 4; i
++)
279 semi
[i
] = strchr (p
, ';');
287 /* Ignore malformed lines */
288 if (!semi
[0] || !semi
[1] || !semi
[2])
291 /* fourth part is an optional, comma-delimited set of flags */
293 const char *redir
= "";
295 char *flag
= semi
[3];
298 if (strncmp(flag
, "noshow", 6) == 0)
300 else if (strncmp(flag
, "redir=", 6) == 0)
303 flag
= strchr (flag
, ',');
308 /* add site to list */
309 site_list_type
newsite (bol
, semi
[0], semi
[1], semi
[2], true, noshow
, redir
);
310 site_list_insert (theSites
, newsite
);
314 Log (LOG_BABBLE
) << "Discarding line '" << bol
<< "' due to unknown protocol" << endLog
;
320 migrate_selected_site_list()
322 const std::string http
= "http://";
324 for (SiteList::iterator i
= site_list
.begin();
325 i
!= site_list
.end();
328 /* If the saved selected site URL starts with "http://", and the same URL,
329 but starting with "https://" appears in the mirror list, migrate to
331 if (strnicmp(i
->url
.c_str(), http
.c_str(), strlen(http
.c_str())) == 0)
333 std::string migrated_site
= "https://";
334 migrated_site
.append(i
->url
.substr(http
.length()));
336 site_list_type
migrate(migrated_site
, "", "", "", false);
337 SiteList::iterator j
= find (all_site_list
.begin(),
338 all_site_list
.end(), migrate
);
339 if (j
!= all_site_list
.end())
341 Log (LOG_PLAIN
) << "Migrated " << i
->url
<< " to " << migrated_site
<< endLog
;
346 /* If the saved selected site URL appears in the site list with a redir
347 flag, replace with the redirected URL */
349 SiteList::iterator j
= find (all_site_list
.begin(),
350 all_site_list
.end(), *i
);
352 if (j
!= all_site_list
.end())
354 if (!j
->redir
.empty())
356 site_list_type
migrate(j
->redir
, "", "", "", false);
357 Log (LOG_PLAIN
) << "Migrated " << i
->url
<< " to " << j
->redir
<< endLog
;
366 get_site_list (HINSTANCE h
, HWND owner
)
368 char *theMirrorString
, *theCachedString
;
370 if (UnsupportedOption
)
373 const char *cached_mirrors
= OnlySiteOption
? NULL
: UserSettings::instance().get ("mirrors-lst");
376 Log (LOG_BABBLE
) << "Loaded cached mirror list" << endLog
;
377 cache_is_usable
= true;
381 Log (LOG_BABBLE
) << "Cached mirror list unavailable" << endLog
;
382 cache_is_usable
= false;
386 std::string mirrors
= OnlySiteOption
? std::string ("") : get_url_to_string (MIRROR_LIST_URL
, owner
);
388 cache_needs_writing
= true;
391 if (!cached_mirrors
[0])
394 note(owner
, IDS_NO_MIRROR_LST
);
395 Log (LOG_BABBLE
) << "Defaulting to empty mirror list" << endLog
;
399 mirrors
= cached_mirrors
;
400 Log (LOG_BABBLE
) << "Using cached mirror list" << endLog
;
402 cache_is_usable
= false;
403 cache_needs_writing
= false;
405 theMirrorString
= new_cstr_char_array (mirrors
);
406 theCachedString
= new_cstr_char_array (cached_mirrors
);
408 load_site_list (all_site_list
, theMirrorString
);
409 load_site_list (cached_site_list
, theCachedString
);
411 delete[] theMirrorString
;
412 delete[] theCachedString
;
414 migrate_selected_site_list();
419 /* List of machines that should not be used by default when saved
421 #define NOSAVE1 "ftp://sourceware.org/"
422 #define NOSAVE1_LEN (sizeof (NOSAVE2) - 1)
423 #define NOSAVE2 "ftp://sources.redhat.com/"
424 #define NOSAVE2_LEN (sizeof (NOSAVE1) - 1)
425 #define NOSAVE3 "ftp://gcc.gnu.org/"
426 #define NOSAVE3_LEN (sizeof (NOSAVE3) - 1)
429 SiteSetting::registerSavedSite (const char * site
)
431 site_list_type
tempSite(site
, "", "", "", false);
433 /* Don't default to certain machines if they suffer from bandwidth
435 if (strnicmp (site
, NOSAVE1
, NOSAVE1_LEN
) == 0
436 || strnicmp (site
, NOSAVE2
, NOSAVE2_LEN
) == 0
437 || strnicmp (site
, NOSAVE3
, NOSAVE3_LEN
) == 0)
440 site_list_insert (all_site_list
, tempSite
);
441 site_list
.push_back (tempSite
);
445 SiteSetting::getSavedSites ()
447 const char *buf
= UserSettings::instance().get (lastMirrorKey ());
450 char *fg_ret
= strdup (buf
);
451 for (char *site
= strtok (fg_ret
, "\n"); site
; site
= strtok (NULL
, "\n"))
452 registerSavedSite (site
);
457 do_download_site_info_thread (void *p
)
462 context
= (HANDLE
*) p
;
464 SetThreadUILanguage(langid
);
468 hinst
= (HINSTANCE
) (context
[0]);
469 h
= (HWND
) (context
[1]);
470 static bool downloaded
= false;
471 if (!downloaded
&& get_site_list (hinst
, h
))
473 // Error: Couldn't download the site info.
474 // Go back to the Net setup page.
475 mbox (h
, IDS_GET_SITELIST_ERROR
, MB_OK
);
477 // Tell the progress page that we're done downloading
478 Progress
.PostMessageNow (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE
, 0, IDD_NET
);
483 // Everything worked, go to the site select page
484 // Tell the progress page that we're done downloading
485 Progress
.PostMessageNow (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE
, 0, IDD_SITE
);
488 TOPLEVEL_CATCH((HWND
) context
[1], "site");
493 static HANDLE context
[2];
496 do_download_site_info (HINSTANCE hinst
, HWND owner
)
503 CreateThread (NULL
, 0, do_download_site_info_thread
, context
, 0, &threadID
);
506 static INT_PTR CALLBACK
507 drop_proc (HWND h
, UINT message
, WPARAM wParam
, LPARAM lParam
)
512 eset(h
, IDC_DROP_MIRRORS
, cache_warn_urls
);
513 /* Should this be set by default? */
514 // CheckDlgButton (h, IDC_DROP_NOWARN, BST_CHECKED);
515 SetFocus (GetDlgItem(h
, IDC_DROP_NOWARN
));
519 switch (LOWORD (wParam
))
522 if (IsDlgButtonChecked (h
, IDC_DROP_NOWARN
) == BST_CHECKED
)
523 EndDialog (h
, CACHE_ACCEPT_NOWARN
);
525 EndDialog (h
, CACHE_ACCEPT_WARN
);
529 EndDialog (h
, CACHE_REJECT
);
542 int check_dropped_mirrors (HWND h
)
544 cache_warn_urls
= "";
545 dropped_site_list
.clear ();
547 for (SiteList::const_iterator n
= site_list
.begin ();
548 n
!= site_list
.end (); ++n
)
550 SiteList::iterator i
= find (all_site_list
.begin(), all_site_list
.end(),
552 if (i
== all_site_list
.end() || !i
->from_mirrors_lst
)
554 SiteList::iterator j
= find (cached_site_list
.begin(),
555 cached_site_list
.end(), *n
);
556 if (j
!= cached_site_list
.end())
558 Log (LOG_PLAIN
) << "Dropped selected mirror: " << n
->url
560 dropped_site_list
.push_back (*j
);
561 if (cache_warn_urls
.size())
562 cache_warn_urls
+= "\r\n";
563 cache_warn_urls
+= i
->url
;
567 if (cache_warn_urls
.size())
570 return CACHE_ACCEPT_WARN
;
571 return DialogBox (hinstance
, MAKEINTRESOURCE (IDD_DROPPED
), h
,
574 return CACHE_ACCEPT_NOWARN
;
577 void write_cache_list (io_stream
*f
, const SiteList
& theSites
)
579 for (SiteList::const_iterator n
= theSites
.begin ();
580 n
!= theSites
.end (); ++n
)
581 if (n
->from_mirrors_lst
)
582 *f
<< (n
->url
+ ";" + n
->servername
+ ";" + n
->area
+ ";"
586 void save_cache_file (int cache_action
)
588 io_stream
*f
= UserSettings::instance().open ("mirrors-lst");
591 write_cache_list (f
, all_site_list
);
592 if (cache_action
== CACHE_ACCEPT_WARN
)
594 Log (LOG_PLAIN
) << "Adding dropped mirrors to cache to warn again."
596 *f
<< "# Following mirrors re-added by setup.exe to warn again about dropped urls.";
597 write_cache_list (f
, dropped_site_list
);
603 bool SitePage::Create ()
605 return PropertyPage::Create (IDD_SITE
);
611 AddTooltip (IDC_EDIT_USER_URL
, IDS_USER_URL_TOOLTIP
);
618 int cache_action
= CACHE_ACCEPT_NOWARN
;
622 if (cache_is_usable
&& !(cache_action
= check_dropped_mirrors (h
)))
625 if (cache_needs_writing
)
626 save_cache_file (cache_action
);
628 // Log all the selected URLs from the list.
629 for (SiteList::const_iterator n
= site_list
.begin ();
630 n
!= site_list
.end (); ++n
)
631 Log (LOG_PLAIN
) << "site: " << n
->url
<< endLog
;
633 Progress
.SetActivateTask (WM_APP_START_SETUP_INI_DOWNLOAD
);
646 // Go back to the net connection type page
651 SitePage::OnActivate ()
653 // Fill the list box with all known sites.
656 // Load the user URL box with nothing - it is in the list already.
657 eset (GetHWND (), IDC_EDIT_USER_URL
, "");
659 // Get the enabled/disabled states of the controls set accordingly.
660 CheckControlsAndDisableAccordingly ();
664 SitePage::OnUnattended ()
666 if (SendMessage (GetDlgItem (IDC_URL_LIST
), LB_GETSELCOUNT
, 0, 0) > 0)
669 Log (LOG_PLAIN
) << "No package repository site(s) specified" << endLog
;
674 SitePage::CheckControlsAndDisableAccordingly () const
676 DWORD ButtonFlags
= PSWIZB_BACK
;
678 // Check that at least one download site is selected.
679 if (SendMessage (GetDlgItem (IDC_URL_LIST
), LB_GETSELCOUNT
, 0, 0) > 0)
681 // At least one site selected, enable "Next".
682 ButtonFlags
|= PSWIZB_NEXT
;
684 GetOwner ()->SetButtons (ButtonFlags
);
688 SitePage::PopulateListBox ()
690 std::vector
<int> sel_indicies
;
691 HWND listbox
= GetDlgItem (IDC_URL_LIST
);
693 // Populate the list box with the URLs.
694 SendMessage (listbox
, LB_RESETCONTENT
, 0, 0);
695 for (SiteList::const_iterator i
= all_site_list
.begin ();
696 i
!= all_site_list
.end (); ++i
)
698 // If selected, always show
699 SiteList::iterator f
= find (site_list
.begin(), site_list
.end(), *i
);
700 if (f
== site_list
.end())
702 // Otherwise, hide redundant legacy URLs:
707 int j
= SendMessage (listbox
, LB_ADDSTRING
, 0,
708 (LPARAM
) i
->displayed_url
.c_str());
709 // Set the ListBox item data to the index into all_site_list
710 SendMessage (listbox
, LB_SETITEMDATA
, j
, (i
- all_site_list
.begin()));
712 // For every selected item, remember the index
713 if (f
!= site_list
.end())
715 sel_indicies
.push_back(j
);
719 // Select the selected ones.
720 for (std::vector
<int>::const_iterator n
= sel_indicies
.begin ();
721 n
!= sel_indicies
.end (); ++n
)
724 // Highlight the selected item
725 SendMessage (listbox
, LB_SELITEMRANGE
, TRUE
, (index
<< 16) | index
);
726 // Make sure it's fully visible
727 SendMessage (listbox
, LB_SETCARETINDEX
, index
, FALSE
);
731 bool SitePage::OnMessageCmd (int id
, HWND hwndctl
, UINT code
)
735 case IDC_EDIT_USER_URL
:
737 // Set the default pushbutton to ADD if the user is entering text.
738 if (code
== EN_CHANGE
)
739 SendMessage (GetHWND (), DM_SETDEFID
, (WPARAM
) IDC_BUTTON_ADD_URL
, 0);
744 if (code
== LBN_SELCHANGE
)
746 CheckControlsAndDisableAccordingly ();
747 save_dialog (GetHWND ());
751 case IDC_BUTTON_ADD_URL
:
753 if (code
== BN_CLICKED
)
755 // User pushed the Add button.
756 std::string other_url
= egetString (GetHWND (), IDC_EDIT_USER_URL
);
757 if (other_url
.size())
759 site_list_type
newsite (other_url
, "", "", "", false);
760 SiteList::iterator i
= find (all_site_list
.begin(),
761 all_site_list
.end(), newsite
);
762 if (i
== all_site_list
.end())
764 all_site_list
.push_back (newsite
);
765 Log (LOG_BABBLE
) << "Adding site: " << other_url
<< endLog
;
766 site_list
.push_back (newsite
);
769 site_list
.push_back (*i
);
771 // Update the list box.
773 // And allow the user to continue
774 CheckControlsAndDisableAccordingly ();
775 eset (GetHWND (), IDC_EDIT_USER_URL
, "");
781 // Wasn't recognized or handled.
785 // Was handled since we never got to default above.