bool CIRCNetwork::ParseConfig(CConfig* pConfig, CString& sError,
                              bool bUpgrade) {
    VCString vsList;

    if (!bUpgrade) {
        TOption<const CString&> StringOptions[] = {
            {"nick", &CIRCNetwork::SetNick},
            {"altnick", &CIRCNetwork::SetAltNick},
            {"ident", &CIRCNetwork::SetIdent},
            {"realname", &CIRCNetwork::SetRealName},
            {"bindhost", &CIRCNetwork::SetBindHost},
            {"encoding", &CIRCNetwork::SetEncoding},
            {"quitmsg", &CIRCNetwork::SetQuitMsg},
        };
        TOption<bool> BoolOptions[] = {
            {"ircconnectenabled", &CIRCNetwork::SetIRCConnectEnabled},
            {"trustallcerts", &CIRCNetwork::SetTrustAllCerts},
            {"trustpki", &CIRCNetwork::SetTrustPKI},
        };
        TOption<double> DoubleOptions[] = {
            {"floodrate", &CIRCNetwork::SetFloodRate},
        };
        TOption<short unsigned int> SUIntOptions[] = {
            {"floodburst", &CIRCNetwork::SetFloodBurst},
            {"joindelay", &CIRCNetwork::SetJoinDelay},
        };

        for (const auto& Option : StringOptions) {
            CString sValue;
            if (pConfig->FindStringEntry(Option.name, sValue))
                (this->*Option.pSetter)(sValue);
        }

        for (const auto& Option : BoolOptions) {
            CString sValue;
            if (pConfig->FindStringEntry(Option.name, sValue))
                (this->*Option.pSetter)(sValue.ToBool());
        }

        for (const auto& Option : DoubleOptions) {
            double fValue;
            if (pConfig->FindDoubleEntry(Option.name, fValue))
                (this->*Option.pSetter)(fValue);
        }

        for (const auto& Option : SUIntOptions) {
            unsigned short value;
            if (pConfig->FindUShortEntry(Option.name, value))
                (this->*Option.pSetter)(value);
        }

        pConfig->FindStringVector("loadmodule", vsList);
        for (const CString& sValue : vsList) {
            CString sModName = sValue.Token(0);
            CString sNotice = "Loading network module [" + sModName + "]";

            // XXX Legacy crap, added in ZNC 0.203, modified in 0.207
            // Note that 0.203 == 0.207
            if (sModName == "away") {
                sNotice =
                    "NOTICE: [away] was renamed, loading [awaystore] instead";
                sModName = "awaystore";
            }

            // XXX Legacy crap, added in ZNC 0.207
            if (sModName == "autoaway") {
                sNotice =
                    "NOTICE: [autoaway] was renamed, loading [awaystore] "
                    "instead";
                sModName = "awaystore";
            }

            // XXX Legacy crap, added in 1.1; fakeonline module was dropped in
            // 1.0 and returned in 1.1
            if (sModName == "fakeonline") {
                sNotice =
                    "NOTICE: [fakeonline] was renamed, loading "
                    "[modules_online] instead";
                sModName = "modules_online";
            }

            CString sModRet;
            CString sArgs = sValue.Token(1, true);

            bool bModRet = LoadModule(sModName, sArgs, sNotice, sModRet);

            if (!bModRet) {
                // XXX The awaynick module was retired in 1.6 (still available
                // as external module)
                if (sModName == "awaynick") {
                    // load simple_away instead, unless it's already on the list
                    bool bFound = false;
                    for (const CString& sLoadMod : vsList) {
                        if (sLoadMod.Token(0).Equals("simple_away")) {
                            bFound = true;
                        }
                    }
                    if (!bFound) {
                        sNotice =
                            "NOTICE: awaynick was retired, loading network "
                            "module [simple_away] instead; if you still need "
                            "awaynick, install it as an external module";
                        sModName = "simple_away";
                        // not a fatal error if simple_away is not available
                        LoadModule(sModName, sArgs, sNotice, sModRet);
                    }
                } else {
                    sError = sModRet;
                    return false;
                }
            }
        }
    }

    pConfig->FindStringVector("server", vsList);
    for (const CString& sServer : vsList) {
        CUtils::PrintAction("Adding server [" + sServer + "]");
        CUtils::PrintStatus(AddServer(sServer));
    }

    pConfig->FindStringVector("trustedserverfingerprint", vsList);
    for (const CString& sFP : vsList) {
        AddTrustedFingerprint(sFP);
    }

    pConfig->FindStringVector("chan", vsList);
    for (const CString& sChan : vsList) {
        AddChan(sChan, true);
    }

    CConfig::SubConfig subConf;
    CConfig::SubConfig::const_iterator subIt;

    pConfig->FindSubConfig("chan", subConf);
    for (subIt = subConf.begin(); subIt != subConf.end(); ++subIt) {
        const CString& sChanName = subIt->first;
        CConfig* pSubConf = subIt->second.m_pSubConfig;
        CChan* pChan = new CChan(sChanName, this, true, pSubConf);

        if (!pSubConf->empty()) {
            sError = "Unhandled lines in config for User [" +
                     m_pUser->GetUserName() + "], Network [" + GetName() +
                     "], Channel [" + sChanName + "]!";
            CUtils::PrintError(sError);

            CZNC::DumpConfig(pSubConf);
            delete pChan;
            return false;
        }

        // Save the channel name, because AddChan
        // deletes the CChannel*, if adding fails
        sError = pChan->GetName();
        if (!AddChan(pChan)) {
            sError = "Channel [" + sError + "] defined more than once";
            CUtils::PrintError(sError);
            return false;
        }
        sError.clear();
    }

    return true;
}