CmdResult CommandAddLine::Handle(User* usr, std::vector<std::string>& params) { XLineFactory* xlf = ServerInstance->XLines->GetFactory(params[0]); const std::string& setter = usr->nick; if (!xlf) { ServerInstance->SNO->WriteToSnoMask('d',"%s sent me an unknown ADDLINE type (%s).",setter.c_str(),params[0].c_str()); return CMD_FAILURE; } XLine* xl = NULL; try { xl = xlf->Generate(ServerInstance->Time(), ConvToInt(params[4]), params[2], params[5], params[1]); } catch (ModuleException &e) { ServerInstance->SNO->WriteToSnoMask('d',"Unable to ADDLINE type %s from %s: %s", params[0].c_str(), setter.c_str(), e.GetReason().c_str()); return CMD_FAILURE; } xl->SetCreateTime(ConvToInt(params[3])); if (ServerInstance->XLines->AddLine(xl, NULL)) { if (xl->duration) { std::string timestr = InspIRCd::TimeString(xl->expiry); ServerInstance->SNO->WriteToSnoMask('X',"%s added %s%s on %s to expire on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "", params[1].c_str(), timestr.c_str(), params[5].c_str()); } else { ServerInstance->SNO->WriteToSnoMask('X',"%s added permanent %s%s on %s: %s",setter.c_str(),params[0].c_str(),params[0].length() == 1 ? "-line" : "", params[1].c_str(),params[5].c_str()); } TreeServer* remoteserver = TreeServer::Get(usr); if (!remoteserver->IsBursting()) { ServerInstance->XLines->ApplyLines(); } return CMD_SUCCESS; } else { delete xl; return CMD_FAILURE; } }
time_t ServerCommand::ExtractTS(const std::string& tsstr) { time_t TS = ConvToInt(tsstr); if (!TS) throw ProtocolException("Invalid TS"); return TS; }
void unserialize(SerializeFormat format, Extensible* container, const std::string& value) { if (format == FORMAT_NETWORK) return; callerid_data* dat = new callerid_data; irc::commasepstream s(value); std::string tok; if (s.GetToken(tok)) dat->lastnotify = ConvToInt(tok); while (s.GetToken(tok)) { User *u = ServerInstance->FindNick(tok); if ((u) && (u->registered == REG_ALL) && (!u->quitting)) { if (dat->accepting.insert(u).second) { callerid_data* other = this->get(u, true); other->wholistsme.push_back(dat); } } } void* old = set_raw(container, dat); if (old) this->free(old); }
bool irc::tokenstream::GetToken(long &token) { std::string tok; bool returnval = GetToken(tok); token = ConvToInt(tok); return returnval; }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos)) { source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str()); return MODEACTION_DENY; } /* Set up the flood parameters for this channel */ unsigned int njoins = ConvToInt(parameter.substr(0, colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if ((njoins<1) || (nsecs<1)) { source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str()); return MODEACTION_DENY; } joinfloodsettings jfs(nsecs, njoins); joinfloodsettings* f = ext.get(channel); if ((f) && (*f == jfs)) // mode params match return MODEACTION_DENY; ext.set(channel, jfs); parameter = ConvToStr(njoins) + ":" + ConvToStr(nsecs); channel->SetModeParam(this, parameter); return MODEACTION_ALLOW; } else { if (!channel->IsModeSet(this)) return MODEACTION_DENY; joinfloodsettings* f = ext.get(channel); if (f) { ext.unset(channel); channel->SetModeParam(this, ""); return MODEACTION_ALLOW; } } return MODEACTION_DENY; }
ModeAction ModeChannelLimit::OnSet(User* user, Channel* chan, std::string& parameter) { int limit = ConvToInt(parameter); if (limit < 0) return MODEACTION_DENY; ext.set(chan, limit); return MODEACTION_ALLOW; }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if (colon == std::string::npos || parameter.find('-') != std::string::npos) { source->WriteNumeric(608, "%s %s :Invalid slowmode parameter", source->nick.c_str(),channel->name.c_str()); return MODEACTION_DENY; } /* Set up the slowmode parameters for this channel */ unsigned int nlines = ConvToInt(parameter.substr(0, colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if ((nlines < 2) || nsecs < 1) { source->WriteNumeric(608, "%s %s :Invalid slowmode parameter", source->nick.c_str(), channel->name.c_str()); return MODEACTION_DENY; } slowmodesettings* f = ext.get(channel); if (f && nlines == f->lines && nsecs == f->secs) // mode params match return MODEACTION_DENY; ext.set(channel, new slowmodesettings(nlines, nsecs)); parameter = ConvToStr(nlines) + ":" + ConvToStr(nsecs); channel->SetModeParam(GetModeChar(), parameter); return MODEACTION_ALLOW; } else { if (!channel->IsModeSet(GetModeChar())) return MODEACTION_DENY; ext.unset(channel); channel->SetModeParam(GetModeChar(), ""); return MODEACTION_ALLOW; } }
ModeAction OnSet(User* source, Channel* channel, std::string& parameter) { int v = ConvToInt(parameter); if (v <= 0) return MODEACTION_DENY; if ((IS_LOCAL(source) && ((unsigned int)v > max))) v = max; ext.set(channel, new KickRejoinData(v)); return MODEACTION_ALLOW; }
ModeAction OnSet(User* source, Channel* channel, std::string& parameter) { std::string::size_type colon = parameter.find(':'); if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos)) { source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str()); return MODEACTION_DENY; } /* Set up the flood parameters for this channel */ unsigned int njoins = ConvToInt(parameter.substr(0, colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if ((njoins<1) || (nsecs<1)) { source->WriteNumeric(608, "%s :Invalid flood parameter",channel->name.c_str()); return MODEACTION_DENY; } ext.set(channel, new joinfloodsettings(nsecs, njoins)); return MODEACTION_ALLOW; }
/** FMODE command - server mode with timestamp checks */ CmdResult CommandFMode::Handle(User* who, std::vector<std::string>& params) { time_t TS = ConvToInt(params[1]); if (!TS) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "*** BUG? *** TS of 0 sent to FMODE. Are some services authors smoking craq, or is it 1970 again?. Dropping link."); ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FMODE with a TS of zero. Total craq, dropping link.", who->server->GetName().c_str()); return CMD_INVALID; } /* Extract the TS value of the object, either User or Channel */ time_t ourTS; if (params[0][0] == '#') { Channel* chan = ServerInstance->FindChan(params[0]); if (!chan) /* Oops, channel doesn't exist! */ return CMD_FAILURE; ourTS = chan->age; } else { User* user = ServerInstance->FindUUID(params[0]); if (!user) return CMD_FAILURE; if (IS_SERVER(user)) return CMD_INVALID; ourTS = user->age; } /* If the TS is greater than ours, we drop the mode and don't pass it anywhere. */ if (TS > ourTS) return CMD_FAILURE; /* TS is equal or less: Merge the mode changes into ours and pass on. */ std::vector<std::string> modelist; modelist.reserve(params.size()-1); /* Insert everything into modelist except the TS (params[1]) */ modelist.push_back(params[0]); modelist.insert(modelist.end(), params.begin()+2, params.end()); ModeParser::ModeProcessFlag flags = ModeParser::MODE_LOCALONLY; if ((TS == ourTS) && IS_SERVER(who)) flags |= ModeParser::MODE_MERGE; ServerInstance->Modes->Process(modelist, who, flags); return CMD_SUCCESS; }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if ((colon == std::string::npos) || (parameter.find('-') != std::string::npos)) { source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str()); return MODEACTION_DENY; } /* Set up the flood parameters for this channel */ bool ban = (parameter[0] == '*'); unsigned int nlines = ConvToInt(parameter.substr(ban ? 1 : 0, ban ? colon-1 : colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if ((nlines<2) || (nsecs<1)) { source->WriteNumeric(608, "%s :Invalid flood parameter", channel->name.c_str()); return MODEACTION_DENY; } floodsettings* f = ext.get(channel); if ((f) && (nlines == f->lines) && (nsecs == f->secs) && (ban == f->ban)) // mode params match return MODEACTION_DENY; ext.set(channel, new floodsettings(ban, nsecs, nlines)); parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" + ConvToStr(nsecs); return MODEACTION_ALLOW; } else { if (!channel->IsModeSet(this)) return MODEACTION_DENY; ext.unset(channel); return MODEACTION_ALLOW; } }
CmdResult Handle(const std::vector<std::string> ¶meters, User *user) { if (!user->server->IsULine()) { // Ulines only return CMD_FAILURE; } Channel* chan = ServerInstance->FindChan(parameters[0]); if (!chan) return CMD_FAILURE; if (parameters.size() == 4) { // 4 parameter version, set all topic data on the channel to the ones given in the parameters time_t topicts = ConvToInt(parameters[1]); if (!topicts) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Received SVSTOPIC with a 0 topicts, dropped."); return CMD_INVALID; } std::string newtopic; newtopic.assign(parameters[3], 0, ServerInstance->Config->Limits.MaxTopic); bool topics_differ = (chan->topic != newtopic); if ((topics_differ) || (chan->topicset != topicts) || (chan->setby != parameters[2])) { // Update when any parameter differs chan->topicset = topicts; chan->setby.assign(parameters[2], 0, 127); chan->topic = newtopic; // Send TOPIC to clients only if the actual topic has changed, be silent otherwise if (topics_differ) chan->WriteChannel(user, "TOPIC %s :%s", chan->name.c_str(), chan->topic.c_str()); } } else { // 1 parameter version, nuke the topic bool topic_empty = chan->topic.empty(); if (!topic_empty || !chan->setby.empty()) { chan->topicset = 0; chan->setby.clear(); chan->topic.clear(); if (!topic_empty) chan->WriteChannel(user, "TOPIC %s :", chan->name.c_str()); } } return CMD_SUCCESS; }
void OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) { if (memb->chan->IsModeSet(&kr) && (source != memb->user)) { delaylist* dl = kr.ext.get(memb->chan); if (!dl) { dl = new delaylist; kr.ext.set(memb->chan, dl); } (*dl)[memb->user] = ServerInstance->Time() + ConvToInt(memb->chan->GetModeParameter(&kr)); } }
ModeAction OnModeChange(User* source, User* dest, Channel* chan, std::string& parameter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if (colon == std::string::npos || parameter.find('-') != std::string::npos) { source->WriteNumeric(608, "%s %s :Invalid join/part spam parameter", source->nick.c_str(), chan->name.c_str()); return MODEACTION_DENY; } unsigned int ncycles = ConvToInt(parameter.substr(0, colon)); unsigned int nsecs = ConvToInt(parameter.substr(colon+1)); if (ncycles < 2 || nsecs < 1) { source->WriteNumeric(608, "%s %s :Invalid join/part spam parameter", source->nick.c_str(), chan->name.c_str()); return MODEACTION_DENY; } joinpartspamsettings* jpss = ext.get(chan); if (jpss && ncycles == jpss->cycles && nsecs == jpss->secs) return MODEACTION_DENY; ext.set(chan, new joinpartspamsettings(ncycles, nsecs)); parameter = ConvToStr(ncycles) + ":" + ConvToStr(nsecs); chan->SetModeParam(GetModeChar(), parameter); return MODEACTION_ALLOW; } else { if (!chan->IsModeSet(GetModeChar())) return MODEACTION_DENY; ext.unset(chan); chan->SetModeParam(GetModeChar(), ""); return MODEACTION_ALLOW; } }
CmdResult CommandMetadata::Handle(const std::vector<std::string>& params, User *srcuser) { if (params[0] == "*") { std::string value = params.size() < 3 ? "" : params[2]; FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(NULL,params[1],value)); return CMD_SUCCESS; } if (params[0][0] == '#') { // Channel METADATA has an additional parameter: the channel TS // :22D METADATA #channel 12345 extname :extdata if (params.size() < 3) return CMD_INVALID; Channel* c = ServerInstance->FindChan(params[0]); if (!c) return CMD_FAILURE; time_t ChanTS = ConvToInt(params[1]); if (!ChanTS) return CMD_INVALID; if (c->age < ChanTS) // Their TS is newer than ours, discard this command and do not propagate return CMD_FAILURE; std::string value = params.size() < 4 ? "" : params[3]; ExtensionItem* item = ServerInstance->Extensions.GetItem(params[2]); if (item) item->unserialize(FORMAT_NETWORK, c, value); FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(c,params[2],value)); } else { User* u = ServerInstance->FindUUID(params[0]); if ((u) && (!IS_SERVER(u))) { ExtensionItem* item = ServerInstance->Extensions.GetItem(params[1]); std::string value = params.size() < 3 ? "" : params[2]; if (item) item->unserialize(FORMAT_NETWORK, u, value); FOREACH_MOD(I_OnDecodeMetaData,OnDecodeMetaData(u,params[1],value)); } } return CMD_SUCCESS; }
CmdResult CommandWhois::HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target) { if (parameters.size() < 2) return CMD_FAILURE; User* user = ServerInstance->FindUUID(parameters[0]); if (!user) return CMD_FAILURE; unsigned long idle = ConvToInt(parameters.back()); DoWhois(user, target, target->signon, idle); return CMD_SUCCESS; }
PBKDF2Hash(const std::string& data) { irc::sepstream ss(data, ':'); std::string tok; ss.GetToken(tok); this->iterations = ConvToInt(tok); ss.GetToken(tok); this->hash = Base64ToBin(tok); ss.GetToken(tok); this->salt = Base64ToBin(tok); this->length = this->hash.length(); }
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { if (adding) { std::string::size_type colon = parameter.find(':'); if (colon == std::string::npos) return MODEACTION_DENY; std::string duration = parameter.substr(colon+1); if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration)))) return MODEACTION_DENY; unsigned int len = ConvToInt(parameter.substr(0, colon)); int time = InspIRCd::Duration(duration); if (len == 0 || time < 0) return MODEACTION_DENY; if (len > maxlines && IS_LOCAL(source)) return MODEACTION_DENY; if (len > maxlines) len = maxlines; if (parameter == channel->GetModeParameter(this)) return MODEACTION_DENY; HistoryList* history = ext.get(channel); if (history) { // Shrink the list if the new line number limit is lower than the old one if (len < history->lines.size()) history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len)); history->maxlen = len; history->maxtime = time; } else { ext.set(channel, new HistoryList(len, time)); } } else { if (!channel->IsModeSet(this)) return MODEACTION_DENY; ext.unset(channel); } return MODEACTION_ALLOW; }
irc::sockets::cidr_mask::cidr_mask(const std::string& mask) { std::string::size_type bits_chars = mask.rfind('/'); irc::sockets::sockaddrs sa; if (bits_chars == std::string::npos) { irc::sockets::aptosa(mask, 0, sa); sa2cidr(*this, sa, 128); } else { int range = ConvToInt(mask.substr(bits_chars + 1)); irc::sockets::aptosa(mask.substr(0, bits_chars), 0, sa); sa2cidr(*this, sa, range); } }
CmdResult CommandNum::HandleServer(TreeServer* server, std::vector<std::string>& params) { User* const target = ServerInstance->FindUUID(params[1]); if (!target) return CMD_FAILURE; LocalUser* const localtarget = IS_LOCAL(target); if (!localtarget) return CMD_SUCCESS; Numeric::Numeric numeric(ConvToInt(params[2])); // Passing NULL is ok, in that case the numeric source becomes this server numeric.SetServer(Utils->FindServerID(params[0])); numeric.GetParams().insert(numeric.GetParams().end(), params.begin()+3, params.end()); localtarget->WriteNumeric(numeric); return CMD_SUCCESS; }
CmdResult CommandAway::Handle(User* u, std::vector<std::string>& params) { if (IS_SERVER(u)) return CMD_INVALID; if (params.size()) { FOREACH_MOD(OnSetAway, (u, params[params.size() - 1])); if (params.size() > 1) u->awaytime = ConvToInt(params[0]); else u->awaytime = ServerInstance->Time(); u->awaymsg = params[params.size() - 1]; } else { FOREACH_MOD(OnSetAway, (u, "")); u->awaymsg.clear(); } return CMD_SUCCESS; }
CmdResult CommandIJoin::HandleRemote(RemoteUser* user, std::vector<std::string>& params) { Channel* chan = ServerInstance->FindChan(params[0]); if (!chan) { // Desync detected, recover // Ignore the join and send RESYNC, this will result in the remote server sending all channel data to us ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Received IJOIN for non-existant channel: " + params[0]); CmdBuilder("RESYNC").push(params[0]).Unicast(user); return CMD_FAILURE; } bool apply_modes; if (params.size() > 1) { time_t RemoteTS = ConvToInt(params[1]); if (!RemoteTS) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Invalid TS in IJOIN: " + params[1]); return CMD_INVALID; } if (RemoteTS < chan->age) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Attempted to lower TS via IJOIN. Channel=" + params[0] + " RemoteTS=" + params[1] + " LocalTS=" + ConvToStr(chan->age)); return CMD_INVALID; } apply_modes = ((params.size() > 2) && (RemoteTS == chan->age)); } else apply_modes = false; chan->ForceJoin(user, apply_modes ? ¶ms[2] : NULL); return CMD_SUCCESS; }
CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::string>& params) { /** Do we have enough parameters: * 0 1 2 3 4 5 6 7 8 9 (n-1) * UID uuid age nick host dhost ident ip.string signon +modes (modepara) :gecos */ time_t age_t = ConvToInt(params[1]); time_t signon = ConvToInt(params[7]); std::string empty; const std::string& modestr = params[8]; /* Is this a valid UID, and not misrouted? */ if (params[0].length() != UIDGenerator::UUID_LENGTH || params[0].substr(0, 3) != remoteserver->GetID()) return CMD_INVALID; /* Check parameters for validity before introducing the client, discovered by dmb */ if (!age_t) return CMD_INVALID; if (!signon) return CMD_INVALID; if (modestr[0] != '+') return CMD_INVALID; /* check for collision */ user_hash::iterator iter = ServerInstance->Users->clientlist->find(params[2]); if (iter != ServerInstance->Users->clientlist->end()) { /* * Nick collision. */ int collide = Utils->DoCollision(iter->second, remoteserver, age_t, params[5], params[6], params[0]); ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "*** Collision on %s, collide=%d", params[2].c_str(), collide); if (collide != 1) { // Remote client lost, make sure we change their nick for the hash too params[2] = params[0]; } } /* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically * sets it up in the UUID hash for us. */ User* _new = NULL; try { _new = new RemoteUser(params[0], remoteserver); } catch (...) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str()); return CMD_INVALID; } (*(ServerInstance->Users->clientlist))[params[2]] = _new; _new->nick = params[2]; _new->host = params[3]; _new->dhost = params[4]; _new->ident = params[5]; _new->fullname = params[params.size() - 1]; _new->registered = REG_ALL; _new->signon = signon; _new->age = age_t; unsigned int paramptr = 9; for (std::string::const_iterator v = modestr.begin(); v != modestr.end(); ++v) { // Accept more '+' chars, for now if (*v == '+') continue; /* For each mode thats set, find the mode handler and set it on the new user */ ModeHandler* mh = ServerInstance->Modes->FindMode(*v, MODETYPE_USER); if (!mh) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Unrecognised mode '%c' for a user in UID, dropping link", *v); return CMD_INVALID; } if (mh->GetNumParams(true)) { if (paramptr >= params.size() - 1) return CMD_INVALID; std::string mp = params[paramptr++]; /* IMPORTANT NOTE: * All modes are assumed to succeed here as they are being set by a remote server. * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important * to note as all but one modules currently cannot ever fail in this situation, except for * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE * but here, at client introduction. You may safely assume this behaviour is standard and * will not change in future versions if you want to make use of this protective behaviour * yourself. */ mh->OnModeChange(_new, _new, NULL, mp, true); } else mh->OnModeChange(_new, _new, NULL, empty, true); _new->SetMode(mh, true); } _new->SetClientIP(params[6].c_str()); ServerInstance->Users->AddGlobalClone(_new); remoteserver->UserCount++; bool dosend = true; if ((Utils->quiet_bursts && remoteserver->bursting) || _new->server->IsSilentULine()) dosend = false; if (dosend) ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", remoteserver->GetName().c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString().c_str(), _new->fullname.c_str()); FOREACH_MOD(OnPostConnect, (_new)); return CMD_SUCCESS; }
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *srcuser) { SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils; /* 1.1+ FJOIN works as follows: * * Each FJOIN is sent along with a timestamp, and the side with the lowest * timestamp 'wins'. From this point on we will refer to this side as the * winner. The side with the higher timestamp loses, from this point on we * will call this side the loser or losing side. This should be familiar to * anyone who's dealt with dreamforge or TS6 before. * * When two sides of a split heal and this occurs, the following things * will happen: * * If the timestamps are exactly equal, both sides merge their privilages * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been * re-created during a split, this is safe to do. * * If the timestamps are NOT equal, the losing side removes all of its * modes from the channel, before introducing new users into the channel * which are listed in the FJOIN command's parameters. The losing side then * LOWERS its timestamp value of the channel to match that of the winning * side, and the modes of the users of the winning side are merged in with * the losing side. * * The winning side on the other hand will ignore all user modes from the * losing side, so only its own modes get applied. Life is simple for those * who succeed at internets. :-) * * Syntax: * :<sid> FJOIN <chan> <TS> <modes> :[[modes,]<uuid> [[modes,]<uuid> ... ]] * The last parameter is a list consisting of zero or more (modelist, uuid) * pairs (permanent channels may have zero users). The mode list for each * user is a concatenation of the mode letters the user has on the channel * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters * are not important but if a server ecounters an unknown mode letter, it will * drop the link to avoid desync. * * InspIRCd 2.0 and older required a comma before the uuid even if the user * had no prefix modes on the channel, InspIRCd 2.2 and later does not require * a comma in this case anymore. * */ time_t TS = ConvToInt(params[1]); if (!TS) { ServerInstance->Logs->Log("m_spanningtree",LOG_DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped."); ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str()); return CMD_INVALID; } const std::string& channel = params[0]; Channel* chan = ServerInstance->FindChan(channel); bool apply_other_sides_modes = true; if (!chan) { chan = new Channel(channel, TS); } else { time_t ourTS = chan->age; if (TS != ourTS) { ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %lu", chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (unsigned long)(ourTS - TS)); /* If our TS is less than theirs, we dont accept their modes */ if (ourTS < TS) { apply_other_sides_modes = false; } else if (ourTS > TS) { /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */ if (Utils->AnnounceTSChange) chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS); // while the name is equal in case-insensitive compare, it might differ in case; use the remote version chan->name = channel; chan->age = TS; chan->ClearInvites(); CommandFJoin::RemoveStatus(chan); // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it. // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then // deleted later) as soon as the permchan mode is removed from them. if (ServerInstance->FindChan(channel) == NULL) { chan = new Channel(channel, TS); } } } } /* First up, apply their channel modes if they won the TS war */ if (apply_other_sides_modes) { std::vector<std::string> modelist; modelist.push_back(channel); /* Remember, params[params.size() - 1] is userlist, and we don't want to apply *that* */ modelist.insert(modelist.end(), params.begin()+2, params.end()-1); ServerInstance->SendMode(modelist, srcuser); } irc::modestacker modestack(true); TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetRoute()->GetSocket(); /* Now, process every 'modes,uuid' pair */ irc::tokenstream users(*params.rbegin()); std::string item; irc::modestacker* modestackptr = (apply_other_sides_modes ? &modestack : NULL); while (users.GetToken(item)) { if (!ProcessModeUUIDPair(item, src_socket, chan, modestackptr)) return CMD_INVALID; } /* Flush mode stacker if we lost the FJOIN or had equal TS */ if (apply_other_sides_modes) CommandFJoin::ApplyModeStack(srcuser, chan, modestack); return CMD_SUCCESS; }
/** FTOPIC command */ CmdResult CommandFTopic::Handle(User* user, std::vector<std::string>& params) { Channel* c = ServerInstance->FindChan(params[0]); if (!c) return CMD_FAILURE; time_t ChanTS = ConvToInt(params[1]); if (!ChanTS) return CMD_INVALID; if (c->age < ChanTS) // Our channel TS is older, nothing to do return CMD_FAILURE; time_t ts = ConvToInt(params[2]); if (!ts) return CMD_INVALID; // Channel::topicset is initialized to 0 on channel creation, so their ts will always win if we never had a topic if (ts < c->topicset) return CMD_FAILURE; // The topic text is always the last parameter const std::string& newtopic = params.back(); // If there is a setter in the message use that, otherwise use the message source const std::string& setter = ((params.size() > 4) ? params[3] : (ServerInstance->Config->FullHostInTopic ? user->GetFullHost() : user->nick)); /* * If the topics were updated at the exact same second, accept * the remote only when it's "bigger" than ours as defined by * string comparision, so non-empty topics always overridde * empty topics if their timestamps are equal * * Similarly, if the topic texts are equal too, keep one topic * setter and discard the other */ if (ts == c->topicset) { // Discard if their topic text is "smaller" if (c->topic > newtopic) return CMD_FAILURE; // If the texts are equal in addition to the timestamps, decide which setter to keep if ((c->topic == newtopic) && (c->setby >= setter)) return CMD_FAILURE; } if (c->topic != newtopic) { // Update topic only when it differs from current topic c->topic.assign(newtopic, 0, ServerInstance->Config->Limits.MaxTopic); c->WriteChannel(user, "TOPIC %s :%s", c->name.c_str(), c->topic.c_str()); } // Update setter and settime c->setby.assign(setter, 0, 128); c->topicset = ts; FOREACH_MOD(OnPostTopicChange, (user, c, c->topic)); return CMD_SUCCESS; }
CmdResult Handle (const std::vector<std::string> ¶meters, User *user) { int n_done = 0; reason = (parameters.size() < 4) ? "Please use this server/port instead" : parameters[3]; bool redirect_all_immediately = false; redirect_new_users = true; bool direction = true; std::string n_done_s; /* No parameters: jumpserver disabled */ if (!parameters.size()) { if (port) user->WriteNotice("*** Disabled jumpserver (previously set to '" + redirect_to + ":" + ConvToStr(port) + "')"); else user->WriteNotice("*** Jumpserver was not enabled."); port = 0; sslport = 0; redirect_to.clear(); return CMD_SUCCESS; } port = 0; redirect_to.clear(); if (parameters.size() >= 3) { for (std::string::const_iterator n = parameters[2].begin(); n != parameters[2].end(); ++n) { switch (*n) { case '+': direction = true; break; case '-': direction = false; break; case 'a': redirect_all_immediately = direction; break; case 'n': redirect_new_users = direction; break; default: user->WriteNotice("*** Invalid JUMPSERVER flag: " + ConvToStr(*n)); return CMD_FAILURE; break; } } size_t delimpos = parameters[1].find(':'); port = ConvToInt(parameters[1].substr(0, delimpos ? delimpos : std::string::npos)); sslport = (delimpos == std::string::npos ? 0 : ConvToInt(parameters[1].substr(delimpos + 1))); if (parameters[1].find_first_not_of("0123456789:") != std::string::npos || parameters[1].rfind(':') != delimpos || port > 65535 || sslport > 65535) { user->WriteNotice("*** Invalid port number"); return CMD_FAILURE; } if (redirect_all_immediately) { /* Redirect everyone but the oper sending the command */ for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); ++i) { LocalUser* t = *i; if (!t->IsOper()) { t->WriteNumeric(10, "%s %s %d :Please use this Server/Port instead", t->nick.c_str(), parameters[0].c_str(), GetPort(t)); ServerInstance->Users->QuitUser(t, reason); n_done++; } } if (n_done) { n_done_s = ConvToStr(n_done); } } if (redirect_new_users) redirect_to = parameters[0]; user->WriteNotice("*** Set jumpserver to server '" + parameters[0] + "' port '" + (port ? ConvToStr(port) : "Auto") + ", SSL " + (sslport ? ConvToStr(sslport) : "Auto") + "', flags '+" + (redirect_all_immediately ? "a" : "") + (redirect_new_users ? "n'" : "'") + (n_done ? " (" + n_done_s + "user(s) redirected): " : ": ") + reason); } return CMD_SUCCESS; }
void TreeSocket::ProcessLine(std::string &line) { std::string prefix; std::string command; parameterlist params; ServerInstance->Logs->Log(MODNAME, LOG_RAWIO, "S[%d] I %s", this->GetFd(), line.c_str()); Split(line, prefix, command, params); if (command.empty()) return; switch (this->LinkState) { case WAIT_AUTH_1: /* * State WAIT_AUTH_1: * Waiting for SERVER command from remote server. Server initiating * the connection sends the first SERVER command, listening server * replies with theirs if its happy, then if the initiator is happy, * it starts to send its net sync, which starts the merge, otherwise * it sends an ERROR. */ if (command == "PASS") { /* * Ignore this silently. Some services packages insist on sending PASS, even * when it is not required (i.e. by us). We have to ignore this here, otherwise * as it's an unknown command (effectively), it will cause the connection to be * closed, which probably isn't what people want. -- w00t */ } else if (command == "SERVER") { this->Inbound_Server(params); } else if (command == "ERROR") { this->Error(params); } else if (command == "USER") { this->SendError("Client connections to this port are prohibited."); } else if (command == "CAPAB") { this->Capab(params); } else { this->SendError("Invalid command in negotiation phase: " + command); } break; case WAIT_AUTH_2: /* * State WAIT_AUTH_2: * We have sent SERVER to the other side of the connection. Now we're waiting for them to start BURST. * The other option at this stage of things, of course, is for them to close our connection thanks * to invalid credentials.. -- w */ if (command == "SERVER") { /* * Connection is either attempting to re-auth itself (stupid) or sending netburst without sending BURST. * Both of these aren't allowable, so block them here. -- w */ this->SendError("You may not re-authenticate or commence netburst without sending BURST."); } else if (command == "BURST") { if (params.size()) { time_t them = ConvToInt(params[0]); time_t delta = them - ServerInstance->Time(); if ((delta < -600) || (delta > 600)) { ServerInstance->SNO->WriteGlobalSno('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs((long)delta)); SendError("Your clocks are out by "+ConvToStr(abs((long)delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!"); return; } else if ((delta < -30) || (delta > 30)) { ServerInstance->SNO->WriteGlobalSno('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs((long)delta)); } } // Check for duplicate server name/sid again, it's possible that a new // server was introduced while we were waiting for them to send BURST. // (we do not reserve their server name/sid when they send SERVER, we do it now) if (!CheckDuplicate(capab->name, capab->sid)) return; this->LinkState = CONNECTED; Utils->timeoutlist.erase(this); linkID = capab->name; MyRoot = new TreeServer(capab->name, capab->description, capab->sid, Utils->TreeRoot, this, capab->hidden); Utils->TreeRoot->AddChild(MyRoot); MyRoot->bursting = true; this->DoBurst(MyRoot); parameterlist sparams; sparams.push_back(MyRoot->GetName()); sparams.push_back("*"); sparams.push_back("0"); sparams.push_back(MyRoot->GetID()); sparams.push_back(":" + MyRoot->GetDesc()); Utils->DoOneToAllButSender(ServerInstance->Config->GetSID(), "SERVER", sparams, MyRoot); Utils->DoOneToAllButSender(MyRoot->GetID(), "BURST", params, MyRoot); } else if (command == "ERROR") { this->Error(params); } else if (command == "CAPAB") { this->Capab(params); } break; case CONNECTING: /* * State CONNECTING: * We're connecting (OUTGOING) to another server. They are in state WAIT_AUTH_1 until they verify * our credentials, when they proceed into WAIT_AUTH_2 and send SERVER to us. We then send BURST * + our netburst, which will put them into CONNECTED state. -- w */ if (command == "SERVER") { // Our credentials have been accepted, send netburst. (this puts US into the CONNECTED state) this->Outbound_Reply_Server(params); } else if (command == "ERROR") { this->Error(params); } else if (command == "CAPAB") { this->Capab(params); } break; case CONNECTED: /* * State CONNECTED: * Credentials have been exchanged, we've gotten their 'BURST' (or sent ours). * Anything from here on should be accepted a little more reasonably. */ this->ProcessConnectedLine(prefix, command, params); break; case DYING: break; } }
/** Handle /INVITE */ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user) { ModResult MOD_RESULT; if (parameters.size() == 2 || parameters.size() == 3) { User* u; if (IS_LOCAL(user)) u = ServerInstance->FindNickOnly(parameters[0]); else u = ServerInstance->FindNick(parameters[0]); Channel* c = ServerInstance->FindChan(parameters[1]); time_t timeout = 0; if (parameters.size() == 3) { if (IS_LOCAL(user)) timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[1]); else timeout = ConvToInt(parameters[2]); } if ((!c) || (!u) || (u->registered != REG_ALL)) { user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), c ? parameters[0].c_str() : parameters[1].c_str()); return CMD_FAILURE; } if ((IS_LOCAL(user)) && (!c->HasUser(user))) { user->WriteNumeric(ERR_NOTONCHANNEL, "%s %s :You're not on that channel!",user->nick.c_str(), c->name.c_str()); return CMD_FAILURE; } if (c->HasUser(u)) { user->WriteNumeric(ERR_USERONCHANNEL, "%s %s %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str()); return CMD_FAILURE; } FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout)); if (MOD_RESULT == MOD_RES_DENY) { return CMD_FAILURE; } else if (MOD_RESULT == MOD_RES_PASSTHRU) { if (IS_LOCAL(user)) { unsigned int rank = c->GetPrefixValue(user); if (rank < HALFOP_VALUE) { // Check whether halfop mode is available and phrase error message accordingly ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL); user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator", user->nick.c_str(), c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : "")); return CMD_FAILURE; } } } if (IS_LOCAL(u)) { Invitation::Create(c, IS_LOCAL(u), timeout); u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str()); } if (IS_LOCAL(user)) user->WriteNumeric(RPL_INVITING, "%s %s %s",user->nick.c_str(),u->nick.c_str(),c->name.c_str()); if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE) { char prefix; switch (ServerInstance->Config->AnnounceInvites) { case ServerConfig::INVITE_ANNOUNCE_OPS: { prefix = '@'; break; } case ServerConfig::INVITE_ANNOUNCE_DYNAMIC: { PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h'); prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@'); break; } default: { prefix = 0; break; } } c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str()); } FOREACH_MOD(OnUserInvite, (user,u,c,timeout)); } else if (IS_LOCAL(user)) { // pinched from ircu - invite with not enough parameters shows channels // youve been invited to but haven't joined yet. InviteList& il = IS_LOCAL(user)->GetInviteList(); for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i) { user->WriteNumeric(RPL_INVITELIST, "%s :%s",user->nick.c_str(), (*i)->chan->name.c_str()); } user->WriteNumeric(RPL_ENDOFINVITELIST, "%s :End of INVITE list",user->nick.c_str()); } return CMD_SUCCESS; }