DaZeus  2.0
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
plugincomm.cpp
Go to the documentation of this file.
1 
6 #define __STDC_LIMIT_MACROS
7 #include <stdint.h>
8 #include <jansson.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 #include <sys/uio.h>
13 #include <sys/select.h>
14 #include <netinet/ip.h>
15 #include <netdb.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <assert.h>
22 #include <errno.h>
23 #include <time.h>
24 #include <ctype.h>
25 #include <poll.h>
26 #include <limits.h>
27 
28 #include <string>
29 #include <sstream>
30 
31 #include "plugincomm.h"
32 #include "network.h"
33 #include "config.h"
34 #include "server.h"
35 #include "config.h"
36 #include "dazeus.h"
37 #include "database.h"
38 #include "utils.h"
39 
40 static std::string realpath(std::string path) {
41  char newpath[PATH_MAX];
42  realpath(path.c_str(), newpath);
43  return std::string(newpath);
44 }
45 
46 #define NOTBLOCKING(x) fcntl(x, F_SETFL, fcntl(x, F_GETFL) | O_NONBLOCK)
47 
50 , tcpServers_()
51 , localServers_()
52 , commandQueue_()
53 , sockets_()
54 , database_(d)
55 , config_(c)
56 , dazeus_(bot)
57 {
58 }
59 
61  std::map<int,SocketInfo>::iterator it;
62  for(it = sockets_.begin(); it != sockets_.end(); ++it) {
63  close(it->first);
64  }
65  std::vector<int>::iterator it2;
66  for(it2 = tcpServers_.begin(); it2 != tcpServers_.end(); ++it2) {
67  close(*it2);
68  }
69  for(it2 = localServers_.begin(); it2 != localServers_.end(); ++it2) {
70  close(*it2);
71  }
72 }
73 
74 void dazeus::PluginComm::run(int timeout_sec) {
75  fd_set sockets, out_sockets;
76  int highest = 0;
77  struct timeval timeout;
78  FD_ZERO(&sockets);
79  FD_ZERO(&out_sockets);
80  std::vector<int>::iterator it;
81  for(it = tcpServers_.begin(); it != tcpServers_.end(); ++it) {
82  if(*it > highest)
83  highest = *it;
84  FD_SET(*it, &sockets);
85  }
86  for(it = localServers_.begin(); it != localServers_.end(); ++it) {
87  if(*it > highest)
88  highest = *it;
89  FD_SET(*it, &sockets);
90  }
91  std::map<int,SocketInfo>::iterator it2;
92  for(it2 = sockets_.begin(); it2 != sockets_.end(); ++it2) {
93  if(it2->first > highest)
94  highest = it2->first;
95  FD_SET(it2->first, &sockets);
96  }
97  // and add the IRC descriptors
98  std::vector<Network*>::const_iterator nit;
99  for(nit = dazeus_->networks().begin(); nit != dazeus_->networks().end(); ++nit) {
100  if((*nit)->activeServer()) {
101  int ircmaxfd = 0;
102  (*nit)->addDescriptors(&sockets, &out_sockets, &ircmaxfd);
103  if(ircmaxfd > highest)
104  highest = ircmaxfd;
105  }
106  }
107  timeout.tv_sec = timeout_sec;
108  timeout.tv_usec = 0;
109  int socks = select(highest + 1, &sockets, &out_sockets, NULL, &timeout);
110  if(socks < 0) {
111  if(errno != EINTR) {
112  fprintf(stderr, "select() failed: %s\n", strerror(errno));
113  }
114 
115  if(errno == EBADF) {
116  // hopefully it was a regular plugin socket that went bad
117  // then poll() will notice it
118  poll();
119  }
120  return;
121  }
122  else if(socks == 0) {
123  // No sockets fired, just check for network timeouts
124  for(nit = dazeus_->networks().begin(); nit != dazeus_->networks().end(); ++nit) {
125  if((*nit)->activeServer()) {
126  (*nit)->checkTimeouts();
127  }
128  }
129  return;
130  }
131  for(it = tcpServers_.begin(); it != tcpServers_.end(); ++it) {
132  if(FD_ISSET(*it, &sockets)) {
133  newTcpConnection();
134  break;
135  }
136  }
137  for(it = localServers_.begin(); it != localServers_.end(); ++it) {
138  if(FD_ISSET(*it, &sockets)) {
139  newLocalConnection();
140  break;
141  }
142  }
143  for(it2 = sockets_.begin(); it2 != sockets_.end(); ++it2) {
144  if(FD_ISSET(it2->first, &sockets)) {
145  poll();
146  break;
147  }
148  }
149  for(nit = dazeus_->networks().begin(); nit != dazeus_->networks().end(); ++nit) {
150  if((*nit)->activeServer()) {
151  (*nit)->processDescriptors(&sockets, &out_sockets);
152  (*nit)->checkTimeouts();
153  }
154  }
155 }
156 
158  std::vector<SocketConfig*>::const_iterator it;
159 
160  for(it = config_->getSockets().begin(); it != config_->getSockets().end(); ++it) {
161  SocketConfig *sc = *it;
162  if(sc->type == "unix") {
163  unlink(sc->path.c_str());
164  int server = ::socket(AF_UNIX, SOCK_STREAM, 0);
165  if(server <= 0) {
166  fprintf(stderr, "(PluginComm) Failed to create socket at %s: %s\n", sc->path.c_str(), strerror(errno));
167  continue;
168  }
169  NOTBLOCKING(server);
170  struct sockaddr_un addr;
171  addr.sun_family = AF_UNIX;
172  strcpy(addr.sun_path, sc->path.c_str());
173  if(bind(server, (struct sockaddr*) &addr, sizeof(struct sockaddr_un)) < 0) {
174  close(server);
175  fprintf(stderr, "(PluginComm) Failed to start listening for local connections on %s: %s\n", sc->path.c_str(), strerror(errno));
176  continue;
177  }
178  if(listen(server, 5) < 0) {
179  close(server);
180  unlink(sc->path.c_str());
181  }
182  // Plugins run in a different working directory, so
183  // make sure the socket path is an absolute path
184  sc->path = realpath(sc->path);
185  localServers_.push_back(server);
186  } else if(sc->type == "tcp") {
187  std::string portStr;
188  {
189  std::stringstream portStrSs;
190  portStrSs << sc->port;
191  portStr = portStrSs.str();
192  }
193 
194  struct addrinfo *hints, *result;
195  hints = (struct addrinfo*)calloc(1, sizeof(struct addrinfo));
196  hints->ai_flags = 0;
197  hints->ai_family = AF_INET;
198  hints->ai_socktype = SOCK_STREAM;
199  hints->ai_protocol = 0;
200 
201  int s = getaddrinfo(sc->host.c_str(), portStr.c_str(), hints, &result);
202  free(hints);
203  hints = 0;
204 
205  if(s != 0) {
206  fprintf(stderr, "(PluginComm) Failed to resolve TCP address: %s\n", (char*)gai_strerror(s));
207  continue;
208  }
209 
210  int server = ::socket(AF_INET, SOCK_STREAM, 0);
211  if(server <= 0) {
212  fprintf(stderr, "(PluginComm) Failed to create socket: %s\n", strerror(errno));
213  continue;
214  }
215  NOTBLOCKING(server);
216  if(bind(server, result->ai_addr, result->ai_addrlen) < 0) {
217  close(server);
218  fprintf(stderr, "(PluginComm) Failed to start listening for TCP connections on %s:%d: %s\n",
219  sc->host.c_str(), sc->port, strerror(errno));
220  }
221  if(listen(server, 5) < 0) {
222  close(server);
223  }
224 
225  // Now, if the IP is inaddr_any, change it to localhost
226  if(result->ai_family == AF_INET) {
227  sockaddr_in *addr = (sockaddr_in*)result->ai_addr;
228  if(addr->sin_addr.s_addr == INADDR_ANY) {
229  sc->host = "127.0.0.1";
230  }
231  }
232 
233  tcpServers_.push_back(server);
234  } else {
235  fprintf(stderr, "(PluginComm) Skipping socket: unknown type >%s<\n", sc->type.c_str());
236  }
237  }
238 }
239 
240 void dazeus::PluginComm::newTcpConnection() {
241  std::vector<int>::iterator it;
242  for(it = tcpServers_.begin(); it != tcpServers_.end(); ++it) {
243  while(1) {
244  int sock = accept(*it, NULL, NULL);
245  if(sock < 0) {
246  if(errno != EWOULDBLOCK)
247  fprintf(stderr, "Error on listening socket: %s\n", strerror(errno));
248  break;
249  }
250  NOTBLOCKING(sock);
251  sockets_[sock] = SocketInfo("tcp");
252  assert(!sockets_[sock].didHandshake());
253  }
254  }
255 }
256 
257 void dazeus::PluginComm::newLocalConnection() {
258  std::vector<int>::iterator it;
259  for(it = localServers_.begin(); it != localServers_.end(); ++it) {
260  while(1) {
261  int sock = accept(*it, NULL, NULL);
262  if(sock < 0) {
263  if(errno != EWOULDBLOCK)
264  fprintf(stderr, "Error on listening socket: %s\n", strerror(errno));
265  break;
266  }
267  NOTBLOCKING(sock);
268  sockets_[sock] = SocketInfo("unix");
269  assert(!sockets_[sock].didHandshake());
270  }
271  }
272 }
273 
274 void dazeus::PluginComm::poll() {
275  std::vector<int> toRemove;
276  std::map<int,SocketInfo>::iterator it;
277  for(it = sockets_.begin(); it != sockets_.end(); ++it) {
278  int dev = it->first;
279  SocketInfo info = it->second;
280 
281  // check if it's in error state
282  struct pollfd fds[1];
283  fds[0].fd = dev;
284  fds[0].events = fds[0].revents = 0;
285  if(::poll(fds, 1, 0) == -1) {
286  perror("Failed to check socket error state");
287  close(dev);
288  toRemove.push_back(dev);
289  continue;
290  } else if(fds[0].revents & POLLERR || fds[0].revents & POLLHUP) {
291  close(dev);
292  toRemove.push_back(dev);
293  continue;
294  }
295 
296  bool appended = false;
297  while(1) {
298  char *readahead = (char*)malloc(512);
299  ssize_t r = read(dev, readahead, 512);
300  if(r == 0) {
301  // end-of-file
302  close(dev);
303  toRemove.push_back(dev);
304  break;
305  } else if(r < 0) {
306  if(errno != EWOULDBLOCK) {
307  fprintf(stderr, "Socket error: %s\n", strerror(errno));
308  close(dev);
309  toRemove.push_back(dev);
310  }
311  free(readahead);
312  break;
313  }
314  appended = true;
315  info.readahead.append(readahead, r);
316  free(readahead);
317  }
318  if(appended) {
319  // try reading as much commands as we can
320  bool parsedPacket;
321  do {
322  parsedPacket = false;
323  if(info.waitingSize == 0) {
324  std::stringstream s;
325  std::string waitingSize;
326  unsigned int j;
327  for(j = 0; j < info.readahead.size(); ++j) {
328  if(isdigit(info.readahead[j])) {
329  s << info.readahead[j];
330  } else if(info.readahead[j] == '{') {
331  int waitingSize;
332  s >> waitingSize;
333  assert(s);
334  info.waitingSize = waitingSize;
335  break;
336  }
337  }
338  // remove everything to j from the readahead buffer
339  if(info.waitingSize != 0)
340  info.readahead = info.readahead.substr(j);
341  }
342  if(info.waitingSize != 0) {
343  if((signed)info.readahead.length() >= info.waitingSize) {
344  std::string packet = info.readahead.substr(0, info.waitingSize);
345  info.readahead = info.readahead.substr(info.waitingSize);
346  handle(dev, packet, info);
347  info.waitingSize = 0;
348  parsedPacket = true;
349  }
350  }
351  } while(parsedPacket);
352  }
353  it->second = info;
354  }
355  std::vector<int>::iterator toRemoveIt;
356  for(toRemoveIt = toRemove.begin(); toRemoveIt != toRemove.end(); ++toRemoveIt) {
357  sockets_.erase(*toRemoveIt);
358  }
359 }
360 
361 void dazeus::PluginComm::dispatch(const std::string &event, const std::vector<std::string> &parameters) {
362  std::map<int,SocketInfo>::iterator it;
363  for(it = sockets_.begin(); it != sockets_.end(); ++it) {
364  SocketInfo &info = it->second;
365  if(info.isSubscribed(event)) {
366  info.dispatch(it->first, event, parameters);
367  }
368  }
369 }
370 
371 void dazeus::PluginComm::flushCommandQueue(const std::string &nick, bool identified) {
372  std::vector<Command*>::iterator cit;
373  for(cit = commandQueue_.begin(); cit != commandQueue_.end(); ++cit) {
374  Command *cmd = *cit;
375  // If there is at least one plugin that does sender checking,
376  // and the sender is not already known as identified, a whois
377  // check is necessary to send it to those plugins.
378  // If the whois check was done and it failed (nick is set and
379  // identified is false), send it to all other relevant plugins
380  // anyway.
381  // Check if we have all needed information
382  bool whoisRequired = false;
383  std::map<int,SocketInfo>::iterator it;
384  for(it = sockets_.begin(); it != sockets_.end(); ++it) {
385  SocketInfo &info = it->second;
386  if(info.commandMightNeedWhois(cmd->command)
387  && !cmd->network.isIdentified(cmd->origin)) {
388  whoisRequired = true;
389  }
390  }
391 
392  // If the whois check was not done yet, do it now, handle this
393  // command when the identified command comes in
394  if(whoisRequired && nick != cmd->origin) {
395  if(!cmd->whoisSent) {
396  cmd->network.sendWhois(cmd->origin);
397  cmd->whoisSent = true;
398  }
399  continue;
400  }
401 
402  // We either don't require whois for this command, or the
403  // whois status of the sender is known at this point (either
404  // saved in the network, or in the 'identified' variable).
405  assert(!whoisRequired || cmd->network.isIdentified(cmd->origin)
406  || nick == cmd->origin);
407 
408  std::vector<std::string> parameters;
409  parameters.push_back(cmd->network.networkName());
410  parameters.push_back(cmd->origin);
411  parameters.push_back(cmd->channel);
412  parameters.push_back(cmd->command);
413  parameters.push_back(cmd->fullArgs);
414  for(std::vector<std::string>::const_iterator itt = cmd->args.begin(); itt != cmd->args.end(); ++itt) {
415  parameters.push_back(*itt);
416  }
417 
418  for(it = sockets_.begin(); it != sockets_.end(); ++it) {
419  SocketInfo &info = it->second;
420  if(!info.isSubscribedToCommand(cmd->command, cmd->channel, cmd->origin,
421  cmd->network.isIdentified(cmd->origin) || identified, cmd->network)) {
422  continue;
423  }
424 
425  // Receiver, sender, network matched; dispatch command to this plugin
426  info.dispatch(it->first, "COMMAND", parameters);
427  }
428 
429  cit = commandQueue_.erase(cit);
430  cit--;
431  delete cmd;
432  }
433 }
434 
435 void dazeus::PluginComm::messageReceived( const std::string &origin, const std::string &message,
436  const std::string &receiver, Network *n ) {
437  assert(n != 0);
438 
439  GlobalConfig *c = config_->getGlobalConfig();
440 
441  std::string payload;
442  if( startsWith(message, n->nick() + ":", true)
443  || startsWith(message, n->nick() + ",", true)) {
444  payload = trim(message.substr(n->nick().length() + 1));
445  } else if(startsWith(message, c->highlight, true)) {
446  payload = trim(message.substr(c->highlight.length()));
447  }
448 
449  if(payload.length() != 0) {
450  // parse arguments from this string
451  bool inQuoteArg = false;
452  bool inEscape = false;
453  bool hasCommand = false;
454  std::vector<std::string> args;
455  std::string stringBuilder;
456  std::string fullArgs;
457  // loop through characters
458  for(unsigned i = 0; i < payload.length(); ++i) {
459  if(hasCommand)
460  fullArgs += payload.at(i);
461  if(inEscape) {
462  inEscape = false;
463  stringBuilder += payload.at(i);
464  } else if(payload.at(i) == '\\') {
465  inEscape = true;
466  } else if(!inQuoteArg && payload.at(i) == ' ') {
467  // finish this word
468  args.push_back(stringBuilder);
469  stringBuilder.clear();
470  hasCommand = true;
471  } else if(payload.at(i) == '"') {
472  inQuoteArg = !inQuoteArg;
473  } else {
474  stringBuilder += payload.at(i);
475  }
476  }
477  if(stringBuilder.length() != 0)
478  args.push_back(stringBuilder);
479 
480  if(args.size() > 0) {
481  const std::string command = args.front();
482  args.erase(args.begin());
483 
484  Command *cmd = new Command(*n);
485  cmd->origin = origin;
486  cmd->channel = receiver;
487  cmd->command = command;
488  cmd->fullArgs = trim(fullArgs);
489  cmd->args = args;
490  commandQueue_.push_back(cmd);
491  flushCommandQueue();
492  }
493  }
494  std::vector<std::string> args;
495  args << n->networkName() << origin << receiver << message << (n->isIdentified(origin) ? "true" : "false");
496  dispatch("PRIVMSG", args);
497 }
498 
499 void dazeus::PluginComm::ircEvent(const std::string &event, const std::string &origin, const std::vector<std::string> &params, Network *n) {
500  assert(n != 0);
501  std::vector<std::string> args;
502  args << n->networkName();
503 #define MIN(a) if(params.size() < a) { fprintf(stderr, "Too few parameters for event %s (%lu)\n", event.c_str(), params.size()); return; }
504  if(event == "PRIVMSG") {
505  MIN(2);
506  messageReceived(origin, params[1], params[0], n);
507  } else if(event == "NOTICE") {
508  MIN(2);
509  args << origin << params[0] << params[1] << (n->isIdentified(origin) ? "true" : "false");
510  dispatch("NOTICE", args);
511  } else if(event == "MODE" || event == "UMODE") {
512  MIN(1);
513  args << origin << params;
514  dispatch("MODE", args);
515  } else if(event == "NICK") {
516  MIN(1);
517  args << origin << params[0];
518  dispatch("NICK", args);
519  } else if(event == "JOIN") {
520  MIN(1);
521  args << origin << params[0];
522  dispatch("JOIN", args);
523  } else if(event == "PART") {
524  MIN(1);
525  std::string message;
526  if(params.size() == 2)
527  message = params[1];
528  args << origin << params[0] << message;
529  dispatch("PART", args);
530  } else if(event == "KICK") {
531  MIN(2);
532  std::string nick = params[1];
533  std::string message;
534  if(params.size() == 3)
535  message = params[2];
536  args << origin << params[0] << nick << message;
537  dispatch("KICK", args);
538  } else if(event == "INVITE") {
539  MIN(2);
540  std::string channel = params[1];
541  args << origin << channel;
542  dispatch("INVITE", args);
543  } else if(event == "QUIT") {
544  std::string message;
545  if(params.size() == 1)
546  message = params[0];
547  args << origin << message;
548  dispatch("QUIT", args);
549  } else if(event == "TOPIC") {
550  MIN(1);
551  std::string topic;
552  if(params.size() > 1)
553  topic = params[1];
554  args << origin << params[0] << topic;
555  dispatch("TOPIC", args);
556  } else if(event == "CONNECT") {
557  dispatch("CONNECT", args);
558  } else if(event == "DISCONNECT") {
559  dispatch("DISCONNECT", args);
560  } else if(event == "CTCP_REQ" || event == "CTCP") {
561  MIN(1);
562  // TODO: libircclient does not seem to tell us where the ctcp
563  // request was sent (user or channel), so just assume it was
564  // sent to our nick
565  std::string to = n->nick();
566  args << origin << to << params[0];
567  dispatch("CTCP", args);
568  } else if(event == "CTCP_REP") {
569  MIN(1);
570  // TODO: see above
571  std::string to = n->nick();
572  args << origin << to << params[0];
573  dispatch("CTCP_REP", args);
574  } else if(event == "CTCP_ACTION" || event == "ACTION") {
575  MIN(1);
576  std::string message;
577  if(params.size() >= 2)
578  message = params[1];
579  args << origin << params[0] << message;
580  dispatch("ACTION", args);
581  } else if(event == "WHOIS") {
582  MIN(2);
583  args << origin << params[0] << params[1];
584  dispatch("WHOIS", args);
585  flushCommandQueue(params[0], params[1] == "true");
586  } else if(event == "NAMES") {
587  MIN(2);
588  args << origin << params;
589  dispatch("NAMES", args);
590  } else if(event == "NUMERIC") {
591  MIN(1);
592  args << origin << params[0] << params;
593  dispatch("NUMERIC", args);
594  } else if(event == "ACTION_ME" || event == "CTCP_ME" || event == "PRIVMSG_ME") {
595  MIN(2);
596  args << origin << params;
597  dispatch(event, args);
598  } else if(event == "PONG") {
599  args << origin << params;
600  dispatch(event, args);
601  } else {
602  fprintf(stderr, "Unknown event: \"%s\" \"%s\" \"%s\"\n", Network::toString(n).c_str(), event.c_str(), origin.c_str());
603  args << origin << params[0] << event << params;
604  dispatch("UNKNOWN", args);
605  }
606 #undef MIN
607 }
608 
609 void dazeus::PluginComm::handle(int dev, const std::string &line, SocketInfo &info) {
610  const std::vector<Network*> &networks = dazeus_->networks();
611  std::vector<Network*>::const_iterator nit;
612 
613  json_error_t error;
614  json_t *n = json_loads(line.c_str(), 0, &error);
615  if(!n) {
616  fprintf(stderr, "Got incorrect JSON, ignoring: %s\n", error.text);
617  return;
618  }
619 
620  std::vector<std::string> params;
621  std::vector<std::string> scope;
622  std::string action;
623 
624  json_t *jParams = json_object_get(n, "params");
625  if(jParams) {
626  if(!json_is_array(jParams)) {
627  fprintf(stderr, "Got params, but of the wrong type, ignoring\n");
628  } else {
629  for(unsigned i = 0; i < json_array_size(jParams); ++i) {
630  json_t *v = json_array_get(jParams, i);
631  std::stringstream value;
632  switch(json_typeof(v)) {
633  case JSON_STRING: value << json_string_value(v); break;
634  case JSON_INTEGER: value << json_integer_value(v); break;
635  case JSON_REAL: value << json_real_value(v); break;
636  case JSON_TRUE: value << "true"; break;
637  case JSON_FALSE: value << "false"; break;
638  default: std::cerr << "Param " << i << " is of wrong type, ignoring\n";
639  }
640  params.push_back(value.str());
641  }
642  }
643  }
644 
645  json_t *jScope = json_object_get(n, "scope");
646  if(jScope) {
647  if(!json_is_array(jScope)) {
648  fprintf(stderr, "Got scope, but of the wrong type, ignoring\n");
649  } else {
650  for(unsigned int i = 0; i < json_array_size(jScope); ++i) {
651  json_t *v = json_array_get(jScope, i);
652  scope.push_back(json_is_string(v) ? json_string_value(v) : "");
653  }
654  }
655  }
656 
657  json_t *jAction = json_object_get(n, "get");
658  if(!jAction)
659  jAction = json_object_get(n, "do");
660  if(jAction) {
661  if(!json_is_string(jAction)) {
662  fprintf(stderr, "Got action, but of the wrong type, ignoring\n");
663  } else {
664  action = json_string_value(jAction);
665  }
666  }
667 
668  json_t *response = json_object();
669  // REQUEST WITHOUT A NETWORK
670  if(action == "networks") {
671  json_object_set_new(response, "got", json_string("networks"));
672  json_object_set_new(response, "success", json_true());
673  json_t *nets = json_array();
674  for(nit = networks.begin(); nit != networks.end(); ++nit) {
675  json_array_append_new(nets, json_string((*nit)->networkName().c_str()));
676  }
677  json_object_set_new(response, "networks", nets);
678  } else if(action == "handshake") {
679  json_object_set_new(response, "did", json_string("handshake"));
680  if(info.didHandshake()) {
681  json_object_set_new(response, "success", json_false());
682  json_object_set_new(response, "error", json_string("Already did handshake"));
683  } else if(params.size() < 4) {
684  json_object_set_new(response, "success", json_false());
685  json_object_set_new(response, "error", json_string("Missing parameters"));
686  } else if(params[2] != "1") {
687  json_object_set_new(response, "succes", json_false());
688  json_object_set_new(response, "error", json_string("Protocol version must be '1'"));
689  } else {
690  json_object_set_new(response, "success", json_true());
691  info.plugin_name = params[0];
692  info.plugin_version = params[1];
693  std::stringstream version(params[2]);
694  version >> info.protocol_version;
695  info.config_group = params[3];
696  std::cout << "Plugin handshake: " << info.plugin_name << " v"
697  << info.plugin_version << " (protocol version "
698  << info.protocol_version << ", config group "
699  << info.config_group << ")" << std::endl;
700  }
701  // REQUESTS ON A NETWORK
702  } else if(action == "channels" || action == "whois" || action == "join" || action == "part"
703  || action == "nick") {
704  if(action == "channels" || action == "nick") {
705  json_object_set_new(response, "got", json_string(action.c_str()));
706  } else {
707  json_object_set_new(response, "did", json_string(action.c_str()));
708  }
709  std::string network = "";
710  if(params.size() > 0) {
711  network = params[0];
712  }
713  json_object_set_new(response, "network", json_string(network.c_str()));
714  Network *net = 0;
715  for(nit = networks.begin(); nit != networks.end(); ++nit) {
716  if((*nit)->networkName() == network) {
717  net = *nit; break;
718  }
719  }
720  if(net == 0) {
721  json_object_set_new(response, "success", json_false());
722  json_object_set_new(response, "error", json_string("Not on that network"));
723  } else {
724  if(action == "channels") {
725  json_object_set_new(response, "success", json_true());
726  json_t *chans = json_array();
727  const std::vector<std::string> &channels = net->joinedChannels();
728  for(unsigned i = 0; i < channels.size(); ++i) {
729  json_array_append_new(chans, json_string(channels[i].c_str()));
730  }
731  json_object_set_new(response, "channels", chans);
732  } else if(action == "whois" || action == "join" || action == "part") {
733  if(params.size() < 2) {
734  json_object_set_new(response, "success", json_false());
735  json_object_set_new(response, "error", json_string("Missing parameters"));
736  } else {
737  json_object_set_new(response, "success", json_true());
738  if(action == "whois") {
739  net->sendWhois(params[1]);
740  } else if(action == "join") {
741  net->joinChannel(params[1]);
742  } else if(action == "part") {
743  net->leaveChannel(params[1]);
744  } else {
745  assert(false);
746  return;
747  }
748  }
749  } else if(action == "nick") {
750  json_object_set_new(response, "success", json_true());
751  json_object_set_new(response, "nick", json_string(net->nick().c_str()));
752  } else {
753  assert(false);
754  return;
755  }
756  }
757  // REQUESTS ON A CHANNEL
758  } else if(action == "message" || action == "action" || action == "names") {
759  json_object_set_new(response, "did", json_string(action.c_str()));
760  if(params.size() < 2 || (action != "names" && params.size() < 3)) {
761  fprintf(stderr, "Wrong parameter size for message, skipping.\n");
762  return;
763  }
764  std::string network = params[0];
765  std::string receiver = params[1];
766  std::string message;
767  if(action != "names") {
768  message = params[2];
769  }
770  json_object_set_new(response, "network", json_string(network.c_str()));
771  if(action == "names") {
772  json_object_set_new(response, "channel", json_string(receiver.c_str()));
773  } else {
774  json_object_set_new(response, "receiver", json_string(receiver.c_str()));
775  json_object_set_new(response, "message", json_string(message.c_str()));
776  }
777 
778  bool netfound = false;
779  for(nit = networks.begin(); nit != networks.end(); ++nit) {
780  Network *n = *nit;
781  if(n->networkName() == network) {
782  netfound = true;
783  if(receiver.substr(0, 1) != "#" || contains_ci(n->joinedChannels(), strToLower(receiver))) {
784  json_object_set_new(response, "success", json_true());
785  if(action == "names") {
786  n->names(receiver);
787  } else if(action == "message") {
788  n->say(receiver, message);
789  } else {
790  n->action(receiver, message);
791  }
792  } else {
793  fprintf(stderr, "Request for communication to network %s receiver %s, but not in that channel, dropping\n",
794  network.c_str(), receiver.c_str());
795  json_object_set_new(response, "success", json_false());
796  json_object_set_new(response, "error", json_string("Not in that channel"));
797  }
798  break;
799  }
800  }
801  if(!netfound) {
802  fprintf(stderr, "Request for communication to network %s, but that network isn't joined, dropping\n", network.c_str());
803  json_object_set_new(response, "success", json_false());
804  json_object_set_new(response, "error", json_string("Not on that network"));
805  }
806  // REQUESTS ON DAZEUS ITSELF
807  } else if(action == "subscribe") {
808  json_object_set_new(response, "did", json_string("subscribe"));
809  json_object_set_new(response, "success", json_true());
810  int added = 0;
811  std::vector<std::string>::const_iterator pit;
812  for(pit = params.begin(); pit != params.end(); ++pit) {
813  if(info.subscribe(*pit))
814  ++added;
815  }
816  json_object_set_new(response, "added", json_integer(added));
817  } else if(action == "unsubscribe") {
818  json_object_set_new(response, "did", json_string("unsubscribe"));
819  json_object_set_new(response, "success", json_true());
820  int removed = 0;
821  std::vector<std::string>::const_iterator pit;
822  for(pit = params.begin(); pit != params.end(); ++pit) {
823  if(info.unsubscribe(*pit))
824  ++removed;
825  }
826  json_object_set_new(response, "removed", json_integer(removed));
827  } else if(action == "command") {
828  json_object_set_new(response, "did", json_string("command"));
829  // {"do":"command", "params":["cmdname"]}
830  // {"do":"command", "params":["cmdname", "network"]}
831  // {"do":"command", "params":["cmdname", "network", true, "sender"]}
832  // {"do":"command", "params":["cmdname", "network", false, "receiver"]}
833  if(params.size() == 0) {
834  json_object_set_new(response, "success", json_false());
835  json_object_set_new(response, "error", json_string("Missing parameters"));
836  } else {
837  std::string commandName = params.front();
838  params.erase(params.begin());
839  RequirementInfo *req = 0;
840  Network *net = 0;
841  if(params.size() > 0) {
842  for(nit = networks.begin(); nit != networks.end(); ++nit) {
843  if((*nit)->networkName() == params.at(0)) {
844  net = *nit; break;
845  }
846  }
847  }
848  if(params.size() == 0) {
849  // Add it as a global command
850  req = new RequirementInfo();
851  } else if(params.size() == 1) {
852  // Network requirement
853  req = new RequirementInfo(net);
854  } else if(params.size() == 3) {
855  // Network and sender/receiver requirement
856  bool isSender = params.at(1) == "true";
857  req = new RequirementInfo(net, params.at(2), isSender);
858  } else {
859  json_object_set_new(response, "success", json_false());
860  json_object_set_new(response, "error", json_string("Wrong number of parameters"));
861  }
862  if(req != NULL) {
863  info.subscribeToCommand(commandName, req);
864  json_object_set_new(response, "success", json_true());
865  }
866  }
867  } else if(action == "property") {
868  json_object_set_new(response, "did", json_string("property"));
869 
870  std::string network, receiver, sender;
871  switch(scope.size()) {
872  // fall-throughs
873  case 3: sender = scope[2];
874  case 2: receiver = scope[1];
875  case 1: network = scope[0];
876  default: break;
877  }
878 
879  if(params.size() < 2) {
880  json_object_set_new(response, "success", json_false());
881  json_object_set_new(response, "error", json_string("Missing parameters"));
882  } else if(params[0] == "get") {
883  std::string value = database_->property(params[1], network, receiver, sender);
884  json_object_set_new(response, "success", json_true());
885  json_object_set_new(response, "variable", json_string(params[1].c_str()));
886  if(value.length() > 0) {
887  json_object_set_new(response, "value", json_string(value.c_str()));
888  }
889  } else if(params[0] == "set") {
890  if(params.size() < 3) {
891  json_object_set_new(response, "success", json_false());
892  json_object_set_new(response, "error", json_string("Missing parameters"));
893  } else {
894  database_->setProperty(params[1], params[2], network, receiver, sender);
895  json_object_set_new(response, "success", json_true());
896  }
897  } else if(params[0] == "unset") {
898  if(params.size() < 2) {
899  json_object_set_new(response, "success", json_false());
900  json_object_set_new(response, "error", json_string("Missing parameters"));
901  } else {
902  database_->setProperty(params[1], std::string(), network, receiver, sender);
903  json_object_set_new(response, "success", json_true());
904  }
905  } else if(params[0] == "keys") {
906  std::vector<std::string> pKeys = database_->propertyKeys(params[1], network, receiver, sender);
907  json_t *keys = json_array();
908  std::vector<std::string>::iterator kit;
909  for(kit = pKeys.begin(); kit != pKeys.end(); ++kit) {
910  json_array_append_new(keys, json_string(kit->c_str()));
911  }
912  json_object_set_new(response, "keys", keys);
913  json_object_set_new(response, "success", json_true());
914  } else {
915  json_object_set_new(response, "success", json_false());
916  json_object_set_new(response, "error", json_string("Did not understand request"));
917  }
918  } else if(action == "config") {
919  json_object_set_new(response, "got", json_string("config"));
920  if(params.size() != 2) {
921  json_object_set_new(response, "success", json_false());
922  json_object_set_new(response, "error", json_string("Missing parameters"));
923  } else {
924  std::string configtype = params[0];
925  std::string configvar = params[1];
926 
927  if(configtype == "plugin" && !info.didHandshake()) {
928  json_object_set_new(response, "success", json_false());
929  json_object_set_new(response, "error", json_string("Need to do handshake for retrieving plugin configuration"));
930  } else if(configtype == "plugin") {
931  json_object_set_new(response, "success", json_true());
932  json_object_set_new(response, "group", json_string(configtype.c_str()));
933  json_object_set_new(response, "variable", json_string(configvar.c_str()));
934 
935  // Get the right plugin config
936  const std::vector<PluginConfig*> &plugins = config_->getPlugins();
937  for(std::vector<PluginConfig*>::const_iterator cit = plugins.begin(); cit != plugins.end(); ++cit) {
938  PluginConfig *pc = *cit;
939  if(pc->name == info.config_group) {
940  std::map<std::string,std::string>::iterator configIt = pc->config.find(configvar);
941  if(configIt != pc->config.end()) {
942  json_object_set_new(response, "value", json_string(configIt->second.c_str()));
943  break;
944  }
945  }
946  }
947  } else if(configtype == "core") {
948  GlobalConfig *global = config_->getGlobalConfig();
949  json_object_set_new(response, "success", json_true());
950  json_object_set_new(response, "group", json_string(configtype.c_str()));
951  json_object_set_new(response, "variable", json_string(configvar.c_str()));
952  if(configvar == "nickname") {
953  json_object_set_new(response, "value", json_string(global->default_nickname.c_str()));
954  } else if(configvar == "username") {
955  json_object_set_new(response, "value", json_string(global->default_username.c_str()));
956  } else if(configvar == "fullname") {
957  json_object_set_new(response, "value", json_string(global->default_fullname.c_str()));
958  } else if(configvar == "highlight") {
959  json_object_set_new(response, "value", json_string(global->highlight.c_str()));
960  }
961  } else {
962  json_object_set_new(response, "success", json_false());
963  json_object_set_new(response, "error", json_string("Unrecognised config group"));
964  }
965  }
966  } else {
967  json_object_set_new(response, "success", json_false());
968  json_object_set_new(response, "error", json_string("Did not understand request"));
969  }
970 
971  char *raw_json = json_dumps(response, 0);
972  std::string jsonMsg = raw_json;
973  free(raw_json);
974  std::stringstream mstr;
975  mstr << jsonMsg.length();
976  mstr << jsonMsg;
977  mstr << "\n";
978  std::string finalResponse = mstr.str();
979  if(write(dev, finalResponse.c_str(), finalResponse.length()) != (unsigned)finalResponse.length()) {
980  fprintf(stderr, "Failed to write correct number of JSON bytes to client socket.\n");
981  close(dev);
982  }
983 
984  json_decref(n);
985  json_decref(response);
986 }