Allow clients to specify an ID via PASS or USER

- PASS [user[@identifier][/network]:]password
- USER user[@identifier][/network] ...

NOTE: There's a slight ambiguosity with the '@' character, which happens
to be a valid character in usernames, but also acts as a marker for the
identifier. Therefore, '@' is considered as part of the username if it's
followed by non-word characters (as in an email address), otherwise as
a marker for an identifier.

This is only an enabler for #343. The rest can be done with modules:
- managing client ID specific playback buffers
- filtering channels based on the client ID

The reason this should be part of ZNC core is that only global modules
have access to OnUnknownUserRaw(), which is needed to capture USER/PASS.
First of all, the aforementioned modules shouldn't be global. Furthermore,
it would be possible to have only one module that parsed and removed the
client ID so that ZNC core woulnd't choke.
This commit is contained in:
J-P Nurmi
2014-09-28 01:17:03 +02:00
parent 5365dc462c
commit 8d77faa260
4 changed files with 145 additions and 21 deletions

View File

@@ -118,20 +118,7 @@ void CClient::ReadLine(const CString& sData) {
m_bGotPass = true;
CString sAuthLine = sLine.Token(1, true).TrimPrefix_n();
// [user[/network]:]password
if (sAuthLine.find(":") == CString::npos) {
m_sPass = sAuthLine;
sAuthLine = "";
} else {
m_sPass = sAuthLine.Token(1, true, ":");
sAuthLine = sAuthLine.Token(0, false, ":");
}
if (!sAuthLine.empty()) {
m_sUser = sAuthLine.Token(0, false, "/");
m_sNetwork = sAuthLine.Token(1, true, "/");
}
ParsePass(sAuthLine);
AuthUser();
return; // Don't forward this msg. ZNC has already registered us.
@@ -144,12 +131,10 @@ void CClient::ReadLine(const CString& sData) {
AuthUser();
return; // Don't forward this msg. ZNC will handle nick changes until auth is complete
} else if (sCommand.Equals("USER")) {
// user[/network]
CString sAuthLine = sLine.Token(1);
if (m_sUser.empty() && !sAuthLine.empty()) {
m_sUser = sAuthLine.Token(0, false, "/");
m_sNetwork = sAuthLine.Token(1, true, "/");
ParseUser(sAuthLine);
}
m_bGotUser = true;
@@ -771,9 +756,12 @@ void CClient::PutIRC(const CString& sLine) {
CString CClient::GetFullName() const {
if (!m_pUser)
return GetRemoteIP();
if (!m_pNetwork)
return m_pUser->GetUserName();
return m_pUser->GetUserName() + "/" + m_pNetwork->GetName();
CString sFullName = m_pUser->GetUserName();
if (!m_sIdentifier.empty())
sFullName += "@" + m_sIdentifier;
if (m_pNetwork)
sFullName += "/" + m_pNetwork->GetName();
return sFullName;
}
void CClient::PutClient(const CString& sLine) {
@@ -849,6 +837,25 @@ CString CClient::GetNickMask() const {
return GetNick() + "!" + (m_pNetwork ? m_pNetwork->GetBindHost() : m_pUser->GetIdent()) + "@" + sHost;
}
bool CClient::IsValidIdentifier(const CString& sIdentifier) {
// ^[-\w]+$
if (sIdentifier.empty()) {
return false;
}
const char *p = sIdentifier.c_str();
while (*p) {
if (*p != '_' && *p != '-' && !isalnum(*p)) {
return false;
}
p++;
}
return true;
}
void CClient::RespondCap(const CString& sResponse)
{
PutClient(":irc.znc.in CAP " + GetNick() + " " + sResponse);
@@ -971,3 +978,47 @@ void CClient::HandleCap(const CString& sLine)
PutClient(":irc.znc.in 410 " + GetNick() + " " + sSubCmd + " :Invalid CAP subcommand");
}
}
void CClient::ParsePass(const CString& sAuthLine) {
// [user[@identifier][/network]:]password
const size_t uColon = sAuthLine.find(":");
if (uColon != CString::npos) {
m_sPass = sAuthLine.substr(uColon + 1);
ParseUser(sAuthLine.substr(0, uColon));
} else {
m_sPass = sAuthLine;
}
}
void CClient::ParseUser(const CString& sAuthLine) {
// user[@identifier][/network]
const size_t uSlash = sAuthLine.rfind("/");
if (uSlash != CString::npos) {
m_sNetwork = sAuthLine.substr(uSlash + 1);
ParseIdentifier(sAuthLine.substr(0, uSlash));
} else {
ParseIdentifier(sAuthLine);
}
}
void CClient::ParseIdentifier(const CString& sAuthLine) {
// user[@identifier]
const size_t uAt = sAuthLine.rfind("@");
if (uAt != CString::npos) {
const CString sId = sAuthLine.substr(uAt + 1);
if (IsValidIdentifier(sId)) {
m_sIdentifier = sId;
m_sUser = sAuthLine.substr(0, uAt);
} else {
m_sUser = sAuthLine;
}
} else {
m_sUser = sAuthLine;
}
}