示例#1
0
        private async void Initialize()
        {
            pluginStatusText.Text = Resources.InitRuntime;

            var container = new TinyIoCContainer();

            logger = new Logger();
            container.Register(logger);
            container.Register <ILogger>(logger);

            asmResolver.ExceptionOccured += (o, e) => logger.Log(LogLevel.Error, Resources.AssemblyResolverError, e.Exception);
            asmResolver.AssemblyLoaded   += (o, e) => logger.Log(LogLevel.Debug, Resources.AssemblyResolverLoaded, e.LoadedAssembly.FullName);

            this.Container = container;
            pluginMain     = new PluginMain(pluginDirectory, logger, container);
            container.Register(pluginMain);

            pluginStatusText.Text = Resources.InitCef;

            SanityChecker.CheckDependencyVersions(logger);

            try
            {
                CurlWrapper.Init(pluginDirectory);
            } catch (Exception ex)
            {
                logger.Log(LogLevel.Error, ex.ToString());
                ActGlobals.oFormActMain.WriteDebugLog(ex.ToString());
            }

            await FinishInit(container);
        }
示例#2
0
        public static Task <(bool, Version, string)> CheckForUpdate(Control parent)
        {
            return(Task.Run(() =>
            {
                var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
                Version remoteVersion;
                string response;
                try
                {
                    response = CurlWrapper.Get(REL_URL);
                }
                catch (CurlException ex)
                {
                    MessageBox.Show(string.Format(Resources.UpdateCheckException, ex.ToString()), Resources.UpdateCheckTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return (false, null, "");
                }

                var releaseNotes = "";
                try
                {
                    // JObject doesn't accept arrays so we have to package the response in a JSON object.
                    var tmp = JObject.Parse("{\"content\":" + response + "}");
                    remoteVersion = Version.Parse(tmp["content"][0]["tag_name"].ToString().Substring(1));

                    foreach (var rel in tmp["content"])
                    {
                        var version = Version.Parse(rel["tag_name"].ToString().Substring(1));
                        if (version.CompareTo(currentVersion) < 1)
                        {
                            break;
                        }

                        releaseNotes += "---\n\n# " + rel["name"].ToString() + "\n\n" + rel["body"].ToString() + "\n\n";
                    }

                    if (releaseNotes.Length > 5)
                    {
                        releaseNotes = releaseNotes.Substring(5);
                    }
                }
                catch (Exception ex)
                {
                    parent.Invoke((Action)(() =>
                    {
                        MessageBox.Show(string.Format(Resources.UpdateParseVersionError, ex.ToString()), Resources.UpdateTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }));
                    return (false, null, null);
                }

                return (remoteVersion.CompareTo(currentVersion) > 0, remoteVersion, releaseNotes);
            }));
        }
示例#3
0
        private void GenSsl()
        {
            try
            {
                var mkcertPath = Path.Combine(_plugin.PluginDirectory, "mkcert.exe");

                if (!File.Exists(mkcertPath))
                {
                    logDisplay.AppendText("Downloading mkcert...\r\n");

                    try
                    {
                        CurlWrapper.Get(MKCERT_DOWNLOAD, new Dictionary <string, string>(), mkcertPath, null, false);
                    }
                    catch (Exception e)
                    {
                        logDisplay.AppendText(string.Format("\nFailed: {0}", e));
                        genSslBtn.Enabled = true;
                        return;
                    }
                }

                logDisplay.AppendText("Installing CA...\r\n");
                if (!RunLogCmd(mkcertPath, "-install"))
                {
                    logDisplay.AppendText("\r\nFailed!\r\n");
                    genSslBtn.Enabled = true;
                    return;
                }

                logDisplay.AppendText("Generating certificate...\r\n");
                if (!RunLogCmd(mkcertPath, string.Format("-pkcs12 -p12-file \"{0}\" localhost 127.0.0.1 ::1", _server.GetCertPath())))
                {
                    logDisplay.AppendText("\r\nFailed!\r\n");
                    genSslBtn.Enabled = true;
                    return;
                }

                logDisplay.AppendText("\r\nDone.\r\n");

                sslBox.Enabled      = _server.IsSSLPossible();
                sslBox.Checked      = sslBox.Enabled;
                _config.WSServerSSL = sslBox.Enabled;
                genSslBtn.Enabled   = true;
            }
            catch (Exception e)
            {
                logDisplay.AppendText(string.Format("\r\nException: {0}", e));
                genSslBtn.Enabled = true;
            }
        }
示例#4
0
        private async void Initialize()
        {
            pluginStatusText.Text = Resources.InitRuntime;

            Registry.Init();
            logger = new Logger();
            asmResolver.ExceptionOccured += (o, e) => logger.Log(LogLevel.Error, Resources.AssemblyResolverError, e.Exception);
            asmResolver.AssemblyLoaded   += (o, e) => logger.Log(LogLevel.Debug, Resources.AssemblyResolverLoaded, e.LoadedAssembly.FullName);
            pluginMain = new PluginMain(pluginDirectory, logger);

            pluginStatusText.Text = Resources.InitCef;

            try
            {
                CurlWrapper.Init(pluginDirectory);
            } catch (Exception ex)
            {
                logger.Log(LogLevel.Error, ex.ToString());
            }

            await FinishInit();
        }
示例#5
0
        private async void Initialize(TabPage pluginScreenSpace, Label pluginStatusText)
        {
            pluginStatusText.Text = Resources.InitRuntime;

            Registry.Init();
            logger = new Logger();
            asmResolver.ExceptionOccured += (o, e) => logger.Log(LogLevel.Error, Resources.AssemblyResolverError, e.Exception);
            asmResolver.AssemblyLoaded   += (o, e) => logger.Log(LogLevel.Debug, Resources.AssemblyResolverLoaded, e.LoadedAssembly.FullName);
            pluginMain = new PluginMain(pluginDirectory, logger);

            pluginStatusText.Text = Resources.InitCef;

            try
            {
                CurlWrapper.Init(pluginDirectory);
            } catch (Exception ex)
            {
                logger.Log(LogLevel.Error, ex.ToString());
            }

            if (await CefInstaller.EnsureCef(GetCefPath()))
            {
                // Finally, load the html renderer. We load it here since HtmlRenderer depends on CEF which we can't load these before
                // the CefInstaller is done.
                if (SanityChecker.LoadSaneAssembly("HtmlRenderer"))
                {
                    // Since this is an async method, we could have switched threds. Make sure InitPlugin() runs on the ACT main thread.
                    ActGlobals.oFormActMain.Invoke((Action)(() =>
                    {
                        pluginMain.InitPlugin(pluginScreenSpace, pluginStatusText);
                    }));
                }
                else
                {
                    pluginStatusText.Text = Resources.CoreOrHtmlRendererInsane;
                }
            }
        }
示例#6
0
        public bool Download(string url, string dest, bool useHttpClient = false)
        {
            try
            {
                if (Directory.Exists(TempDir))
                {
                    Directory.Delete(TempDir, true);
                }

                Directory.CreateDirectory(TempDir);
            } catch (Exception ex)
            {
                _display.Log(string.Format(Resources.CreatingTempDirFailed, TempDir, ex));
                return(false);
            }

            _display.UpdateStatus(0, string.Format(Resources.StatusDownloadStarted, 1, 2));

            // Avoid confusing users with the DO_NOT_DOWNLOAD extension. Users aren't supposed to manually download
            // these files from the GH releases page so I added that extension and didn't expect people to pay
            // attention to the download URL in the updater log.
            _display.Log(string.Format(Resources.LogDownloading, url.Replace(".DO_NOT_DOWNLOAD", ""), dest));

            var success = false;
            var cancel  = _display.GetCancelToken();

            _token = cancel;

            try
            {
                var retries = 10;

                while (retries > 0 && !cancel.IsCancellationRequested)
                {
                    try
                    {
                        if (useHttpClient)
                        {
                            HttpClientWrapper.Get(url, new Dictionary <string, string>(), dest, DlProgressCallback, true);
                        }
                        else
                        {
                            CurlWrapper.Get(url, new Dictionary <string, string>(), dest, DlProgressCallback, true);
                        }

                        success = true;
                        break;
                    }
                    catch (Exception ex)
                    {
                        _display.Log(string.Format(Resources.LogDownloadInterrupted, ex));

                        if (retries > 0 && !cancel.IsCancellationRequested)
                        {
                            // If this is a curl exception, it's most likely network related. Wait a second
                            // before trying again. We don't want to spam the other side with download requests.
                            if (ex.GetType() == typeof(CurlException))
                            {
                                if (!((CurlException)ex).Retry)
                                {
                                    // Retrying won't fix this kind of error. Abort.
                                    success = false;
                                    break;
                                }

                                Thread.Sleep(1000);
                            }

                            _display.Log(Resources.LogResumingDownload);
                            success = false;
                            continue;
                        }
                    }
                }

                if (cancel.IsCancellationRequested || !success)
                {
                    _display.UpdateStatus(0, Resources.StatusCancelling);
                    File.Delete(dest);

                    if (!cancel.IsCancellationRequested)
                    {
                        _display.UpdateStatus(0, Resources.OutOfRetries);
                        _display.Log(Resources.OutOfRetries);
                    }
                    else
                    {
                        _display.UpdateStatus(0, Resources.StatusCancelled);
                        _display.Log(Resources.LogAbortedByUser);
                    }

                    return(false);
                }
            }
            catch (Exception ex)
            {
                if (cancel.IsCancellationRequested)
                {
                    _display.UpdateStatus(1, Resources.StatusCancelled);
                    _display.Log(Resources.LogAbortedByUser);
                    return(false);
                }

                _display.Log(string.Format(Resources.Exception, ex));
                return(false);
            }
            finally
            {
                _display.DisposeCancelSource();

                if (!success)
                {
                    Cleanup();
                }
            }

            return(true);
        }
示例#7
0
        public static Task <(bool, Version, string, string)> CheckForGitHubUpdate(UpdaterOptions options, TinyIoCContainer container)
        {
            var logger = container.Resolve <ILogger>();

            return(Task.Run(() =>
            {
                Version remoteVersion = null;
                string response;

                if (options.actPluginId > 0)
                {
                    try
                    {
                        response = ActGlobals.oFormActMain.PluginGetRemoteVersion(options.actPluginId);
                        if (!response.StartsWith("v") || !Version.TryParse(response.Substring(1), out remoteVersion))
                        {
                            logger.Log(LogLevel.Warning, string.Format(Resources.ActUpdateCheckFailed, options.project));
                        }
                    } catch (Exception ex)
                    {
                        logger.Log(LogLevel.Error, string.Format(Resources.ActUpdateException, options.project, ex));
                    }
                }

                if (remoteVersion == null)
                {
                    try
                    {
                        response = CurlWrapper.Get(CHECK_URL.Replace("{REPO}", options.repo));

                        var tmp = JObject.Parse(response);
                        remoteVersion = Version.Parse(tmp["tag_name"].ToString().Substring(1));
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(
                            string.Format(Resources.UpdateCheckException, ex.ToString()),
                            string.Format(Resources.UpdateCheckTitle, options.project),
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error
                            );
                        return (false, null, "", "");
                    }
                }

                var releaseNotes = "";
                var downloadUrl = "";
                try
                {
                    if (remoteVersion <= options.currentVersion)
                    {
                        // Exit early if no new version is available.
                        return (false, remoteVersion, "", "");
                    }

                    response = CurlWrapper.Get(ALL_RELEASES_URL.Replace("{REPO}", options.repo));

                    // JObject doesn't accept arrays so we have to package the response in a JSON object.
                    var tmp = JObject.Parse("{\"content\":" + response + "}");

                    downloadUrl = options.downloadUrl.Replace("{REPO}", options.repo).Replace("{VERSION}", remoteVersion.ToString());

                    foreach (var rel in tmp["content"])
                    {
                        var version = Version.Parse(rel["tag_name"].ToString().Substring(1));
                        if (version < options.currentVersion)
                        {
                            break;
                        }

                        releaseNotes += "---\n\n# " + rel["name"].ToString() + "\n\n" + rel["body"].ToString() + "\n\n";
                    }

                    if (releaseNotes.Length > 5)
                    {
                        releaseNotes = releaseNotes.Substring(5);
                    }
                }
                catch (Exception ex)
                {
                    ActGlobals.oFormActMain.Invoke((Action)(() =>
                    {
                        MessageBox.Show(
                            string.Format(Resources.UpdateParseVersionError, ex.ToString()),
                            string.Format(Resources.UpdateTitle, options.project),
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error
                            );
                    }));
                    return (false, null, "", "");
                }

                try
                {
                    releaseNotes = Regex.Replace(releaseNotes, @"<!-- TRAILER BEGIN -->(?:[^<]|<(?!!-- TRAILER END -->))+<!-- TRAILER END -->", "");
                }
                catch (Exception ex)
                {
                    logger.Log(LogLevel.Error, $"Failed to remove trailers from release notes: {ex}");
                }

                releaseNotes = RenderMarkdown(releaseNotes);

                return (remoteVersion.CompareTo(options.currentVersion) > 0, remoteVersion, releaseNotes, downloadUrl);
            }));
        }
示例#8
0
        public static Task <(bool, Version, string, string)> CheckForManifestUpdate(UpdaterOptions options)
        {
            return(Task.Run(() =>
            {
                Version remoteVersion;
                string response;
                try
                {
                    response = CurlWrapper.Get(options.manifestUrl);
                }
                catch (CurlException ex)
                {
                    MessageBox.Show(
                        string.Format(Resources.UpdateCheckException, ex.ToString()),
                        string.Format(Resources.UpdateCheckTitle, options.project),
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error
                        );
                    return (false, null, "", "");
                }

                var releaseNotes = "";
                var downloadUrl = "";
                try
                {
                    var tmp = JObject.Parse(response);
                    remoteVersion = Version.Parse(tmp["version"].ToString());
                    if (remoteVersion.CompareTo(options.currentVersion) <= 0)
                    {
                        // Exit early if no new version is available.
                        return (false, remoteVersion, "", "");
                    }

                    response = CurlWrapper.Get(options.notesUrl);

                    // JObject doesn't accept arrays so we have to package the response in a JSON object.
                    tmp = JObject.Parse("{\"content\":" + response + "}");
                    downloadUrl = tmp[0]["download"].ToString();

                    foreach (var rel in tmp["content"])
                    {
                        var version = Version.Parse(rel["version"].ToString());
                        if (version.CompareTo(options.currentVersion) <= 0)
                        {
                            break;
                        }

                        releaseNotes += rel["notes"];
                    }
                }
                catch (Exception ex)
                {
                    ActGlobals.oFormActMain.Invoke((Action)(() =>
                    {
                        MessageBox.Show(
                            string.Format(Resources.UpdateParseVersionError, ex.ToString()),
                            string.Format(Resources.UpdateTitle, options.project),
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error
                            );
                    }));
                    return (false, null, "", "");
                }

                return (remoteVersion.CompareTo(options.currentVersion) > 0, remoteVersion, releaseNotes, downloadUrl);
            }));
        }
示例#9
0
        public bool Download(string url, string dest)
        {
            try
            {
                if (Directory.Exists(_tempDir))
                {
                    Directory.Delete(_tempDir, true);
                }

                Directory.CreateDirectory(_tempDir);
            } catch (Exception ex)
            {
                _display.Log(string.Format(Resources.CreatingTempDirFailed, _tempDir, ex));
                return(false);
            }

            _display.UpdateStatus(0, string.Format(Resources.StatusDownloadStarted, 1, 2));
            _display.Log(string.Format(Resources.LogDownloading, url, dest));

            var success = false;
            var cancel  = _display.GetCancelToken();

            _token = cancel;

            try
            {
                var retries = 10;

                while (retries > 0 && !cancel.IsCancellationRequested)
                {
                    try
                    {
                        CurlWrapper.Get(url, new Dictionary <string, string>(), dest, DlProgressCallback, true);
                        success = true;
                        break;
                    }
                    catch (Exception ex)
                    {
                        _display.Log(string.Format(Resources.LogDownloadInterrupted, ex));

                        if (retries > 0)
                        {
                            if (ex.GetType().IsSubclassOf(typeof(CurlException)))
                            {
                                Thread.Sleep(1000);
                            }

                            _display.Log(Resources.LogResumingDownload);
                            success = false;
                            continue;
                        }
                    }
                }

                if (cancel.IsCancellationRequested || !success)
                {
                    _display.UpdateStatus(0, Resources.StatusCancelling);
                    File.Delete(dest);

                    if (!cancel.IsCancellationRequested)
                    {
                        _display.UpdateStatus(0, Resources.OutOfRetries);
                        _display.Log(Resources.OutOfRetries);
                    }
                    else
                    {
                        _display.UpdateStatus(0, Resources.StatusCancelled);
                        _display.Log(Resources.LogAbortedByUser);
                    }

                    return(false);
                }
            }
            catch (Exception ex)
            {
                if (cancel.IsCancellationRequested)
                {
                    _display.UpdateStatus(1, Resources.StatusCancelled);
                    _display.Log(Resources.LogAbortedByUser);
                    return(false);
                }

                _display.Log(string.Format(Resources.Exception, ex));
                return(false);
            }
            finally
            {
                _display.DisposeCancelSource();

                if (!success)
                {
                    Cleanup();
                }
            }

            return(true);
        }
示例#10
0
        private bool FetchNgrok(string ngrokPath)
        {
            try
            {
                UpdateTunnelStatus(TunnelStatus.Downloading);

                simpLogBox.AppendText("Fetching latest ngrok version...\r\n");
                string dlPage;
                try
                {
                    dlPage = CurlWrapper.Get(NGROK_DOWNLOAD_IDX);
                } catch (Exception e)
                {
                    simpLogBox.AppendText(string.Format("\r\nFailed: {0}\r\n\r\n", e));
                    return(false);
                }

                var arch = Environment.Is64BitOperatingSystem ? "amd64" : "386";
                // <a id="dl-windows-amd64" href="https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-amd64.zip" class="download-btn"
                var match = Regex.Match(dlPage, " href=\"(https://bin.equinox.io/c/[^/]+/ngrok-stable-windows-" + arch + "\\.zip)\"");
                if (match == Match.Empty)
                {
                    simpLogBox.AppendText("Failed to find version on the download page! Please notify ngld or some other dev working on OverlayPlugin.\r\n");
                    return(false);
                }

                simpLogBox.AppendText("Downloading ngrok client...\r\n");
                try
                {
                    CurlWrapper.Get(match.Groups[1].Captures[0].Value, new Dictionary <string, string>(), ngrokPath + ".zip", NgrokProgressCallback, false);
                }
                catch (Exception e)
                {
                    simpLogBox.AppendText(string.Format("\r\nFailed: {0}\r\n\r\n", e));
                    return(false);
                }

                simpLogBox.AppendText("\r\nExtracting ngrok client...\r\n");
                try
                {
                    using (var archive = ZipArchive.Open(ngrokPath + ".zip"))
                        using (var reader = archive.ExtractAllEntries())
                        {
                            while (reader.MoveToNextEntry())
                            {
                                if (reader.Entry.Key == "ngrok.exe")
                                {
                                    using (var writer = File.OpenWrite(ngrokPath))
                                    {
                                        reader.WriteEntryTo(writer);
                                    }
                                    break;
                                }
                            }
                        }

                    File.Delete(ngrokPath + ".zip");
                } catch (Exception e)
                {
                    simpLogBox.AppendText(string.Format("\r\n{0}\r\n\r\n", e));
                }

                if (!File.Exists(ngrokPath))
                {
                    simpLogBox.AppendText("\r\nExtraction failed!\r\n");
                    return(false);
                }

                return(true);
            }
            catch (Exception e)
            {
                simpLogBox.AppendText(string.Format("\r\nException: {0}\r\n\r\n", e));
                return(false);
            }
        }
示例#11
0
        private void simpStartBtn_Click(object sender, EventArgs e)
        {
            simpStartBtn.Enabled = false;

            Task.Run(() => {
                try
                {
                    var ngrokPath = Path.Combine(ActGlobals.oFormActMain.AppDataFolder.FullName, "ngrok-" + (Environment.Is64BitOperatingSystem ? "x64" : "x86") + ".exe");
                    if (!File.Exists(ngrokPath))
                    {
                        if (!FetchNgrok(ngrokPath))
                        {
                            return;
                        }
                    }

                    UpdateTunnelStatus(TunnelStatus.Launching);

                    if (_ngrok != null && !_ngrok.HasExited)
                    {
                        simpLogBox.AppendText("Detected left over ngrok process. Performing cleanup...\r\n");
                        _ngrok.Kill();
                        _ngrok = null;
                    }

                    // Enforce sensible settings
                    ipTxt.Text     = "127.0.0.1";
                    sslBox.Checked = false;

                    _config.WSServerIP  = "127.0.0.1";
                    _config.WSServerSSL = false;

                    if (_config.WSServerPort < 1024)
                    {
                        portTxt.Text         = "10501";
                        _config.WSServerPort = 10501;
                    }

                    simpLogBox.AppendText("Launching WSServer...\r\n");
                    _config.WSServerRunning = true;
                    _server.Start();

                    simpLogBox.AppendText("Launching ngrok...\r\n");

                    var region = _config.TunnelRegion;
                    if (region == null)
                    {
                        region = "us";
                    }
                    else
                    {
                        region = region.Split(' ')[0];
                    }

                    var config          = @"
region: " + region + @"
console_ui: false
web_addr: 127.0.0.1:" + (_config.WSServerPort + 1) + @"

tunnels:
    wsserver:
        proto: http
        addr: 127.0.0.1:" + _config.WSServerPort + @"
        inspect: false
        bind_tls: true
    ";
                    var ngrokConfigPath = Path.Combine(ActGlobals.oFormActMain.AppDataFolder.FullName, "ngrok.yml");
                    File.WriteAllText(ngrokConfigPath, config);

                    var p = new Process();
                    p.StartInfo.FileName               = ngrokPath;
                    p.StartInfo.Arguments              = "start -config=\"" + ngrokConfigPath + "\" wsserver";
                    p.StartInfo.UseShellExecute        = false;
                    p.StartInfo.CreateNoWindow         = true;
                    p.StartInfo.RedirectStandardError  = true;
                    p.StartInfo.RedirectStandardOutput = true;

                    DataReceivedEventHandler showLine = (_, ev) =>
                    {
                        if (ev.Data != null)
                        {
                            simpLogBox.AppendText(ev.Data.Replace("\n", "\r\n") + "\r\n");
                        }
                    };

                    p.OutputDataReceived += showLine;
                    p.ErrorDataReceived  += showLine;

                    p.Start();
                    p.BeginOutputReadLine();
                    p.BeginErrorReadLine();

                    if (p.WaitForExit(3000))
                    {
                        simpLogBox.AppendText("ngrok crashed!\r\n");
                        UpdateTunnelStatus(TunnelStatus.Error);
                        return;
                    }

                    _ngrok = p;

                    var apiUrl  = "http://127.0.0.1:" + (_config.WSServerPort + 1) + "/api/tunnels";
                    var headers = new Dictionary <string, string>()
                    {
                        { "Content-Type", "application/json" },
                    };

                    string data = null;
                    while (true)
                    {
                        try
                        {
                            data = CurlWrapper.Get(apiUrl, headers, null, null, false);
                            break;
                        } catch (CurlException ex)
                        {
                            if (!ex.Retry)
                            {
                                simpLogBox.AppendText(string.Format("Failed: {0}\r\n", ex));
                                UpdateTunnelStatus(TunnelStatus.Error);
                                return;
                            }
                            else
                            {
                                Thread.Sleep(500);
                            }
                        } catch (Exception ex)
                        {
                            simpLogBox.AppendText(string.Format("Failed: {0}\r\n", ex));
                            UpdateTunnelStatus(TunnelStatus.Error);
                            return;
                        }
                    }

                    var tunnels = JObject.Parse(data);
                    var done    = false;
                    foreach (var tun in tunnels["tunnels"])
                    {
                        if (tun["name"] != null && tun["name"].ToString() == "wsserver" && tun["public_url"] != null)
                        {
                            _ngrokPrefix = tun["public_url"].ToString().Replace("https://", "wss://");

                            // Update the generated URL box
                            cbOverlay_SelectedIndexChanged(null, null);
                            done = true;
                            break;
                        }
                    }

                    if (done)
                    {
                        simpLogBox.AppendText("Done!\r\n");
                        simpLogBox.AppendText("\r\n#############################################\r\nUse the URL Generator below to generate URLs for you.\r\n\r\n");
                        simpLogBox.AppendText("\r\nIf you know what you're doing or you're using an overlay that isn't listed, here are some query strings for you:\r\n");
                        simpLogBox.AppendText("\r\n    ?HOST_PORT=" + _ngrokPrefix + "\r\n    ?OVERLAY_WS=" + _ngrokPrefix + "/ws\r\n");
                        simpLogBox.AppendText("#############################################\r\n");

                        UpdateTunnelStatus(TunnelStatus.Active);

                        p.Exited += (_, ev) =>
                        {
                            UpdateTunnelStatus(TunnelStatus.Error);
                        };
                    }
                    else
                    {
                        simpLogBox.AppendText("Failed!\r\n");
                        UpdateTunnelStatus(TunnelStatus.Error);
                    }
                } catch (Exception ex)
                {
                    simpLogBox.AppendText(string.Format("\r\nUncaught exception: {0}\r\n\r\n", ex));
                    UpdateTunnelStatus(TunnelStatus.Error);
                }
            });
        }