Fix several bugs; make off-channel a per-channel option
[srvx.git] / src / proto-p10.c
index b96e115f816e84cfa01fdf5db7adb433bd82b531..046030ade02dcef2ea8ef79e85cfdda6dee0713b 100644 (file)
@@ -1,11 +1,12 @@
 /* proto-p10.c - IRC protocol output
  * 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 "proto-common.c"
@@ -284,9 +286,13 @@ static unsigned int num_privmsg_funcs;
 static privmsg_func_t *notice_funcs;
 static unsigned int num_notice_funcs;
 static struct dict *unbursted_channels;
+static char *his_servername;
+static char *his_servercomment;
 
 static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, time_t timestamp, const char *realip);
 
+extern int off_channel;
+
 /* Numerics can be XYY, XYYY, or XXYYY; with X's identifying the
  * server and Y's indentifying the client on that server. */
 struct server*
@@ -472,6 +478,12 @@ irc_notice(struct userNode *from, const char *to, const char *message)
     putsock("%s " P10_NOTICE " %s :%s", from->numeric, to, message);
 }
 
+void
+irc_notice_user(struct userNode *from, struct userNode *to, const char *message)
+{
+    putsock("%s " P10_NOTICE " %s :%s", from->numeric, to->numeric, message);
+}
+
 void
 irc_privmsg(struct userNode *from, const char *to, const char *message)
 {
@@ -552,8 +564,6 @@ irc_burst(struct chanNode *chan)
     long last_mode=-1;
     unsigned int n;
 
-    if (!chan->members.used)
-        return;
     base_len = sprintf(burst_line, "%s " P10_BURST " %s " FMT_TIME_T " ",
                        self->numeric, chan->name, chan->timestamp);
     len = irc_make_chanmode(chan, burst_line+base_len);
@@ -672,7 +682,7 @@ irc_kick(struct userNode *who, struct userNode *target, struct chanNode *channel
 {
     const char *numeric;
     struct modeNode *mn = GetUserMode(channel, who);
-    numeric = (mn && (mn->modes & MODE_CHANOP)) ? who->numeric : self->numeric;
+    numeric = ((mn && (mn->modes & MODE_CHANOP)) || off_channel) ? who->numeric : self->numeric;
     putsock("%s " P10_KICK " %s %s :%s",
             numeric, channel->name, target->numeric, msg);
 }
@@ -742,6 +752,38 @@ change_nicklen(int new_nicklen)
     }
 }
 
+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);
+    if (his_servername && his_servercomment)
+      irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
+    else
+    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_server)
 {
     struct server *srv;
@@ -875,20 +917,14 @@ static void
 create_helper(char *name, void *data)
 {
     struct create_desc *cd = data;
-    /* We can't assume the channel create was allowed because of the
-     * bad-word channel checking.
-     */
-    struct chanNode *cn;
-    struct modeNode *mn;
+
     if (!strcmp(name, "0")) {
         while (cd->user->channels.used > 0)
             DelChannelUser(cd->user, cd->user->channels.list[0]->channel, 0, 0);
         return;
     }
-    cn = AddChannel(name, cd->when, NULL, NULL);
-    mn = AddChannelUser(cd->user, cn);
-    if (mn && (cn->members.used == 1))
-        mn->modes = MODE_CHANOP;
+
+    AddChannelUser(cd->user, AddChannel(name, cd->when, NULL, NULL));
 }
 
 static CMD_FUNC(cmd_create)
@@ -1403,6 +1439,12 @@ init_parse(void)
         inttobase64(numer, (numnick << 12) + (usermask & 0x00fff), 3);
     else
         inttobase64(numer, (numnick << 18) + (usermask & 0x3ffff), 5);
+
+    str = conf_get_data("server/his_servername", RECDB_QSTRING);
+    his_servername = str ? strdup(str) : NULL;
+    str = conf_get_data("server/his_servercomment", RECDB_QSTRING);
+    his_servercomment = str ? strdup(str) : NULL;
+
     str = conf_get_data("server/hostname", RECDB_QSTRING);
     desc = conf_get_data("server/description", RECDB_QSTRING);
     if (!str || !desc) {
@@ -1516,6 +1558,7 @@ init_parse(void)
     dict_insert(irc_func_dict, "442", cmd_dummy); /* you aren't on that channel */
     dict_insert(irc_func_dict, "443", cmd_dummy); /* is already on channel (after invite?) */
     dict_insert(irc_func_dict, "461", cmd_dummy); /* Not enough parameters (after TOPIC w/ 0 args) */
+    dict_insert(irc_func_dict, "467", cmd_dummy); /* Channel key already set */
 
     num_privmsg_funcs = 16;
     privmsg_funcs = malloc(sizeof(privmsg_func_t)*num_privmsg_funcs);
@@ -1734,7 +1777,7 @@ void DelServer(struct server* serv, int announce, const char *message)
 }
 
 struct userNode *
-AddService(const char *nick, const char *desc)
+AddService(const char *nick, const char *desc, const char *hostname)
 {
     char numeric[COMBO_NUMERIC_LEN+1];
     int local_num = get_local_numeric();
@@ -1750,8 +1793,10 @@ AddService(const char *nick, const char *desc)
         log_module(MAIN_LOG, LOG_ERROR, "Unable to allocate numnick for service %s", nick);
         return 0;
     }
+    if (!hostname)
+        hostname = self->name;
     make_numeric(self, local_num, numeric);
-    return AddUser(self, nick, nick, self->name, "+oik", numeric, desc, now, "AAAAAA");
+    return AddUser(self, nick, nick, hostname, "+oik", numeric, desc, now, "AAAAAA");
 }
 
 struct userNode *
@@ -2051,10 +2096,16 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 victim = GetUserN(modes[in_arg++]);
             else
                 victim = GetUserH(modes[in_arg++]);
+            if (!victim)
+                continue;
             if ((change->args[ch_arg].member = GetUserMode(channel, victim)))
                 ch_arg++;
             break;
         }
+        default:
+            if (!(flags & MCP_FROM_SERVER))
+                goto error;
+            break;
         }
     }
     change->argc = ch_arg; /* in case any turned out to be ignored */
@@ -2110,11 +2161,14 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
     struct modeNode *mn;
     char int_buff[32], mode = '\0';
 
+    assert(change->argc <= change->alloc_argc);
     memset(&chbuf, 0, sizeof(chbuf));
     chbuf.channel = channel;
     chbuf.actor = who;
     chbuf.chname_len = strlen(channel->name);
-    if ((mn = GetUserMode(channel, who)) && (mn->modes & MODE_CHANOP))
+
+    mn = GetUserMode(channel, who);
+    if ((mn && (mn->modes & MODE_CHANOP)) || off_channel)
         chbuf.is_chanop = 1;
 
     /* First remove modes */
@@ -2211,6 +2265,7 @@ char *
 mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
 {
     unsigned int used = 0;
+    assert(change->argc <= change->alloc_argc);
     if (change->modes_clear) {
         outbuff[used++] = '-';
 #define DO_MODE_CHAR(BIT, CHAR) if (change->modes_clear & MODE_##BIT) outbuff[used++] = CHAR
@@ -2330,6 +2385,24 @@ reg_privmsg_func(struct userNode *user, privmsg_func_t handler)
     privmsg_funcs[numeric] = handler;
 }
 
+void
+unreg_privmsg_func(struct userNode *user, privmsg_func_t handler)
+{
+    unsigned int x;
+
+    if (!user || handler)
+      return; /* this really only works with users */
+
+    memset(privmsg_funcs+user->num_local, 0, sizeof(privmsg_func_t));
+
+    for (x = user->num_local+1; x < num_privmsg_funcs; x++) 
+       memmove(privmsg_funcs+x-1, privmsg_funcs+x, sizeof(privmsg_func_t));
+    
+    privmsg_funcs = realloc(privmsg_funcs, num_privmsg_funcs*sizeof(privmsg_func_t)); 
+    num_privmsg_funcs--;
+}
+
+
 void
 reg_notice_func(struct userNode *user, privmsg_func_t handler)
 {
@@ -2345,6 +2418,24 @@ reg_notice_func(struct userNode *user, privmsg_func_t handler)
     notice_funcs[numeric] = handler;
 }
 
+void
+unreg_notice_func(struct userNode *user, privmsg_func_t handler)
+{
+    unsigned int x;
+
+    if (!user || handler)
+          return; /* this really only works with users */
+
+    memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t));
+
+    for (x = user->num_local+1; x < num_notice_funcs; x++)
+       memmove(notice_funcs+x-1, notice_funcs+x, sizeof(privmsg_func_t));
+
+    memset(notice_funcs+user->num_local, 0, sizeof(privmsg_func_t));
+    notice_funcs = realloc(notice_funcs, num_notice_funcs*sizeof(privmsg_func_t));
+    num_notice_funcs--;
+}
+
 void
 reg_oper_func(oper_func_t handler)
 {