Anope IRC Services  Version 2.0
config.cpp
Go to the documentation of this file.
1 /* Configuration file handling.
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  *
11  */
12 
13 #include "services.h"
14 #include "config.h"
15 #include "bots.h"
16 #include "access.h"
17 #include "opertype.h"
18 #include "channels.h"
19 #include "hashcomp.h"
20 
21 using namespace Configuration;
22 
23 File ServicesConf("services.conf", false); // Services configuration file name
24 Conf *Config = NULL;
25 
26 Block::Block(const Anope::string &n) : name(n), linenum(-1)
27 {
28 }
29 
31 {
32  return name;
33 }
34 
36 {
37  if (!this)
38  return 0;
39 
40  return blocks.count(bname);
41 }
42 
43 Block* Block::GetBlock(const Anope::string &bname, int num)
44 {
45  if (!this)
46  return NULL;
47 
48  std::pair<block_map::iterator, block_map::iterator> it = blocks.equal_range(bname);
49 
50  for (int i = 0; it.first != it.second; ++it.first, ++i)
51  if (i == num)
52  return &it.first->second;
53  return NULL;
54 }
55 
56 bool Block::Set(const Anope::string &tag, const Anope::string &value)
57 {
58  if (!this)
59  return false;
60 
61  items[tag] = value;
62  return true;
63 }
64 
66 {
67  if (this)
68  return &items;
69  else
70  return NULL;
71 }
72 
73 template<> const Anope::string Block::Get(const Anope::string &tag, const Anope::string& def) const
74 {
75  if (!this)
76  return def;
77 
79  if (it != items.end())
80  return it->second;
81 
82  return def;
83 }
84 
85 template<> time_t Block::Get(const Anope::string &tag, const Anope::string &def) const
86 {
87  return Anope::DoTime(Get<const Anope::string>(tag, def));
88 }
89 
90 template<> bool Block::Get(const Anope::string &tag, const Anope::string &def) const
91 {
92  const Anope::string &str = Get<const Anope::string>(tag, def);
93  return !str.empty() && !str.equals_ci("no") && !str.equals_ci("off") && !str.equals_ci("false") && !str.equals_ci("0");
94 }
95 
96 static void ValidateNotEmpty(const Anope::string &block, const Anope::string &name, const Anope::string &value)
97 {
98  if (value.empty())
99  throw ConfigException("The value for <" + block + ":" + name + "> cannot be empty!");
100 }
101 
102 static void ValidateNoSpaces(const Anope::string &block, const Anope::string &name, const Anope::string &value)
103 {
104  if (value.find(' ') != Anope::string::npos)
105  throw ConfigException("The value for <" + block + ":" + name + "> may not contain spaces!");
106 }
107 
108 template<typename T> static void ValidateNotZero(const Anope::string &block, const Anope::string &name, T value)
109 {
110  if (!value)
111  throw ConfigException("The value for <" + block + ":" + name + "> cannot be zero!");
112 }
113 
115 {
116  ReadTimeout = 0;
117  UsePrivmsg = DefPrivmsg = false;
118 
119  this->LoadConf(ServicesConf);
120 
121  for (int i = 0; i < this->CountBlock("include"); ++i)
122  {
123  Block *include = this->GetBlock("include", i);
124 
125  const Anope::string &type = include->Get<const Anope::string>("type"),
126  &file = include->Get<const Anope::string>("name");
127 
128  File f(file, type == "executable");
129  this->LoadConf(f);
130  }
131 
132  FOREACH_MOD(OnReload, (this));
133 
134  /* Check for modified values that aren't allowed to be modified */
135  if (Config)
136  {
137  struct
138  {
139  Anope::string block;
141  } noreload[] = {
142  {"serverinfo", "name"},
143  {"serverinfo", "description"},
144  {"serverinfo", "localhost"},
145  {"serverinfo", "id"},
146  {"serverinfo", "pid"},
147  {"networkinfo", "nicklen"},
148  {"networkinfo", "userlen"},
149  {"networkinfo", "hostlen"},
150  {"networkinfo", "chanlen"},
151  };
152 
153  for (unsigned i = 0; i < sizeof(noreload) / sizeof(noreload[0]); ++i)
154  if (this->GetBlock(noreload[i].block)->Get<const Anope::string>(noreload[i].name) != Config->GetBlock(noreload[i].block)->Get<const Anope::string>(noreload[i].name))
155  throw ConfigException("<" + noreload[i].block + ":" + noreload[i].name + "> can not be modified once set");
156  }
157 
158  Block *serverinfo = this->GetBlock("serverinfo"), *options = this->GetBlock("options"),
159  *mail = this->GetBlock("mail"), *networkinfo = this->GetBlock("networkinfo");
160 
161  ValidateNotEmpty("serverinfo", "name", serverinfo->Get<const Anope::string>("name"));
162  ValidateNotEmpty("serverinfo", "description", serverinfo->Get<const Anope::string>("description"));
163  ValidateNotEmpty("serverinfo", "pid", serverinfo->Get<const Anope::string>("pid"));
164  ValidateNotEmpty("serverinfo", "motd", serverinfo->Get<const Anope::string>("motd"));
165 
166  ValidateNotZero("options", "readtimeout", options->Get<time_t>("readtimeout"));
167  ValidateNotZero("options", "warningtimeout", options->Get<time_t>("warningtimeout"));
168 
169  ValidateNotZero("networkinfo", "nicklen", networkinfo->Get<unsigned>("nicklen"));
170  ValidateNotZero("networkinfo", "userlen", networkinfo->Get<unsigned>("userlen"));
171  ValidateNotZero("networkinfo", "hostlen", networkinfo->Get<unsigned>("hostlen"));
172  ValidateNotZero("networkinfo", "chanlen", networkinfo->Get<unsigned>("chanlen"));
173 
174  spacesepstream(options->Get<const Anope::string>("ulineservers")).GetTokens(this->Ulines);
175 
176  if (mail->Get<bool>("usemail"))
177  {
178  Anope::string check[] = { "sendmailpath", "sendfrom", "registration_subject", "registration_message", "emailchange_subject", "emailchange_message", "memo_subject", "memo_message" };
179  for (unsigned i = 0; i < sizeof(check) / sizeof(Anope::string); ++i)
180  ValidateNotEmpty("mail", check[i], mail->Get<const Anope::string>(check[i]));
181  }
182 
183  this->ReadTimeout = options->Get<time_t>("readtimeout");
184  this->UsePrivmsg = options->Get<bool>("useprivmsg");
185  this->UseStrictPrivmsg = options->Get<bool>("usestrictprivmsg");
186  this->StrictPrivmsg = !UseStrictPrivmsg ? "/msg " : "/";
187  {
188  std::vector<Anope::string> defaults;
189  spacesepstream(this->GetModule("nickserv")->Get<const Anope::string>("defaults")).GetTokens(defaults);
190  this->DefPrivmsg = std::find(defaults.begin(), defaults.end(), "msg") != defaults.end();
191  }
192  this->DefLanguage = options->Get<const Anope::string>("defaultlanguage");
193  this->TimeoutCheck = options->Get<time_t>("timeoutcheck");
194 
195  for (int i = 0; i < this->CountBlock("uplink"); ++i)
196  {
197  Block *uplink = this->GetBlock("uplink", i);
198 
199  const Anope::string &host = uplink->Get<const Anope::string>("host");
200  bool ipv6 = uplink->Get<bool>("ipv6");
201  int port = uplink->Get<int>("port");
202  const Anope::string &password = uplink->Get<const Anope::string>("password");
203 
204  ValidateNotEmpty("uplink", "host", host);
205  ValidateNotZero("uplink", "port", port);
206  ValidateNotEmpty("uplink", "password", password);
207 
208  this->Uplinks.push_back(Uplink(host, port, password, ipv6));
209  }
210 
211  for (int i = 0; i < this->CountBlock("module"); ++i)
212  {
213  Block *module = this->GetBlock("module", i);
214 
215  const Anope::string &modname = module->Get<const Anope::string>("name");
216 
217  ValidateNotEmpty("module", "name", modname);
218 
219  this->ModulesAutoLoad.push_back(modname);
220  }
221 
222  for (int i = 0; i < this->CountBlock("opertype"); ++i)
223  {
224  Block *opertype = this->GetBlock("opertype", i);
225 
226  const Anope::string &oname = opertype->Get<const Anope::string>("name"),
227  &modes = opertype->Get<const Anope::string>("modes"),
228  &inherits = opertype->Get<const Anope::string>("inherits"),
229  &commands = opertype->Get<const Anope::string>("commands"),
230  &privs = opertype->Get<const Anope::string>("privs");
231 
232  ValidateNotEmpty("opertype", "name", oname);
233 
234  OperType *ot = new OperType(oname);
235  ot->modes = modes;
236 
237  spacesepstream cmdstr(commands);
238  for (Anope::string str; cmdstr.GetToken(str);)
239  ot->AddCommand(str);
240 
241  spacesepstream privstr(privs);
242  for (Anope::string str; privstr.GetToken(str);)
243  ot->AddPriv(str);
244 
245  commasepstream inheritstr(inherits);
246  for (Anope::string str; inheritstr.GetToken(str);)
247  {
248  /* Strip leading ' ' after , */
249  if (str.length() > 1 && str[0] == ' ')
250  str.erase(str.begin());
251  for (unsigned j = 0; j < this->MyOperTypes.size(); ++j)
252  {
253  OperType *ot2 = this->MyOperTypes[j];
254 
255  if (ot2->GetName().equals_ci(str))
256  {
257  Log() << "Inheriting commands and privs from " << ot2->GetName() << " to " << ot->GetName();
258  ot->Inherits(ot2);
259  break;
260  }
261  }
262  }
263 
264  this->MyOperTypes.push_back(ot);
265  }
266 
267  for (int i = 0; i < this->CountBlock("oper"); ++i)
268  {
269  Block *oper = this->GetBlock("oper", i);
270 
271  const Anope::string &nname = oper->Get<const Anope::string>("name"),
272  &type = oper->Get<const Anope::string>("type"),
273  &password = oper->Get<const Anope::string>("password"),
274  &certfp = oper->Get<const Anope::string>("certfp"),
275  &host = oper->Get<const Anope::string>("host"),
276  &vhost = oper->Get<const Anope::string>("vhost");
277  bool require_oper = oper->Get<bool>("require_oper");
278 
279  ValidateNotEmpty("oper", "name", nname);
280  ValidateNotEmpty("oper", "type", type);
281 
282  OperType *ot = NULL;
283  for (unsigned j = 0; j < this->MyOperTypes.size(); ++j)
284  if (this->MyOperTypes[j]->GetName() == type)
285  ot = this->MyOperTypes[j];
286  if (ot == NULL)
287  throw ConfigException("Oper block for " + nname + " has invalid oper type " + type);
288 
289  Oper *o = new Oper(nname, ot);
290  o->require_oper = require_oper;
291  o->password = password;
292  o->certfp = certfp;
293  spacesepstream(host).GetTokens(o->hosts);
294  o->vhost = vhost;
295 
296  this->Opers.push_back(o);
297  }
298 
299  for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
300  it->second->conf = false;
301  for (int i = 0; i < this->CountBlock("service"); ++i)
302  {
303  Block *service = this->GetBlock("service", i);
304 
305  const Anope::string &nick = service->Get<const Anope::string>("nick"),
306  &user = service->Get<const Anope::string>("user"),
307  &host = service->Get<const Anope::string>("host"),
308  &gecos = service->Get<const Anope::string>("gecos"),
309  &modes = service->Get<const Anope::string>("modes"),
310  &channels = service->Get<const Anope::string>("channels");
311 
312  ValidateNotEmpty("service", "nick", nick);
313  ValidateNotEmpty("service", "user", user);
314  ValidateNotEmpty("service", "host", host);
315  ValidateNotEmpty("service", "gecos", gecos);
316  ValidateNoSpaces("service", "channels", channels);
317 
318  BotInfo *bi = BotInfo::Find(nick, true);
319  if (!bi)
320  bi = new BotInfo(nick, user, host, gecos, modes);
321  bi->conf = true;
322 
323  std::vector<Anope::string> oldchannels = bi->botchannels;
324  bi->botchannels.clear();
325  commasepstream sep(channels);
326  for (Anope::string token; sep.GetToken(token);)
327  {
328  bi->botchannels.push_back(token);
329  size_t ch = token.find('#');
330  Anope::string chname, want_modes;
331  if (ch == Anope::string::npos)
332  chname = token;
333  else
334  {
335  want_modes = token.substr(0, ch);
336  chname = token.substr(ch);
337  }
338  bi->Join(chname);
339  Channel *c = Channel::Find(chname);
340  if (!c)
341  continue; // Can't happen
342 
343  c->botchannel = true;
344 
345  /* Remove all existing modes */
346  ChanUserContainer *cu = c->FindUser(bi);
347  if (cu != NULL)
348  for (size_t j = 0; j < cu->status.Modes().length(); ++j)
350  /* Set the new modes */
351  for (unsigned j = 0; j < want_modes.length(); ++j)
352  {
353  ChannelMode *cm = ModeManager::FindChannelModeByChar(want_modes[j]);
354  if (cm == NULL)
356  if (cm && cm->type == MODE_STATUS)
357  c->SetMode(bi, cm, bi->GetUID());
358  }
359  }
360  for (unsigned k = 0; k < oldchannels.size(); ++k)
361  {
362  size_t ch = oldchannels[k].find('#');
363  Anope::string chname = oldchannels[k].substr(ch != Anope::string::npos ? ch : 0);
364 
365  bool found = false;
366  for (unsigned j = 0; j < bi->botchannels.size(); ++j)
367  {
368  ch = bi->botchannels[j].find('#');
369  Anope::string ochname = bi->botchannels[j].substr(ch != Anope::string::npos ? ch : 0);
370 
371  if (chname.equals_ci(ochname))
372  found = true;
373  }
374 
375  if (found)
376  continue;
377 
378  Channel *c = Channel::Find(chname);
379  if (c)
380  {
381  c->botchannel = false;
382  bi->Part(c);
383  }
384  }
385  }
386 
387  for (int i = 0; i < this->CountBlock("log"); ++i)
388  {
389  Block *log = this->GetBlock("log", i);
390 
391  int logage = log->Get<int>("logage");
392  bool rawio = log->Get<bool>("rawio");
393  bool debug = log->Get<bool>("debug");
394 
395  LogInfo l(logage, rawio, debug);
396 
397  l.bot = BotInfo::Find(log->Get<const Anope::string>("bot", "Global"), true);
398  spacesepstream(log->Get<const Anope::string>("target")).GetTokens(l.targets);
399  spacesepstream(log->Get<const Anope::string>("source")).GetTokens(l.sources);
400  spacesepstream(log->Get<const Anope::string>("admin")).GetTokens(l.admin);
401  spacesepstream(log->Get<const Anope::string>("override")).GetTokens(l.override);
402  spacesepstream(log->Get<const Anope::string>("commands")).GetTokens(l.commands);
403  spacesepstream(log->Get<const Anope::string>("servers")).GetTokens(l.servers);
404  spacesepstream(log->Get<const Anope::string>("channels")).GetTokens(l.channels);
405  spacesepstream(log->Get<const Anope::string>("users")).GetTokens(l.users);
406  spacesepstream(log->Get<const Anope::string>("other")).GetTokens(l.normal);
407 
408  this->LogInfos.push_back(l);
409  }
410 
411  for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
412  it->second->commands.clear();
413  for (int i = 0; i < this->CountBlock("command"); ++i)
414  {
415  Block *command = this->GetBlock("command", i);
416 
417  const Anope::string &service = command->Get<const Anope::string>("service"),
418  &nname = command->Get<const Anope::string>("name"),
419  &cmd = command->Get<const Anope::string>("command"),
420  &permission = command->Get<const Anope::string>("permission"),
421  &group = command->Get<const Anope::string>("group");
422  bool hide = command->Get<bool>("hide");
423 
424  ValidateNotEmpty("command", "service", service);
425  ValidateNotEmpty("command", "name", nname);
426  ValidateNotEmpty("command", "command", cmd);
427 
428  BotInfo *bi = this->GetClient(service);
429  if (!bi)
430  continue;
431 
432  CommandInfo &ci = bi->SetCommand(nname, cmd, permission);
433  ci.group = group;
434  ci.hide = hide;
435  }
436 
438  for (int i = 0; i < this->CountBlock("privilege"); ++i)
439  {
440  Block *privilege = this->GetBlock("privilege", i);
441 
442  const Anope::string &nname = privilege->Get<const Anope::string>("name"),
443  &desc = privilege->Get<const Anope::string>("desc");
444  int rank = privilege->Get<int>("rank");
445 
447  }
448 
449  for (int i = 0; i < this->CountBlock("fantasy"); ++i)
450  {
451  Block *fantasy = this->GetBlock("fantasy", i);
452 
453  const Anope::string &nname = fantasy->Get<const Anope::string>("name"),
454  &service = fantasy->Get<const Anope::string>("command"),
455  &permission = fantasy->Get<const Anope::string>("permission"),
456  &group = fantasy->Get<const Anope::string>("group");
457  bool hide = fantasy->Get<bool>("hide"),
458  prepend_channel = fantasy->Get<bool>("prepend_channel", "yes");
459 
460  ValidateNotEmpty("fantasy", "name", nname);
461  ValidateNotEmpty("fantasy", "command", service);
462 
463  CommandInfo &c = this->Fantasy[nname];
464  c.name = service;
465  c.permission = permission;
466  c.group = group;
467  c.hide = hide;
468  c.prepend_channel = prepend_channel;
469  }
470 
471  for (int i = 0; i < this->CountBlock("command_group"); ++i)
472  {
473  Block *command_group = this->GetBlock("command_group", i);
474 
475  const Anope::string &nname = command_group->Get<const Anope::string>("name"),
476  &description = command_group->Get<const Anope::string>("description");
477 
478  CommandGroup gr;
479  gr.name = nname;
480  gr.description = description;
481 
482  this->CommandGroups.push_back(gr);
483  }
484 
485  /* Below here can't throw */
486 
487  if (Config)
488  /* Clear existing conf opers */
489  for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
490  {
491  NickCore *nc = it->second;
492  if (nc->o && std::find(Config->Opers.begin(), Config->Opers.end(), nc->o) != Config->Opers.end())
493  nc->o = NULL;
494  }
495  /* Apply new opers */
496  for (unsigned i = 0; i < this->Opers.size(); ++i)
497  {
498  Oper *o = this->Opers[i];
499 
500  NickAlias *na = NickAlias::Find(o->name);
501  if (!na)
502  continue;
503 
504  na->nc->o = o;
505  Log() << "Tied oper " << na->nc->display << " to type " << o->ot->GetName();
506  }
507 
508  if (options->Get<const Anope::string>("casemap", "ascii") == "ascii")
509  Anope::casemap = std::locale(std::locale(), new Anope::ascii_ctype<char>());
510  else if (options->Get<const Anope::string>("casemap") == "rfc1459")
511  Anope::casemap = std::locale(std::locale(), new Anope::rfc1459_ctype<char>());
512  else
513  {
514  try
515  {
516  Anope::casemap = std::locale(options->Get<const Anope::string>("casemap").c_str());
517  }
518  catch (const std::runtime_error &)
519  {
520  Log() << "Unknown casemap " << options->Get<const Anope::string>("casemap") << " - casemap not changed";
521  }
522  }
524 
525  /* Check the user keys */
526  if (!options->Get<unsigned>("seed"))
527  Log() << "Configuration option options:seed should be set. It's for YOUR safety! Remember that!";
528 }
529 
531 {
532  for (unsigned i = 0; i < MyOperTypes.size(); ++i)
533  delete MyOperTypes[i];
534  for (unsigned i = 0; i < Opers.size(); ++i)
535  delete Opers[i];
536 }
537 
538 void Conf::Post(Conf *old)
539 {
540  /* Apply module changes */
541  for (unsigned i = 0; i < old->ModulesAutoLoad.size(); ++i)
542  if (std::find(this->ModulesAutoLoad.begin(), this->ModulesAutoLoad.end(), old->ModulesAutoLoad[i]) == this->ModulesAutoLoad.end())
544  for (unsigned i = 0; i < this->ModulesAutoLoad.size(); ++i)
545  if (std::find(old->ModulesAutoLoad.begin(), old->ModulesAutoLoad.end(), this->ModulesAutoLoad[i]) == old->ModulesAutoLoad.end())
547 
548  /* Apply opertype changes, as non-conf opers still point to the old oper types */
549  for (unsigned i = Oper::opers.size(); i > 0; --i)
550  {
551  Oper *o = Oper::opers[i - 1];
552 
553  /* Oper's type is in the old config, so update it */
554  if (std::find(old->MyOperTypes.begin(), old->MyOperTypes.end(), o->ot) != old->MyOperTypes.end())
555  {
556  OperType *ot = o->ot;
557  o->ot = NULL;
558 
559  for (unsigned j = 0; j < MyOperTypes.size(); ++j)
560  if (ot->GetName() == MyOperTypes[j]->GetName())
561  o->ot = MyOperTypes[j];
562 
563  if (o->ot == NULL)
564  {
565  /* Oper block has lost type */
566  std::vector<Oper *>::iterator it = std::find(old->Opers.begin(), old->Opers.end(), o);
567  if (it != old->Opers.end())
568  old->Opers.erase(it);
569 
570  it = std::find(this->Opers.begin(), this->Opers.end(), o);
571  if (it != this->Opers.end())
572  this->Opers.erase(it);
573 
574  delete o;
575  }
576  }
577  }
578 }
579 
581 {
582  if (!m)
583  return NULL;
584 
585  return GetModule(m->name);
586 }
587 
589 {
590  std::map<Anope::string, Block *>::iterator it = modules.find(mname);
591  if (it != modules.end())
592  return it->second;
593 
594  Block* &block = modules[mname];
595 
596  /* Search for the block */
597  for (std::pair<block_map::iterator, block_map::iterator> iters = blocks.equal_range("module"); iters.first != iters.second; ++iters.first)
598  {
599  Block *b = &iters.first->second;
600 
601  if (b->Get<const Anope::string>("name") == mname)
602  {
603  block = b;
604  break;
605  }
606  }
607 
608  return GetModule(mname);
609 }
610 
612 {
613  Anope::map<Anope::string>::iterator it = bots.find(cname);
614  if (it != bots.end())
615  return BotInfo::Find(!it->second.empty() ? it->second : cname, true);
616 
617  Block *block = GetModule(cname.lower());
618  const Anope::string &client = block->Get<const Anope::string>("client");
619  bots[cname] = client;
620  return GetClient(cname);
621 }
622 
624 {
625  const Anope::string &block_name = source.c ? "fantasy" : "command";
626 
627  for (std::pair<block_map::iterator, block_map::iterator> iters = blocks.equal_range(block_name); iters.first != iters.second; ++iters.first)
628  {
629  Block *b = &iters.first->second;
630 
631  if (b->Get<Anope::string>("name") == source.command)
632  return b;
633  }
634 
635  return NULL;
636 }
637 
638 File::File(const Anope::string &n, bool e) : name(n), executable(e), fp(NULL)
639 {
640 }
641 
643 {
644  this->Close();
645 }
646 
648 {
649  return this->name;
650 }
651 
652 bool File::IsOpen() const
653 {
654  return this->fp != NULL;
655 }
656 
658 {
659  this->Close();
660  this->fp = (this->executable ? popen(this->name.c_str(), "r") : fopen((Anope::ConfigDir + "/" + this->name).c_str(), "r"));
661  return this->fp != NULL;
662 }
663 
665 {
666  if (this->fp != NULL)
667  {
668  if (this->executable)
669  pclose(this->fp);
670  else
671  fclose(this->fp);
672  this->fp = NULL;
673  }
674 }
675 
676 bool File::End() const
677 {
678  return !this->IsOpen() || feof(this->fp);
679 }
680 
682 {
683  Anope::string ret;
684  char buf[BUFSIZE];
685  while (fgets(buf, sizeof(buf), this->fp) != NULL)
686  {
687  char *nl = strchr(buf, '\n');
688  if (nl != NULL)
689  *nl = 0;
690  else if (!this->End())
691  {
692  ret += buf;
693  continue;
694  }
695 
696  ret = buf;
697  break;
698  }
699 
700  return ret;
701 }
702 
703 void Conf::LoadConf(File &file)
704 {
705  if (!file.Open())
706  throw ConfigException("File " + file.GetName() + " could not be opened.");
707 
708  Anope::string itemname, wordbuffer;
709  std::stack<Block *> block_stack;
710  int linenumber = 0;
711  bool in_word = false, in_quote = false, in_comment = false;
712 
713  Log(LOG_DEBUG) << "Start to read conf " << file.GetName();
714  // Start reading characters...
715  while (!file.End())
716  {
717  Anope::string line = file.Read();
718  ++linenumber;
719 
720  /* If this line is completely empty and we are in a quote, just append a newline */
721  if (line.empty() && in_quote)
722  wordbuffer += "\n";
723 
724  for (unsigned c = 0, len = line.length(); c < len; ++c)
725  {
726  char ch = line[c];
727  if (in_quote)
728  {
729  /* Strip leading white spaces from multi line quotes */
730  if (c == 0)
731  {
732  while (c < len && isspace(line[c]))
733  ++c;
734  ch = line[c];
735  }
736 
737  /* Allow \" in quotes */
738  if (ch == '\\' && c + 1 < len && line[c + 1] == '"')
739  wordbuffer += line[++c];
740  else if (ch == '"')
741  in_quote = in_word = false;
742  else if (ch)
743  wordbuffer += ch;
744  }
745  else if (in_comment)
746  {
747  if (ch == '*' && c + 1 < len && line[c + 1] == '/')
748  {
749  in_comment = false;
750  ++c;
751  // We might be at an eol, so continue on and process it
752  }
753  else
754  continue;
755  }
756  else if (ch == '#' || (ch == '/' && c + 1 < len && line[c + 1] == '/'))
757  c = len - 1; // Line comment, ignore the rest of the line (much like this one!)
758  else if (ch == '/' && c + 1 < len && line[c + 1] == '*')
759  {
760  // Multiline (or less than one line) comment
761  in_comment = true;
762  ++c;
763  continue;
764  }
765  else if (!in_word && (ch == '(' || ch == '_' || ch == ')'))
766  ;
767  else if (ch == '"')
768  {
769  // Quotes are valid only in the value position
770  if (block_stack.empty() || itemname.empty())
771  {
772  file.Close();
773  throw ConfigException("Unexpected quoted string: " + file.GetName() + ":" + stringify(linenumber));
774  }
775  if (in_word || !wordbuffer.empty())
776  {
777  file.Close();
778  throw ConfigException("Unexpected quoted string (prior unhandled words): " + file.GetName() + ":" + stringify(linenumber));
779  }
780  in_quote = in_word = true;
781  }
782  else if (ch == '=')
783  {
784  if (block_stack.empty())
785  {
786  file.Close();
787  throw ConfigException("Config item outside of section (or stray '='): " + file.GetName() + ":" + stringify(linenumber));
788  }
789 
790  if (!itemname.empty() || wordbuffer.empty())
791  {
792  file.Close();
793  throw ConfigException("Stray '=' sign or item without value: " + file.GetName() + ":" + stringify(linenumber));
794  }
795 
796  in_word = false;
797  itemname = wordbuffer;
798  wordbuffer.clear();
799  }
800  else if (ch == '{')
801  {
802  if (wordbuffer.empty())
803  {
804  block_stack.push(NULL);
805  // Commented or unnamed section
806  continue;
807  }
808 
809  if (!block_stack.empty() && !block_stack.top())
810  {
811  // Named block inside of a commented block
812  in_word = false;
813  wordbuffer.clear();
814  block_stack.push(NULL);
815  continue;
816  }
817 
818  Block *b = block_stack.empty() ? this : block_stack.top();
819  block_map::iterator it = b->blocks.insert(std::make_pair(wordbuffer, Configuration::Block(wordbuffer)));
820  b = &it->second;
821  b->linenum = linenumber;
822  block_stack.push(b);
823 
824  in_word = false;
825  wordbuffer.clear();
826  continue;
827  }
828  else if (ch == ' ' || ch == '\r' || ch == '\t')
829  {
830  // Terminate word
831  in_word = false;
832  }
833  else if (ch == ';' || ch == '}')
834  ;
835  else
836  {
837  if (!in_word && !wordbuffer.empty())
838  {
839  file.Close();
840  throw ConfigException("Unexpected word: " + file.GetName() + ":" + stringify(linenumber));
841  }
842  wordbuffer += ch;
843  in_word = true;
844  }
845 
846  if (ch == ';' || ch == '}' || c + 1 >= len)
847  {
848  bool eol = c + 1 >= len;
849 
850  if (!eol && in_quote)
851  // Allow ; and } in quoted strings
852  continue;
853 
854  if (in_quote)
855  {
856  // Quotes can span multiple lines; all we need to do is go to the next line without clearing things
857  wordbuffer += "\n";
858  continue;
859  }
860 
861  in_word = false;
862  if (!itemname.empty())
863  {
864  if (block_stack.empty())
865  {
866  file.Close();
867  throw ConfigException("Stray ';' outside of block: " + file.GetName() + ":" + stringify(linenumber));
868  }
869 
870  Block *b = block_stack.top();
871 
872  if (b)
873  Log(LOG_DEBUG) << "ln " << linenumber << " EOL: s='" << b->name << "' '" << itemname << "' set to '" << wordbuffer << "'";
874 
875  /* Check defines */
876  for (int i = 0; i < this->CountBlock("define"); ++i)
877  {
878  Block *define = this->GetBlock("define", i);
879 
880  const Anope::string &dname = define->Get<const Anope::string>("name");
881 
882  if (dname == wordbuffer && define != b)
883  wordbuffer = define->Get<const Anope::string>("value");
884  }
885 
886  if (b)
887  b->items[itemname] = wordbuffer;
888 
889  wordbuffer.clear();
890  itemname.clear();
891  }
892 
893  if (ch == '}')
894  {
895  if (block_stack.empty())
896  {
897  file.Close();
898  throw ConfigException("Stray '}': " + file.GetName() + ":" + stringify(linenumber));
899  }
900 
901  block_stack.pop();
902  }
903  }
904  }
905  }
906 
907  file.Close();
908 
909  if (in_comment)
910  throw ConfigException("Unterminated multiline comment at end of file: " + file.GetName());
911  if (in_quote)
912  throw ConfigException("Unterminated quote at end of file: " + file.GetName());
913  if (!itemname.empty() || !wordbuffer.empty())
914  throw ConfigException("Unexpected garbage at end of file: " + file.GetName());
915  if (!block_stack.empty())
916  throw ConfigException("Unterminated block at end of file: " + file.GetName() + ". Block was opened on line " + stringify(block_stack.top()->linenum));
917 }
918 
Serialize::Reference< NickCore > nc
Definition: account.h:47
Definition: bots.h:24
Anope::string permission
Definition: commands.h:34
static NickAlias * Find(const Anope::string &nick)
Definition: nickalias.cpp:121
File ServicesConf("services.conf", false)
void CaseMapRebuild()
Definition: hashcomp.cpp:20
CoreExport Serialize::Checker< nickcore_map > NickCoreList
Anope::string name
Definition: modules.h:221
int CountBlock(const Anope::string &name)
Definition: config.cpp:35
std::vector< Anope::string > botchannels
Definition: bots.h:37
static Module * FindModule(const Anope::string &name)
std::vector< CommandGroup > CommandGroups
Definition: config.h:122
Anope::string name
Definition: commands.h:32
std::vector< Anope::string > ModulesAutoLoad
Definition: config.h:124
Definition: hashcomp.h:84
void clear()
Definition: anope.h:187
void Join(Channel *c, ChannelStatus *status=NULL)
Definition: bots.cpp:193
Anope::string certfp
Definition: opertype.h:27
Anope::string desc
Definition: access.cpp:23
Definition: opertype.h:18
Anope::string group
Definition: commands.h:36
static ModuleReturn UnloadModule(Module *m, User *u)
static void AddPrivilege(Privilege p)
Definition: access.cpp:78
Reference< Channel > c
Definition: commands.h:65
static void ClearPrivileges()
Definition: access.cpp:118
bool UseStrictPrivmsg
Definition: config.h:105
void GetTokens(T &token)
Definition: anope.h:587
#define BUFSIZE
Definition: services.h:18
const Anope::string & GetName() const
Definition: opertype.cpp:113
time_t ReadTimeout
Definition: config.h:95
item_map items
Definition: config.h:34
void AddPriv(const Anope::string &privstr)
Definition: opertype.cpp:108
Anope::string name
Definition: opertype.h:21
CommandInfo & SetCommand(const Anope::string &cname, const Anope::string &sname, const Anope::string &permission="")
Definition: bots.cpp:232
Anope::string name
Definition: config.h:33
bool conf
Definition: bots.h:43
Anope::string name
Definition: commands.h:21
bool equals_ci(const char *_str) const
Definition: anope.h:78
Anope::string vhost
Definition: opertype.h:30
string lower() const
Definition: anope.h:255
std::vector< OperType * > MyOperTypes
Definition: config.h:116
#define FOREACH_MOD(ename, args)
Definition: modules.h:62
std::vector< LogInfo > LogInfos
Definition: config.h:112
bool botchannel
Definition: channels.h:52
Anope::string modes
Definition: opertype.h:72
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
static std::vector< Oper * > opers
Definition: opertype.h:35
size_type length() const
Definition: anope.h:131
void LoadConf(File &file)
Definition: config.cpp:703
Block * GetBlock(const Anope::string &name, int num=0)
Definition: config.cpp:43
Block * GetCommand(CommandSource &)
Definition: config.cpp:623
ModeType type
Definition: modes.h:49
Definition: Config.cs:26
OperType * ot
Definition: opertype.h:23
Anope::string description
Definition: commands.h:21
static char GetStatusChar(char symbol)
Definition: modes.cpp:558
static const size_type npos
Definition: anope.h:44
Anope::string command
Definition: commands.h:69
CoreExport time_t DoTime(const Anope::string &s)
Definition: misc.cpp:275
const Anope::string & GetName() const
Definition: config.cpp:647
const Anope::string & GetUID() const
Definition: users.cpp:230
CoreExport Serialize::Checker< botinfo_map > BotListByNick
Anope::string Read()
Definition: config.cpp:681
std::locale casemap
Definition: hashcomp.cpp:15
static ChannelMode * FindChannelModeByChar(char mode)
Definition: modes.cpp:524
void Part(Channel *c, const Anope::string &reason="")
Definition: bots.cpp:211
Oper * o
Definition: account.h:131
std::vector< Oper * > Opers
Definition: config.h:118
static Anope::map< std::pair< bool, Anope::string > > modes
Definition: cs_mode.cpp:745
bool empty() const
Definition: anope.h:126
std::vector< Anope::string > Ulines
Definition: config.h:114
bool require_oper
Definition: opertype.h:25
BotInfo * GetClient(const Anope::string &name)
Definition: config.cpp:611
static void ValidateNotZero(const Anope::string &block, const Anope::string &name, T value)
Definition: config.cpp:108
time_t TimeoutCheck
Definition: config.h:103
ChanUserContainer * FindUser(User *u) const
Definition: channels.cpp:173
CoreExport Anope::string ConfigDir
Definition: init.cpp:33
bool Set(const Anope::string &tag, const Anope::string &value)
Definition: config.cpp:56
ChannelStatus status
Definition: channels.h:28
Anope::string stringify(const T &x)
Definition: anope.h:710
bool hide
Definition: commands.h:38
static ModuleReturn LoadModule(const Anope::string &modname, User *u)
std::map< Anope::string, Block * > modules
Definition: config.h:127
static Channel * Find(const Anope::string &name)
Definition: channels.cpp:920
bool GetToken(Anope::string &token)
Definition: hashcomp.cpp:99
Anope::string name
Definition: access.cpp:22
void SetMode(BotInfo *bi, ChannelMode *cm, const Anope::string &param="", bool enforce_mlock=true)
Definition: channels.cpp:401
bool prepend_channel
Definition: commands.h:40
Anope::map< Anope::string > bots
Definition: config.h:128
const Anope::string & Modes() const
Definition: modes.cpp:96
const char * c_str() const
Definition: anope.h:117
void Inherits(OperType *ot)
Definition: opertype.cpp:118
Definition: logger.h:53
T Get(const Anope::string &tag)
Definition: config.h:44
File(const Anope::string &, bool)
Definition: config.cpp:638
void AddCommand(const Anope::string &cmdstr)
Definition: opertype.cpp:103
const Anope::string & GetName() const
Definition: config.cpp:30
Anope::string name
Definition: config.h:75
Block * GetModule(Module *)
Definition: config.cpp:580
Anope::string StrictPrivmsg
Definition: config.h:108
void Post(Conf *old)
Definition: config.cpp:538
const item_map * GetItems() const
Definition: config.cpp:65
bool IsOpen() const
Definition: config.cpp:652
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
static void ValidateNotEmpty(const Anope::string &block, const Anope::string &name, const Anope::string &value)
Definition: config.cpp:96
static BotInfo * Find(const Anope::string &nick, bool nick_only=false)
Definition: bots.cpp:249
Block(const Anope::string &)
Definition: config.cpp:26
block_map blocks
Definition: config.h:35
Anope::string password
Definition: opertype.h:26
std::vector< Anope::string > hosts
Definition: opertype.h:29
std::vector< Uplink > Uplinks
Definition: config.h:110
Anope::string DefLanguage
Definition: config.h:101
bool End() const
Definition: config.cpp:676
void RemoveMode(BotInfo *bi, ChannelMode *cm, const Anope::string &param="", bool enforce_mlock=true)
Definition: channels.cpp:459
static void ValidateNoSpaces(const Anope::string &block, const Anope::string &name, const Anope::string &value)
Definition: config.cpp:102