Use git-describe to determine release version
[cygwin-setup.git] / site.cc
blob46caef10eeccf1bc71e61e295f099a298c25e3c7
1 /*
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
10 * http://www.gnu.org/
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. */
19 #if 0
20 static const char *cvsid =
21 "\n%%% $Id$\n";
22 #endif
24 #include <string>
25 #include <algorithm>
27 #include "site.h"
28 #include "win32.h"
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <process.h>
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"
42 #include "propsheet.h"
44 #include "threebar.h"
45 #include "ControlAdjuster.h"
46 #include "Exception.h"
47 #include "String++.h"
49 using namespace std;
51 extern ThreeBarProgressPage Progress;
55 What to do if dropped mirrors are selected.
57 enum
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.
65 Sizing information.
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}
75 SitePage::SitePage ()
77 sizeProcessor.AddControlInfo (SiteControlsInfo);
80 #include "getopt++/StringArrayOption.h"
81 #include "getopt++/BoolOption.h"
82 #include "UserSettings.h"
84 using namespace std;
86 bool cache_is_usable;
87 bool cache_needs_writing;
88 string cache_warn_urls;
90 /* Selected sites */
91 SiteList site_list;
93 /* Fresh mirrors + selected sites */
94 SiteList all_site_list;
96 /* Previously fresh + cached before */
97 SiteList cached_site_list;
99 /* Stale selected sites to warn about and add to cache */
100 SiteList dropped_site_list;
102 StringArrayOption SiteOption('s', "site", "Download site");
104 BoolOption OnlySiteOption(false, 'O', "only-site", "Ignore all sites except for -s");
106 SiteSetting::SiteSetting (): saved (false)
108 vector<string> SiteOptionStrings = SiteOption;
109 if (SiteOptionStrings.size())
111 for (vector<string>::const_iterator n = SiteOptionStrings.begin ();
112 n != SiteOptionStrings.end (); ++n)
113 registerSavedSite (n->c_str ());
115 else
116 getSavedSites ();
119 void
120 SiteSetting::save()
122 io_stream *f = UserSettings::instance().open ("last-mirror");
123 if (f)
125 for (SiteList::const_iterator n = site_list.begin ();
126 n != site_list.end (); ++n)
127 *f << n->url;
128 delete f;
130 saved = true;
133 SiteSetting::~SiteSetting ()
135 if (!saved)
136 save ();
139 void
140 site_list_type::init (const string &_url, const string &_servername,
141 const string &_area, const string &_location)
143 url = _url;
144 servername = _servername;
145 area = _area;
146 location = _location;
148 /* Canonicalize URL to ensure it ends with a '/' */
149 if (url.at(url.length()-1) != '/')
150 url.append("/");
152 /* displayed_url is protocol and site name part of url */
153 string::size_type path_offset = url.find ("/", url.find ("//") + 2);
154 displayed_url = url.substr(0, path_offset);
156 /* the sorting key is hostname components in reverse order (to sort by country code)
157 plus the url (to ensure uniqueness) */
158 key = string();
159 string::size_type last_idx = displayed_url.length () - 1;
160 string::size_type idx = url.find_last_of("./", last_idx);
161 if (last_idx - idx == 3)
163 /* Sort non-country TLDs (.com, .net, ...) together. */
164 key += " ";
168 key += url.substr(idx + 1, last_idx - idx);
169 key += " ";
170 last_idx = idx - 1;
171 idx = url.find_last_of("./", last_idx);
172 if (idx == string::npos)
173 idx = 0;
174 } while (idx > 0);
175 key += url;
178 site_list_type::site_list_type (const string &_url,
179 const string &_servername,
180 const string &_area,
181 const string &_location)
183 init (_url, _servername, _area, _location);
186 site_list_type::site_list_type (site_list_type const &rhs)
188 key = rhs.key;
189 url = rhs.url;
190 servername = rhs.servername;
191 area = rhs.area;
192 location = rhs.location;
193 displayed_url = rhs.displayed_url;
196 site_list_type &
197 site_list_type::operator= (site_list_type const &rhs)
199 key = rhs.key;
200 url = rhs.url;
201 servername = rhs.servername;
202 area = rhs.area;
203 location = rhs.location;
204 displayed_url = rhs.displayed_url;
205 return *this;
208 bool
209 site_list_type::operator == (site_list_type const &rhs) const
211 return stricmp (key.c_str(), rhs.key.c_str()) == 0;
214 bool
215 site_list_type::operator < (site_list_type const &rhs) const
217 return stricmp (key.c_str(), rhs.key.c_str()) < 0;
220 static void
221 save_dialog (HWND h)
223 // Remove anything that was previously in the selected site list.
224 site_list.clear ();
226 HWND listbox = GetDlgItem (h, IDC_URL_LIST);
227 int sel_count = SendMessage (listbox, LB_GETSELCOUNT, 0, 0);
228 if (sel_count > 0)
230 int sel_buffer[sel_count];
231 SendMessage (listbox, LB_GETSELITEMS, sel_count, (LPARAM) sel_buffer);
232 for (int n = 0; n < sel_count; n++)
234 int mirror =
235 SendMessage (listbox, LB_GETITEMDATA, sel_buffer[n], 0);
236 site_list.push_back (all_site_list[mirror]);
241 void
242 load_site_list (SiteList& theSites, char *theString)
244 char *bol, *eol, *nl;
246 nl = theString;
247 while (*nl)
249 bol = nl;
250 for (eol = bol; *eol && *eol != '\n'; eol++);
251 if (*eol)
252 nl = eol + 1;
253 else
254 nl = eol;
255 while (eol > bol && eol[-1] == '\r')
256 eol--;
257 *eol = 0;
258 if (*bol == '#' || !*bol)
259 continue;
260 /* Accept only the URL schemes we can understand. */
261 if (strncmp(bol, "http://", 7) == 0 ||
262 strncmp(bol, "ftp://", 6) == 0)
264 char *semi = strchr (bol, ';');
265 char *semi2 = NULL;
266 char *semi3 = NULL;
267 if (semi)
269 *semi = 0;
270 semi++;
271 semi2 = strchr (semi, ';');
272 if (semi2)
274 *semi2 = 0;
275 semi2++;
276 semi3 = strchr (semi2, ';');
277 if (semi3)
279 *semi3 = 0;
280 semi3++;
284 site_list_type newsite (bol, semi, semi2, semi3);
285 SiteList::iterator i = find (theSites.begin(),
286 theSites.end(), newsite);
287 if (i == theSites.end())
289 SiteList result;
290 merge (theSites.begin(), theSites.end(),
291 &newsite, &newsite + 1,
292 inserter (result, result.begin()));
293 theSites = result;
295 else
296 //TODO: remove and remerge
297 *i = newsite;
302 static int
303 get_site_list (HINSTANCE h, HWND owner)
305 char mirror_url[1000];
307 char *theMirrorString, *theCachedString;
308 const char *cached_mirrors = OnlySiteOption ? NULL : UserSettings::instance().get ("mirrors-lst");
309 if (cached_mirrors)
311 Log (LOG_BABBLE) << "Loaded cached mirror list" << endLog;
312 cache_is_usable = true;
314 else
316 Log (LOG_BABBLE) << "Cached mirror list unavailable" << endLog;
317 cache_is_usable = false;
318 cached_mirrors = "";
321 if (LoadString (h, IDS_MIRROR_LST, mirror_url, sizeof (mirror_url)) <= 0)
322 return 1;
324 string mirrors = OnlySiteOption ? string ("") : get_url_to_string (mirror_url, owner);
325 if (mirrors.size())
326 cache_needs_writing = true;
327 else
329 if (!cached_mirrors[0])
330 Log (LOG_BABBLE) << "Defaulting to empty mirror list" << endLog;
331 else
333 mirrors = cached_mirrors;
334 Log (LOG_BABBLE) << "Using cached mirror list" << endLog;
336 cache_is_usable = false;
337 cache_needs_writing = false;
339 theMirrorString = new_cstr_char_array (mirrors);
340 theCachedString = new_cstr_char_array (cached_mirrors);
342 load_site_list (all_site_list, theMirrorString);
343 load_site_list (cached_site_list, theCachedString);
345 delete[] theMirrorString;
346 delete[] theCachedString;
348 return 0;
351 /* List of machines that should not be used by default when saved
352 in "last-mirror". */
353 #define NOSAVE1 "ftp://sourceware.org/"
354 #define NOSAVE1_LEN (sizeof (NOSAVE2) - 1)
355 #define NOSAVE2 "ftp://sources.redhat.com/"
356 #define NOSAVE2_LEN (sizeof (NOSAVE1) - 1)
357 #define NOSAVE3 "ftp://gcc.gnu.org/"
358 #define NOSAVE3_LEN (sizeof (NOSAVE3) - 1)
360 void
361 SiteSetting::registerSavedSite (const char * site)
363 site_list_type tempSite(site, "", "", "");
364 SiteList::iterator i = find (all_site_list.begin(),
365 all_site_list.end(), tempSite);
366 if (i == all_site_list.end())
368 /* Don't default to certain machines if they suffer
369 from bandwidth limitations. */
370 if (strnicmp (site, NOSAVE1, NOSAVE1_LEN) == 0
371 || strnicmp (site, NOSAVE2, NOSAVE2_LEN) == 0
372 || strnicmp (site, NOSAVE3, NOSAVE3_LEN) == 0)
373 return;
374 SiteList result;
375 merge (all_site_list.begin(), all_site_list.end(),
376 &tempSite, &tempSite + 1,
377 inserter (result, result.begin()));
378 all_site_list = result;
379 site_list.push_back (tempSite);
381 else
382 site_list.push_back (tempSite);
385 void
386 SiteSetting::getSavedSites ()
388 const char *buf = UserSettings::instance().get ("last-mirror");
389 if (!buf)
390 return;
391 char *fg_ret = strdup (buf);
392 for (char *site = strtok (fg_ret, "\n"); site; site = strtok (NULL, "\n"))
393 registerSavedSite (site);
394 free (fg_ret);
397 static DWORD WINAPI
398 do_download_site_info_thread (void *p)
400 HANDLE *context;
401 HINSTANCE hinst;
402 HWND h;
403 context = (HANDLE *) p;
407 hinst = (HINSTANCE) (context[0]);
408 h = (HWND) (context[1]);
409 static bool downloaded = false;
410 if (!downloaded && get_site_list (hinst, h))
412 // Error: Couldn't download the site info.
413 // Go back to the Net setup page.
414 MessageBox (h, TEXT ("Can't get list of download sites.\n")
415 TEXT("Make sure your network settings are correct and try again."),
416 NULL, MB_OK);
418 // Tell the progress page that we're done downloading
419 Progress.PostMessageNow (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE, 0, IDD_NET);
421 else
423 downloaded = true;
424 // Everything worked, go to the site select page
425 // Tell the progress page that we're done downloading
426 Progress.PostMessageNow (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE, 0, IDD_SITE);
429 TOPLEVEL_CATCH("site");
431 ExitThread(0);
434 static HANDLE context[2];
436 void
437 do_download_site_info (HINSTANCE hinst, HWND owner)
440 context[0] = hinst;
441 context[1] = owner;
443 DWORD threadID;
444 CreateThread (NULL, 0, do_download_site_info_thread, context, 0, &threadID);
447 static INT_PTR CALLBACK
448 drop_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
450 switch (message)
452 case WM_INITDIALOG:
453 eset(h, IDC_DROP_MIRRORS, cache_warn_urls);
454 /* Should this be set by default? */
455 // CheckDlgButton (h, IDC_DROP_NOWARN, BST_CHECKED);
456 SetFocus (GetDlgItem(h, IDC_DROP_NOWARN));
457 return FALSE;
458 break;
459 case WM_COMMAND:
460 switch (LOWORD (wParam))
462 case IDYES:
463 if (IsDlgButtonChecked (h, IDC_DROP_NOWARN) == BST_CHECKED)
464 EndDialog (h, CACHE_ACCEPT_NOWARN);
465 else
466 EndDialog (h, CACHE_ACCEPT_WARN);
467 break;
469 case IDNO:
470 EndDialog (h, CACHE_REJECT);
471 break;
473 default:
474 return 0;
476 return TRUE;
477 break;
478 default:
479 return FALSE;
483 int check_dropped_mirrors (HWND h)
485 cache_warn_urls = "";
486 dropped_site_list.clear ();
488 for (SiteList::const_iterator n = site_list.begin ();
489 n != site_list.end (); ++n)
491 SiteList::iterator i = find (all_site_list.begin(), all_site_list.end(),
492 *n);
493 if (i == all_site_list.end() || !i->servername.size())
495 SiteList::iterator j = find (cached_site_list.begin(),
496 cached_site_list.end(), *n);
497 if (j != cached_site_list.end())
499 Log (LOG_PLAIN) << "Dropped selected mirror: " << n->url
500 << endLog;
501 dropped_site_list.push_back (*j);
502 if (cache_warn_urls.size())
503 cache_warn_urls += "\r\n";
504 cache_warn_urls += i->url;
508 if (cache_warn_urls.size())
510 if (unattended_mode)
511 return CACHE_ACCEPT_WARN;
512 return DialogBox (hinstance, MAKEINTRESOURCE (IDD_DROPPED), h,
513 drop_proc);
515 return CACHE_ACCEPT_NOWARN;
518 void write_cache_list (io_stream *f, const SiteList& theSites)
520 string s;
521 for (SiteList::const_iterator n = theSites.begin ();
522 n != theSites.end (); ++n)
523 if (n->servername.size())
524 *f << (n->url + ";" + n->servername + ";" + n->area + ";"
525 + n->location);
528 void save_cache_file (int cache_action)
530 string s;
531 io_stream *f = UserSettings::instance().open ("mirrors-lst");
532 if (f)
534 write_cache_list (f, all_site_list);
535 if (cache_action == CACHE_ACCEPT_WARN)
537 Log (LOG_PLAIN) << "Adding dropped mirrors to cache to warn again."
538 << endLog;
539 *f << "# Following mirrors re-added by setup.exe to warn again about dropped urls.";
540 write_cache_list (f, dropped_site_list);
542 delete f;
546 bool SitePage::Create ()
548 return PropertyPage::Create (IDD_SITE);
551 long
552 SitePage::OnNext ()
554 HWND h = GetHWND ();
555 int cache_action = CACHE_ACCEPT_NOWARN;
557 save_dialog (h);
559 if (cache_is_usable && !(cache_action = check_dropped_mirrors (h)))
560 return -1;
562 if (cache_needs_writing)
563 save_cache_file (cache_action);
565 // Log all the selected URLs from the list.
566 for (SiteList::const_iterator n = site_list.begin ();
567 n != site_list.end (); ++n)
568 Log (LOG_PLAIN) << "site: " << n->url << endLog;
570 Progress.SetActivateTask (WM_APP_START_SETUP_INI_DOWNLOAD);
571 return IDD_INSTATUS;
573 return 0;
576 long
577 SitePage::OnBack ()
579 HWND h = GetHWND ();
581 save_dialog (h);
583 // Go back to the net connection type page
584 return 0;
587 void
588 SitePage::OnActivate ()
590 // Fill the list box with all known sites.
591 PopulateListBox ();
593 // Load the user URL box with nothing - it is in the list already.
594 eset (GetHWND (), IDC_EDIT_USER_URL, "");
596 // Get the enabled/disabled states of the controls set accordingly.
597 CheckControlsAndDisableAccordingly ();
600 long
601 SitePage::OnUnattended ()
603 if (SendMessage (GetDlgItem (IDC_URL_LIST), LB_GETSELCOUNT, 0, 0) > 0)
604 return OnNext ();
605 else
606 return -2;
609 void
610 SitePage::CheckControlsAndDisableAccordingly () const
612 DWORD ButtonFlags = PSWIZB_BACK;
614 // Check that at least one download site is selected.
615 if (SendMessage (GetDlgItem (IDC_URL_LIST), LB_GETSELCOUNT, 0, 0) > 0)
617 // At least one site selected, enable "Next".
618 ButtonFlags |= PSWIZB_NEXT;
620 GetOwner ()->SetButtons (ButtonFlags);
623 void
624 SitePage::PopulateListBox ()
626 int j;
627 HWND listbox = GetDlgItem (IDC_URL_LIST);
629 // Populate the list box with the URLs.
630 SendMessage (listbox, LB_RESETCONTENT, 0, 0);
631 for (SiteList::const_iterator i = all_site_list.begin ();
632 i != all_site_list.end (); ++i)
634 j = SendMessage (listbox, LB_ADDSTRING, 0,
635 (LPARAM) i->displayed_url.c_str());
636 SendMessage (listbox, LB_SETITEMDATA, j, j);
639 // Select the selected ones.
640 for (SiteList::const_iterator n = site_list.begin ();
641 n != site_list.end (); ++n)
643 SiteList::iterator i = find (all_site_list.begin(),
644 all_site_list.end(), *n);
645 if (i != all_site_list.end())
647 int index = i - all_site_list.begin();
649 // Highlight the selected item
650 SendMessage (listbox, LB_SELITEMRANGE, TRUE, (index << 16) | index);
651 // Make sure it's fully visible
652 SendMessage (listbox, LB_SETCARETINDEX, index, FALSE);
657 bool SitePage::OnMessageCmd (int id, HWND hwndctl, UINT code)
659 switch (id)
661 case IDC_EDIT_USER_URL:
663 // FIXME: Make Enter here cause an ADD, not a NEXT.
664 break;
666 case IDC_URL_LIST:
668 if (code == LBN_SELCHANGE)
670 CheckControlsAndDisableAccordingly ();
671 save_dialog (GetHWND ());
673 break;
675 case IDC_BUTTON_ADD_URL:
677 if (code == BN_CLICKED)
679 // User pushed the Add button.
680 std::string other_url = egetString (GetHWND (), IDC_EDIT_USER_URL);
681 if (other_url.size())
683 site_list_type newsite (other_url, "", "", "");
684 SiteList::iterator i = find (all_site_list.begin(),
685 all_site_list.end(), newsite);
686 if (i == all_site_list.end())
688 all_site_list.push_back (newsite);
689 Log (LOG_BABBLE) << "Adding site: " << other_url << endLog;
691 else
693 *i = newsite;
694 Log (LOG_BABBLE) << "Replacing site: " << other_url << endLog;
697 // Assume the user wants to use it and select it for him.
698 site_list.push_back (newsite);
700 // Update the list box.
701 PopulateListBox ();
702 // And allow the user to continue
703 CheckControlsAndDisableAccordingly ();
704 eset (GetHWND (), IDC_EDIT_USER_URL, "");
707 break;
709 default:
710 // Wasn't recognized or handled.
711 return false;
714 // Was handled since we never got to default above.
715 return true;