Ejemplo n.º 1
0
        public void NavigateGeminiScheme(string fullQuery, System.Windows.Navigation.NavigatingCancelEventArgs e, SiteIdentity siteIdentity, bool requireSecure = true)
        {
            var geminiUri = e.Uri.OriginalString;

            var sessionPath = Session.Instance.SessionPath;
            var appDir      = AppDomain.CurrentDomain.BaseDirectory;

            var settings = new UserSettings();

            var hash = HashService.GetMd5Hash(fullQuery);

            //uses .txt as extension so content loaded as text/plain not interpreted by the browser
            //if user requests a view-source.
            var rawFile  = sessionPath + "\\" + hash + ".txt";
            var gmiFile  = sessionPath + "\\" + hash + ".gmi";
            var htmlFile = sessionPath + "\\" + hash + ".htm";

            //delete txt file as GemGet seems to sometimes overwrite not create afresh
            File.Delete(rawFile);
            File.Delete(gmiFile);

            //delete any existing html file to encourage webbrowser to reload it
            File.Delete(htmlFile);

            var uri = new Uri(fullQuery);
            //use a proxy for any other scheme that is not gemini
            var proxy           = ""; //use none
            var connectInsecure = false;

            X509Certificate2 certificate;

            certificate = Session.Instance.CertificatesManager.GetCertificate(uri.Host);   //may be null if none assigned or available



            if (uri.Scheme != "gemini")
            {
                proxy = settings.HttpSchemeProxy;
            }

            try
            {
                GeminiResponse geminiResponse;
                try
                {
                    geminiResponse = (GeminiResponse)Gemini.Fetch(uri, certificate, proxy, connectInsecure, settings.MaxDownloadSizeMb * 1024, settings.MaxDownloadTimeSeconds);
                }
                catch (Exception err)
                {
                    //warn, but continue if there are server validation errors
                    //in these early days of Gemini we dont forbid visiting a site with an expired cert or mismatched host name
                    //but we do give a warning each time
                    if (err.Message == "The remote certificate was rejected by the provided RemoteCertificateValidationCallback.")
                    {
                        mMainWindow.ToastNotify("Note: the certificate from: " + e.Uri.Authority + " is expired or invalid.", ToastMessageStyles.Warning);

                        //try again insecure this time
                        geminiResponse = (GeminiResponse)Gemini.Fetch(uri, certificate, proxy, true, settings.MaxDownloadSizeMb * 1024, settings.MaxDownloadTimeSeconds);
                    }
                    else
                    {
                        //reraise
                        throw;
                    }
                }

                if (geminiResponse.codeMajor == '1')
                {
                    //needs input, then refetch

                    mMainWindow.ToggleContainerControlsForBrowser(true);

                    NavigateGeminiWithInput(e, geminiResponse);
                }

                else if (geminiResponse.codeMajor == '2')
                {
                    //success
                    File.WriteAllBytes(rawFile, geminiResponse.bytes.ToArray());


                    if (File.Exists(rawFile))
                    {
                        if (geminiResponse.meta.Contains("text/gemini"))
                        {
                            File.Copy(rawFile, gmiFile);
                        }
                        else if (geminiResponse.meta.Contains("text/html"))
                        {
                            //is an html file served over gemini - probably not common, but not unheard of
                            var htmltoGmiResult = ConverterService.HtmlToGmi(rawFile, gmiFile);

                            if (htmltoGmiResult.Item1 != 0)
                            {
                                mMainWindow.ToastNotify("Could not convert HTML to GMI: " + fullQuery, ToastMessageStyles.Error);
                                mMainWindow.ToggleContainerControlsForBrowser(true);
                                e.Cancel = true;
                                return;
                            }
                        }
                        else if (geminiResponse.meta.Contains("text/"))
                        {
                            //convert plain text to a gemini version (wraps it in a preformatted section)
                            var textToGmiResult = ConverterService.TextToGmi(rawFile, gmiFile);

                            if (textToGmiResult.Item1 != 0)
                            {
                                mMainWindow.ToastNotify("Could not render text as GMI: " + fullQuery, ToastMessageStyles.Error);
                                mMainWindow.ToggleContainerControlsForBrowser(true);
                                e.Cancel = true;
                                return;
                            }
                        }
                        else
                        {
                            //a download
                            //its an image - rename the raw file and just show it
                            var pathFragment = (new UriBuilder(fullQuery)).Path;
                            var ext          = Path.GetExtension(pathFragment);

                            var binFile = rawFile + (ext == "" ? ".tmp" : ext);
                            File.Copy(rawFile, binFile, true); //rename overwriting

                            if (geminiResponse.meta.Contains("image/"))
                            {
                                mMainWindow.ShowImage(fullQuery, binFile, e);
                            }
                            else
                            {
                                SaveFileDialog saveFileDialog = new SaveFileDialog();

                                saveFileDialog.FileName = Path.GetFileName(pathFragment);

                                if (saveFileDialog.ShowDialog() == true)
                                {
                                    try
                                    {
                                        //save the file
                                        var savePath = saveFileDialog.FileName;

                                        File.Copy(binFile, savePath, true); //rename overwriting

                                        mMainWindow.ToastNotify("File saved to " + savePath, ToastMessageStyles.Success);
                                    }
                                    catch (SystemException err)
                                    {
                                        mMainWindow.ToastNotify("Could not save the file due to: " + err.Message, ToastMessageStyles.Error);
                                    }
                                }

                                mMainWindow.ToggleContainerControlsForBrowser(true);
                                e.Cancel = true;
                            }

                            return;
                        }

                        if (geminiResponse.uri.ToString() != fullQuery)
                        {
                            string redirectUri = fullQuery;

                            if (geminiResponse.uri.ToString().Contains("://"))
                            {
                                //a full url
                                //normalise the URi (e.g. remove default port if specified)
                                redirectUri = UriTester.NormaliseUri(new Uri(geminiResponse.uri.ToString())).ToString();
                            }
                            else
                            {
                                //a relative one
                                var baseUri   = new Uri(fullQuery);
                                var targetUri = new Uri(baseUri, geminiResponse.uri.ToString());
                                redirectUri = UriTester.NormaliseUri(targetUri).ToString();
                            }

                            var finalUri = new Uri(redirectUri);

                            if (e.Uri.Scheme == "gemini" && finalUri.Scheme != "gemini")
                            {
                                //cross-scheme redirect, not supported
                                mMainWindow.ToastNotify("Cross scheme redirect from Gemini not supported: " + redirectUri, ToastMessageStyles.Warning);
                                mMainWindow.ToggleContainerControlsForBrowser(true);
                                e.Cancel = true;
                                return;
                            }
                            else
                            {
                                //others e.g. http->https redirect are fine
                            }

                            //redirected to a full gemini url
                            geminiUri = redirectUri;

                            //regenerate the hashes using the redirected target url
                            hash = HashService.GetMd5Hash(geminiUri);

                            var gmiFileNew  = sessionPath + "\\" + hash + ".txt";
                            var htmlFileNew = sessionPath + "\\" + hash + ".htm";

                            //move the source file
                            try
                            {
                                if (File.Exists(gmiFileNew))
                                {
                                    File.Delete(gmiFileNew);
                                }
                                File.Move(gmiFile, gmiFileNew);
                            }
                            catch (Exception err)
                            {
                                mMainWindow.ToastNotify(err.ToString(), ToastMessageStyles.Error);
                            }

                            //update locations of gmi and html file
                            gmiFile  = gmiFileNew;
                            htmlFile = htmlFileNew;
                        }
                        else
                        {
                            geminiUri = fullQuery;
                        }

                        var userThemesFolder = ResourceFinder.LocalOrDevFolder(appDir, @"GmiConverters\themes", @"..\..\..\GmiConverters\themes");

                        var userThemeBase = Path.Combine(userThemesFolder, settings.Theme);

                        mMainWindow.ShowUrl(geminiUri, gmiFile, htmlFile, userThemeBase, siteIdentity, e);
                    }
                }

                // codemajor = 3 is redirect - should eventually end in success or raise an error

                else if (geminiResponse.codeMajor == '4')
                {
                    mMainWindow.ToastNotify("Temporary failure (status 4X)\n\n" + e.Uri.ToString(), ToastMessageStyles.Warning);
                }
                else if (geminiResponse.codeMajor == '5')
                {
                    if (geminiResponse.codeMinor == '1')
                    {
                        mMainWindow.ToastNotify("Page not found\n\n" + e.Uri.ToString(), ToastMessageStyles.Warning);
                    }
                    else
                    {
                        mMainWindow.ToastNotify("Permanent failure (status 5X)\n\n" + geminiResponse.meta + "\n\n" + e.Uri.ToString(), ToastMessageStyles.Warning);
                    }
                }
                else if (geminiResponse.codeMajor == '6')
                {
                    mMainWindow.ToastNotify("Certificate requried. Choose one and try again.\n\n" + e.Uri.ToString(), ToastMessageStyles.Warning);
                }

                else
                {
                    mMainWindow.ToastNotify("Unexpected output from server " +
                                            "(status " + geminiResponse.codeMajor + "." + geminiResponse.codeMinor + ") " +
                                            geminiResponse.meta + "\n\n"
                                            + e.Uri.ToString(), ToastMessageStyles.Warning);
                }
            }
            catch (Exception err)
            {
                //generic handler for other runtime errors
                mMainWindow.ToastNotify("Error getting gemini content for " + e.Uri.ToString() + "\n\n" + err.Message, ToastMessageStyles.Warning);
            }


            //make the window responsive again
            mMainWindow.ToggleContainerControlsForBrowser(true);

            //no further navigation right now
            e.Cancel = true;
        }
Ejemplo n.º 2
0
        public void NavigateNimigemScheme(string fullQuery, System.Windows.Navigation.NavigatingCancelEventArgs e, string payload, bool requireSecure = true)
        {
            var NimigemUri = e.Uri.OriginalString;

            //at present we only support UTF8 plain text payloads
            byte[] nimigemBody = Encoding.UTF8.GetBytes(payload);
            var    mime        = "text/plain; charset=utf-8";

            var settings = new UserSettings();

            var uri = new Uri(fullQuery);
            //use a proxy for any other scheme that is not Nimigem
            var proxy = "";     //use none

            var connectInsecure = false;

            if (uri.Host == "localhost")
            {
                //to support local testing servers, dont require secure connection on localhost
                //**FIX ME, or have an option
                connectInsecure = true;
            }

            X509Certificate2 certificate;

            certificate = Session.Instance.CertificatesManager.GetCertificate(uri.Host);

            try
            {
                NimigemResponse nimigemResponse;
                try
                {
                    nimigemResponse = (NimigemResponse)Nimigem.Fetch(uri, nimigemBody, mime, certificate, proxy, connectInsecure, settings.MaxDownloadSizeMb * 1024, settings.MaxDownloadTimeSeconds);
                }
                catch (Exception err)
                {
                    //warn, but continue if there are server validation errors
                    //in these early days of Nimigem we dont forbid visiting a site with an expired cert or mismatched host name
                    //but we do give a warning each time
                    if (err.Message == "The remote certificate was rejected by the provided RemoteCertificateValidationCallback.")
                    {
                        mMainWindow.ToastNotify("Note: " + err.Message + " for: " + e.Uri.Authority, ToastMessageStyles.Warning);

                        //try again insecure this time
                        nimigemResponse = (NimigemResponse)Nimigem.Fetch(uri, nimigemBody, mime, certificate, proxy, connectInsecure, settings.MaxDownloadSizeMb * 1024, settings.MaxDownloadTimeSeconds);
                    }

                    else
                    {
                        //reraise
                        throw;
                    }
                }

                if (nimigemResponse.codeMajor == '1')
                {
                    //invalid in nimigem
                    HandleInvalidResponse(nimigemResponse);
                }

                else if (nimigemResponse.codeMajor == '2')
                {
                    //success
                    if (nimigemResponse.codeMinor == '5')
                    {
                        //valid submission - get the new target to retrieve
                        mMainWindow.ToastNotify(String.Format("Submit successful: retrieving result: {0}", nimigemResponse.meta));

                        var successUri = new Uri(nimigemResponse.meta);

                        //must be a response redirect to gemini URL
                        if (successUri.Scheme != "gemini")
                        {
                            HandleInvalidResponse(nimigemResponse);
                        }


                        var geminiTarget  = new GeminiNavigator(mMainWindow, mMainWindow.BrowserControl);
                        var normalisedUri = UriTester.NormaliseUri(successUri);

                        var siteIdentity = new SiteIdentity(normalisedUri, Session.Instance);

                        geminiTarget.NavigateGeminiScheme(successUri.OriginalString, e, siteIdentity);
                    }
                    else
                    {
                        //no other 2X responses are valid
                        HandleInvalidResponse(nimigemResponse);
                    }
                }

                // codemajor = 3 is redirect - should eventually end in success or raise an error

                else if (nimigemResponse.codeMajor == '4')
                {
                    //same as normal Gemini
                    mMainWindow.ToastNotify("Temporary failure (status 4X)\n\n" +
                                            nimigemResponse.meta + "\n\n" +
                                            e.Uri.ToString(), ToastMessageStyles.Warning);
                }
                else if (nimigemResponse.codeMajor == '5')
                {
                    //same as normal Gemini
                    if (nimigemResponse.codeMinor == '1')
                    {
                        mMainWindow.ToastNotify("Page not found\n\n" + e.Uri.ToString(), ToastMessageStyles.Warning);
                    }
                    else
                    {
                        mMainWindow.ToastNotify("Permanent failure (status 5X)\n\n" +
                                                nimigemResponse.meta + "\n\n" +
                                                e.Uri.ToString(), ToastMessageStyles.Warning);
                    }
                }
                else if (nimigemResponse.codeMajor == '6')
                {
                    mMainWindow.ToastNotify("Certificate required. Choose one and try again.\n\n" + e.Uri.ToString(), ToastMessageStyles.Warning);
                }

                else
                {
                    mMainWindow.ToastNotify("Unexpected output from server " +
                                            "(status " + nimigemResponse.codeMajor + "." + nimigemResponse.codeMinor + ") " +
                                            nimigemResponse.meta + "\n\n"
                                            + e.Uri.ToString(), ToastMessageStyles.Warning);
                }
            }
            catch (Exception err)
            {
                //generic handler for other runtime errors
                mMainWindow.ToastNotify("Error getting Nimigem content for " + e.Uri.ToString() + "\n\n" + err.Message, ToastMessageStyles.Warning);
            }


            //make the window responsive again
            mMainWindow.ToggleContainerControlsForBrowser(true);

            //no further navigation right now
            e.Cancel = true;
        }
Ejemplo n.º 3
0
        public bool NavigateGeminiScheme(string fullQuery, Uri uri, SiteIdentity siteIdentity, bool requireSecure = true)
        {
            bool navigated = true;

            var geminiUri = uri.OriginalString;

            var sessionPath = Session.Instance.SessionPath;
            var appDir      = AppDomain.CurrentDomain.BaseDirectory;

            //use local or dev binary for gemget
            var gemGet = ResourceFinder.LocalOrDevFile(appDir, "Gemget", "..\\..\\..\\..\\Gemget", "gemget-windows-386.exe");

            var hash = HashService.GetMd5Hash(fullQuery);

            //uses .txt as extension so content loaded as text/plain not interpreted by the browser
            //if user requests a view-source.
            var rawFile  = sessionPath + "\\" + hash + ".txt";
            var gmiFile  = sessionPath + "\\" + hash + ".gmi";
            var htmlFile = sessionPath + "\\" + hash + ".htm";

            //delete txt file as GemGet seems to sometimes overwrite not create afresh
            File.Delete(rawFile);
            File.Delete(gmiFile);

            //delete any existing html file to encourage webbrowser to reload it
            File.Delete(htmlFile);

            var    settings = new Settings();
            string command  = "";

            var secureFlag = requireSecure ? "" : " -i ";

            if (uri.Scheme == "gemini")
            {
                //pass options to gemget for download
                command = string.Format(
                    "\"{0}\" {1} --header --no-progress-bar -m \"{2}\"Mb -t {3} -o \"{4}\" \"{5}\"",
                    gemGet,
                    secureFlag,
                    settings.MaxDownloadSizeMb,
                    settings.MaxDownloadTimeSeconds,
                    rawFile,
                    fullQuery);
            }
            else
            {
                //pass options to gemget for download using the assigned http proxy, such as
                //duckling-proxy https://github.com/LukeEmmet/duckling-proxy
                //this should obviously be a trusted server since it is in the middle of the
                //request
                command = string.Format(
                    "\"{0}\" {1} --header --no-progress-bar -m \"{2}\"Mb -t {3} -o \"{4}\"  -p \"{5}\" \"{6}\"",
                    gemGet,
                    secureFlag,
                    settings.MaxDownloadSizeMb,
                    settings.MaxDownloadTimeSeconds,
                    rawFile,
                    settings.HttpSchemeProxy,
                    fullQuery);
            }

            var result = ExecuteProcess.ExecuteCommand(command, true, true);

            var geminiResponse = new Response.GeminiResponse(fullQuery);

            geminiResponse.ParseGemGet(result.Item2);   //parse stdout
            geminiResponse.ParseGemGet(result.Item3);   //parse stderr

            //ToastNotify(geminiResponse.Status + " " + geminiResponse.Meta);

            //in these early days of Gemini we dont forbid visiting a site with an expired cert or mismatched host name
            //but we do give a warning each time
            if (result.Item1 == 1 && requireSecure)
            {
                var tryInsecure   = false;
                var securityError = "";
                if (geminiResponse.Errors[0].Contains("server cert is expired"))
                {
                    tryInsecure   = true;
                    securityError = "Server certificate is expired";
                }
                else if (geminiResponse.Errors[0].Contains("hostname does not verify"))
                {
                    tryInsecure   = true;
                    securityError = "Host name does not verify";
                }
                if (tryInsecure)
                {
                    //give a warning and try again with insecure
                    mMainWindow.ToastNotify("Note: " + securityError + " for: " + uri.Authority, ToastMessageStyles.Warning);
                    NavigateGeminiScheme(fullQuery, uri, siteIdentity, false);
                    return(true);
                }
            }

            if (geminiResponse.AbandonedTimeout || geminiResponse.AbandonedSize)
            {
                var abandonMessage = string.Format(
                    "Download was abandoned as it exceeded the max size ({0}) or time ({1} s). See GemiNaut settings for details.\n\n{2}",
                    settings.MaxDownloadSizeMb,
                    settings.MaxDownloadTimeSeconds,
                    fullQuery);

                mMainWindow.ToastNotify(abandonMessage, ToastMessageStyles.Warning);
                mMainWindow.ToggleContainerControlsForBrowser(true);
                return(false);
            }

            if (File.Exists(rawFile))
            {
                if (geminiResponse.Meta.Contains("text/gemini"))
                {
                    File.Copy(rawFile, gmiFile);
                }
                else if (geminiResponse.Meta.Contains("text/html"))
                {
                    //is an html file served over gemini - probably not common, but not unheard of
                    var htmltoGmiResult = ConverterService.HtmlToGmi(rawFile, gmiFile);

                    if (htmltoGmiResult.Item1 != 0)
                    {
                        mMainWindow.ToastNotify("Could not convert HTML to GMI: " + fullQuery, ToastMessageStyles.Error);
                        mMainWindow.ToggleContainerControlsForBrowser(true);
                        return(false);
                    }
                }
                else if (geminiResponse.Meta.Contains("text/"))
                {
                    //convert plain text to a gemini version (wraps it in a preformatted section)
                    var textToGmiResult = ConverterService.TextToGmi(rawFile, gmiFile);

                    if (textToGmiResult.Item1 != 0)
                    {
                        mMainWindow.ToastNotify("Could not render text as GMI: " + fullQuery, ToastMessageStyles.Error);
                        mMainWindow.ToggleContainerControlsForBrowser(true);
                        return(false);
                    }
                }
                else
                {
                    //a download
                    //its an image - rename the raw file and just show it
                    var pathFragment = (new UriBuilder(fullQuery)).Path;
                    var ext          = Path.GetExtension(pathFragment);

                    var binFile = rawFile + (ext == "" ? ".tmp" : ext);
                    File.Copy(rawFile, binFile, true); //rename overwriting

                    if (geminiResponse.Meta.Contains("image/"))
                    {
                        mMainWindow.ShowImage(fullQuery, binFile);
                    }
                    else
                    {
                        SaveFileDialog saveFileDialog = new SaveFileDialog();

                        saveFileDialog.FileName = Path.GetFileName(pathFragment);

                        if (saveFileDialog.ShowDialog() == true)
                        {
                            try
                            {
                                //save the file
                                var savePath = saveFileDialog.FileName;

                                File.Copy(binFile, savePath, true); //rename overwriting

                                mMainWindow.ToastNotify("File saved to " + savePath, ToastMessageStyles.Success);
                            }
                            catch (SystemException err)
                            {
                                mMainWindow.ToastNotify("Could not save the file due to: " + err.Message, ToastMessageStyles.Error);
                            }
                        }

                        mMainWindow.ToggleContainerControlsForBrowser(true);
                        return(false);
                    }

                    return(true);
                }

                if (geminiResponse.Redirected)
                {
                    string redirectUri = fullQuery;

                    if (geminiResponse.FinalUrl.Contains("://"))
                    {
                        //a full url
                        //normalise the URi (e.g. remove default port if specified)
                        redirectUri = UriTester.NormaliseUri(new Uri(geminiResponse.FinalUrl)).ToString();
                    }
                    else
                    {
                        //a relative one
                        var baseUri   = new Uri(fullQuery);
                        var targetUri = new Uri(baseUri, geminiResponse.FinalUrl);
                        redirectUri = UriTester.NormaliseUri(targetUri).ToString();
                    }

                    var finalUri = new Uri(redirectUri);

                    if (uri.Scheme == "gemini" && finalUri.Scheme != "gemini")
                    {
                        //cross-scheme redirect, not supported
                        mMainWindow.ToastNotify("Cross scheme redirect from Gemini not supported: " + redirectUri, ToastMessageStyles.Warning);
                        mMainWindow.ToggleContainerControlsForBrowser(true);
                        return(false);
                    }
                    else
                    {
                        //others e.g. http->https redirect are fine
                    }

                    //redirected to a full gemini url
                    geminiUri = redirectUri;

                    //regenerate the hashes using the redirected target url
                    hash = HashService.GetMd5Hash(geminiUri);

                    var gmiFileNew  = sessionPath + "\\" + hash + ".txt";
                    var htmlFileNew = sessionPath + "\\" + hash + ".htm";

                    //move the source file
                    try
                    {
                        if (File.Exists(gmiFileNew))
                        {
                            File.Delete(gmiFileNew);
                        }
                        File.Move(gmiFile, gmiFileNew);
                    }
                    catch (Exception err)
                    {
                        mMainWindow.ToastNotify(err.ToString(), ToastMessageStyles.Error);
                    }

                    //update locations of gmi and html file
                    gmiFile  = gmiFileNew;
                    htmlFile = htmlFileNew;
                }
                else
                {
                    geminiUri = fullQuery;
                }

                var userThemesFolder = ResourceFinder.LocalOrDevFolder(appDir, @"GmiConverters\themes", @"..\..\GmiConverters\themes");

                var userThemeBase = Path.Combine(userThemesFolder, settings.Theme);

                mMainWindow.ShowUrl(geminiUri, gmiFile, htmlFile, userThemeBase, siteIdentity);
            }
            else if (geminiResponse.Status == 10 || geminiResponse.Status == 11)
            {
                //needs input

                mMainWindow.ToggleContainerControlsForBrowser(true);

                navigated = NavigateGeminiWithInput(uri, geminiResponse.Meta);
            }
            else if (geminiResponse.Status == 50 || geminiResponse.Status == 51)
            {
                mMainWindow.ToastNotify("Page not found (status 51)\n\n" + uri.ToString(), ToastMessageStyles.Warning);
            }
            else
            {
                //some othe error - show to the user for info
                mMainWindow.ToastNotify(string.Format(
                                            "Cannot retrieve the content (exit code {0}): \n\n{1} \n\n{2}",
                                            result.Item1,
                                            string.Join("\n\n", geminiResponse.Info),
                                            string.Join("\n\n", geminiResponse.Errors)
                                            ),
                                        ToastMessageStyles.Error);
            }

            mMainWindow.ToggleContainerControlsForBrowser(true);

            //no further navigation right now
            return(navigated);
        }
Ejemplo n.º 4
0
        private void BrowserControl_Navigating(object sender, System.Windows.Navigation.NavigatingCancelEventArgs e)
        {
            var normalisedUri = UriTester.NormaliseUri(e.Uri);

            var siteIdentity = new SiteIdentity(normalisedUri, Session.Instance);

            var fullQuery = normalisedUri.OriginalString;

            //sanity check we have a valid URL syntax at least
            if (e.Uri.Scheme == null)
            {
                ToastNotify("Invalid URL: " + normalisedUri.OriginalString, ToastMessageStyles.Error);
                e.Cancel = true;
            }

            var settings = new Settings();

            ToggleContainerControlsForBrowser(false);

            //these are the only ones we "navigate" to. We do this by downloading the GMI content
            //converting to HTML and then actually navigating to that.
            if (normalisedUri.Scheme == "gemini")
            {
                var geminiNavigator = new GeminiNavigator(this, this.BrowserControl);
                geminiNavigator.NavigateGeminiScheme(fullQuery, e, siteIdentity);
            }

            else if (normalisedUri.Scheme == "gopher")
            {
                var gopherNavigator = new GopherNavigator(this, this.BrowserControl);
                gopherNavigator.NavigateGopherScheme(fullQuery, e, siteIdentity);
            }

            else if (normalisedUri.Scheme == "about")
            {
                var aboutNavigator = new AboutNavigator(this, this.BrowserControl);
                aboutNavigator.NavigateAboutScheme(e, siteIdentity);
            }
            else if (normalisedUri.Scheme.StartsWith("http"))       //both http and https
            {
                var linkId = "";
                ////doc might be null - you need to check when using!
                var doc = (HTMLDocument)BrowserControl.Document;
                ////this is how we could detect a click on a link to an image...
                if (doc?.activeElement != null)
                {
                    linkId = doc.activeElement.id;
                }

                //detect ctrl click
                if (
                    Keyboard.IsKeyDown(Key.LeftCtrl) ||
                    Keyboard.IsKeyDown(Key.RightCtrl) ||
                    settings.HandleWebLinks == "System web browser" ||
                    linkId == "web-launch-external"
                    )
                {
                    //open in system web browser
                    var launcher = new ExternalNavigator(this);
                    launcher.LaunchExternalUri(e.Uri.ToString());
                    ToggleContainerControlsForBrowser(true);
                    e.Cancel = true;
                }
                else if (settings.HandleWebLinks == "Gemini HTTP proxy")
                {
                    // use a gemini proxy for http links
                    var geminiNavigator = new GeminiNavigator(this, this.BrowserControl);
                    geminiNavigator.NavigateGeminiScheme(fullQuery, e, siteIdentity);
                }
                else
                {
                    //use internal navigator
                    var httpNavigator = new HttpNavigator(this, this.BrowserControl);
                    httpNavigator.NavigateHttpScheme(fullQuery, e, siteIdentity, linkId);
                }
            }
            else if (normalisedUri.Scheme == "file")
            {
                //just load the converted html file
                //no further action.
            }
            else
            {
                //we don't care about any other protocols
                //so we open those in system web browser to deal with
                var launcher = new ExternalNavigator(this);
                launcher.LaunchExternalUri(e.Uri.ToString());
                ToggleContainerControlsForBrowser(true);
                e.Cancel = true;
            }
        }