]> jfr.im git - irc/quakenet/newserv.git/blobdiff - nterfacer/nterfacer.c
Port to git.
[irc/quakenet/newserv.git] / nterfacer / nterfacer.c
index 604de3ca7fcc25c1617937c14059bccae98b7573..5efe8d977d1fd93684efeee7eec122c358903c61 100644 (file)
 #include <netdb.h>
 #include <string.h>
 #include <strings.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "../lib/sstring.h"
 #include "../lib/irc_string.h"
 #include "../core/config.h"
 #include "../core/events.h"
 #include "../lib/version.h"
+#include "../core/schedule.h"
 
 #include "nterfacer.h"
 #include "logging.h"
@@ -40,6 +43,7 @@ struct permitted *permits;
 int permit_count = 0;
 
 int ping_handler(struct rline *ri, int argc, char **argv);
+static void nterfacer_sendcallback(struct rline *ri, int error, char *buf);
 
 void _init(void) {
   int loaded;
@@ -48,12 +52,10 @@ void _init(void) {
   nrl = nterface_open_log("nterfacer", "logs/nterfacer.log", debug_mode);
 
   loaded = load_permits();
-  if(!loaded) {
-    nterface_log(nrl, NL_ERROR, "No permits loaded successfully.");
+  nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s");
+
+  if(!loaded)
     return;
-  } else {
-    nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s");
-  }
 
   nterfacer_events.on_accept = nterfacer_accept_event;
   nterfacer_events.on_line = nterfacer_line_event;
@@ -80,6 +82,29 @@ void _init(void) {
 }
 
 void free_handler(struct handler *hp) {
+  struct rline *li, *pi = NULL;
+
+  for(li=rlines;li;) {
+    if(li->handler == hp) {
+      if(li->socket) {
+        esocket_write_line(li->socket, "%d,OE%d,%s", li->id, BF_UNLOADED, "Service was unloaded.");
+      } else if(li->callback) {
+        nterfacer_sendcallback(li, BF_UNLOADED, "Service was unloaded.");
+      }
+      if(pi) {
+        pi->next = li->next;
+        ntfree(li);
+        li = pi->next;
+      } else {
+        rlines = li->next;
+        ntfree(li);
+        li = rlines;
+      }
+    } else {
+      pi=li,li=li->next;
+    }
+  }
+
   freesstring(hp->command);
   ntfree(hp);
 }
@@ -140,10 +165,8 @@ int load_permits(void) {
 
   hostnamesa = getconfigitems("nterfacer", "hostname");
   passwordsa = getconfigitems("nterfacer", "password");
-  if(!hostnamesa || !passwordsa) {
-    nterface_log(nrl, NL_ERROR, "Unable to load hostnames/passwords.");
+  if(!hostnamesa || !passwordsa)
     return 0;
-  }
   if(hostnamesa->cursi != passwordsa->cursi) {
     nterface_log(nrl, NL_ERROR, "Different number of hostnames/passwords in config file.");
     return 0;
@@ -166,7 +189,7 @@ int load_permits(void) {
       continue;
     }
 
-    item->ihost = (*(struct in_addr *)host->h_addr).s_addr;
+    item->ihost = (*(struct in_addr *)host->h_addr_list[0]).s_addr;
     for(j=0;j<loaded_lines;j++) {
       if(new_permits[j].ihost == item->ihost) {
         nterface_log(nrl, NL_WARNING, "Host with items %d and %d is identical, dropping item %d.", j + 1, i + 1, i + 1);
@@ -228,6 +251,7 @@ int setup_listening_socket(void) {
   
   if(bind(fd, (struct sockaddr *) &sin, sizeof(sin))) {
     nterface_log(nrl, NL_ERROR, "Unable to bind listen socket (%d).", errno);
+    close(fd);
     return -1;
   }
   
@@ -235,6 +259,7 @@ int setup_listening_socket(void) {
   
   if(ioctl(fd, FIONBIO, &opt)) {
     nterface_log(nrl, NL_ERROR, "Unable to set listen socket non-blocking.");
+    close(fd);
     return -1;
   }
   
@@ -298,7 +323,6 @@ void deregister_handler(struct handler *hl) {
 
 void deregister_service(struct service_node *service) {
   struct service_node *sp, *lp = NULL;
-  struct rline *li, *pi = NULL;
 
   for(sp=tree;sp;lp=sp,sp=sp->next) {
     if(sp == service) {
@@ -316,21 +340,6 @@ void deregister_service(struct service_node *service) {
 
   free_handlers(service);
 
-  for(li=rlines;li;) {
-    if(li->service == service) {
-      if(pi) {
-        pi->next = li->next;
-        ntfree(li);
-        li = pi->next;
-      } else {
-        rlines = li->next;
-        ntfree(li);
-        li = rlines;
-      }
-    } else {
-      pi=li,li=li->next;
-    }
-  }
   freesstring(service->name);
 
   ntfree(service);
@@ -352,6 +361,7 @@ void nterfacer_accept_event(struct esocket *socket) {
 
   if(ioctl(newfd, FIONBIO, &opt)) {
     nterface_log(nrl, NL_ERROR, "Unable to set accepted socket non-blocking.");
+    close(newfd);
     return;
   }
   
@@ -363,8 +373,7 @@ void nterfacer_accept_event(struct esocket *socket) {
   }
 
   if(!item) {
-    /* Someone needs to figure out how to print the IP :) */
-    nterface_log(nrl, NL_INFO, "Unauthorised connection closed");
+    nterface_log(nrl, NL_INFO, "Unauthorised connection from %s closed", inet_ntoa(sin.sin_addr));
     close(newfd);
     return;
   }
@@ -547,7 +556,7 @@ int nterfacer_new_rline(char *line, struct esocket *socket, int *number) {
     if(*p == ',')
       break;
 
-  if(!*p || !(p + 1))
+  if(!*p || !*(p + 1))
     return RE_BAD_LINE;
   
   *p = '\0';
@@ -596,7 +605,6 @@ int nterfacer_new_rline(char *line, struct esocket *socket, int *number) {
   if(argcount) {
     parsebuf = (char *)ntmalloc(strlen(pp) + 1);
     MemCheckR(parsebuf, RE_MEM_ERROR);
-    newp = parsebuf;
   
     for(newp=args[0]=parsebuf,pp++;*pp;pp++) {
       if((*pp == '\\') && *(pp + 1)) {
@@ -641,6 +649,7 @@ int nterfacer_new_rline(char *line, struct esocket *socket, int *number) {
   prequest->id = *number;
   prequest->next = rlines;
   prequest->socket = socket;
+  prequest->callback = NULL;
 
   rlines = prequest;
   re = (hl->function)(prequest, argcount, args);
@@ -705,7 +714,7 @@ int ri_error(struct rline *li, int error_code, char *format, ...) {
   va_list ap;
   int retval = RE_OK;
 
-  if(li->socket) {
+  if(li->socket || li->callback) {
     va_start(ap, format);
     vsnprintf(buf, sizeof(buf), format, ap);
     va_end(ap);
@@ -713,9 +722,15 @@ int ri_error(struct rline *li, int error_code, char *format, ...) {
     for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++)
       if((*p == ',') || (*p == '\\'))
         *tp++ = '\\';
+    if(li->socket) {
+      if(esocket_write_line(li->socket, "%d,OE%d,%s", li->id, error_code, escapedbuf))
+        retval = RE_SOCKET_ERROR;
+    } else {
+      if(error_code == 0) /* :P */
+        error_code = -10000;
 
-    if(esocket_write_line(li->socket, "%d,OE%d,%s", li->id, error_code, escapedbuf))
-      retval = RE_SOCKET_ERROR;
+      nterfacer_sendcallback(li, error_code, escapedbuf);
+    }
   }
 
   for(pp=rlines;pp;lp=pp,pp=pp->next) {
@@ -737,9 +752,12 @@ int ri_final(struct rline *li) {
   struct rline *pp, *lp = NULL;
   int retval = RE_OK;
 
-  if(li->socket)
+  if(li->socket) {
     if(esocket_write_line(li->socket, "%d,OO%s", li->id, li->buf))
       retval = RE_SOCKET_ERROR;
+  } else if(li->callback) {
+    nterfacer_sendcallback(li, 0, li->buf);
+  }
 
   for(pp=rlines;pp;lp=pp,pp=pp->next) {
     if(pp == li) {
@@ -760,3 +778,149 @@ int ping_handler(struct rline *ri, int argc, char **argv) {
   ri_append(ri, "OK");
   return ri_final(ri);
 }
+
+struct sched_rline {
+  rline rl;
+  int argc;
+  handler *hl;
+  void *schedule;
+  char argv[];
+};
+
+static const int XMAXARGS = 50;
+
+static void execrline(void *arg) {
+  struct sched_rline *sr = arg;
+  int re, i;
+  char *argv[XMAXARGS], *buf;
+
+  sr->schedule = NULL;
+
+  buf = sr->argv;
+  for(i=0;i<sr->argc;i++) {
+    argv[i] = buf;
+    buf+=strlen(buf) + 1;
+  }
+
+  re = (sr->hl->function)(&sr->rl, sr->argc, argv);
+
+  if(re)
+    Error("nterfacer", ERR_WARNING, "sendline: error occured calling %p %d: %s", sr->hl->function, re, request_error(re));
+}
+
+void *nterfacer_sendline(char *service, char *command, int argc, char **argv, rline_callback callback, void *tag) {
+  struct service_node *servicep;
+  struct rline *prequest;
+  struct sched_rline *sr;
+  struct handler *hl;
+  int totallen, i;
+  char *buf;
+
+  for(servicep=tree;servicep;servicep=servicep->next)
+    if(!strcmp(servicep->name->content, service))
+      break;
+
+  if(argc > XMAXARGS)
+    Error("nterfacer", ERR_STOP, "Over maximum arguments.");
+
+  if(!servicep) {
+    Error("nterfacer", ERR_WARNING, "sendline: service not found: %s", service);
+    return NULL;
+  }
+
+  for(hl=servicep->handlers;hl;hl=hl->next)
+    if(!strcmp(hl->command->content, command))
+      break;
+
+  if(!hl) {
+    Error("nterfacer", ERR_WARNING, "sendline: command not found: %s", command);
+    return NULL;
+  }
+
+  if(argc < hl->args) {
+    Error("nterfacer", ERR_WARNING, "sendline: wrong number of arguments: %s", command);
+    return NULL;
+  }
+
+  /* we have to create a copy of the arguments for reentrancy reasons, grr */
+  totallen = 0;
+  for(i=0;i<argc;i++)
+    totallen+=strlen(argv[i]) + 1;
+
+  /* HACKY but allows existing code to still work */
+  sr = (struct sched_rline *)ntmalloc(sizeof(struct sched_rline) + totallen);
+  if(!sr) {
+    MemError();
+    return NULL;
+  }
+  prequest = &sr->rl;
+
+  sr->argc = argc;
+  buf = sr->argv;
+  for(i=0;i<argc;i++) {
+    size_t len = strlen(argv[i]) + 1;
+    memcpy(buf, argv[i], len);
+    buf+=len;
+  }
+  sr->hl = hl;
+
+  prequest->service = servicep;
+  prequest->handler = hl;
+  prequest->buf[0] = '\0';
+  prequest->curpos = prequest->buf;
+  prequest->tag = tag;
+  prequest->id = 0;
+  prequest->socket = NULL;
+  prequest->callback = callback;
+
+  prequest->next = rlines;
+  rlines = prequest;
+
+  scheduleoneshot(time(NULL), execrline, sr);
+
+  return (void *)sr;
+}
+
+void nterfacer_freeline(void *tag) {
+  struct sched_rline *prequest = tag;
+
+  prequest->rl.callback = NULL;
+  if(prequest->schedule)
+    deleteschedule(prequest->schedule, execrline, NULL);
+}
+
+#define MAX_LINES 8192
+
+/* this bites */
+static void nterfacer_sendcallback(struct rline *ri, int error, char *buf) {
+  char *lines[MAX_LINES+1];
+  char newbuf[MAX_BUFSIZE+5];
+  char *s, *d, *laststart;
+  int linec = 0;
+
+  for(s=buf,laststart=d=newbuf;*s;s++) {
+    if((*s == '\\') && *(s + 1)) {
+      if(*(s + 1) == ',') {
+        *d++ = ',';
+      } else if(*(s + 1) == '\\') {
+        *d++ = '\\';
+      }
+      s++;
+    } else if(*s == ',') {
+      *d++ = '\0';
+      if(linec >= MAX_LINES - 5) {
+        nterfacer_sendcallback(ri, BF_OVER, "Buffer overflow.");
+        return;
+      }
+
+      lines[linec++] = laststart;
+      laststart = d;
+    } else {
+      *d++ = *s;
+    }
+  }
+  *d = '\0';
+  lines[linec++] = laststart;
+
+  ri->callback(error, linec, lines, ri->tag);
+}