]> cygwin.com Git - cygwin-apps/setup.git/blob - site.cc
Run libgcrypt self-tests
[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 #include <string>
20 #include <algorithm>
21
22 #include "site.h"
23 #include "win32.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <process.h>
27
28 #include "dialog.h"
29 #include "resource.h"
30 #include "state.h"
31 #include "geturl.h"
32 #include "msg.h"
33 #include "LogSingleton.h"
34 #include "io_stream.h"
35 #include "site.h"
36
37 #include "propsheet.h"
38
39 #include "threebar.h"
40 #include "ControlAdjuster.h"
41 #include "Exception.h"
42 #include "String++.h"
43
44 extern ThreeBarProgressPage Progress;
45
46 /*
47 What to do if dropped mirrors are selected.
48 */
49 enum
50 {
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.
54 };
55
56 /*
57 Sizing information.
58 */
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},
64 {0, CP_LEFT, CP_TOP}
65 };
66
67 SitePage::SitePage ()
68 {
69 sizeProcessor.AddControlInfo (SiteControlsInfo);
70 }
71
72 #include "getopt++/StringArrayOption.h"
73 #include "getopt++/BoolOption.h"
74 #include "UserSettings.h"
75
76 bool cache_is_usable;
77 bool cache_needs_writing;
78 std::string cache_warn_urls;
79
80 /* Selected sites */
81 SiteList site_list;
82
83 /* Fresh mirrors + selected sites */
84 SiteList all_site_list;
85
86 /* Previously fresh + cached before */
87 SiteList cached_site_list;
88
89 /* Stale selected sites to warn about and add to cache */
90 SiteList dropped_site_list;
91
92 StringArrayOption SiteOption('s', "site", "Download site URL");
93
94 BoolOption OnlySiteOption(false, 'O', "only-site", "Do not download mirror list. Only use sites specified with -s.");
95 extern BoolOption UnsupportedOption;
96
97 SiteSetting::SiteSetting (): saved (false)
98 {
99 std::vector<std::string> SiteOptionStrings = SiteOption;
100 if (SiteOptionStrings.size())
101 {
102 for (std::vector<std::string>::const_iterator n = SiteOptionStrings.begin ();
103 n != SiteOptionStrings.end (); ++n)
104 registerSavedSite (n->c_str ());
105 }
106 else
107 getSavedSites ();
108 }
109
110 const char *
111 SiteSetting::lastMirrorKey ()
112 {
113 if (UnsupportedOption)
114 return "last-mirror-unsupported";
115
116 return "last-mirror";
117 }
118
119 void
120 SiteSetting::save()
121 {
122 io_stream *f = UserSettings::instance().open (lastMirrorKey ());
123 if (f)
124 {
125 for (SiteList::const_iterator n = site_list.begin ();
126 n != site_list.end (); ++n)
127 *f << n->url;
128 delete f;
129 }
130 saved = true;
131 }
132
133 SiteSetting::~SiteSetting ()
134 {
135 if (!saved)
136 save ();
137 }
138
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)
144 {
145 url = _url;
146 servername = _servername;
147 area = _area;
148 location = _location;
149 from_mirrors_lst = _from_mirrors_lst;
150
151 /* Canonicalize URL to ensure it ends with a '/' */
152 if (url.at(url.length()-1) != '/')
153 url.append("/");
154
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);
158
159 /* the sorting key is hostname components in reverse order (to sort by country code)
160 plus the url (to ensure uniqueness) */
161 key = std::string();
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)
165 {
166 /* Sort non-country TLDs (.com, .net, ...) together. */
167 key += " ";
168 }
169 do
170 {
171 key += url.substr(idx + 1, last_idx - idx);
172 key += " ";
173 last_idx = idx - 1;
174 idx = url.find_last_of("./", last_idx);
175 if (idx == std::string::npos)
176 idx = 0;
177 } while (idx > 0);
178 key += url;
179 }
180
181 site_list_type::site_list_type (site_list_type const &rhs)
182 {
183 key = rhs.key;
184 url = rhs.url;
185 servername = rhs.servername;
186 area = rhs.area;
187 location = rhs.location;
188 from_mirrors_lst = rhs.from_mirrors_lst;
189 displayed_url = rhs.displayed_url;
190 }
191
192 site_list_type &
193 site_list_type::operator= (site_list_type const &rhs)
194 {
195 key = rhs.key;
196 url = rhs.url;
197 servername = rhs.servername;
198 area = rhs.area;
199 location = rhs.location;
200 from_mirrors_lst = rhs.from_mirrors_lst;
201 displayed_url = rhs.displayed_url;
202 return *this;
203 }
204
205 bool
206 site_list_type::operator == (site_list_type const &rhs) const
207 {
208 return stricmp (key.c_str(), rhs.key.c_str()) == 0;
209 }
210
211 bool
212 site_list_type::operator < (site_list_type const &rhs) const
213 {
214 return stricmp (key.c_str(), rhs.key.c_str()) < 0;
215 }
216
217 static void
218 save_dialog (HWND h)
219 {
220 // Remove anything that was previously in the selected site list.
221 site_list.clear ();
222
223 HWND listbox = GetDlgItem (h, IDC_URL_LIST);
224 int sel_count = SendMessage (listbox, LB_GETSELCOUNT, 0, 0);
225 if (sel_count > 0)
226 {
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++)
230 {
231 int mirror =
232 SendMessage (listbox, LB_GETITEMDATA, sel_buffer[n], 0);
233 site_list.push_back (all_site_list[mirror]);
234 }
235 }
236 }
237
238 // This is called only for lists of mirrors that came (now or in a
239 // previous setup run) from mirrors.lst.
240 void
241 load_site_list (SiteList& theSites, char *theString)
242 {
243 char *bol, *eol, *nl;
244
245 nl = theString;
246 while (*nl)
247 {
248 bol = nl;
249 for (eol = bol; *eol && *eol != '\n'; eol++);
250 if (*eol)
251 nl = eol + 1;
252 else
253 nl = eol;
254 while (eol > bol && eol[-1] == '\r')
255 eol--;
256 *eol = 0;
257 if (*bol == '#' || !*bol)
258 continue;
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)
264 {
265 char *semi = strchr (bol, ';');
266 char *semi2 = NULL;
267 char *semi3 = NULL;
268 if (semi)
269 {
270 *semi = 0;
271 semi++;
272 semi2 = strchr (semi, ';');
273 if (semi2)
274 {
275 *semi2 = 0;
276 semi2++;
277 semi3 = strchr (semi2, ';');
278 if (semi3)
279 {
280 *semi3 = 0;
281 semi3++;
282 }
283 }
284 }
285
286 /* Ignore malformed lines */
287 if (!semi || !semi2 || !semi3)
288 continue;
289
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())
294 {
295 SiteList result;
296 merge (theSites.begin(), theSites.end(),
297 &newsite, &newsite + 1,
298 inserter (result, result.begin()));
299 theSites = result;
300 }
301 else
302 //TODO: remove and remerge
303 *i = newsite;
304 }
305 else
306 {
307 Log (LOG_BABBLE) << "Discarding line '" << bol << "' due to unknown protocol" << endLog;
308 }
309 }
310 }
311
312 static int
313 get_site_list (HINSTANCE h, HWND owner)
314 {
315 char mirror_url[1000];
316
317 char *theMirrorString, *theCachedString;
318
319 if (UnsupportedOption)
320 return 0;
321
322 const char *cached_mirrors = OnlySiteOption ? NULL : UserSettings::instance().get ("mirrors-lst");
323 if (cached_mirrors)
324 {
325 Log (LOG_BABBLE) << "Loaded cached mirror list" << endLog;
326 cache_is_usable = true;
327 }
328 else
329 {
330 Log (LOG_BABBLE) << "Cached mirror list unavailable" << endLog;
331 cache_is_usable = false;
332 cached_mirrors = "";
333 }
334
335 if (LoadString (h, IDS_MIRROR_LST, mirror_url, sizeof (mirror_url)) <= 0)
336 return 1;
337
338 std::string mirrors = OnlySiteOption ? std::string ("") : get_url_to_string (mirror_url, owner);
339 if (mirrors.size())
340 cache_needs_writing = true;
341 else
342 {
343 if (!cached_mirrors[0])
344 {
345 if (!OnlySiteOption)
346 note(owner, IDS_NO_MIRROR_LST);
347 Log (LOG_BABBLE) << "Defaulting to empty mirror list" << endLog;
348 }
349 else
350 {
351 mirrors = cached_mirrors;
352 Log (LOG_BABBLE) << "Using cached mirror list" << endLog;
353 }
354 cache_is_usable = false;
355 cache_needs_writing = false;
356 }
357 theMirrorString = new_cstr_char_array (mirrors);
358 theCachedString = new_cstr_char_array (cached_mirrors);
359
360 load_site_list (all_site_list, theMirrorString);
361 load_site_list (cached_site_list, theCachedString);
362
363 delete[] theMirrorString;
364 delete[] theCachedString;
365
366 return 0;
367 }
368
369 /* List of machines that should not be used by default when saved
370 in "last-mirror". */
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)
377
378 void
379 SiteSetting::registerSavedSite (const char * site)
380 {
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())
385 {
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)
391 return;
392 SiteList result;
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);
398 }
399 else
400 site_list.push_back (*i);
401 }
402
403 void
404 SiteSetting::getSavedSites ()
405 {
406 const char *buf = UserSettings::instance().get (lastMirrorKey ());
407 if (!buf)
408 return;
409 char *fg_ret = strdup (buf);
410 for (char *site = strtok (fg_ret, "\n"); site; site = strtok (NULL, "\n"))
411 registerSavedSite (site);
412 free (fg_ret);
413 }
414
415 static DWORD WINAPI
416 do_download_site_info_thread (void *p)
417 {
418 HANDLE *context;
419 HINSTANCE hinst;
420 HWND h;
421 context = (HANDLE *) p;
422
423 try
424 {
425 hinst = (HINSTANCE) (context[0]);
426 h = (HWND) (context[1]);
427 static bool downloaded = false;
428 if (!downloaded && get_site_list (hinst, h))
429 {
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."),
434 NULL, MB_OK);
435
436 // Tell the progress page that we're done downloading
437 Progress.PostMessageNow (WM_APP_SITE_INFO_DOWNLOAD_COMPLETE, 0, IDD_NET);
438 }
439 else
440 {
441 downloaded = true;
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);
445 }
446 }
447 TOPLEVEL_CATCH((HWND) context[1], "site");
448
449 ExitThread(0);
450 }
451
452 static HANDLE context[2];
453
454 void
455 do_download_site_info (HINSTANCE hinst, HWND owner)
456 {
457
458 context[0] = hinst;
459 context[1] = owner;
460
461 DWORD threadID;
462 CreateThread (NULL, 0, do_download_site_info_thread, context, 0, &threadID);
463 }
464
465 static INT_PTR CALLBACK
466 drop_proc (HWND h, UINT message, WPARAM wParam, LPARAM lParam)
467 {
468 switch (message)
469 {
470 case WM_INITDIALOG:
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));
475 return FALSE;
476 break;
477 case WM_COMMAND:
478 switch (LOWORD (wParam))
479 {
480 case IDYES:
481 if (IsDlgButtonChecked (h, IDC_DROP_NOWARN) == BST_CHECKED)
482 EndDialog (h, CACHE_ACCEPT_NOWARN);
483 else
484 EndDialog (h, CACHE_ACCEPT_WARN);
485 break;
486
487 case IDNO:
488 EndDialog (h, CACHE_REJECT);
489 break;
490
491 default:
492 return 0;
493 }
494 return TRUE;
495 break;
496 default:
497 return FALSE;
498 }
499 }
500
501 int check_dropped_mirrors (HWND h)
502 {
503 cache_warn_urls = "";
504 dropped_site_list.clear ();
505
506 for (SiteList::const_iterator n = site_list.begin ();
507 n != site_list.end (); ++n)
508 {
509 SiteList::iterator i = find (all_site_list.begin(), all_site_list.end(),
510 *n);
511 if (i == all_site_list.end() || !i->from_mirrors_lst)
512 {
513 SiteList::iterator j = find (cached_site_list.begin(),
514 cached_site_list.end(), *n);
515 if (j != cached_site_list.end())
516 {
517 Log (LOG_PLAIN) << "Dropped selected mirror: " << n->url
518 << endLog;
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;
523 }
524 }
525 }
526 if (cache_warn_urls.size())
527 {
528 if (unattended_mode)
529 return CACHE_ACCEPT_WARN;
530 return DialogBox (hinstance, MAKEINTRESOURCE (IDD_DROPPED), h,
531 drop_proc);
532 }
533 return CACHE_ACCEPT_NOWARN;
534 }
535
536 void write_cache_list (io_stream *f, const SiteList& theSites)
537 {
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 + ";"
542 + n->location);
543 }
544
545 void save_cache_file (int cache_action)
546 {
547 io_stream *f = UserSettings::instance().open ("mirrors-lst");
548 if (f)
549 {
550 write_cache_list (f, all_site_list);
551 if (cache_action == CACHE_ACCEPT_WARN)
552 {
553 Log (LOG_PLAIN) << "Adding dropped mirrors to cache to warn again."
554 << endLog;
555 *f << "# Following mirrors re-added by setup.exe to warn again about dropped urls.";
556 write_cache_list (f, dropped_site_list);
557 }
558 delete f;
559 }
560 }
561
562 bool SitePage::Create ()
563 {
564 return PropertyPage::Create (IDD_SITE);
565 }
566
567 long
568 SitePage::OnNext ()
569 {
570 HWND h = GetHWND ();
571 int cache_action = CACHE_ACCEPT_NOWARN;
572
573 save_dialog (h);
574
575 if (cache_is_usable && !(cache_action = check_dropped_mirrors (h)))
576 return -1;
577
578 if (cache_needs_writing)
579 save_cache_file (cache_action);
580
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;
585
586 Progress.SetActivateTask (WM_APP_START_SETUP_INI_DOWNLOAD);
587 return IDD_INSTATUS;
588
589 return 0;
590 }
591
592 long
593 SitePage::OnBack ()
594 {
595 HWND h = GetHWND ();
596
597 save_dialog (h);
598
599 // Go back to the net connection type page
600 return 0;
601 }
602
603 void
604 SitePage::OnActivate ()
605 {
606 // Fill the list box with all known sites.
607 PopulateListBox ();
608
609 // Load the user URL box with nothing - it is in the list already.
610 eset (GetHWND (), IDC_EDIT_USER_URL, "");
611
612 // Get the enabled/disabled states of the controls set accordingly.
613 CheckControlsAndDisableAccordingly ();
614 }
615
616 long
617 SitePage::OnUnattended ()
618 {
619 if (SendMessage (GetDlgItem (IDC_URL_LIST), LB_GETSELCOUNT, 0, 0) > 0)
620 return OnNext ();
621 else
622 return -2;
623 }
624
625 void
626 SitePage::CheckControlsAndDisableAccordingly () const
627 {
628 DWORD ButtonFlags = PSWIZB_BACK;
629
630 // Check that at least one download site is selected.
631 if (SendMessage (GetDlgItem (IDC_URL_LIST), LB_GETSELCOUNT, 0, 0) > 0)
632 {
633 // At least one site selected, enable "Next".
634 ButtonFlags |= PSWIZB_NEXT;
635 }
636 GetOwner ()->SetButtons (ButtonFlags);
637 }
638
639 void
640 SitePage::PopulateListBox ()
641 {
642 int j;
643 HWND listbox = GetDlgItem (IDC_URL_LIST);
644
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)
649 {
650 j = SendMessage (listbox, LB_ADDSTRING, 0,
651 (LPARAM) i->displayed_url.c_str());
652 SendMessage (listbox, LB_SETITEMDATA, j, j);
653 }
654
655 // Select the selected ones.
656 for (SiteList::const_iterator n = site_list.begin ();
657 n != site_list.end (); ++n)
658 {
659 SiteList::iterator i = find (all_site_list.begin(),
660 all_site_list.end(), *n);
661 if (i != all_site_list.end())
662 {
663 int index = i - all_site_list.begin();
664
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);
669 }
670 }
671 }
672
673 bool SitePage::OnMessageCmd (int id, HWND hwndctl, UINT code)
674 {
675 switch (id)
676 {
677 case IDC_EDIT_USER_URL:
678 {
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);
682 break;
683 }
684 case IDC_URL_LIST:
685 {
686 if (code == LBN_SELCHANGE)
687 {
688 CheckControlsAndDisableAccordingly ();
689 save_dialog (GetHWND ());
690 }
691 break;
692 }
693 case IDC_BUTTON_ADD_URL:
694 {
695 if (code == BN_CLICKED)
696 {
697 // User pushed the Add button.
698 std::string other_url = egetString (GetHWND (), IDC_EDIT_USER_URL);
699 if (other_url.size())
700 {
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())
705 {
706 all_site_list.push_back (newsite);
707 Log (LOG_BABBLE) << "Adding site: " << other_url << endLog;
708 site_list.push_back (newsite);
709 }
710 else
711 site_list.push_back (*i);
712
713 // Update the list box.
714 PopulateListBox ();
715 // And allow the user to continue
716 CheckControlsAndDisableAccordingly ();
717 eset (GetHWND (), IDC_EDIT_USER_URL, "");
718 }
719 }
720 break;
721 }
722 default:
723 // Wasn't recognized or handled.
724 return false;
725 }
726
727 // Was handled since we never got to default above.
728 return true;
729 }
This page took 0.06861 seconds and 5 git commands to generate.