Commit message: Fix NULL pointer dereference in webadmin.

Triggerable by any non-admin, if webadmin is loaded.

The only affected version is 1.0

Thanks to ChauffeR (Simone Esposito) for reporting this.
CVE: CVE-2013-2130
CWE: CWE-703
Vulnerability description: ZNC 1.0 allows remote authenticated users to cause a denial of service (NULL pointer reference and crash) via a crafted request to the (1) editnetwork, (2) editchan, (3) addchan, or (4) delchan page in modules/webadmin.cpp.

Project: znc
File: webadmin.cpp

	bool UserPage(CWebSock& WebSock, CTemplate& Tmpl, CUser* pUser = NULL) {
		CSmartPtr<CWebSession> spSession = WebSock.GetSession();
		Tmpl.SetFile("add_edit_user.tmpl");

		if (!WebSock.GetParam("submitted").ToUInt()) {
			CString sAllowedHosts, sServers, sChans, sCTCPReplies;

			if (pUser) {
				Tmpl["Action"] = "edituser";
				Tmpl["Title"] = "Edit User [" + pUser->GetUserName() + "]";
				Tmpl["Edit"] = "true";
			} else {
				CString sUsername = WebSock.GetParam("clone", false);
				pUser = CZNC::Get().FindUser(sUsername);

				if (pUser) {
					Tmpl["Title"] = "Clone User [" + pUser->GetUserName() + "]";
					Tmpl["Clone"] = "true";
					Tmpl["CloneUsername"] = pUser->GetUserName();
				}
			}

			Tmpl["ImAdmin"] = CString(spSession->IsAdmin());

			if (pUser) {
				Tmpl["Username"] = pUser->GetUserName();
				Tmpl["Nick"] = pUser->GetNick();
				Tmpl["AltNick"] = pUser->GetAltNick();
				Tmpl["StatusPrefix"] = pUser->GetStatusPrefix();
				Tmpl["Ident"] = pUser->GetIdent();
				Tmpl["RealName"] = pUser->GetRealName();
				Tmpl["QuitMsg"] = pUser->GetQuitMsg();
				Tmpl["DefaultChanModes"] = pUser->GetDefaultChanModes();
				Tmpl["BufferCount"] = CString(pUser->GetBufferCount());
				Tmpl["TimestampFormat"] = pUser->GetTimestampFormat();
				Tmpl["Timezone"] = pUser->GetTimezone();
				Tmpl["JoinTries"] = CString(pUser->JoinTries());
				Tmpl["MaxNetworks"] = CString(pUser->MaxNetworks());

				const set<CString>& ssAllowedHosts = pUser->GetAllowedHosts();
				for (set<CString>::const_iterator it = ssAllowedHosts.begin(); it != ssAllowedHosts.end(); ++it) {
					CTemplate& l = Tmpl.AddRow("AllowedHostLoop");
					l["Host"] = *it;
				}

				const vector<CIRCNetwork*>& vNetworks = pUser->GetNetworks();
				for (unsigned int a = 0; a < vNetworks.size(); a++) {
					CTemplate& l = Tmpl.AddRow("NetworkLoop");
					l["Name"] = vNetworks[a]->GetName();
					l["Username"] = pUser->GetUserName();
					l["Clients"] = CString(vNetworks[a]->GetClients().size());
					l["IRCNick"] = vNetworks[a]->GetIRCNick().GetNick();
					CServer* pServer = vNetworks[a]->GetCurrentServer();
					if (pServer) {
						l["Server"] = pServer->GetName();
					}
				}

				const MCString& msCTCPReplies = pUser->GetCTCPReplies();
				for (MCString::const_iterator it2 = msCTCPReplies.begin(); it2 != msCTCPReplies.end(); ++it2) {
					CTemplate& l = Tmpl.AddRow("CTCPLoop");
					l["CTCP"] = it2->first + " " + it2->second;
				}
			} else {
				Tmpl["Action"] = "adduser";
				Tmpl["Title"] = "Add User";
				Tmpl["StatusPrefix"] = "*";
			}

			SCString ssTimezones = CUtils::GetTimezones();
			for (SCString::iterator i = ssTimezones.begin(); i != ssTimezones.end(); ++i) {
				CTemplate& l = Tmpl.AddRow("TZLoop");
				l["TZ"] = *i;
			}

			// To change BindHosts be admin or don't have DenySetBindHost
			if (spSession->IsAdmin() || !spSession->GetUser()->DenySetBindHost()) {
				Tmpl["BindHostEdit"] = "true";
				const VCString& vsBindHosts = CZNC::Get().GetBindHosts();
				if (vsBindHosts.empty()) {
					if (pUser) {
						Tmpl["BindHost"] = pUser->GetBindHost();
						Tmpl["DCCBindHost"] = pUser->GetDCCBindHost();
					}
				} else {
					bool bFoundBindHost = false;
					bool bFoundDCCBindHost = false;
					for (unsigned int b = 0; b < vsBindHosts.size(); b++) {
						const CString& sBindHost = vsBindHosts[b];
						CTemplate& l = Tmpl.AddRow("BindHostLoop");
						CTemplate& k = Tmpl.AddRow("DCCBindHostLoop");

						l["BindHost"] = sBindHost;
						k["BindHost"] = sBindHost;

						if (pUser && pUser->GetBindHost() == sBindHost) {
							l["Checked"] = "true";
							bFoundBindHost = true;
						}

						if (pUser && pUser->GetDCCBindHost() == sBindHost) {
							k["Checked"] = "true";
							bFoundDCCBindHost = true;
						}
					}

					// If our current bindhost is not in the global list...
					if (pUser && !bFoundBindHost && !pUser->GetBindHost().empty()) {
						CTemplate& l = Tmpl.AddRow("BindHostLoop");

						l["BindHost"] = pUser->GetBindHost();
						l["Checked"] = "true";
					}
					if (pUser && !bFoundDCCBindHost && !pUser->GetDCCBindHost().empty()) {
						CTemplate& l = Tmpl.AddRow("DCCBindHostLoop");

						l["BindHost"] = pUser->GetDCCBindHost();
						l["Checked"] = "true";
					}
				}
			}

			vector<CString> vDirs;
			WebSock.GetAvailSkins(vDirs);

			for (unsigned int d = 0; d < vDirs.size(); d++) {
				const CString& SubDir = vDirs[d];
				CTemplate& l = Tmpl.AddRow("SkinLoop");
				l["Name"] = SubDir;

				if (pUser && SubDir == pUser->GetSkinName()) {
					l["Checked"] = "true";
				}
			}

			set<CModInfo> ssUserMods;
			CZNC::Get().GetModules().GetAvailableMods(ssUserMods);

			for (set<CModInfo>::iterator it = ssUserMods.begin(); it != ssUserMods.end(); ++it) {
				const CModInfo& Info = *it;
				CTemplate& l = Tmpl.AddRow("ModuleLoop");

				l["Name"] = Info.GetName();
				l["Description"] = Info.GetDescription();
				l["Wiki"] = Info.GetWikiPage();
				l["HasArgs"] = CString(Info.GetHasArgs());
				l["ArgsHelpText"] = Info.GetArgsHelpText();

				CModule *pModule = NULL;
				if (pUser)
					pModule = pUser->GetModules().FindModule(Info.GetName());
				if (pModule) {
					l["Checked"] = "true";
					l["Args"] = pModule->GetArgs();
					if (CModInfo::UserModule == GetType() && Info.GetName() == GetModName()) {
						l["Disabled"] = "true";
					}
				}

				if (!spSession->IsAdmin() && pUser && pUser->DenyLoadMod()) {
					l["Disabled"] = "true";
				}
			}

			CTemplate& o1 = Tmpl.AddRow("OptionLoop");
			o1["Name"] = "autoclearchanbuffer";
			o1["DisplayName"] = "Auto Clear Chan Buffer";
			o1["Tooltip"] = "Automatically Clear Channel Buffer After Playback (the default value for new channels)";
			if (!pUser || pUser->AutoClearChanBuffer()) { o1["Checked"] = "true"; }

			/* o2 used to be auto cycle which was removed */

			CTemplate& o4 = Tmpl.AddRow("OptionLoop");
			o4["Name"] = "multiclients";
			o4["DisplayName"] = "Multi Clients";
			if (!pUser || pUser->MultiClients()) { o4["Checked"] = "true"; }

			CTemplate& o7 = Tmpl.AddRow("OptionLoop");
			o7["Name"] = "appendtimestamp";
			o7["DisplayName"] = "Append Timestamps";
			if (pUser && pUser->GetTimestampAppend()) { o7["Checked"] = "true"; }

			CTemplate& o8 = Tmpl.AddRow("OptionLoop");
			o8["Name"] = "prependtimestamp";
			o8["DisplayName"] = "Prepend Timestamps";
			if (pUser && pUser->GetTimestampPrepend()) { o8["Checked"] = "true"; }

			if (spSession->IsAdmin()) {
				CTemplate& o9 = Tmpl.AddRow("OptionLoop");
				o9["Name"] = "denyloadmod";
				o9["DisplayName"] = "Deny LoadMod";
				if (pUser && pUser->DenyLoadMod()) { o9["Checked"] = "true"; }

				CTemplate& o10 = Tmpl.AddRow("OptionLoop");
				o10["Name"] = "isadmin";
				o10["DisplayName"] = "Admin";
				if (pUser && pUser->IsAdmin()) { o10["Checked"] = "true"; }
				if (pUser && pUser == CZNC::Get().FindUser(WebSock.GetUser())) { o10["Disabled"] = "true"; }

				CTemplate& o11 = Tmpl.AddRow("OptionLoop");
				o11["Name"] = "denysetbindhost";
				o11["DisplayName"] = "Deny SetBindHost";
				if (pUser && pUser->DenySetBindHost()) { o11["Checked"] = "true"; }
			}

			FOR_EACH_MODULE(i, pUser) {
				CTemplate& mod = Tmpl.AddRow("EmbeddedModuleLoop");
				mod.insert(Tmpl.begin(), Tmpl.end());
				mod["WebadminAction"] = "display";
				if ((*i)->OnEmbeddedWebRequest(WebSock, "webadmin/user", mod)) {
					mod["Embed"] = WebSock.FindTmpl(*i, "WebadminUser.tmpl");
					mod["ModName"] = (*i)->GetModName();
				}
			}

			return true;
		}

		/* If pUser is NULL, we are adding a user, else we are editing this one */

		CString sUsername = WebSock.GetParam("user");
		if (!pUser && CZNC::Get().FindUser(sUsername)) {
			WebSock.PrintErrorPage("Invalid Submission [User " + sUsername + " already exists]");
			return true;
		}

		CUser* pNewUser = GetNewUser(WebSock, pUser);
		if (!pNewUser) {
			WebSock.PrintErrorPage("Invalid user settings");
			return true;
		}

		CString sErr;
		CString sAction;

		if (!pUser) {
			CString sClone = WebSock.GetParam("clone");
			if (CUser *pCloneUser = CZNC::Get().FindUser(sClone)) {
				pNewUser->CloneNetworks(*pCloneUser);
			}

			// Add User Submission
			if (!CZNC::Get().AddUser(pNewUser, sErr)) {
				delete pNewUser;
				WebSock.PrintErrorPage("Invalid submission [" + sErr + "]");
				return true;
			}

			pUser = pNewUser;
			sAction = "added";
		} else {
			// Edit User Submission
			if (!pUser->Clone(*pNewUser, sErr, false)) {
				delete pNewUser;
				WebSock.PrintErrorPage("Invalid Submission [" + sErr + "]");
				return true;
			}

			delete pNewUser;
			sAction = "edited";
		}

		CTemplate TmplMod;
		TmplMod["Username"] = sUsername;
		TmplMod["WebadminAction"] = "change";
		FOR_EACH_MODULE(it, pUser) {
			(*it)->OnEmbeddedWebRequest(WebSock, "webadmin/user", TmplMod);
		}

		if (!CZNC::Get().WriteConfig()) {
			WebSock.PrintErrorPage("User " + sAction + ", but config was not written");
			return true;
		}

		if (!spSession->IsAdmin()) {
			WebSock.Redirect("edituser");
		} else {
			WebSock.Redirect("listusers");
		}

		/* we don't want the template to be printed while we redirect */
		return false;
	}

Project: znc
File: webadmin.cpp

	CUser* GetNewUser(CWebSock& WebSock, CUser* pUser) {
		CSmartPtr<CWebSession> spSession = WebSock.GetSession();
		CString sUsername = WebSock.GetParam("newuser");

		if (sUsername.empty()) {
			sUsername = WebSock.GetParam("user");
		}

		if (sUsername.empty()) {
			WebSock.PrintErrorPage("Invalid Submission [Username is required]");
			return NULL;
		}

		if (pUser) {
			/* If we are editing a user we must not change the user name */
			sUsername = pUser->GetUserName();
		}

		CString sArg = WebSock.GetParam("password");

		if (sArg != WebSock.GetParam("password2")) {
			WebSock.PrintErrorPage("Invalid Submission [Passwords do not match]");
			return NULL;
		}

		CUser* pNewUser = new CUser(sUsername);

		if (!sArg.empty()) {
			CString sSalt = CUtils::GetSalt();
			CString sHash = CUser::SaltedHash(sArg, sSalt);
			pNewUser->SetPass(sHash, CUser::HASH_DEFAULT, sSalt);
		}

		VCString vsArgs;
		unsigned int a = 0;

		WebSock.GetRawParam("allowedips").Split("\n", vsArgs);
		if (vsArgs.size()) {
			for (a = 0; a < vsArgs.size(); a++) {
				pNewUser->AddAllowedHost(vsArgs[a].Trim_n());
			}
		} else {
			pNewUser->AddAllowedHost("*");
		}

		WebSock.GetRawParam("ctcpreplies").Split("\n", vsArgs);
		for (a = 0; a < vsArgs.size(); a++) {
			CString sReply = vsArgs[a].TrimRight_n("\r");
			pNewUser->AddCTCPReply(sReply.Token(0).Trim_n(), sReply.Token(1, true).Trim_n());
		}

		sArg = WebSock.GetParam("nick"); if (!sArg.empty()) { pNewUser->SetNick(sArg); }
		sArg = WebSock.GetParam("altnick"); if (!sArg.empty()) { pNewUser->SetAltNick(sArg); }
		sArg = WebSock.GetParam("statusprefix"); if (!sArg.empty()) { pNewUser->SetStatusPrefix(sArg); }
		sArg = WebSock.GetParam("ident"); if (!sArg.empty()) { pNewUser->SetIdent(sArg); }
		sArg = WebSock.GetParam("skin"); if (!sArg.empty()) { pNewUser->SetSkinName(sArg); }
		sArg = WebSock.GetParam("realname"); if (!sArg.empty()) { pNewUser->SetRealName(sArg); }
		sArg = WebSock.GetParam("quitmsg"); if (!sArg.empty()) { pNewUser->SetQuitMsg(sArg); }
		sArg = WebSock.GetParam("chanmodes"); if (!sArg.empty()) { pNewUser->SetDefaultChanModes(sArg); }
		sArg = WebSock.GetParam("timestampformat"); if (!sArg.empty()) { pNewUser->SetTimestampFormat(sArg); }

		sArg = WebSock.GetParam("bindhost");
		// To change BindHosts be admin or don't have DenySetBindHost
		if (spSession->IsAdmin() || !spSession->GetUser()->DenySetBindHost()) {
			CString sArg2 = WebSock.GetParam("dccbindhost");
			if (!sArg.empty()) {
				pNewUser->SetBindHost(sArg);
			}
			if (!sArg2.empty()) {
				pNewUser->SetDCCBindHost(sArg2);
			}

			const VCString& vsHosts = CZNC::Get().GetBindHosts();
			if (!spSession->IsAdmin() && !vsHosts.empty()) {
				VCString::const_iterator it;
				bool bFound = false;
				bool bFoundDCC = false;

				for (it = vsHosts.begin(); it != vsHosts.end(); ++it) {
					if (sArg.Equals(*it)) {
						bFound = true;
					}
					if (sArg2.Equals(*it)) {
						bFoundDCC = true;
					}
				}

				if (!bFound) {
					pNewUser->SetBindHost(pUser ? pUser->GetBindHost() : "");
				}
				if (!bFoundDCC) {
					pNewUser->SetDCCBindHost(pUser ? pUser->GetDCCBindHost() : "");
				}
			}
		} else if (pUser){
			pNewUser->SetBindHost(pUser->GetBindHost());
			pNewUser->SetDCCBindHost(pUser->GetDCCBindHost());
		}

		sArg = WebSock.GetParam("bufsize"); if (!sArg.empty()) pNewUser->SetBufferCount(sArg.ToUInt(), spSession->IsAdmin());
		if (!sArg.empty()) {
			// First apply the old limit in case the new one is too high
			if (pUser)
				pNewUser->SetBufferCount(pUser->GetBufferCount(), true);
			pNewUser->SetBufferCount(sArg.ToUInt(), spSession->IsAdmin());
		}

		pNewUser->SetSkinName(WebSock.GetParam("skin"));
		pNewUser->SetAutoClearChanBuffer(WebSock.GetParam("autoclearchanbuffer").ToBool());
		pNewUser->SetMultiClients(WebSock.GetParam("multiclients").ToBool());
		pNewUser->SetTimestampAppend(WebSock.GetParam("appendtimestamp").ToBool());
		pNewUser->SetTimestampPrepend(WebSock.GetParam("prependtimestamp").ToBool());
		pNewUser->SetTimezone(WebSock.GetParam("timezone"));
		pNewUser->SetJoinTries(WebSock.GetParam("jointries").ToUInt());

		if (spSession->IsAdmin()) {
			pNewUser->SetDenyLoadMod(WebSock.GetParam("denyloadmod").ToBool());
			pNewUser->SetDenySetBindHost(WebSock.GetParam("denysetbindhost").ToBool());
			sArg = WebSock.GetParam("maxnetworks"); if (!sArg.empty()) pNewUser->SetMaxNetworks(sArg.ToUInt());
		} else if (pUser) {
			pNewUser->SetDenyLoadMod(pUser->DenyLoadMod());
			pNewUser->SetDenySetBindHost(pUser->DenySetBindHost());
			pNewUser->SetMaxNetworks(pUser->MaxNetworks());
		}

		// If pUser is not NULL, we are editing an existing user.
		// Users must not be able to change their own admin flag.
		if (pUser != CZNC::Get().FindUser(WebSock.GetUser())) {
			pNewUser->SetAdmin(WebSock.GetParam("isadmin").ToBool());
		} else if (pUser) {
			pNewUser->SetAdmin(pUser->IsAdmin());
		}

		if (spSession->IsAdmin() || (pUser && !pUser->DenyLoadMod())) {
			WebSock.GetParamValues("loadmod", vsArgs);

			// disallow unload webadmin from itself
			if (CModInfo::UserModule == GetType() && pUser == CZNC::Get().FindUser(WebSock.GetUser())) {
				bool bLoadedWebadmin = false;
				for (a = 0; a < vsArgs.size(); ++a) {
					CString sModName = vsArgs[a].TrimRight_n("\r");
					if (sModName == GetModName()) {
						bLoadedWebadmin = true;
						break;
					}
				}
				if (!bLoadedWebadmin) {
					vsArgs.push_back(GetModName());
				}
			}

			for (a = 0; a < vsArgs.size(); a++) {
				CString sModRet;
				CString sModName = vsArgs[a].TrimRight_n("\r");
				CString sModLoadError;

				if (!sModName.empty()) {
					CString sArgs = WebSock.GetParam("modargs_" + sModName);

					try {
						if (!pNewUser->GetModules().LoadModule(sModName, sArgs, CModInfo::UserModule, pNewUser, NULL, sModRet)) {
							sModLoadError = "Unable to load module [" + sModName + "] [" + sModRet + "]";
						}
					} catch (...) {
						sModLoadError = "Unable to load module [" + sModName + "] [" + sArgs + "]";
					}

					if (!sModLoadError.empty()) {
						DEBUG(sModLoadError);
						spSession->AddError(sModLoadError);
					}
				}
			}
		} else if (pUser) {
			CModules& Modules = pUser->GetModules();

			for (a = 0; a < Modules.size(); a++) {
				CString sModName = Modules[a]->GetModName();
				CString sArgs = Modules[a]->GetArgs();
				CString sModRet;
				CString sModLoadError;

				try {
					if (!pNewUser->GetModules().LoadModule(sModName, sArgs, CModInfo::UserModule, pNewUser, NULL, sModRet)) {
						sModLoadError = "Unable to load module [" + sModName + "] [" + sModRet + "]";
					}
				} catch (...) {
					sModLoadError = "Unable to load module [" + sModName + "]";
				}

				if (!sModLoadError.empty()) {
					DEBUG(sModLoadError);
					spSession->AddError(sModLoadError);
				}
			}
		}

		return pNewUser;
	}

Project: znc
File: webadmin.cpp

	bool NetworkPage(CWebSock& WebSock, CTemplate& Tmpl, CUser* pUser, CIRCNetwork* pNetwork = NULL) {
		CSmartPtr<CWebSession> spSession = WebSock.GetSession();
		Tmpl.SetFile("add_edit_network.tmpl");

		if (!WebSock.GetParam("submitted").ToUInt()) {
			Tmpl["Username"] = pUser->GetUserName();

			set<CModInfo> ssNetworkMods;
			CZNC::Get().GetModules().GetAvailableMods(ssNetworkMods, CModInfo::NetworkModule);
			for (set<CModInfo>::iterator it = ssNetworkMods.begin(); it != ssNetworkMods.end(); ++it) {
				const CModInfo& Info = *it;
				CTemplate& l = Tmpl.AddRow("ModuleLoop");

				l["Name"] = Info.GetName();
				l["Description"] = Info.GetDescription();
				l["Wiki"] = Info.GetWikiPage();
				l["HasArgs"] = CString(Info.GetHasArgs());
				l["ArgsHelpText"] = Info.GetArgsHelpText();

				if (pNetwork) {
					CModule *pModule = pNetwork->GetModules().FindModule(Info.GetName());
					if (pModule) {
						l["Checked"] = "true";
						l["Args"] = pModule->GetArgs();
					}
				}

				if (!spSession->IsAdmin() && pUser->DenyLoadMod()) {
					l["Disabled"] = "true";
				}
			}

			// To change BindHosts be admin or don't have DenySetBindHost
			if (spSession->IsAdmin() || !spSession->GetUser()->DenySetBindHost()) {
				Tmpl["BindHostEdit"] = "true";
				const VCString& vsBindHosts = CZNC::Get().GetBindHosts();
				if (vsBindHosts.empty()) {
					if (pNetwork) {
						Tmpl["BindHost"] = pNetwork->GetBindHost();
					}
				} else {
					bool bFoundBindHost = false;
					for (unsigned int b = 0; b < vsBindHosts.size(); b++) {
						const CString& sBindHost = vsBindHosts[b];
						CTemplate& l = Tmpl.AddRow("BindHostLoop");

						l["BindHost"] = sBindHost;

						if (pNetwork && pNetwork->GetBindHost() == sBindHost) {
							l["Checked"] = "true";
							bFoundBindHost = true;
						}
					}

					// If our current bindhost is not in the global list...
					if (pNetwork && !bFoundBindHost && !pNetwork->GetBindHost().empty()) {
						CTemplate& l = Tmpl.AddRow("BindHostLoop");

						l["BindHost"] = pNetwork->GetBindHost();
						l["Checked"] = "true";
					}
				}
			}

			if (pNetwork) {
				Tmpl["Action"] = "editnetwork";
				Tmpl["Edit"] = "true";
				Tmpl["Title"] = "Edit Network" + CString(" [" + pNetwork->GetName() + "]") + " of User [" + pUser->GetUserName() + "]";
				Tmpl["Name"] = pNetwork->GetName();

				Tmpl["Nick"] = pNetwork->GetNick();
				Tmpl["AltNick"] = pNetwork->GetAltNick();
				Tmpl["Ident"] = pNetwork->GetIdent();
				Tmpl["RealName"] = pNetwork->GetRealName();

				Tmpl["FloodProtection"] = CString(CIRCSock::IsFloodProtected(pNetwork->GetFloodRate()));
				Tmpl["FloodRate"] = CString(pNetwork->GetFloodRate());
				Tmpl["FloodBurst"] = CString(pNetwork->GetFloodBurst());

				Tmpl["IRCConnectEnabled"] = CString(pNetwork->GetIRCConnectEnabled());

				const vector<CServer*>& vServers = pNetwork->GetServers();
				for (unsigned int a = 0; a < vServers.size(); a++) {
					CTemplate& l = Tmpl.AddRow("ServerLoop");
					l["Server"] = vServers[a]->GetString();
				}

				const vector<CChan*>& Channels = pNetwork->GetChans();
				for (unsigned int c = 0; c < Channels.size(); c++) {
					CChan* pChan = Channels[c];
					CTemplate& l = Tmpl.AddRow("ChannelLoop");

					l["Network"] = pNetwork->GetName();
					l["Username"] = pUser->GetUserName();
					l["Name"] = pChan->GetName();
					l["Perms"] = pChan->GetPermStr();
					l["CurModes"] = pChan->GetModeString();
					l["DefModes"] = pChan->GetDefaultModes();
					l["BufferCount"] = CString(pChan->GetBufferCount());
					l["Options"] = pChan->GetOptions();

					if (pChan->InConfig()) {
						l["InConfig"] = "true";
					}
				}
			} else {
				if (!spSession->IsAdmin() && !pUser->HasSpaceForNewNetwork()) {
					WebSock.PrintErrorPage("Network number limit reached. Ask an admin to increase the limit for you, or delete few old ones from Your Settings");
					return true;
				}

				Tmpl["Action"] = "addnetwork";
				Tmpl["Title"] = "Add Network for User [" + pUser->GetUserName() + "]";
				Tmpl["IRCConnectEnabled"] = "true";
				Tmpl["FloodProtection"] = "true";
				Tmpl["FloodRate"] = "1.0";
				Tmpl["FloodBurst"] = "4";
			}

			FOR_EACH_MODULE(i, make_pair(pUser, pNetwork)) {
				CTemplate& mod = Tmpl.AddRow("EmbeddedModuleLoop");
				mod.insert(Tmpl.begin(), Tmpl.end());
				mod["WebadminAction"] = "display";
				if ((*i)->OnEmbeddedWebRequest(WebSock, "webadmin/network", mod)) {
					mod["Embed"] = WebSock.FindTmpl(*i, "WebadminNetwork.tmpl");
					mod["ModName"] = (*i)->GetModName();
				}
			}

			return true;
		}

		CString sName = WebSock.GetParam("network").Trim_n();
		if (sName.empty()) {
			WebSock.PrintErrorPage("Network name is a required argument");
			return true;
		}

		if (!pNetwork) {
			if (!spSession->IsAdmin() && !pUser->HasSpaceForNewNetwork()) {
				WebSock.PrintErrorPage("Network number limit reached. Ask an admin to increase the limit for you, or delete few old ones from Your Settings");
				return true;
			}
			if (!CIRCNetwork::IsValidNetwork(sName)) {
				WebSock.PrintErrorPage("Network name should be alphanumeric");
				return true;
			}
			pNetwork = pUser->AddNetwork(sName);
			if (!pNetwork) {
				WebSock.PrintErrorPage("Network [" + sName.Token(0) + "] already exists");
				return true;
			}
		}

		CString sArg;

		pNetwork->SetNick(WebSock.GetParam("nick"));
		pNetwork->SetAltNick(WebSock.GetParam("altnick"));
		pNetwork->SetIdent(WebSock.GetParam("ident"));
		pNetwork->SetRealName(WebSock.GetParam("realname"));

		pNetwork->SetIRCConnectEnabled(WebSock.GetParam("doconnect").ToBool());

		sArg = WebSock.GetParam("bindhost");
		// To change BindHosts be admin or don't have DenySetBindHost
		if (spSession->IsAdmin() || !spSession->GetUser()->DenySetBindHost()) {
			CString sHost = WebSock.GetParam("bindhost");
			const VCString& vsHosts = CZNC::Get().GetBindHosts();
			if (!spSession->IsAdmin() && !vsHosts.empty()) {
				VCString::const_iterator it;
				bool bFound = false;

				for (it = vsHosts.begin(); it != vsHosts.end(); ++it) {
					if (sHost.Equals(*it)) {
						bFound = true;
						break;
					}
				}

				if (!bFound) {
					sHost = pNetwork->GetBindHost();
				}
			}
			pNetwork->SetBindHost(sHost);
		}

		if (WebSock.GetParam("floodprotection").ToBool()) {
			pNetwork->SetFloodRate(WebSock.GetParam("floodrate").ToDouble());
			pNetwork->SetFloodBurst(WebSock.GetParam("floodburst").ToUShort());
		} else {
			pNetwork->SetFloodRate(-1);
		}

		VCString vsArgs;

		pNetwork->DelServers();
		WebSock.GetRawParam("servers").Split("\n", vsArgs);
		for (unsigned int a = 0; a < vsArgs.size(); a++) {
			pNetwork->AddServer(vsArgs[a].Trim_n());
		}

		WebSock.GetParamValues("channel", vsArgs);
		for (unsigned int a = 0; a < vsArgs.size(); a++) {
			const CString& sChan = vsArgs[a];
			CChan *pChan = pNetwork->FindChan(sChan.TrimRight_n("\r"));
			if (pChan) {
				pChan->SetInConfig(WebSock.GetParam("save_" + sChan).ToBool());
			}
		}

		set<CString> ssArgs;
		WebSock.GetParamValues("loadmod", ssArgs);
		if (spSession->IsAdmin() || !pUser->DenyLoadMod()) {
			for (set<CString>::iterator it = ssArgs.begin(); it != ssArgs.end(); ++it) {
				CString sModRet;
				CString sModName = (*it).TrimRight_n("\r");
				CString sModLoadError;

				if (!sModName.empty()) {
					CString sArgs = WebSock.GetParam("modargs_" + sModName);

					CModule *pMod = pNetwork->GetModules().FindModule(sModName);

					if (!pMod) {
						if (!pNetwork->GetModules().LoadModule(sModName, sArgs, CModInfo::NetworkModule, pUser, pNetwork, sModRet)) {
							sModLoadError = "Unable to load module [" + sModName + "] [" + sModRet + "]";
						}
					} else if (pMod->GetArgs() != sArgs) {
						if (!pNetwork->GetModules().ReloadModule(sModName, sArgs, pUser, pNetwork, sModRet)) {
							sModLoadError = "Unable to reload module [" + sModName + "] [" + sModRet + "]";
						}
					}

					if (!sModLoadError.empty()) {
						DEBUG(sModLoadError);
						WebSock.GetSession()->AddError(sModLoadError);
					}
				}
			}
		}

		const CModules& vCurMods = pNetwork->GetModules();
		set<CString> ssUnloadMods;

		for (unsigned int a = 0; a < vCurMods.size(); a++) {
			CModule* pCurMod = vCurMods[a];

			if (ssArgs.find(pCurMod->GetModName()) == ssArgs.end() && pCurMod->GetModName() != GetModName()) {
				ssUnloadMods.insert(pCurMod->GetModName());
			}
		}

		for (set<CString>::iterator it2 = ssUnloadMods.begin(); it2 != ssUnloadMods.end(); ++it2) {
			pNetwork->GetModules().UnloadModule(*it2);
		}

		CTemplate TmplMod;
		TmplMod["Username"] = pUser->GetUserName();
		TmplMod["Name"] = pNetwork->GetName();
		TmplMod["WebadminAction"] = "change";
		FOR_EACH_MODULE(it, make_pair(pUser, pNetwork)) {
			(*it)->OnEmbeddedWebRequest(WebSock, "webadmin/network", TmplMod);
		}

		if (!CZNC::Get().WriteConfig()) {
			WebSock.PrintErrorPage("Network added/modified, but config was not written");
			return true;
		}

		WebSock.Redirect("edituser?user=" + pUser->GetUserName().Escape_n(CString::EURL));
		return true;
	}