bool CZNC::LoadGlobal(CConfig& config, CString& sError) {
    sError.clear();

    MCString msModules;  // Modules are queued for later loading

    VCString vsList;
    config.FindStringVector("loadmodule", vsList);
    for (const CString& sModLine : vsList) {
        CString sModName = sModLine.Token(0);
        CString sArgs = sModLine.Token(1, true);

        // compatibility for pre-1.0 configs
        CString sSavedVersion;
        config.FindStringEntry("version", sSavedVersion);
        tuple<unsigned int, unsigned int> tSavedVersion =
            make_tuple(sSavedVersion.Token(0, false, ".").ToUInt(),
                       sSavedVersion.Token(1, false, ".").ToUInt());
        if (sModName == "saslauth" && tSavedVersion < make_tuple(0, 207)) {
            CUtils::PrintMessage(
                "saslauth module was renamed to cyrusauth. Loading cyrusauth "
                "instead.");
            sModName = "cyrusauth";
        }
        // end-compatibility for pre-1.0 configs

        if (msModules.find(sModName) != msModules.end()) {
            sError = "Module [" + sModName + "] already loaded";
            CUtils::PrintError(sError);
            return false;
        }
        CString sModRet;
        CModule* pOldMod;

        pOldMod = GetModules().FindModule(sModName);
        if (!pOldMod) {
            CUtils::PrintAction("Loading global module [" + sModName + "]");

            bool bModRet =
                GetModules().LoadModule(sModName, sArgs, CModInfo::GlobalModule,
                                        nullptr, nullptr, sModRet);

            CUtils::PrintStatus(bModRet, bModRet ? "" : sModRet);
            if (!bModRet) {
                sError = sModRet;
                return false;
            }
        } else if (pOldMod->GetArgs() != sArgs) {
            CUtils::PrintAction("Reloading global module [" + sModName + "]");

            bool bModRet = GetModules().ReloadModule(sModName, sArgs, nullptr,
                                                     nullptr, sModRet);

            CUtils::PrintStatus(bModRet, sModRet);
            if (!bModRet) {
                sError = sModRet;
                return false;
            }
        } else
            CUtils::PrintMessage("Module [" + sModName + "] already loaded.");

        msModules[sModName] = sArgs;
    }

    m_vsMotd.clear();
    config.FindStringVector("motd", vsList);
    for (const CString& sMotd : vsList) {
        AddMotd(sMotd);
    }

    if (config.FindStringVector("bindhost", vsList)) {
        CUtils::PrintStatus(false,
                            "WARNING: the global BindHost list is deprecated. "
                            "Ignoring the following lines:");
        for (const CString& sHost : vsList) {
            CUtils::PrintStatus(false, "BindHost = " + sHost);
        }
    }
    if (config.FindStringVector("vhost", vsList)) {
        CUtils::PrintStatus(false,
                            "WARNING: the global vHost list is deprecated. "
                            "Ignoring the following lines:");
        for (const CString& sHost : vsList) {
            CUtils::PrintStatus(false, "vHost = " + sHost);
        }
    }

    m_vsTrustedProxies.clear();
    config.FindStringVector("trustedproxy", vsList);
    for (const CString& sProxy : vsList) {
        AddTrustedProxy(sProxy);
    }

    CString sVal;
    if (config.FindStringEntry("pidfile", sVal)) m_sPidFile = sVal;
    if (config.FindStringEntry("statusprefix", sVal)) m_sStatusPrefix = sVal;
    if (config.FindStringEntry("sslcertfile", sVal)) m_sSSLCertFile = sVal;
    if (config.FindStringEntry("sslkeyfile", sVal)) m_sSSLKeyFile = sVal;
    if (config.FindStringEntry("ssldhparamfile", sVal))
        m_sSSLDHParamFile = sVal;
    if (config.FindStringEntry("sslciphers", sVal)) m_sSSLCiphers = sVal;
    if (config.FindStringEntry("skin", sVal)) SetSkinName(sVal);
    if (config.FindStringEntry("connectdelay", sVal))
        SetConnectDelay(sVal.ToUInt());
    if (config.FindStringEntry("serverthrottle", sVal))
        m_sConnectThrottle.SetTTL(sVal.ToUInt() * 1000);
    if (config.FindStringEntry("anoniplimit", sVal))
        m_uiAnonIPLimit = sVal.ToUInt();
    if (config.FindStringEntry("maxbuffersize", sVal))
        m_uiMaxBufferSize = sVal.ToUInt();
    if (config.FindStringEntry("protectwebsessions", sVal))
        m_bProtectWebSessions = sVal.ToBool();
    if (config.FindStringEntry("hideversion", sVal))
        m_bHideVersion = sVal.ToBool();
    if (config.FindStringEntry("authonlyviamodule", sVal))
        m_bAuthOnlyViaModule = sVal.ToBool();
    if (config.FindStringEntry("sslprotocols", sVal)) {
        if (!SetSSLProtocols(sVal)) {
            VCString vsProtocols = GetAvailableSSLProtocols();
            CUtils::PrintError("Invalid SSLProtocols value [" + sVal + "]");
            CUtils::PrintError(
                "The syntax is [SSLProtocols = [+|-]<protocol> ...]");
            CUtils::PrintError(
                "Available protocols are [" +
                CString(", ").Join(vsProtocols.begin(), vsProtocols.end()) +
                "]");
            return false;
        }
    }
    if (config.FindStringEntry("configwritedelay", sVal))
        m_uiConfigWriteDelay = sVal.ToUInt();

    UnloadRemovedModules(msModules);

    if (!LoadListeners(config, sError)) return false;

    return true;
}