add FAQ to distribition; make compatible with gcc-2.95
[srvx.git] / src / proto-common.c
index d5ebc8ac4a55e42409efec447cd77da134b6aa6c..22334026c9e412cebe6b658ef78dbcb7d942f95d 100644 (file)
@@ -1,11 +1,12 @@
 /* proto-common.c - common IRC protocol parsing/sending support
  * Copyright 2000-2004 srvx Development Team
  *
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of srvx.
+ *
+ * srvx is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.  Important limitations are
- * listed in the COPYING file that accompanies this software.
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -13,7 +14,8 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, email srvx-maintainers@srvx.net.
+ * along with srvx; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
  */
 
 #include "conf.h"
@@ -79,7 +81,8 @@ uplink_readable(struct io_fd *fd) {
         close_socket();
         return;
     }
-    if ((eol = strpbrk(buffer, "\r\n"))) *eol = 0;
+    if ((eol = strpbrk(buffer, "\r\n")))
+        *eol = 0;
     log_replay(MAIN_LOG, false, buffer);
     if (cManager.uplink->state != DISCONNECTED)
         parse_line(buffer, 0);
@@ -93,7 +96,8 @@ socket_destroyed(struct io_fd *fd)
         log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
     socket_io_fd = NULL;
     cManager.uplink->state = DISCONNECTED;
-    if (self->uplink) DelServer(self->uplink, 0, NULL);
+    if (self->uplink)
+        DelServer(self->uplink, 0, NULL);
 }
 
 void replay_event_loop(void)
@@ -362,34 +366,6 @@ static CMD_FUNC(cmd_stats)
     return 1;
 }
 
-static CMD_FUNC(cmd_whois)
-{
-    struct userNode *from;
-    struct userNode *who;
-
-    if (argc < 3)
-        return 0;
-    if (!(from = GetUserH(origin))) {
-        log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
-        return 0;
-    }
-    if(!(who = GetUserH(argv[2]))) {
-        irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
-        return 1;
-    }
-    if (IsHiddenHost(who) && !IsOper(from)) {
-        /* Just stay quiet. */
-        return 1;
-    }
-    irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
-    irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
-    if (IsOper(who)) {
-        irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
-    }
-    irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
-    return 1;
-}
-
 static CMD_FUNC(cmd_version)
 {
     struct userNode *user;
@@ -438,6 +414,11 @@ static struct chanmsg_func {
     struct userNode *service;
 } chanmsg_funcs[256]; /* indexed by trigger character */
 
+static struct allchanmsg_func {
+    chanmsg_func_t func;
+    struct userNode *service;
+} allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
+
 struct privmsg_desc {
     struct userNode *user;
     char *text;
@@ -451,6 +432,7 @@ privmsg_chan_helper(struct chanNode *cn, void *data)
     struct privmsg_desc *pd = data;
     struct modeNode *mn;
     struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
+    int x;
 
     /* Don't complain if it can't find the modeNode because the channel might
      * be -n */
@@ -460,6 +442,15 @@ privmsg_chan_helper(struct chanNode *cn, void *data)
     /* Never send a NOTICE to a channel to one of the services */
     if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
         cf->func(pd->user, cn, pd->text+1, cf->service);
+
+    /* This catches *all* text sent to the channel that the services server sees */
+    for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
+       cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
+       if (!cf->func)
+         break; /* end of list */
+       else
+       cf->func(pd->user, cn, pd->text, cf->service);
+    }
 }
 
 static void
@@ -487,6 +478,19 @@ reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t
     chanmsg_funcs[prefix].service = service;
 }
 
+void
+reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
+{
+    int x;
+    for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
+       if (allchanmsg_funcs[x].func)
+         continue;
+       allchanmsg_funcs[x].func = handler;
+       allchanmsg_funcs[x].service = service;
+       break;
+    }
+}
+
 struct userNode *
 get_chanmsg_bot(unsigned char prefix)
 {
@@ -519,8 +523,12 @@ mod_chanmode_alloc(unsigned int argc)
         res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
     else
         res = calloc(1, sizeof(*res));
-    if (res)
+    if (res) {
+#if !defined(NDEBUG)
+        res->alloc_argc = argc;
+#endif
         res->argc = argc;
+    }
     return res;
 }
 
@@ -532,6 +540,8 @@ mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
     if (res) {
         res->modes_set = orig->modes_set;
         res->modes_clear = orig->modes_clear;
+        res->new_limit = orig->new_limit;
+        memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
         res->argc = orig->argc;
         memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
     }
@@ -544,6 +554,7 @@ mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_ch
     struct banNode *bn;
     unsigned int ii, jj;
 
+    assert(change->argc <= change->alloc_argc);
     channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
     if (change->modes_set & MODE_LIMIT)
         channel->limit = change->new_limit;
@@ -557,34 +568,44 @@ mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_ch
              * to be more specific than an existing ban.
              */
             for (jj=0; jj<channel->banlist.used; ++jj) {
-                if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
+                if (match_ircglobs(change->args[ii].u.hostmask, channel->banlist.list[jj]->ban)) {
                     banList_remove(&channel->banlist, channel->banlist.list[jj]);
                     free(channel->banlist.list[jj]);
                     jj--;
                 }
             }
             bn = calloc(1, sizeof(*bn));
-            safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
-            safestrncpy(bn->who, who->nick, sizeof(bn->who));
+            safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
+            if (who)
+                safestrncpy(bn->who, who->nick, sizeof(bn->who));
+            else
+                safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
             bn->set = now;
             banList_append(&channel->banlist, bn);
             break;
         case MODE_REMOVE|MODE_BAN:
             for (jj=0; jj<channel->banlist.used; ++jj) {
-                if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
+                if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].u.hostmask))
                     continue;
                 free(channel->banlist.list[jj]);
                 banList_remove(&channel->banlist, channel->banlist.list[jj]);
                 break;
             }
             break;
-        default:
-            assert((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP|MODE_VOICE)) != 0);
+        case MODE_CHANOP:
+        case MODE_VOICE:
+        case MODE_VOICE|MODE_CHANOP:
+        case MODE_REMOVE|MODE_CHANOP:
+        case MODE_REMOVE|MODE_VOICE:
+        case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
             if (change->args[ii].mode & MODE_REMOVE)
-                change->args[ii].member->modes &= ~change->args[ii].mode;
+                change->args[ii].u.member->modes &= ~change->args[ii].mode;
             else
-                change->args[ii].member->modes |= change->args[ii].mode;
+                change->args[ii].u.member->modes |= change->args[ii].mode;
             break;
+        default:
+            assert(0 && "Invalid mode argument");
+            continue;
         }
     }
 }
@@ -617,10 +638,11 @@ mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsig
 }
 
 int
-irc_make_chanmode(struct chanNode *chan, char *out) {
+irc_make_chanmode(struct chanNode *chan, char *out)
+{
     struct mod_chanmode change;
+    mod_chanmode_init(&change);
     change.modes_set = chan->modes;
-    change.modes_clear = change.argc = 0;
     change.new_limit = chan->limit;
     safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
     return strlen(mod_chanmode_format(&change, out));
@@ -650,7 +672,9 @@ generate_hostmask(struct userNode *user, int options)
         strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
     }
     hostname = user->hostname;
-    if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
+    if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
+        hostname = user->fakehost;
+    } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
         hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
         sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
     } else if (options & GENMASK_STRICT_HOST) {