bool CZNC::WriteNewConfig(const CString& sConfigFile) {
    CString sAnswer, sUser, sNetwork;
    VCString vsLines;

    vsLines.push_back(MakeConfigHeader());
    vsLines.push_back("Version = " + CString(VERSION_STR));

    m_sConfigFile = ExpandConfigPath(sConfigFile);

    if (CFile::Exists(m_sConfigFile)) {
        CUtils::PrintStatus(
            false, "WARNING: config [" + m_sConfigFile + "] already exists.");
    }

    CUtils::PrintMessage("");
    CUtils::PrintMessage("-- Global settings --");
    CUtils::PrintMessage("");

// Listen
#ifdef HAVE_IPV6
    bool b6 = true;
#else
    bool b6 = false;
#endif
    CString sListenHost;
    CString sURIPrefix;
    bool bListenSSL = false;
    unsigned int uListenPort = 0;
    bool bSuccess;

    do {
        bSuccess = true;
        while (true) {
            if (!CUtils::GetNumInput("Listen on port", uListenPort, 1025,
                                     65534)) {
                continue;
            }
            if (uListenPort == 6667) {
                CUtils::PrintStatus(false,
                                    "WARNING: Some web browsers reject port "
                                    "6667. If you intend to");
                CUtils::PrintStatus(false,
                                    "use ZNC's web interface, you might want "
                                    "to use another port.");
                if (!CUtils::GetBoolInput("Proceed with port 6667 anyway?",
                                          true)) {
                    continue;
                }
            }
            break;
        }

#ifdef HAVE_LIBSSL
        bListenSSL = CUtils::GetBoolInput("Listen using SSL", bListenSSL);
#endif

#ifdef HAVE_IPV6
        b6 = CUtils::GetBoolInput("Listen using both IPv4 and IPv6", b6);
#endif

        // Don't ask for listen host, it may be configured later if needed.

        CUtils::PrintAction("Verifying the listener");
        CListener* pListener = new CListener(
            (unsigned short int)uListenPort, sListenHost, sURIPrefix,
            bListenSSL, b6 ? ADDR_ALL : ADDR_IPV4ONLY, CListener::ACCEPT_ALL);
        if (!pListener->Listen()) {
            CUtils::PrintStatus(false, FormatBindError());
            bSuccess = false;
        } else
            CUtils::PrintStatus(true);
        delete pListener;
    } while (!bSuccess);

#ifdef HAVE_LIBSSL
    CString sPemFile = GetPemLocation();
    if (!CFile::Exists(sPemFile)) {
        CUtils::PrintMessage("Unable to locate pem file: [" + sPemFile +
                             "], creating it");
        WritePemFile();
    }
#endif

    vsLines.push_back("<Listener l>");
    vsLines.push_back("\tPort = " + CString(uListenPort));
    vsLines.push_back("\tIPv4 = true");
    vsLines.push_back("\tIPv6 = " + CString(b6));
    vsLines.push_back("\tSSL = " + CString(bListenSSL));
    if (!sListenHost.empty()) {
        vsLines.push_back("\tHost = " + sListenHost);
    }
    vsLines.push_back("</Listener>");
    // !Listen

    set<CModInfo> ssGlobalMods;
    GetModules().GetDefaultMods(ssGlobalMods, CModInfo::GlobalModule);
    vector<CString> vsGlobalModNames;
    for (const CModInfo& Info : ssGlobalMods) {
        vsGlobalModNames.push_back(Info.GetName());
        vsLines.push_back("LoadModule = " + Info.GetName());
    }
    CUtils::PrintMessage(
        "Enabled global modules [" +
        CString(", ").Join(vsGlobalModNames.begin(), vsGlobalModNames.end()) +
        "]");

    // User
    CUtils::PrintMessage("");
    CUtils::PrintMessage("-- Admin user settings --");
    CUtils::PrintMessage("");

    vsLines.push_back("");
    CString sNick;
    do {
        CUtils::GetInput("Username", sUser, "", "alphanumeric");
    } while (!CUser::IsValidUserName(sUser));

    vsLines.push_back("<User " + sUser + ">");
    CString sSalt;
    sAnswer = CUtils::GetSaltedHashPass(sSalt);
    vsLines.push_back("\tPass       = " + CUtils::sDefaultHash + "#" + sAnswer +
                      "#" + sSalt + "#");

    vsLines.push_back("\tAdmin      = true");

    CUtils::GetInput("Nick", sNick, CUser::MakeCleanUserName(sUser));
    vsLines.push_back("\tNick       = " + sNick);
    CUtils::GetInput("Alternate nick", sAnswer, sNick + "_");
    if (!sAnswer.empty()) {
        vsLines.push_back("\tAltNick    = " + sAnswer);
    }
    CUtils::GetInput("Ident", sAnswer, sUser);
    vsLines.push_back("\tIdent      = " + sAnswer);
    CUtils::GetInput("Real name", sAnswer, "", "optional");
    if (!sAnswer.empty()) {
        vsLines.push_back("\tRealName   = " + sAnswer);
    }
    CUtils::GetInput("Bind host", sAnswer, "", "optional");
    if (!sAnswer.empty()) {
        vsLines.push_back("\tBindHost   = " + sAnswer);
    }

    set<CModInfo> ssUserMods;
    GetModules().GetDefaultMods(ssUserMods, CModInfo::UserModule);
    vector<CString> vsUserModNames;
    for (const CModInfo& Info : ssUserMods) {
        vsUserModNames.push_back(Info.GetName());
        vsLines.push_back("\tLoadModule = " + Info.GetName());
    }
    CUtils::PrintMessage(
        "Enabled user modules [" +
        CString(", ").Join(vsUserModNames.begin(), vsUserModNames.end()) + "]");

    CUtils::PrintMessage("");
    if (CUtils::GetBoolInput("Set up a network?", true)) {
        vsLines.push_back("");

        CUtils::PrintMessage("");
        CUtils::PrintMessage("-- Network settings --");
        CUtils::PrintMessage("");

        do {
            CUtils::GetInput("Name", sNetwork, "freenode");
        } while (!CIRCNetwork::IsValidNetwork(sNetwork));

        vsLines.push_back("\t<Network " + sNetwork + ">");

        set<CModInfo> ssNetworkMods;
        GetModules().GetDefaultMods(ssNetworkMods, CModInfo::NetworkModule);
        vector<CString> vsNetworkModNames;
        for (const CModInfo& Info : ssNetworkMods) {
            vsNetworkModNames.push_back(Info.GetName());
            vsLines.push_back("\t\tLoadModule = " + Info.GetName());
        }

        CString sHost, sPass, sHint;
        bool bSSL = false;
        unsigned int uServerPort = 0;

        if (sNetwork.Equals("freenode")) {
            sHost = "chat.freenode.net";
#ifdef HAVE_LIBSSL
            bSSL = true;
#endif
        } else {
            sHint = "host only";
        }

        while (!CUtils::GetInput("Server host", sHost, sHost, sHint) ||
               !CServer::IsValidHostName(sHost))
            ;
#ifdef HAVE_LIBSSL
        bSSL = CUtils::GetBoolInput("Server uses SSL?", bSSL);
#endif
        while (!CUtils::GetNumInput("Server port", uServerPort, 1, 65535,
                                    bSSL ? 6697 : 6667))
            ;
        CUtils::GetInput("Server password (probably empty)", sPass);

        vsLines.push_back("\t\tServer     = " + sHost + ((bSSL) ? " +" : " ") +
                          CString(uServerPort) + " " + sPass);

        CString sChans;
        if (CUtils::GetInput("Initial channels", sChans)) {
            vsLines.push_back("");
            VCString vsChans;
            sChans.Replace(",", " ");
            sChans.Replace(";", " ");
            sChans.Split(" ", vsChans, false, "", "", true, true);
            for (const CString& sChan : vsChans) {
                vsLines.push_back("\t\t<Chan " + sChan + ">");
                vsLines.push_back("\t\t</Chan>");
            }
        }

        CUtils::PrintMessage("Enabled network modules [" +
                             CString(", ").Join(vsNetworkModNames.begin(),
                                                vsNetworkModNames.end()) +
                             "]");

        vsLines.push_back("\t</Network>");
    }

    vsLines.push_back("</User>");

    CUtils::PrintMessage("");
    // !User

    CFile File;
    bool bFileOK, bFileOpen = false;
    do {
        CUtils::PrintAction("Writing config [" + m_sConfigFile + "]");

        bFileOK = true;
        if (CFile::Exists(m_sConfigFile)) {
            if (!File.TryExLock(m_sConfigFile)) {
                CUtils::PrintStatus(false,
                                    "ZNC is currently running on this config.");
                bFileOK = false;
            } else {
                File.Close();
                CUtils::PrintStatus(false, "This config already exists.");
                if (CUtils::GetBoolInput(
                        "Are you sure you want to overwrite it?", false))
                    CUtils::PrintAction("Overwriting config [" + m_sConfigFile +
                                        "]");
                else
                    bFileOK = false;
            }
        }

        if (bFileOK) {
            File.SetFileName(m_sConfigFile);
            if (File.Open(O_WRONLY | O_CREAT | O_TRUNC, 0600)) {
                bFileOpen = true;
            } else {
                CUtils::PrintStatus(false, "Unable to open file");
                bFileOK = false;
            }
        }
        if (!bFileOK) {
            while (!CUtils::GetInput("Please specify an alternate location",
                                     m_sConfigFile, "",
                                     "or \"stdout\" for displaying the config"))
                ;
            if (m_sConfigFile.Equals("stdout"))
                bFileOK = true;
            else
                m_sConfigFile = ExpandConfigPath(m_sConfigFile);
        }
    } while (!bFileOK);

    if (!bFileOpen) {
        CUtils::PrintMessage("");
        CUtils::PrintMessage("Printing the new config to stdout:");
        CUtils::PrintMessage("");
        cout << endl << "------------------------------------------------------"
                        "----------------------" << endl << endl;
    }

    for (const CString& sLine : vsLines) {
        if (bFileOpen) {
            File.Write(sLine + "\n");
        } else {
            cout << sLine << endl;
        }
    }

    if (bFileOpen) {
        File.Close();
        if (File.HadError())
            CUtils::PrintStatus(false,
                                "There was an error while writing the config");
        else
            CUtils::PrintStatus(true);
    } else {
        cout << endl << "------------------------------------------------------"
                        "----------------------" << endl << endl;
    }

    if (File.HadError()) {
        bFileOpen = false;
        CUtils::PrintMessage("Printing the new config to stdout instead:");
        cout << endl << "------------------------------------------------------"
                        "----------------------" << endl << endl;
        for (const CString& sLine : vsLines) {
            cout << sLine << endl;
        }
        cout << endl << "------------------------------------------------------"
                        "----------------------" << endl << endl;
    }

    const CString sProtocol(bListenSSL ? "https" : "http");
    const CString sSSL(bListenSSL ? "+" : "");
    CUtils::PrintMessage("");
    CUtils::PrintMessage(
        "To connect to this ZNC you need to connect to it as your IRC server",
        true);
    CUtils::PrintMessage(
        "using the port that you supplied.  You have to supply your login info",
        true);
    CUtils::PrintMessage(
        "as the IRC server password like this: user/network:pass.", true);
    CUtils::PrintMessage("");
    CUtils::PrintMessage("Try something like this in your IRC client...", true);
    CUtils::PrintMessage("/server <znc_server_ip> " + sSSL +
                             CString(uListenPort) + " " + sUser + ":<pass>",
                         true);
    CUtils::PrintMessage("");
    CUtils::PrintMessage(
        "To manage settings, users and networks, point your web browser to",
        true);
    CUtils::PrintMessage(
        sProtocol + "://<znc_server_ip>:" + CString(uListenPort) + "/", true);
    CUtils::PrintMessage("");

    File.UnLock();

    bool bWantLaunch = bFileOpen;
    if (bWantLaunch) {
        // "export ZNC_NO_LAUNCH_AFTER_MAKECONF=1" would cause znc --makeconf to
        // not offer immediate launch.
        // Useful for distros which want to create config when znc package is
        // installed.
        // See https://github.com/znc/znc/pull/257
        char* szNoLaunch = getenv("ZNC_NO_LAUNCH_AFTER_MAKECONF");
        if (szNoLaunch && *szNoLaunch == '1') {
            bWantLaunch = false;
        }
    }
    if (bWantLaunch) {
        bWantLaunch = CUtils::GetBoolInput("Launch ZNC now?", true);
    }
    return bWantLaunch;
}