DaZeus  2.0
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
config.cpp
Go to the documentation of this file.
1 
6 #include <iostream>
7 #include <dotconf.h>
8 #include <cassert>
9 #include "./config.h"
10 #include "utils.h"
11 #include "../contrib/libdazeus-irc/src/utils.h"
12 #include "server.h"
13 #include <limits>
14 
15 enum section {
22 };
23 
30 
32  std::vector<SocketConfig*> sockets;
33  std::vector<NetworkConfig*> networks;
34  std::vector<PluginConfig*> plugins;
35 
42  std::string error;
43 };
44 
46 : global(0)
47 , database(0)
48 , file(file)
49 , state(new ConfigReaderState())
50 , is_read(false)
51 {}
52 
54  delete database;
55  std::vector<NetworkConfig*>::iterator it;
56  for(it = networks.begin(); it != networks.end(); ++it) {
57  std::vector<ServerConfig*>::iterator sit;
58  for(sit = (*it)->servers.begin(); sit != (*it)->servers.end(); ++sit) {
59  delete *sit;
60  }
61  (*it)->servers.clear();
62  delete *it;
63  }
64  networks.clear();
65  std::vector<SocketConfig*>::iterator sockit;
66  for(sockit = sockets.begin(); sockit != sockets.end(); ++sockit) {
67  delete *sockit;
68  }
69  sockets.clear();
70  std::vector<PluginConfig*>::iterator pit;
71  for(pit = plugins.begin(); pit != plugins.end(); ++pit) {
72  delete *pit;
73  }
74  plugins.clear();
75  delete global;
76  delete state;
77 }
78 
79 static DOTCONF_CB(sect_open);
80 static DOTCONF_CB(sect_close);
81 static DOTCONF_CB(option);
82 
83 static const configoption_t options[] = {
84  {"<socket>", ARG_NONE, sect_open, NULL, CTX_ALL},
85  {"</socket>", ARG_NONE, sect_close, NULL, CTX_ALL},
86  {"<database>", ARG_NONE, sect_open, NULL, CTX_ALL},
87  {"</database>", ARG_NONE, sect_close, NULL, CTX_ALL},
88  {"<network", ARG_STR, sect_open, NULL, CTX_ALL},
89  {"</network>", ARG_NONE, sect_close, NULL, CTX_ALL},
90  {"<server>", ARG_NONE, sect_open, NULL, CTX_ALL},
91  {"</server>", ARG_NONE, sect_close, NULL, CTX_ALL},
92  {"<plugin", ARG_STR, sect_open, NULL, CTX_ALL},
93  {"</plugin>", ARG_NONE, sect_close, NULL, CTX_ALL},
94  {"nickname", ARG_RAW, option, NULL, CTX_ALL},
95  {"username", ARG_RAW, option, NULL, CTX_ALL},
96  {"fullname", ARG_RAW, option, NULL, CTX_ALL},
97  {"plugindirectory", ARG_RAW, option, NULL, CTX_ALL},
98  {"highlight", ARG_RAW, option, NULL, CTX_ALL},
99  {"type", ARG_RAW, option, NULL, CTX_ALL},
100  {"path", ARG_RAW, option, NULL, CTX_ALL},
101  {"host", ARG_RAW, option, NULL, CTX_ALL},
102  {"port", ARG_INT, option, NULL, CTX_ALL},
103  {"password", ARG_RAW, option, NULL, CTX_ALL},
104  {"database", ARG_RAW, option, NULL, CTX_ALL},
105  {"options", ARG_RAW, option, NULL, CTX_ALL},
106  {"autoconnect", ARG_RAW, option, NULL, CTX_ALL},
107  {"priority", ARG_INT, option, NULL, CTX_ALL},
108  {"ssl", ARG_RAW, option, NULL, CTX_ALL},
109  {"sslverify", ARG_RAW, option, NULL, CTX_ALL},
110  {"executable", ARG_RAW, option, NULL, CTX_ALL},
111  {"scope", ARG_RAW, option, NULL, CTX_ALL},
112  {"parameters", ARG_RAW, option, NULL, CTX_ALL},
113  {"var", ARG_RAW, option, NULL, CTX_ALL},
114  LAST_OPTION
115 };
116 
117 FUNC_ERRORHANDLER(error_handler);
118 
119 static bool bool_is_true(std::string s) {
120  s = strToLower(trim(s));
121  return s == "true" || s == "yes" || s == "1";
122 }
123 
125  if(is_read) return;
126 
127  configfile_t *configfile = dotconf_create(
128  const_cast<char*>(file.c_str()), options,
129  this, CASE_INSENSITIVE);
130  if(!configfile) {
131  throw exception(state->error = "Error opening config file.");
132  }
133 
134  configfile->errorhandler = (dotconf_errorhandler_t) error_handler;
135 
136  // Initialise global config in progress. Other fields will be
137  // initialised when a section is started, global starts now.
138  state->global_progress = new GlobalConfig();
139  if(dotconf_command_loop(configfile) == 0 || state->error.length() > 0) {
140  dotconf_cleanup(configfile);
141  if(state->error.size() == 0)
142  state->error = "Error reading config file.";
143  throw exception(state->error);
144  }
145 
146  if(state->database_progress == 0) {
147  throw exception("No Database block defined in config file.");
148  }
149 
150  assert(state->socket_progress == 0);
151  assert(state->network_progress == 0);
152  assert(state->server_progress == 0);
153  assert(state->plugin_progress == 0);
154 
155  sockets = state->sockets;
156  networks = state->networks;
157  plugins = state->plugins;
158  global = state->global_progress;
159  database = state->database_progress;
160 
161  state->sockets.clear();
162  state->networks.clear();
163  state->plugins.clear();
164 
165  dotconf_cleanup(configfile);
166  is_read = true;
167 }
168 
169 FUNC_ERRORHANDLER(error_handler)
170 {
171  (void)type;
172  (void)dc_errno;
173 
174  dazeus::ConfigReaderState *s = ((dazeus::ConfigReader*)configfile->context)->_state();
175  if(s->error.length() == 0) {
176  s->error = msg;
177  } else {
178  s->error.append("\n" + std::string(msg));
179  }
180  return 1;
181 }
182 
183 static DOTCONF_CB(sect_open)
184 {
185  (void)ctx;
186 
187  dazeus::ConfigReaderState *s = ((dazeus::ConfigReader*)cmd->context)->_state();
188  std::string name(cmd->name);
189 
190  switch(s->current_section) {
191  case S_ROOT:
192  assert(s->global_progress != NULL);
193  assert(s->socket_progress == NULL);
194  assert(s->network_progress == NULL);
195  assert(s->server_progress == NULL);
196  if(name == "<socket>") {
199  } else if(name == "<database>") {
200  if(s->database_progress != NULL) {
201  return "More than one Database block defined in configuration file.";
202  }
205  } else if(name == "<network") {
206  std::string networkname = cmd->data.str;
207  networkname.resize(networkname.length() - 1);
209  std::string default_nick = s->global_progress->default_nickname;
210  std::string default_user = s->global_progress->default_username;
211  std::string default_full = s->global_progress->default_fullname;
212  s->network_progress = new dazeus::NetworkConfig(networkname, networkname,
213  default_nick, default_user, default_full);
214  } else if(name == "<plugin") {
215  std::string pluginname = cmd->data.str;
216  pluginname.resize(pluginname.length() - 1);
217  if(pluginname.length() == 0) {
218  return "All plugins must have a name in their <Plugin> tag.";
219  }
221  s->plugin_progress = new dazeus::PluginConfig(pluginname);
222  } else {
223  return "Logic error";
224  }
225  break;
226  case S_NETWORK:
227  assert(s->network_progress != NULL);
228  assert(s->server_progress == NULL);
229  if(name == "<server>") {
232  }
233  break;
234  default:
235  return "Logic error";
236  }
237  return NULL;
238 }
239 
240 static DOTCONF_CB(sect_close)
241 {
242  (void)ctx;
243 
244  dazeus::ConfigReaderState *s = ((dazeus::ConfigReader*)cmd->context)->_state();
245 
246  std::string name(cmd->name);
247  switch(s->current_section) {
248  // we can never have a section end in the root context
249  case S_ROOT: return "Logic error";
250  case S_SOCKET:
251  if(name == "</socket>") {
252  s->sockets.push_back(s->socket_progress);
253  s->socket_progress = 0;
254  s->current_section = S_ROOT;
255  } else {
256  return "Logic error";
257  }
258  break;
259  case S_DATABASE:
260  if(name == "</database>") {
261  s->current_section = S_ROOT;
262  } else {
263  return "Logic error";
264  }
265  break;
266  case S_NETWORK:
267  if(name == "</network>") {
268  s->networks.push_back(s->network_progress);
269  s->network_progress = 0;
270  s->current_section = S_ROOT;
271  } else {
272  return "Logic error";
273  }
274  break;
275  case S_SERVER:
276  if(name == "</server>") {
277  s->network_progress->servers.push_back(s->server_progress);
278  s->server_progress = 0;
280  } else {
281  return "Logic error";
282  }
283  break;
284  case S_PLUGIN:
285  if(name == "</plugin>") {
286  s->plugins.push_back(s->plugin_progress);
287  s->plugin_progress = 0;
288  s->current_section = S_ROOT;
289  } else {
290  return "Logic error";
291  }
292  break;
293  default:
294  return "Logic error";
295  }
296 
297  return NULL;
298 }
299 static DOTCONF_CB(option)
300 {
301  (void)ctx;
302 
303  dazeus::ConfigReaderState *s = ((dazeus::ConfigReader*)cmd->context)->_state();
304  std::string name(cmd->name);
305  switch(s->current_section) {
306  case S_ROOT: {
308  assert(g);
309  if(name == "nickname") {
310  g->default_nickname = trim(cmd->data.str);
311  } else if(name == "username") {
312  g->default_username = trim(cmd->data.str);
313  } else if(name == "fullname") {
314  g->default_fullname = trim(cmd->data.str);
315  } else if(name == "plugindirectory") {
316  g->plugindirectory = trim(cmd->data.str);
317  } else if(name == "highlight") {
318  g->highlight = trim(cmd->data.str);
319  } else {
320  s->error = "Invalid option name in root context: " + name;
321  return "Configuration file contains errors";
322  }
323  break;
324  }
325  case S_SOCKET: {
327  assert(sc);
328  if(name == "type") {
329  sc->type = trim(cmd->data.str);
330  } else if(name == "path") {
331  sc->path = trim(cmd->data.str);
332  } else if(name == "host") {
333  sc->host = trim(cmd->data.str);
334  } else if(name == "port") {
335  if(cmd->data.value > std::numeric_limits<uint16_t>::max() || cmd->data.value < 0) {
336  return "Invalid value for 'port'";
337  }
338  sc->port = cmd->data.value;
339  } else {
340  s->error = "Invalid option name in socket context: " + name;
341  return "Configuration file contains errors";
342  }
343  break;
344  }
345  case S_DATABASE: {
347  assert(dc);
348  if(name == "type") {
349  dc->type = trim(cmd->data.str);
350  } else if(name == "host") {
351  dc->hostname = trim(cmd->data.str);
352  } else if(name == "port") {
353  dc->port = cmd->data.value;
354  } else if(name == "username") {
355  dc->username = trim(cmd->data.str);
356  } else if(name == "password") {
357  dc->password = trim(cmd->data.str);
358  } else if(name == "database") {
359  dc->database = trim(cmd->data.str);
360  } else if(name == "options") {
361  dc->options = trim(cmd->data.str);
362  } else {
363  s->error = "Invalid option name in database context: " + name;
364  return "Configuration file contains errors";
365  }
366  break;
367  }
368  case S_NETWORK: {
370  assert(nc);
371  if(name == "autoconnect") {
372  nc->autoConnect = bool_is_true(cmd->data.str);
373  } else if(name == "nickname") {
374  nc->nickName = trim(cmd->data.str);
375  } else if(name == "username") {
376  nc->userName = trim(cmd->data.str);
377  } else if(name == "fullname") {
378  nc->fullName = trim(cmd->data.str);
379  } else if(name == "password") {
380  nc->password = trim(cmd->data.str);
381  } else {
382  s->error = "Invalid option name in network context: " + name;
383  return "Configuration file contains errors";
384  }
385  break;
386  }
387  case S_SERVER: {
389  assert(sc);
390  if(name == "host") {
391  sc->host = trim(cmd->data.str);
392  } else if(name == "port") {
393  sc->port = cmd->data.value;
394  } else if(name == "priority") {
395  sc->priority = cmd->data.value;
396  } else if(name == "ssl") {
397  sc->ssl = bool_is_true(cmd->data.str);
398  } else if(name == "sslverify") {
399  sc->ssl_verify = bool_is_true(cmd->data.str);
400  } else {
401  s->error = "Invalid option name in server context: " + name;
402  return "Configuration file contains errors";
403  }
404  break;
405  }
406  case S_PLUGIN: {
408  assert(pc);
409  if(name == "path") {
410  pc->path = trim(cmd->data.str);
411  } else if(name == "executable") {
412  pc->executable = trim(cmd->data.str);
413  } else if(name == "scope") {
414  std::string scope = strToLower(trim(cmd->data.str));
415  if(scope == "network") {
416  pc->per_network = true;
417  } else if(scope != "global") {
418  s->error = "Invalid value for Scope for plugin " + pc->name;
419  return "Configuration file contains errors";
420  }
421  } else if(name == "parameters") {
422  pc->parameters = trim(cmd->data.str);
423  } else if(name == "var") {
424  if(cmd->arg_count != 2) {
425  s->error = "Invalid amount of parameters to Var in plugin context";
426  return "Configuration file contains errors";
427  }
428  pc->config[trim(cmd->data.list[0])] = trim(cmd->data.list[1]);
429  } else {
430  s->error = "Invalid option name in plugin context: " + name;
431  return "Configuration file contains errors";
432  }
433  break;
434  }
435  default:
436  return "Logic error";
437  }
438  return NULL;
439 }