Index: plugins/roard/protocol-irc.c
===================================================================
--- plugins/roard/protocol-irc.c	(revision 5899)
+++ plugins/roard/protocol-irc.c	(revision 5900)
@@ -29,25 +29,4 @@
 #define MAX_CHANNELS 8
 
-static int on_nick(int client, const char * cmd, char * args, char * text);
-static int on_user(int client, const char * cmd, char * args, char * text);
-static int on_quit(int client, const char * cmd, char * args, char * text);
-static int on_ping(int client, const char * cmd, char * args, char * text);
-static int on_lusers(int client, const char * cmd, char * args, char * text);
-static int on_whois(int client, const char * cmd, char * args, char * text);
-static int on_privmsg(int client, const char * cmd, char * args, char * text);
-
-static int on_list(int client, const char * cmd, char * args, char * text);
-static int on_join(int client, const char * cmd, char * args, char * text);
-static int on_part(int client, const char * cmd, char * args, char * text);
-static int on_names(int client, const char * cmd, char * args, char * text);
-
-static int on_topic(int client, const char * cmd, char * args, char * text);
-
-static int do_join(int client, const char * channel);
-static int do_part(int client, const char * channel);
-static int do_names(int client, const char * channel);
-
-static void cb_client_delete(struct roar_notify_core * core, struct roar_event * event, void * userdata);
-
 struct channel {
  char * name;
@@ -80,6 +59,767 @@
 static const struct command {
  const char * name;
- int (*func)(int client, const char * cmd, char * args, char * text);
-} g_commands[] = {
+ int (*func)(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text);
+} g_commands[];
+
+static int is_valid_name (const char * name) {
+ register char c;
+
+ for (; (c = *name) != 0; name++) {
+  if ( !(isalnum(c) || c == '-' || c == '_') )
+   return 0;
+ }
+
+ return 1;
+}
+
+static void strip_nl(char * str) {
+ register char c;
+
+ for (; (c = *str) != 0; str++) {
+  if ( c == '\r' || c == '\n' ) {
+   *str = 0;
+   return;
+  }
+ }
+}
+
+static char * get_nick(int client) {
+ struct roar_client * c;
+ clients_get(client, &c);
+ return c->name;
+}
+
+static const char * get_ident(int client) {
+ static char buf[80];
+ struct roar_client * c;
+ clients_get(client, &c);
+
+ if ( c->uid == -1 ) {
+  buf[0] = '~';
+  buf[1] = 0;
+ } else {
+  snprintf(buf, sizeof(buf)-1, "uid%i~", c->uid);
+  buf[sizeof(buf)-1] = 0;
+ }
+
+ return buf;
+}
+
+static const char * get_node(int client) {
+ struct roar_client * c;
+ static char buf_nnode[80];
+ char * nnode;
+
+ clients_get(client, &c);
+
+ roar_nnode_to_str(&(c->nnode), buf_nnode, sizeof(buf_nnode));
+
+ nnode  = strstr(buf_nnode, ": ");
+ if ( nnode == NULL ) {
+  nnode  = "unknown~";
+ } else {
+  nnode += 2;
+ }
+
+ return nnode;
+}
+
+static const char * get_ufull(int client) {
+ struct roar_client * c;
+ static char buf[80];
+ const char * ident = get_ident(client);
+ const char * nnode = get_node(client);
+
+ clients_get(client, &c);
+
+ snprintf(buf, sizeof(buf)-1, "%s!%s@%s", c->name, ident, nnode);
+
+ buf[sizeof(buf)-1] = 0;
+
+ return buf;
+}
+
+static const char * get_realname(int client) {
+ (void)client;
+ return "(no realname)";
+}
+
+static int get_client_by_nick(const char * nick) {
+ struct roar_client * c;
+ int i;
+
+ for (i = 0; i < ROAR_CLIENTS_MAX; i++) {
+  if ( clients_get(i, &c) != 0 )
+   continue;
+
+  if ( !!strcasecmp(c->name, nick) )
+   continue;
+
+  return i;
+ }
+
+ return -1;
+}
+
+static struct channel * get_channel(const char * name) {
+ struct channel * c;
+ size_t i;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  c = &(g_state->g_channels[i]);
+
+  if ( !c->client_count )
+   continue;
+
+  if ( !!strcasecmp(c->name, name) )
+   continue;
+
+  return c;
+ }
+
+ return NULL;
+}
+
+static size_t get_listener_list(int client, const char * channel, int ** listener) {
+ struct channel * c;
+ static int clients[ROAR_CLIENTS_MAX];
+ size_t ret = 0;
+ size_t i, k;
+ int j;
+ int found;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  c = &(g_state->g_channels[i]);
+
+  if ( !c->client_count )
+   continue;
+
+  if ( c->clients[client] < 1 )
+   continue;
+
+  if ( channel != NULL && !!strcasecmp(c->name, channel) )
+   continue;
+
+  for (j = 0; j < ROAR_CLIENTS_MAX; j++) {
+   if ( c->clients[j] > 0 ) {
+    found = 0;
+    for (k = 0; k < ret; k++) {
+     if ( clients[k] == j ) {
+      found = 1;
+     }
+    }
+    if ( !found ) {
+     clients[ret] = j;
+     ret++;
+    }
+   }
+  }
+ }
+
+ *listener = clients;
+
+ return ret;
+}
+
+static void put_printf(int client, struct roar_buffer ** obuffer, const char *format, ...) {
+ va_list ap;
+ struct roar_buffer * buf;
+ size_t len = 2048;
+ void * data;
+ int ret;
+
+ if ( roar_buffer_new_data(&buf, len, &data) == -1 )
+  return;
+
+ va_start(ap, format);
+ ret = vsnprintf(data, len-1, format, ap);
+ va_end(ap);
+
+ if ( roar_buffer_set_len(buf, strlen(data)) == -1 ) {
+  roar_buffer_free(buf);
+  return;
+ }
+
+ if ( obuffer == NULL ) {
+  // TODO: FIXME: find a better way to to this.
+  clients_add_output(client, &buf);
+ } else {
+  if ( roar_buffer_moveintoqueue(obuffer, &buf) == -1 )
+   roar_buffer_free(buf);
+ }
+}
+
+static int do_join(int client, const char * channel) {
+ struct channel * c = NULL;
+ size_t i;
+
+ if ( channel[0] != '#' )
+  return -1;
+
+ if ( !is_valid_name(channel+1) )
+  return -1;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  if ( !g_state->g_channels[i].client_count )
+   continue;
+
+  if ( !!strcasecmp(g_state->g_channels[i].name, channel) )
+   continue;
+
+  c = &(g_state->g_channels[i]);
+  break;
+ }
+
+ if ( c == NULL ) {
+  for (i = 0; i < MAX_CHANNELS; i++) {
+   if ( g_state->g_channels[i].client_count )
+    continue;
+
+   c = &(g_state->g_channels[i]);
+   break;
+  }
+
+  if ( c == NULL )
+   return -1;
+
+  memset(c, 0, sizeof(*c));
+
+  c->name = roar_mm_strdup(channel);
+ }
+
+ if ( c->clients[client] )
+  return -1;
+
+ c->clients[client] = 1;
+ c->client_count++;
+
+ return 0;
+}
+
+static int do_part(int client, const char * channel) {
+ struct channel * c = NULL;
+ size_t i;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  if ( !g_state->g_channels[i].client_count )
+   continue;
+
+  if ( !!strcasecmp(g_state->g_channels[i].name, channel) )
+   continue;
+
+  if ( !g_state->g_channels[i].clients[client] )
+   return -1;
+
+  c = &(g_state->g_channels[i]);
+  break;
+ }
+
+ c->clients[client] = 0;
+ c->client_count--;
+
+ if ( !c->client_count ) {
+  roar_mm_free(c->name);
+
+  if ( c->topic.text != NULL )
+   roar_mm_free(c->topic.text);
+  if ( c->topic.user != NULL )
+   roar_mm_free(c->topic.user);
+ }
+
+ return 0;
+}
+
+static int do_names(int client, struct roar_buffer ** obuffer, const char * channel) {
+ const char * nick = get_nick(client);
+ size_t i;
+ char buf[256];
+ size_t len, offset;
+ char * c_nick;
+ int j;
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  if ( !g_state->g_channels[i].client_count )
+   continue;
+
+  if ( !!strcasecmp(g_state->g_channels[i].name, channel) )
+   continue;
+
+  offset = 0;
+
+  for (j = 0; j < ROAR_CLIENTS_MAX; j++) {
+   if ( g_state->g_channels[i].clients[j] == 0 )
+    continue;
+
+   c_nick = get_nick(j);
+   len = strlen(c_nick);
+   if ( (offset + len + 3) > sizeof(buf) ) {
+    buf[offset] = 0;
+    put_printf(client, obuffer, ":%s 353 %s = %s :%s\n", g_state->server_name, nick, channel, buf);
+    offset = 0;
+   }
+
+   memcpy(buf + offset, c_nick, len);
+   offset += len;
+   buf[offset] = ' ';
+   offset++;
+   buf[offset] = 0;
+  }
+
+  if ( offset ) {
+   buf[offset] = 0;
+   put_printf(client, obuffer, ":%s 353 %s = %s :%s\n", g_state->server_name, nick, channel, buf);
+  }
+  put_printf(client, obuffer, ":%s 366 %s %s :End of /NAMES list.\n", g_state->server_name, nick, channel);
+
+  return 0;
+ }
+
+ return -1;
+}
+
+static int __run_command(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ size_t i;
+ int found = 0;
+
+ for (i = 0; g_commands[i].name != NULL; i++) {
+  if ( !strcasecmp(g_commands[i].name, cmd) ) {
+   found = 1;
+   g_commands[i].func(client, obuffer, cmd, args, text);
+  }
+ }
+
+ if ( !found ) {
+  put_printf(client, obuffer, ":%s 421 %s %s :Unknown command\n", g_state->server_name, get_nick(client), cmd);
+ }
+
+ return 0;
+}
+
+static void cb_client_delete(struct roar_notify_core * core, struct roar_event * event, void * userdata) {
+ int * listener;
+ size_t count;
+ struct channel * c;
+ const char * text = g_state->quit_msg;
+ int client = event->target;
+ const char * ufull = get_ufull(client);
+ size_t i;
+
+ (void)core, (void)userdata;
+
+ if ( text == NULL )
+  text = "Client deleted. Died, kicked or internal error.";
+
+ if ( g_state->quit_msg != NULL )
+  put_printf(client, NULL, "ERROR :Closing Link: %s (Quit: %s)\n", ufull, text);
+
+ count = get_listener_list(client, NULL, &listener);
+ for (; count; count--, listener++)
+  put_printf(*listener, NULL, ":%s QUIT :Quit: %s\n", ufull, text);
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  c = &(g_state->g_channels[i]);
+
+  if ( !c->client_count )
+   continue;
+
+  if ( !c->clients[client] )
+   continue;
+
+  c->clients[client] = 0;
+  c->client_count--;
+
+  if ( !c->client_count ) {
+   roar_mm_free(c->name);
+
+   if ( c->topic.text != NULL )
+    roar_mm_free(c->topic.text);
+   if ( c->topic.user != NULL )
+    roar_mm_free(c->topic.user);
+  }
+ }
+
+ clients_flush(client);
+}
+
+static int set_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
+ struct roar_client_server * cs;
+ char * name;
+
+ (void)client, (void)vio, (void)obuffer, (void)userdata, (void)protopara, (void)protoparalen, (void)pluginpara;
+
+ clients_get_server(client, &cs);
+
+ name = ROAR_CLIENT(cs)->name;
+ snprintf(name, sizeof(ROAR_CLIENT(cs)->name), "Client%i~", client);
+
+/*
+:ph7.ph.sft NOTICE AUTH :*** Looking up your hostname...
+:ph7.ph.sft NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
+NICK nick
+USER A B C D
+:ph7.ph.sft NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
+:ph7.ph.sft 002 nick :Your host is ph7.ph.sft, running version Unreal3.2.7
+:ph7.ph.sft 003 nick :This server was created Tue Aug 28 2007 at 10:02:00 CEST
+:ph7.ph.sft 004 nick ph7.ph.sft Unreal3.2.7 iowghraAsORTVSxNCWqBzvdHtGp lvhopsmntikrRcaqOALQbSeIKVfMCuzNTGj
+:ph7.ph.sft 005 nick NAMESX SAFELIST HCN MAXCHANNELS=10 CHANLIMIT=#:10 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 MAXTARGETS=20 WALLCHOPS :are supported by this server
+:ph7.ph.sft 005 nick WATCH=128 SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(ohv)@%+ CHANMODES=beIqa,kfL,lj,psmntirRcOAQKVCuzNSMTG NETWORK=PH2 CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT STATUSMSG=@%+ EXCEPTS INVEX :are supported by this server
+:ph7.ph.sft 005 nick CMDS=KNOCK,MAP,DCCALLOW,USERIP :are supported by this server
+*/
+
+ put_printf(client, obuffer,
+      ":%s 001 %s :Welcome to the roard based IRC server.\n"
+      ":%s 375 %s :- %s Message of the Day -\n"
+      ":%s 372 %s :- MotD goes here...\n"
+      ":%s 376 %s :End of /MOTD command.\n",
+   g_state->server_name, name,
+   g_state->server_name, name, g_state->server_name,
+   g_state->server_name, name,
+   g_state->server_name, name
+ );
+
+ __run_command(client, obuffer, "LUSERS", NULL, NULL);
+
+ return 0;
+}
+
+static int check_client(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
+ char cmd[1024*2];
+ char * args;
+ char * text;
+ ssize_t len;
+
+ (void)obuffer, (void)userdata, (void)protopara, (void)protoparalen, (void)pluginpara;
+
+ len = roar_vio_read(vio, cmd, sizeof(cmd)-1);
+ if ( len < 1 ) {
+  clients_delete(client);
+  return -1;
+ }
+
+ cmd[len] = 0;
+
+ strip_nl(cmd);
+
+ ROAR_DBG("check_client(client=%i, vio=?): cmd=\"%s\"", client, cmd);
+
+ if ( cmd[0] == 0 )
+  return 0;
+
+ args = strstr(cmd, " ");
+
+ if ( args != NULL ) {
+  *args = 0;
+  args++;
+  if ( *args == ':' ) {
+   text = args + 1;
+   args = NULL;
+  } else {
+   text = strstr(args, " :");
+   if ( text != NULL ) {
+    *text = 0;
+    text += 2;
+   }
+  }
+ } else {
+  text = NULL;
+ }
+
+ __run_command(client, obuffer, cmd, args, text);
+
+ return 0;
+}
+
+// commands:
+static int on_nick(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ char * nick = get_nick(client);
+ int * listener;
+ size_t count;
+ const char * ufull;
+
+ (void)cmd, (void)text;
+
+ if ( args != NULL && args[0] != 0 && is_valid_name(args) && strlen(args) < ROAR_BUFFER_NAME ) {
+  if ( get_client_by_nick(args) != -1 ) {
+   put_printf(client, obuffer, ":%s 433 %s %s :Nickname is already in use.\n", g_state->server_name, nick, args);
+  } else {
+   ufull = get_ufull(client);
+   put_printf(client, obuffer, ":%s NICK :%s\n", ufull, args);
+   count = get_listener_list(client, NULL, &listener);
+   for (; count; count--, listener++)
+    if ( *listener != client )
+     put_printf(*listener, NULL, ":%s NICK :%s\n", ufull, args);
+
+   strcpy(nick, args);
+  }
+ } else {
+  put_printf(client, obuffer, ":%s 432 %s %s :Erroneous Nickname: Illegal characters\n", g_state->server_name, nick, args);
+ }
+
+ return 0;
+}
+
+static int on_user(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ (void)client, (void)obuffer, (void)cmd, (void)args, (void)text;
+ return 0;
+}
+
+static int on_quit(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ int ret;
+
+ (void)obuffer, (void)cmd, (void)args;
+
+ if ( text == NULL )
+  text = "... have a cuddle ...";
+
+ g_state->quit_msg = text;
+ ret = clients_delete(client);
+ g_state->quit_msg = NULL;
+
+ return ret;
+}
+
+static int on_ping(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ (void)cmd, (void)args;
+
+ put_printf(client, obuffer, "PONG :%s\n", text);
+
+ return 0;
+}
+
+static int on_lusers(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ const char * clientnick = get_nick(client);
+ size_t i;
+ size_t numchans = 0;
+
+ (void)cmd, (void)args, (void)text;
+
+/*
+:v0.fellig.org 251 nick :There are 9 users and 11 invisible on 1 servers
+
+:v0.fellig.org 252 nick 1 :operator(s) online
+
+:v0.fellig.org 254 nick 16 :channels formed
+:v0.fellig.org 255 nick :I have 20 clients and 0 servers
+:v0.fellig.org 265 nick :Current Local Users: 20  Max: 52
+:v0.fellig.org 266 nick :Current Global Users: 20  Max: 22
+*/
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  if ( !g_state->g_channels[i].client_count )
+   continue;
+  numchans++;
+ }
+
+ put_printf(client, obuffer, ":%s 251 %s :There are %zu users and 0 invisible on 1 servers\n",
+            g_state->server_name, clientnick, counters_get(cur, clients));
+ put_printf(client, obuffer, ":%s 254 %s %zu :channels formed\n",
+            g_state->server_name, clientnick, numchans);
+ put_printf(client, obuffer, ":%s 255 %s :I have %zu clients and 0 servers\n",
+            g_state->server_name, clientnick, counters_get(cur, clients));
+ put_printf(client, obuffer, ":%s 265 %s :Current Local Users: %zu  Max: <unknown>\n",
+            g_state->server_name, clientnick, counters_get(cur, clients));
+ put_printf(client, obuffer, ":%s 266 %s :Current Global Users: %zu  Max: <unknown>\n",
+            g_state->server_name, clientnick, counters_get(cur, clients));
+
+ return 0;
+}
+
+static int on_whois(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ const char * clientnick = get_nick(client);
+ const char * tnick = args;
+ int tclient;
+
+ (void)cmd, (void)text;
+
+/*
+:ph7.ph.sft 317 nick phi 131 1322456381 :seconds idle, signon time
+*/
+
+ if ( (tclient = get_client_by_nick(tnick)) == -1 ) {
+  put_printf(client, obuffer, ":%s 401 %s %s :No such nick/channel\n", g_state->server_name, clientnick, tnick);
+ } else {
+  put_printf(client, obuffer, ":%s 311 %s %s %s %s * :%s\n", g_state->server_name, clientnick, tnick,
+             get_ident(tclient), get_node(tclient), get_realname(tclient));
+  put_printf(client, obuffer, ":%s 312 %s %s %s :%s\n", g_state->server_name, clientnick, tnick, g_state->server_name, g_state->server_fullname);
+ }
+ put_printf(client, obuffer, ":%s 318 %s %s :End of /WHOIS list.\n", g_state->server_name, clientnick, tnick);
+
+ return 0;
+}
+
+static int on_privmsg(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ const char * ufull = get_ufull(client);
+ int * listener;
+ size_t count;
+ char * next;
+ int tmp;
+
+ if ( args == NULL || text == NULL )
+  return -1;
+
+ if ( text[0] == 0 )
+  return 0;
+
+ while (args != NULL) {
+  next = strstr(args, ",");
+  if ( next != NULL ) {
+   *next = 0;
+   next++;
+  }
+
+  if ( args[0] == '#' ) {
+   count = get_listener_list(client, args, &listener);
+   for (; count; count--, listener++)
+    if ( *listener != client )
+     put_printf(*listener, NULL, ":%s %s %s :%s\n", ufull, cmd, args, text);
+  } else {
+   if ( (tmp = get_client_by_nick(args)) == -1 ) {
+    put_printf(client, obuffer, ":%s 401 %s %s :No such nick/channel\n", g_state->server_name, get_nick(client), args);
+   } else {
+    put_printf(tmp, NULL, ":%s %s %s :%s\n", ufull, cmd, args, text);
+   }
+  }
+
+  args = next;
+ }
+
+ return 0;
+}
+
+
+static int on_list(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ const char * clientnick = get_nick(client);
+ struct channel * c;
+ size_t i;
+
+ (void)cmd, (void)args, (void)text;
+
+ put_printf(client, obuffer, ":%s 321 %s Channel :Users  Name\n", g_state->server_name, clientnick);
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+  c = &(g_state->g_channels[i]);
+  if ( !c->client_count )
+   continue;
+
+  put_printf(client, obuffer, ":%s 322 %s %s %zu :[+] %s\n",
+             g_state->server_name, clientnick, c->name, c->client_count, c->topic.text == NULL ? "" : c->topic.text);
+ }
+
+ put_printf(client, obuffer, ":%s 323 %s :End of /LIST\n", g_state->server_name, clientnick);
+
+ return 0;
+}
+
+static int on_join(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ struct channel * c;
+ const char * ufull = get_ufull(client);
+ int * listener;
+ size_t count;
+ const char * nick;
+
+ (void)cmd, (void)text;
+
+ if ( args == NULL )
+  return -1;
+
+ if ( do_join(client, args) != 0 ) {
+  return -1;
+ }
+
+ count = get_listener_list(client, args, &listener);
+ for (; count; count--, listener++)
+  put_printf(*listener, NULL, ":%s JOIN :%s\n", ufull, args);
+
+ c = get_channel(args);
+
+ if ( c->topic.text != NULL ) {
+  nick = get_nick(client);
+  put_printf(client, obuffer, ":%s 332 %s %s :%s\n"
+                     ":%s 333 %s %s %s %li\n",
+         g_state->server_name, nick, c->name, c->topic.text,
+         g_state->server_name, nick, c->name, c->topic.user, (long int)c->topic.ts
+  );
+ }
+
+ do_names(client, obuffer, args);
+
+ return 0;
+}
+
+static int on_part(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ const char * ufull = get_ufull(client);
+ int * listener;
+ size_t count;
+
+ (void)obuffer, (void)cmd;
+
+ if ( args == NULL )
+  return -1;
+
+ if ( text == NULL )
+  text = "Dejoined.";
+
+ count = get_listener_list(client, args, &listener);
+ for (; count; count--, listener++)
+  put_printf(*listener, NULL, ":%s PART %s :%s\n", ufull, args, text);
+
+ do_part(client, args);
+
+ return 0;
+}
+
+static int on_names(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ (void)cmd, (void)text;
+
+ if ( args == NULL )
+  return -1;
+
+ return do_names(client, obuffer, args);
+}
+
+static int on_topic(int client, struct roar_buffer ** obuffer, const char * cmd, char * args, char * text) {
+ struct channel * c;
+ const char * ufull = get_ufull(client);
+ int * listener;
+ size_t count;
+ char * nick = get_nick(client);
+
+ (void)obuffer, (void)cmd;
+
+ if ( args == NULL )
+  return -1;
+
+ c = get_channel(args);
+
+ if ( c == NULL )
+  return -1;
+
+ if ( !c->clients[client] ) {
+  return -1;
+ }
+
+ if ( c->topic.text != NULL )
+  roar_mm_free(c->topic.text);
+ if ( c->topic.user != NULL )
+  roar_mm_free(c->topic.user);
+
+ c->topic.text = NULL;
+ c->topic.user = roar_mm_strdup(nick);
+ c->topic.ts   = time(NULL);
+
+ if ( text != NULL )
+  c->topic.text = roar_mm_strdup(text);
+
+ if ( text == NULL )
+  text = "";
+
+ count = get_listener_list(client, c->name, &listener);
+ for (; count; count--, listener++)
+  put_printf(*listener, NULL, ":%s TOPIC %s :%s\n", ufull, c->name, text);
+
+ return 0;
+}
+
+static const struct command g_commands[] = {
  {"NICK", on_nick},
  {"USER", on_user},
@@ -98,750 +838,4 @@
 };
 
-static int is_valid_name (const char * name) {
- register char c;
-
- for (; (c = *name) != 0; name++) {
-  if ( !(isalnum(c) || c == '-' || c == '_') )
-   return 0;
- }
-
- return 1;
-}
-
-static void strip_nl(char * str) {
- register char c;
-
- for (; (c = *str) != 0; str++) {
-  if ( c == '\r' || c == '\n' ) {
-   *str = 0;
-   return;
-  }
- }
-}
-
-static char * get_nick(int client) {
- struct roar_client * c;
- clients_get(client, &c);
- return c->name;
-}
-
-static const char * get_ident(int client) {
- static char buf[80];
- struct roar_client * c;
- clients_get(client, &c);
-
- if ( c->uid == -1 ) {
-  buf[0] = '~';
-  buf[1] = 0;
- } else {
-  snprintf(buf, sizeof(buf)-1, "uid%i~", c->uid);
-  buf[sizeof(buf)-1] = 0;
- }
-
- return buf;
-}
-
-static const char * get_node(int client) {
- struct roar_client * c;
- static char buf_nnode[80];
- char * nnode;
-
- clients_get(client, &c);
-
- roar_nnode_to_str(&(c->nnode), buf_nnode, sizeof(buf_nnode));
-
- nnode  = strstr(buf_nnode, ": ");
- if ( nnode == NULL ) {
-  nnode  = "unknown~";
- } else {
-  nnode += 2;
- }
-
- return nnode;
-}
-
-static const char * get_ufull(int client) {
- struct roar_client * c;
- static char buf[80];
- const char * ident = get_ident(client);
- const char * nnode = get_node(client);
-
- clients_get(client, &c);
-
- snprintf(buf, sizeof(buf)-1, "%s!%s@%s", c->name, ident, nnode);
-
- buf[sizeof(buf)-1] = 0;
-
- return buf;
-}
-
-static const char * get_realname(int client) {
- (void)client;
- return "(no realname)";
-}
-
-static int get_client_by_nick(const char * nick) {
- struct roar_client * c;
- int i;
-
- for (i = 0; i < ROAR_CLIENTS_MAX; i++) {
-  if ( clients_get(i, &c) != 0 )
-   continue;
-
-  if ( !!strcasecmp(c->name, nick) )
-   continue;
-
-  return i;
- }
-
- return -1;
-}
-
-static struct channel * get_channel(const char * name) {
- struct channel * c;
- size_t i;
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  c = &(g_state->g_channels[i]);
-
-  if ( !c->client_count )
-   continue;
-
-  if ( !!strcasecmp(c->name, name) )
-   continue;
-
-  return c;
- }
-
- return NULL;
-}
-
-static size_t get_listener_list(int client, const char * channel, int ** listener) {
- struct channel * c;
- static int clients[ROAR_CLIENTS_MAX];
- size_t ret = 0;
- size_t i, k;
- int j;
- int found;
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  c = &(g_state->g_channels[i]);
-
-  if ( !c->client_count )
-   continue;
-
-  if ( c->clients[client] < 1 )
-   continue;
-
-  if ( channel != NULL && !!strcasecmp(c->name, channel) )
-   continue;
-
-  for (j = 0; j < ROAR_CLIENTS_MAX; j++) {
-   if ( c->clients[j] > 0 ) {
-    found = 0;
-    for (k = 0; k < ret; k++) {
-     if ( clients[k] == j ) {
-      found = 1;
-     }
-    }
-    if ( !found ) {
-     clients[ret] = j;
-     ret++;
-    }
-   }
-  }
- }
-
- *listener = clients;
-
- return ret;
-}
-
-static void put_printf(int client, const char *format, ...) {
- va_list ap;
- struct roar_buffer * buf;
- size_t len = 2048;
- void * data;
- int ret;
-
- if ( roar_buffer_new_data(&buf, len, &data) == -1 )
-  return;
-
- va_start(ap, format);
- ret = vsnprintf(data, len-1, format, ap);
- va_end(ap);
-
- if ( roar_buffer_set_len(buf, strlen(data)) == -1 ) {
-  roar_buffer_free(buf);
-  return;
- }
-
- clients_add_output(client, &buf);
-}
-
-static int do_join(int client, const char * channel) {
- struct channel * c = NULL;
- size_t i;
-
- if ( channel[0] != '#' )
-  return -1;
-
- if ( !is_valid_name(channel+1) )
-  return -1;
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  if ( !g_state->g_channels[i].client_count )
-   continue;
-
-  if ( !!strcasecmp(g_state->g_channels[i].name, channel) )
-   continue;
-
-  c = &(g_state->g_channels[i]);
-  break;
- }
-
- if ( c == NULL ) {
-  for (i = 0; i < MAX_CHANNELS; i++) {
-   if ( g_state->g_channels[i].client_count )
-    continue;
-
-   c = &(g_state->g_channels[i]);
-   break;
-  }
-
-  if ( c == NULL )
-   return -1;
-
-  memset(c, 0, sizeof(*c));
-
-  c->name = roar_mm_strdup(channel);
- }
-
- if ( c->clients[client] )
-  return -1;
-
- c->clients[client] = 1;
- c->client_count++;
-
- return 0;
-}
-
-static int do_part(int client, const char * channel) {
- struct channel * c = NULL;
- size_t i;
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  if ( !g_state->g_channels[i].client_count )
-   continue;
-
-  if ( !!strcasecmp(g_state->g_channels[i].name, channel) )
-   continue;
-
-  if ( !g_state->g_channels[i].clients[client] )
-   return -1;
-
-  c = &(g_state->g_channels[i]);
-  break;
- }
-
- c->clients[client] = 0;
- c->client_count--;
-
- if ( !c->client_count ) {
-  roar_mm_free(c->name);
-
-  if ( c->topic.text != NULL )
-   roar_mm_free(c->topic.text);
-  if ( c->topic.user != NULL )
-   roar_mm_free(c->topic.user);
- }
-
- return 0;
-}
-
-static int do_names(int client, const char * channel) {
- const char * nick = get_nick(client);
- size_t i;
- char buf[256];
- size_t len, offset;
- char * c_nick;
- int j;
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  if ( !g_state->g_channels[i].client_count )
-   continue;
-
-  if ( !!strcasecmp(g_state->g_channels[i].name, channel) )
-   continue;
-
-  offset = 0;
-
-  for (j = 0; j < ROAR_CLIENTS_MAX; j++) {
-   if ( g_state->g_channels[i].clients[j] == 0 )
-    continue;
-
-   c_nick = get_nick(j);
-   len = strlen(c_nick);
-   if ( (offset + len + 3) > sizeof(buf) ) {
-    buf[offset] = 0;
-    put_printf(client, ":%s 353 %s = %s :%s\n", g_state->server_name, nick, channel, buf);
-    offset = 0;
-   }
-
-   memcpy(buf + offset, c_nick, len);
-   offset += len;
-   buf[offset] = ' ';
-   offset++;
-   buf[offset] = 0;
-  }
-
-  if ( offset ) {
-   buf[offset] = 0;
-   put_printf(client, ":%s 353 %s = %s :%s\n", g_state->server_name, nick, channel, buf);
-  }
-  put_printf(client, ":%s 366 %s %s :End of /NAMES list.\n", g_state->server_name, nick, channel);
-
-  return 0;
- }
-
- return -1;
-}
-
-static void cb_client_delete(struct roar_notify_core * core, struct roar_event * event, void * userdata) {
- int * listener;
- size_t count;
- struct channel * c;
- const char * text = g_state->quit_msg;
- int client = event->target;
- const char * ufull = get_ufull(client);
- size_t i;
-
- (void)core, (void)userdata;
-
- if ( text == NULL )
-  text = "Client deleted. Died, kicked or internal error.";
-
- if ( g_state->quit_msg != NULL )
-  put_printf(client, "ERROR :Closing Link: %s (Quit: %s)\n", ufull, text);
-
- count = get_listener_list(client, NULL, &listener);
- for (; count; count--, listener++)
-  put_printf(*listener, ":%s QUIT :Quit: %s\n", ufull, text);
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  c = &(g_state->g_channels[i]);
-
-  if ( !c->client_count )
-   continue;
-
-  if ( !c->clients[client] )
-   continue;
-
-  c->clients[client] = 0;
-  c->client_count--;
-
-  if ( !c->client_count ) {
-   roar_mm_free(c->name);
-
-   if ( c->topic.text != NULL )
-    roar_mm_free(c->topic.text);
-   if ( c->topic.user != NULL )
-    roar_mm_free(c->topic.user);
-  }
- }
-
- clients_flush(client);
-}
-
-static int set_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
- struct roar_client_server * cs;
- char * name;
-
- (void)client, (void)vio, (void)obuffer, (void)userdata, (void)protopara, (void)protoparalen, (void)pluginpara;
-
- clients_get_server(client, &cs);
-
- name = ROAR_CLIENT(cs)->name;
- snprintf(name, sizeof(ROAR_CLIENT(cs)->name), "Client%i~", client);
-
-/*
-:ph7.ph.sft NOTICE AUTH :*** Looking up your hostname...
-:ph7.ph.sft NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
-NICK nick
-USER A B C D
-:ph7.ph.sft NOTICE AUTH :*** Couldn't resolve your hostname; using your IP address instead
-:ph7.ph.sft 002 nick :Your host is ph7.ph.sft, running version Unreal3.2.7
-:ph7.ph.sft 003 nick :This server was created Tue Aug 28 2007 at 10:02:00 CEST
-:ph7.ph.sft 004 nick ph7.ph.sft Unreal3.2.7 iowghraAsORTVSxNCWqBzvdHtGp lvhopsmntikrRcaqOALQbSeIKVfMCuzNTGj
-:ph7.ph.sft 005 nick NAMESX SAFELIST HCN MAXCHANNELS=10 CHANLIMIT=#:10 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 MAXTARGETS=20 WALLCHOPS :are supported by this server
-:ph7.ph.sft 005 nick WATCH=128 SILENCE=15 MODES=12 CHANTYPES=# PREFIX=(ohv)@%+ CHANMODES=beIqa,kfL,lj,psmntirRcOAQKVCuzNSMTG NETWORK=PH2 CASEMAPPING=ascii EXTBAN=~,cqnr ELIST=MNUCT STATUSMSG=@%+ EXCEPTS INVEX :are supported by this server
-:ph7.ph.sft 005 nick CMDS=KNOCK,MAP,DCCALLOW,USERIP :are supported by this server
-*/
-
- put_printf(client,
-      ":%s 001 %s :Welcome to the roard based IRC server.\n"
-      ":%s 375 %s :- %s Message of the Day -\n"
-      ":%s 372 %s :- MotD goes here...\n"
-      ":%s 376 %s :End of /MOTD command.\n",
-   g_state->server_name, name,
-   g_state->server_name, name, g_state->server_name,
-   g_state->server_name, name,
-   g_state->server_name, name
- );
-
- on_lusers(client, NULL, NULL, NULL);
-
- return 0;
-}
-
-static int check_client(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
- char cmd[1024*2];
- char * args;
- char * text;
- ssize_t len;
- size_t i;
- int found = 0;
-
- (void)obuffer, (void)userdata, (void)protopara, (void)protoparalen, (void)pluginpara;
-
- len = roar_vio_read(vio, cmd, sizeof(cmd)-1);
- if ( len < 1 ) {
-  clients_delete(client);
-  return -1;
- }
-
- cmd[len] = 0;
-
- strip_nl(cmd);
-
- ROAR_DBG("check_client(client=%i, vio=?): cmd=\"%s\"", client, cmd);
-
- if ( cmd[0] == 0 )
-  return 0;
-
- args = strstr(cmd, " ");
-
- if ( args != NULL ) {
-  *args = 0;
-  args++;
-  if ( *args == ':' ) {
-   text = args + 1;
-   args = NULL;
-  } else {
-   text = strstr(args, " :");
-   if ( text != NULL ) {
-    *text = 0;
-    text += 2;
-   }
-  }
- } else {
-  text = NULL;
- }
-
- for (i = 0; g_commands[i].name != NULL; i++) {
-  if ( !strcasecmp(g_commands[i].name, cmd) ) {
-   found = 1;
-   g_commands[i].func(client, cmd, args, text);
-  }
- }
-
- if ( !found ) {
-  put_printf(client, ":%s 421 %s %s :Unknown command\n", g_state->server_name, get_nick(client), cmd);
- }
-
- return 0;
-}
-
-// commands:
-static int on_nick(int client, const char * cmd, char * args, char * text) {
- char * nick = get_nick(client);
- int * listener;
- size_t count;
- const char * ufull;
-
- (void)cmd, (void)text;
-
- if ( args != NULL && args[0] != 0 && is_valid_name(args) && strlen(args) < ROAR_BUFFER_NAME ) {
-  if ( get_client_by_nick(args) != -1 ) {
-   put_printf(client, ":%s 433 %s %s :Nickname is already in use.\n", g_state->server_name, nick, args);
-  } else {
-   ufull = get_ufull(client);
-   put_printf(client, ":%s NICK :%s\n", ufull, args);
-   count = get_listener_list(client, NULL, &listener);
-   for (; count; count--, listener++)
-    if ( *listener != client )
-     put_printf(*listener, ":%s NICK :%s\n", ufull, args);
-
-   strcpy(nick, args);
-  }
- } else {
-  put_printf(client, ":%s 432 %s %s :Erroneous Nickname: Illegal characters\n", g_state->server_name, nick, args);
- }
-
- return 0;
-}
-
-static int on_user(int client, const char * cmd, char * args, char * text) {
- (void)client, (void)cmd, (void)args, (void)text;
- return 0;
-}
-
-static int on_quit(int client, const char * cmd, char * args, char * text) {
- int ret;
-
- (void)cmd, (void)args;
-
- if ( text == NULL )
-  text = "... have a cuddle ...";
-
- g_state->quit_msg = text;
- ret = clients_delete(client);
- g_state->quit_msg = NULL;
-
- return ret;
-}
-
-static int on_ping(int client, const char * cmd, char * args, char * text) {
- (void)cmd, (void)args;
-
- put_printf(client, "PONG :%s\n", text);
-
- return 0;
-}
-
-static int on_lusers(int client, const char * cmd, char * args, char * text) {
- const char * clientnick = get_nick(client);
- size_t i;
- size_t numchans = 0;
-
- (void)cmd, (void)args, (void)text;
-
-/*
-:v0.fellig.org 251 nick :There are 9 users and 11 invisible on 1 servers
-
-:v0.fellig.org 252 nick 1 :operator(s) online
-
-:v0.fellig.org 254 nick 16 :channels formed
-:v0.fellig.org 255 nick :I have 20 clients and 0 servers
-:v0.fellig.org 265 nick :Current Local Users: 20  Max: 52
-:v0.fellig.org 266 nick :Current Global Users: 20  Max: 22
-*/
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  if ( !g_state->g_channels[i].client_count )
-   continue;
-  numchans++;
- }
-
- put_printf(client, ":%s 251 %s :There are %zu users and 0 invisible on 1 servers\n",
-            g_state->server_name, clientnick, counters_get(cur, clients));
- put_printf(client, ":%s 254 %s %zu :channels formed\n",
-            g_state->server_name, clientnick, numchans);
- put_printf(client, ":%s 255 %s :I have %zu clients and 0 servers\n",
-            g_state->server_name, clientnick, counters_get(cur, clients));
- put_printf(client, ":%s 265 %s :Current Local Users: %zu  Max: <unknown>\n",
-            g_state->server_name, clientnick, counters_get(cur, clients));
- put_printf(client, ":%s 266 %s :Current Global Users: %zu  Max: <unknown>\n",
-            g_state->server_name, clientnick, counters_get(cur, clients));
-
- return 0;
-}
-
-static int on_whois(int client, const char * cmd, char * args, char * text) {
- const char * clientnick = get_nick(client);
- const char * tnick = args;
- int tclient;
-
- (void)cmd, (void)text;
-
-/*
-:ph7.ph.sft 317 nick phi 131 1322456381 :seconds idle, signon time
-*/
-
- if ( (tclient = get_client_by_nick(tnick)) == -1 ) {
-  put_printf(client, ":%s 401 %s %s :No such nick/channel\n", g_state->server_name, clientnick, tnick);
- } else {
-  put_printf(client, ":%s 311 %s %s %s %s * :%s\n", g_state->server_name, clientnick, tnick,
-             get_ident(tclient), get_node(tclient), get_realname(tclient));
-  put_printf(client, ":%s 312 %s %s %s :%s\n", g_state->server_name, clientnick, tnick, g_state->server_name, g_state->server_fullname);
- }
- put_printf(client, ":%s 318 %s %s :End of /WHOIS list.\n", g_state->server_name, clientnick, tnick);
-
- return 0;
-}
-
-static int on_privmsg(int client, const char * cmd, char * args, char * text) {
- const char * ufull = get_ufull(client);
- int * listener;
- size_t count;
- char * next;
- int tmp;
-
- if ( args == NULL || text == NULL )
-  return -1;
-
- if ( text[0] == 0 )
-  return 0;
-
- while (args != NULL) {
-  next = strstr(args, ",");
-  if ( next != NULL ) {
-   *next = 0;
-   next++;
-  }
-
-  if ( args[0] == '#' ) {
-   count = get_listener_list(client, args, &listener);
-   for (; count; count--, listener++)
-    if ( *listener != client )
-     put_printf(*listener, ":%s %s %s :%s\n", ufull, cmd, args, text);
-  } else {
-   if ( (tmp = get_client_by_nick(args)) == -1 ) {
-    put_printf(client, ":%s 401 %s %s :No such nick/channel\n", g_state->server_name, get_nick(client), args);
-   } else {
-    put_printf(tmp, ":%s %s %s :%s\n", ufull, cmd, args, text);
-   }
-  }
-
-  args = next;
- }
-
- return 0;
-}
-
-
-static int on_list(int client, const char * cmd, char * args, char * text) {
- const char * clientnick = get_nick(client);
- struct channel * c;
- size_t i;
-
- (void)cmd, (void)args, (void)text;
-
- put_printf(client, ":%s 321 %s Channel :Users  Name\n", g_state->server_name, clientnick);
-
- for (i = 0; i < MAX_CHANNELS; i++) {
-  c = &(g_state->g_channels[i]);
-  if ( !c->client_count )
-   continue;
-
-  put_printf(client, ":%s 322 %s %s %zu :[+] %s\n",
-             g_state->server_name, clientnick, c->name, c->client_count, c->topic.text == NULL ? "" : c->topic.text);
- }
-
- put_printf(client, ":%s 323 %s :End of /LIST\n", g_state->server_name, clientnick);
-
- return 0;
-}
-
-static int on_join(int client, const char * cmd, char * args, char * text) {
- struct channel * c;
- const char * ufull = get_ufull(client);
- int * listener;
- size_t count;
- const char * nick;
-
- (void)cmd, (void)text;
-
- if ( args == NULL )
-  return -1;
-
- if ( do_join(client, args) != 0 ) {
-  return -1;
- }
-
- count = get_listener_list(client, args, &listener);
- for (; count; count--, listener++)
-  put_printf(*listener, ":%s JOIN :%s\n", ufull, args);
-
- c = get_channel(args);
-
- if ( c->topic.text != NULL ) {
-  nick = get_nick(client);
-  put_printf(client, ":%s 332 %s %s :%s\n"
-                     ":%s 333 %s %s %s %li\n",
-         g_state->server_name, nick, c->name, c->topic.text,
-         g_state->server_name, nick, c->name, c->topic.user, (long int)c->topic.ts
-  );
- }
-
- do_names(client, args);
-
- return 0;
-}
-
-static int on_part(int client, const char * cmd, char * args, char * text) {
- const char * ufull = get_ufull(client);
- int * listener;
- size_t count;
-
- (void)cmd;
-
- if ( args == NULL )
-  return -1;
-
- if ( text == NULL )
-  text = "Dejoined.";
-
- count = get_listener_list(client, args, &listener);
- for (; count; count--, listener++)
-  put_printf(*listener, ":%s PART %s :%s\n", ufull, args, text);
-
- do_part(client, args);
-
- return 0;
-}
-
-static int on_names(int client, const char * cmd, char * args, char * text) {
- (void)cmd, (void)text;
-
- if ( args == NULL )
-  return -1;
-
- return do_names(client, args);
-}
-
-static int on_topic(int client, const char * cmd, char * args, char * text) {
- struct channel * c;
- const char * ufull = get_ufull(client);
- int * listener;
- size_t count;
- char * nick = get_nick(client);
-
- (void)cmd;
-
- if ( args == NULL )
-  return -1;
-
- c = get_channel(args);
-
- if ( c == NULL )
-  return -1;
-
- if ( !c->clients[client] ) {
-  return -1;
- }
-
- if ( c->topic.text != NULL )
-  roar_mm_free(c->topic.text);
- if ( c->topic.user != NULL )
-  roar_mm_free(c->topic.user);
-
- c->topic.text = NULL;
- c->topic.user = roar_mm_strdup(nick);
- c->topic.ts   = time(NULL);
-
- if ( text != NULL )
-  c->topic.text = roar_mm_strdup(text);
-
- if ( text == NULL )
-  text = "";
-
- count = get_listener_list(client, c->name, &listener);
- for (; count; count--, listener++)
-  put_printf(*listener, ":%s TOPIC %s :%s\n", ufull, c->name, text);
-
- return 0;
-}
-
 // plugin handling suff:
 static int init(struct roar_dl_librarypara * para, struct roar_dl_libraryinst * lib) {
