ircd/client_state.cpp
#include "client_state.hpp"
#include "nickserv.hpp"
#include "irc_cmd.hpp"
#include <assert.h>
#define IRC_MAX_NICK_LEN 16
#define IRC_MAX_AWAY_LEN 128
#define IRC_MAX_REAL_LEN 64
bool ClientState::is_nick(const char* buf) {
/*
* <nick> ::= <letter> { <letter> | <number> | <special> }
* <number> ::= '0' ... '9'
* <special> ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'
* however, we will only support '_' and '-'
*/
size_t len = 0;
const char* p = buf;
if (!is_letter(*p)) {
return false;
}
do {
if (!is_letter(*p) && !is_number(*p) && *p != '_' && *p != '-') {
return false;
}
++p;
++len;
if (len > IRC_MAX_NICK_LEN) {
return false;
}
} while (*p);
if (Command::is_command(buf)) {
return false;
}
return true;
}
std::map<std::string, Client*> ClientState::nicks; // TODO: store direct pointers to client_state_t nicks (need comparison/searching then)
ClientState::ClientState(Client* c):
client(c),
has_pass(false), is_oper(false),
nickserv_reg(false), nickserv_ident(false),
nick(NULL), user(NULL), real(NULL), away(NULL) {
}
ClientState::~ClientState() {
if (nick) {
nicks.erase(std::string(nick));
messages.push((msg_t*)nick);
}
if (user) messages.push((msg_t*)user);
if (real) messages.push((msg_t*)real);
if (away) messages.push((msg_t*)away);
}
Client* ClientState::nick_find(const char* n) {
if (!is_nick(n)) return NULL;
std::map<std::string, Client*>::iterator it = nicks.find(std::string(n));
return (it == nicks.end())? NULL: it->second;
}
bool ClientState::nick_set(const char* n) {
if (!is_nick(n)) {
return false;
}
const std::string nn(n);
NickServ* ns = NickServ::getInst();
if (nick) {
if (nickserv_reg || ns->is_reg(n)) {
return false; // no moving away from registered nick, no switching to
}
}
if (nicks.find(nn) != nicks.end()) {
return false;
}
if (nick) {
nicks.erase(std::string(nick));
messages.push((msg_t*)nick);
}
nick = strcpy((char*)messages.pop(), n);
nicks.insert(std::pair<std::string, Client*>(nn, client));
nickserv_reg = ns->is_reg(nick);
nickserv_ident = false;
return true;
}
bool ClientState::user_set(const char* u, const char* r) {
if (user || real || !u || !r) {
return false;
}
if (!is_nick(u)) {
return false;
}
if (!*r || strlen(r) > IRC_MAX_REAL_LEN) {
return false;
}
user = strcpy((char*)messages.pop(), u);
real = strcpy((char*)messages.pop(), r);
return true;
}
bool ClientState::away_set(const char* a) {
if (a && strlen(a) > IRC_MAX_AWAY_LEN) {
return false;
}
if (away) {
messages.push((msg_t*)away);
}
if (!a || !*a) {
away = NULL;
} else {
away = strcpy((char*)messages.pop(), a);
}
return true;
}
const char* ClientState::prefix_get(bool details, bool reveal, const char* n) const {
static char rv[IRC_MAX_NICK_LEN + 1 + 1 + IRC_MAX_NICK_LEN + 1 + sizeof(ip_str_t) + 1];
assert(nick); // XXX:
if (!nick && !n) {
return "*";
}
if (!details || !user) {
return n ?: nick;
}
strcpy(rv, n ?: nick);
strcat(rv, "!");
if (!nickserv_ident) strcat(rv, "~");
strcat(rv, user);
strcat(rv, "@");
strcat(rv, reveal? client->host: Client::dummy_host);
return rv;
}
const char* ClientState::mode_get() const {
if (is_oper) return "o";
if (nickserv_ident) return "v"; // voice. or h for half-op?
return "";
}
const char* ClientState::state_get() const {
if (is_oper) return "@";
if (nickserv_ident) return "+"; // voice. or % for half-op?
return "";
}