1 /* +------------------------------------+
2 * | Inspire Internet Relay Chat Daemon |
3 * +------------------------------------+
5 * InspIRCd: (C) 2002-2010 InspIRCd Development Team
6 * See: http://wiki.inspircd.org/Credits
8 * This program is free but copyrighted software; see
9 * the file COPYING for details.
11 * ---------------------------------------------------
17 #include "treesocket.h"
18 #include "treeserver.h"
22 std::string TreeSocket::MyModules(int filter)
24 std::vector<std::string> modlist = ServerInstance->Modules->GetAllModuleNames(filter);
26 if (filter == VF_COMMON && proto_version != ProtocolVersion)
27 CompatAddModules(modlist);
29 std::string capabilities;
30 sort(modlist.begin(),modlist.end());
31 for (unsigned int i = 0; i < modlist.size(); i++)
34 capabilities.push_back(proto_version > 1201 ? ' ' : ',');
35 capabilities.append(modlist[i]);
36 Module* m = ServerInstance->Modules->Find(modlist[i]);
37 if (m && proto_version > 1201)
39 Version v = m->GetVersion();
40 if (!v.link_data.empty())
42 capabilities.push_back('=');
43 capabilities.append(v.link_data);
50 static std::string BuildModeList(ModeType type)
52 std::vector<std::string> modes;
53 for(char c='A'; c <= 'z'; c++)
55 ModeHandler* mh = ServerInstance->Modes->FindMode(c, type)
58 std::string mdesc = mh->name;
61 mdesc.push_back(mh->GetPrefix());
62 if (mh->GetModeChar())
63 mdesc.push_back(mh->GetModeChar());
64 modes.push_back(mdesc);
67 sort(modes.begin(), modes.end());
68 irc::stringjoiner line(" ", modes, 0, modes.size() - 1);
69 return line.GetJoined();
72 void TreeSocket::SendCapabilities(int phase)
74 if (capab->capab_phase >= phase)
77 if (capab->capab_phase < 1 && phase >= 1)
78 WriteLine("CAPAB START " + ConvToStr(ProtocolVersion));
80 capab->capab_phase = phase;
84 char sep = proto_version > 1201 ? ' ' : ',';
85 irc::sepstream modulelist(MyModules(VF_COMMON), sep);
86 irc::sepstream optmodulelist(MyModules(VF_OPTCOMMON), sep);
87 /* Send module names, split at 509 length */
89 std::string line = "CAPAB MODULES :";
90 while (modulelist.GetToken(item))
92 if (line.length() + item.length() + 1 > 509)
94 this->WriteLine(line);
95 line = "CAPAB MODULES :";
98 if (line != "CAPAB MODULES :")
103 if (line != "CAPAB MODULES :")
104 this->WriteLine(line);
106 line = "CAPAB MODSUPPORT :";
107 while (optmodulelist.GetToken(item))
109 if (line.length() + item.length() + 1 > 509)
111 this->WriteLine(line);
112 line = "CAPAB MODSUPPORT :";
115 if (line != "CAPAB MODSUPPORT :")
120 if (line != "CAPAB MODSUPPORT :")
121 this->WriteLine(line);
123 WriteLine("CAPAB CHANMODES :" + BuildModeList(MODETYPE_CHANNEL));
124 WriteLine("CAPAB USERMODES :" + BuildModeList(MODETYPE_USER));
131 /* Do we have sha256 available? If so, we send a challenge */
132 if (Utils->ChallengeResponse && (ServerInstance->Modules->Find("m_sha256.so")))
134 SetOurChallenge(ServerInstance->GenRandomStr(20));
135 extra = " CHALLENGE=" + this->GetOurChallenge();
138 this->WriteLine("CAPAB CAPABILITIES " /* Preprocessor does this one. */
139 ":NICKMAX="+ConvToStr(ServerInstance->Config->Limits.NickMax)+
140 " CHANMAX="+ConvToStr(ServerInstance->Config->Limits.ChanMax)+
141 " MAXMODES="+ConvToStr(ServerInstance->Config->Limits.MaxModes)+
142 " IDENTMAX="+ConvToStr(ServerInstance->Config->Limits.IdentMax)+
143 " MAXQUIT="+ConvToStr(ServerInstance->Config->Limits.MaxQuit)+
144 " MAXTOPIC="+ConvToStr(ServerInstance->Config->Limits.MaxTopic)+
145 " MAXKICK="+ConvToStr(ServerInstance->Config->Limits.MaxKick)+
146 " MAXGECOS="+ConvToStr(ServerInstance->Config->Limits.MaxGecos)+
147 " MAXAWAY="+ConvToStr(ServerInstance->Config->Limits.MaxAway)+
148 " IP6NATIVE="+ConvToStr(ip6)+
150 " PROTOCOL="+ConvToStr(ProtocolVersion)+extra+
151 " PREFIX="+ServerInstance->Modes->BuildPrefixes()+
152 " CHANMODES="+ServerInstance->Modes->GiveModeList(MASK_CHANNEL)+
153 " USERMODES="+ServerInstance->Modes->GiveModeList(MASK_USER)+
156 this->WriteLine("CAPAB END");
159 /* Isolate and return the elements that are different between two comma seperated lists */
160 void TreeSocket::ListDifference(const std::string &one, const std::string &two, char sep,
161 std::string& mleft, std::string& mright)
163 std::set<std::string> values;
164 irc::sepstream sepleft(one, sep);
165 irc::sepstream sepright(two, sep);
167 while (sepleft.GetToken(item))
171 while (sepright.GetToken(item))
173 if (!values.erase(item))
175 mright.push_back(sep);
179 for(std::set<std::string>::iterator i = values.begin(); i != values.end(); ++i)
181 mleft.push_back(sep);
186 bool TreeSocket::Capab(const parameterlist ¶ms)
188 if (params.size() < 1)
190 this->SendError("Invalid number of parameters for CAPAB - Mismatched version");
193 if (params[0] == "START")
195 capab->ModuleList.clear();
196 capab->OptModuleList.clear();
197 capab->CapKeys.clear();
198 if (params.size() > 1)
199 proto_version = atoi(params[1].c_str());
202 else if (params[0] == "END")
205 /* Compare ModuleList and check CapKeys */
206 if ((this->capab->ModuleList != this->MyModules(VF_COMMON)) && (this->capab->ModuleList.length()))
208 std::string diffIneed, diffUneed;
209 ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), proto_version > 1201 ? ' ' : ',', diffIneed, diffUneed);
210 if (diffIneed.length() || diffUneed.length())
212 reason = "Modules incorrectly matched on these servers.";
213 if (diffIneed.length())
214 reason += " Not loaded here:" + diffIneed;
215 if (diffUneed.length())
216 reason += " Not loaded there:" + diffUneed;
217 this->SendError("CAPAB negotiation failed: "+reason);
221 if (this->capab->OptModuleList != this->MyModules(VF_OPTCOMMON) && this->capab->OptModuleList.length())
223 std::string diffIneed, diffUneed;
224 ListDifference(this->capab->OptModuleList, this->MyModules(VF_OPTCOMMON), ' ', diffIneed, diffUneed);
225 if (diffIneed.length() || diffUneed.length())
227 if (Utils->AllowOptCommon)
229 ServerInstance->SNO->WriteToSnoMask('l',
230 "Optional module lists do not match, some commands may not work globally.%s%s%s%s",
231 diffIneed.length() ? " Not loaded here:" : "", diffIneed.c_str(),
232 diffUneed.length() ? " Not loaded there:" : "", diffUneed.c_str());
236 reason = "Optional modules incorrectly matched on these servers, and options::allowmismatch not set.";
237 if (diffIneed.length())
238 reason += " Not loaded here:" + diffIneed;
239 if (diffUneed.length())
240 reason += " Not loaded there:" + diffUneed;
241 this->SendError("CAPAB negotiation failed: "+reason);
247 if (this->capab->CapKeys.find("PROTOCOL") == this->capab->CapKeys.end())
249 reason = "Protocol version not specified";
253 proto_version = atoi(capab->CapKeys.find("PROTOCOL")->second.c_str());
254 if (proto_version < MinCompatProtocol)
256 reason = "Server is using protocol version " + ConvToStr(proto_version) +
257 " which is too old to link with this server (version " + ConvToStr(ProtocolVersion)
258 + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")");
262 if(this->capab->CapKeys.find("PREFIX") != this->capab->CapKeys.end() && this->capab->CapKeys.find("PREFIX")->second != ServerInstance->Modes->BuildPrefixes())
263 reason = "One or more of the prefixes on the remote server are invalid on this server.";
265 if (!capab->ChanModes.empty())
267 if (capab->ChanModes != BuildModeList(MODETYPE_CHANNEL))
269 std::string diffIneed, diffUneed;
270 ListDifference(capab->ChanModes, BuildModeList(MODETYPE_CHANNEL), ' ', diffIneed, diffUneed);
271 if (diffIneed.length() || diffUneed.length())
273 reason = "Channel modes not matched on these servers.";
274 if (diffIneed.length())
275 reason += " Not loaded here:" + diffIneed;
276 if (diffUneed.length())
277 reason += " Not loaded there:" + diffUneed;
281 else if (this->capab->CapKeys.find("CHANMODES") != this->capab->CapKeys.end())
283 if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MASK_CHANNEL))
284 reason = "One or more of the channel modes on the remote server are invalid on this server.";
287 if (!capab->UserModes.empty())
289 if (capab->UserModes != BuildModeList(MODETYPE_USER))
291 std::string diffIneed, diffUneed;
292 ListDifference(capab->UserModes, BuildModeList(MODETYPE_USER), ' ', diffIneed, diffUneed);
293 if (diffIneed.length() || diffUneed.length())
295 reason = "User modes not matched on these servers.";
296 if (diffIneed.length())
297 reason += " Not loaded here:" + diffIneed;
298 if (diffUneed.length())
299 reason += " Not loaded there:" + diffUneed;
303 else if (this->capab->CapKeys.find("USERMODES") != this->capab->CapKeys.end())
305 if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MASK_USER))
306 reason = "One or more of the user modes on the remote server are invalid on this server.";
309 /* Challenge response, store their challenge for our password */
310 std::map<std::string,std::string>::iterator n = this->capab->CapKeys.find("CHALLENGE");
311 if (Utils->ChallengeResponse && (n != this->capab->CapKeys.end()) && (ServerInstance->Modules->Find("m_sha256.so")))
313 /* Challenge-response is on now */
314 this->SetTheirChallenge(n->second);
315 if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING))
317 this->SendCapabilities(2);
318 this->WriteLine(std::string("SERVER ")+ServerInstance->Config->ServerName+" "+this->MakePass(capab->OutboundPass, this->GetTheirChallenge())+" 0 "+
319 ServerInstance->Config->GetSID()+" :"+ServerInstance->Config->ServerDesc);
324 /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */
325 if (this->LinkState == CONNECTING)
327 this->SendCapabilities(2);
328 this->WriteLine(std::string("SERVER ")+ServerInstance->Config->ServerName+" "+capab->OutboundPass+" 0 "+ServerInstance->Config->GetSID()+" :"+ServerInstance->Config->ServerDesc);
334 this->SendError("CAPAB negotiation failed: "+reason);
338 else if ((params[0] == "MODULES") && (params.size() == 2))
340 if (!capab->ModuleList.length())
342 capab->ModuleList = params[1];
346 capab->ModuleList.push_back(proto_version > 1201 ? ' ' : ',');
347 capab->ModuleList.append(params[1]);
350 else if ((params[0] == "MODSUPPORT") && (params.size() == 2))
352 if (!capab->OptModuleList.length())
354 capab->OptModuleList = params[1];
358 capab->OptModuleList.push_back(' ');
359 capab->OptModuleList.append(params[1]);
362 else if ((params[0] == "CHANMODES") && (params.size() == 2))
364 capab->ChanModes = params[1];
366 else if ((params[0] == "USERMODES") && (params.size() == 2))
368 capab->UserModes = params[1];
370 else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
372 irc::tokenstream capabs(params[1]);
374 while (capabs.GetToken(item))
376 /* Process each key/value pair */
377 std::string::size_type equals = item.find('=');
378 if (equals != std::string::npos)
380 std::string var = item.substr(0, equals);
381 std::string value = item.substr(equals+1, item.length());
382 capab->CapKeys[var] = value;