- Nick, AltNick, Ident, RealName can be left empty to use the value from the user.
+ Nick, AltNick, Ident, RealName, BindHost can be left empty to use the value from the user.
IF !Edit ?>
@@ -48,6 +48,21 @@
title="Your real name." />
+ IF BindHostEdit ?>
+
+
BindHost:
+ IF BindHostLoop ?>
+
+ ELSE ?>
+
+ ENDIF ?>
+
+
+ ENDIF ?>
+
Active:
checked="checked" ENDIF ?> />
diff --git a/modules/webadmin.cpp b/modules/webadmin.cpp
index 7a444770..f2e3076e 100644
--- a/modules/webadmin.cpp
+++ b/modules/webadmin.cpp
@@ -684,6 +684,38 @@ public:
}
}
+ // 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";
@@ -767,6 +799,12 @@ public:
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()) {
+ pNetwork->SetBindHost(WebSock.GetParam("bindhost"));
+ }
+
if (WebSock.GetParam("floodprotection").ToBool()) {
pNetwork->SetFloodRate(WebSock.GetParam("floodrate").ToDouble());
pNetwork->SetFloodBurst(WebSock.GetParam("floodburst").ToUInt());
diff --git a/src/Client.cpp b/src/Client.cpp
index b3d03735..20082885 100644
--- a/src/Client.cpp
+++ b/src/Client.cpp
@@ -783,12 +783,12 @@ CString CClient::GetNickMask() const {
return GetIRCSock()->GetNickMask();
}
- CString sHost = m_pUser->GetBindHost();
+ CString sHost = m_pNetwork ? m_pNetwork->GetBindHost() : m_pUser->GetBindHost();
if (sHost.empty()) {
sHost = "irc.znc.in";
}
- return GetNick() + "!" + m_pUser->GetIdent() + "@" + sHost;
+ return GetNick() + "!" + (m_pNetwork ? m_pNetwork->GetBindHost() : m_pUser->GetIdent()) + "@" + sHost;
}
void CClient::RespondCap(const CString& sResponse)
diff --git a/src/ClientCommand.cpp b/src/ClientCommand.cpp
index 9ed513f8..16fb8631 100644
--- a/src/ClientCommand.cpp
+++ b/src/ClientCommand.cpp
@@ -1004,6 +1004,10 @@ void CClient::UserCommand(CString& sLine) {
}
PutStatus(Table);
} else if ((sCommand.Equals("SETBINDHOST") || sCommand.Equals("SETVHOST")) && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
+ if (!m_pNetwork) {
+ PutStatus("You must be connected with a network to use this command. Try SetUserBindHost instead");
+ return;
+ }
CString sHost = sLine.Token(1);
if (sHost.empty()) {
@@ -1011,6 +1015,39 @@ void CClient::UserCommand(CString& sLine) {
return;
}
+ if (sHost.Equals(m_pNetwork->GetBindHost())) {
+ PutStatus("You already have this bind host!");
+ return;
+ }
+
+ const VCString& vsHosts = CZNC::Get().GetBindHosts();
+ if (!m_pUser->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) {
+ PutStatus("You may not use this bind host. See [ListBindHosts] for a list");
+ return;
+ }
+ }
+
+ m_pNetwork->SetBindHost(sHost);
+ PutStatus("Set bind host for network [" + m_pNetwork->GetName() + "] to [" + m_pNetwork->GetBindHost() + "]");
+ } else if (sCommand.Equals("SETUSERBINDHOST") && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
+ CString sHost = sLine.Token(1);
+
+ if (sHost.empty()) {
+ PutStatus("Usage: SetUserBindHost ");
+ return;
+ }
+
if (sHost.Equals(m_pUser->GetBindHost())) {
PutStatus("You already have this bind host!");
return;
@@ -1037,8 +1074,15 @@ void CClient::UserCommand(CString& sLine) {
m_pUser->SetBindHost(sHost);
PutStatus("Set bind host to [" + m_pUser->GetBindHost() + "]");
} else if ((sCommand.Equals("CLEARBINDHOST") || sCommand.Equals("CLEARVHOST")) && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
+ if (!m_pNetwork) {
+ PutStatus("You must be connected with a network to use this command. Try ClearUserBindHost instead");
+ return;
+ }
+ m_pNetwork->SetBindHost("");
+ PutStatus("Bind host cleared");
+ } else if (sCommand.Equals("CLEARUSERBINDHOST") && (m_pUser->IsAdmin() || !m_pUser->DenySetBindHost())) {
m_pUser->SetBindHost("");
- PutStatus("Bind Host Cleared");
+ PutStatus("Bind host cleared");
} else if (sCommand.Equals("PLAYBUFFER")) {
if (!m_pNetwork) {
PutStatus("You must be connected with a network to use this command");
@@ -1419,9 +1463,18 @@ void CClient::HelpUser() {
Table.SetCell("Arguments", "");
Table.SetCell("Description", "Set the bind host for this connection");
+ Table.AddRow();
+ Table.SetCell("Command", "SetUserBindHost");
+ Table.SetCell("Arguments", "");
+ Table.SetCell("Description", "Set the default bind host for this user");
+
Table.AddRow();
Table.SetCell("Command", "ClearBindHost");
Table.SetCell("Description", "Clear the bind host for this connection");
+
+ Table.AddRow();
+ Table.SetCell("Command", "ClearUserBindHost");
+ Table.SetCell("Description", "Clear the default bind host for this user");
}
Table.AddRow();
diff --git a/src/IRCNetwork.cpp b/src/IRCNetwork.cpp
index 18e88e25..9b84367d 100644
--- a/src/IRCNetwork.cpp
+++ b/src/IRCNetwork.cpp
@@ -88,6 +88,7 @@ void CIRCNetwork::Clone(const CIRCNetwork& Network) {
SetAltNick(Network.GetAltNick());
SetIdent(Network.GetIdent());
SetRealName(Network.GetRealName());
+ SetBindHost(Network.GetBindHost());
// Servers
const vector& vServers = Network.GetServers();
@@ -246,7 +247,8 @@ bool CIRCNetwork::ParseConfig(CConfig *pConfig, CString& sError, bool bUpgrade)
{ "nick", &CIRCNetwork::SetNick },
{ "altnick", &CIRCNetwork::SetAltNick },
{ "ident", &CIRCNetwork::SetIdent },
- { "realname", &CIRCNetwork::SetRealName }
+ { "realname", &CIRCNetwork::SetRealName },
+ { "bindhost", &CIRCNetwork::SetBindHost },
};
size_t numStringOptions = sizeof(StringOptions) / sizeof(StringOptions[0]);
TOption BoolOptions[] = {
@@ -380,6 +382,9 @@ CConfig CIRCNetwork::ToConfig() {
if (!m_sRealName.empty()) {
config.AddKeyValuePair("RealName", m_sRealName);
}
+ if (!m_sBindHost.empty()) {
+ config.AddKeyValuePair("BindHost", m_sBindHost);
+ }
config.AddKeyValuePair("IRCConnectEnabled", CString(GetIRCConnectEnabled()));
config.AddKeyValuePair("FloodRate", CString(GetFloodRate()));
@@ -947,7 +952,7 @@ bool CIRCNetwork::Connect() {
);
CString sSockName = "IRC::" + m_pUser->GetUserName() + "::" + m_sName;
- CZNC::Get().GetManager().Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, m_pUser->GetBindHost(), pIRCSock);
+ CZNC::Get().GetManager().Connect(pServer->GetName(), pServer->GetPort(), sSockName, 120, bSSL, GetBindHost(), pIRCSock);
return true;
}
@@ -1034,6 +1039,14 @@ const CString& CIRCNetwork::GetRealName() const {
return m_sRealName;
}
+const CString& CIRCNetwork::GetBindHost() const {
+ if (m_sBindHost.empty()) {
+ return m_pUser->GetBindHost();
+ }
+
+ return m_sBindHost;
+}
+
void CIRCNetwork::SetNick(const CString& s) {
if (m_pUser->GetNick().Equals(s)) {
m_sNick = "";
@@ -1066,6 +1079,14 @@ void CIRCNetwork::SetRealName(const CString& s) {
}
}
+void CIRCNetwork::SetBindHost(const CString& s) {
+ if (m_pUser->GetBindHost().Equals(s)) {
+ m_sBindHost = "";
+ } else {
+ m_sBindHost = s;
+ }
+}
+
CString CIRCNetwork::ExpandString(const CString& sStr) const {
CString sRet;
return ExpandString(sStr, sRet);
@@ -1078,6 +1099,7 @@ CString& CIRCNetwork::ExpandString(const CString& sStr, CString& sRet) const {
sRet.Replace("%altnick%", GetAltNick());
sRet.Replace("%ident%", GetIdent());
sRet.Replace("%realname%", GetRealName());
+ sRet.Replace("%bindhost%", GetBindHost());
return m_pUser->ExpandString(sRet, sRet);
}
diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp
index 37cef69f..279a187a 100644
--- a/src/IRCSock.cpp
+++ b/src/IRCSock.cpp
@@ -54,7 +54,7 @@ CIRCSock::CIRCSock(CIRCNetwork* pNetwork) : CZNCSock() {
m_iSendsAllowed = m_uFloodBurst;
EnableReadLine();
m_Nick.SetIdent(m_pNetwork->GetIdent());
- m_Nick.SetHost(m_pNetwork->GetUser()->GetBindHost());
+ m_Nick.SetHost(m_pNetwork->GetBindHost());
m_uMaxNickLen = 9;
m_uCapPaused = 0;
diff --git a/src/Socket.cpp b/src/Socket.cpp
index 457b4b34..54591b8d 100644
--- a/src/Socket.cpp
+++ b/src/Socket.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include
@@ -248,7 +249,7 @@ void CSockManager::SetTDNSThreadFinished(TDNSTask* task, bool bBind, addrinfo* a
aiTarget = aiTarget4;
#endif
} else if (!aiBind4 && !aiBind6) {
- throw "Can't resolve bind hostname. Try /znc clearbindhost";
+ throw "Can't resolve bind hostname. Try /znc clearbindhost and /znc clearuserbindhost";
} else if (aiBind6 && aiTarget6) {
aiTarget = aiTarget6;
aiBind = aiBind6;
@@ -457,7 +458,12 @@ bool CSocket::Connect(const CString& sHostname, unsigned short uPort, bool bSSL,
if (pUser) {
sSockName += "::" + pUser->GetUserName();
- sBindHost = m_pModule->GetUser()->GetBindHost();
+ sBindHost = pUser->GetBindHost();
+ CIRCNetwork* pNetwork = m_pModule->GetNetwork();
+ if (pNetwork) {
+ sSockName += "::" + pNetwork->GetName();
+ sBindHost = pNetwork->GetBindHost();
+ }
}
// Don't overwrite the socket name if one is already set