Merge remote branch 'upstream/master'
authorroot <root@localhost>
Thu, 2 Feb 2012 21:44:45 +0000 (22:44 +0100)
committerroot <root@localhost>
Thu, 2 Feb 2012 21:44:45 +0000 (22:44 +0100)
Conflicts:
src/proto-p10.c

1  2 
configure.in
src/proto-bahamut.c
src/proto-common.c
src/proto-p10.c
src/proto.h

diff --combined configure.in
index 9540a00373dbddae5d06372010055601abca051b,5b350a985dfd344e391d97260f830f51c3cee088..a3a885165a080b602c80af02da02b311a5ace647
@@@ -3,12 -3,13 +3,13 @@@ dnl Process this file with autoconf to 
  dnl General initialization.
  AC_PREREQ(2.64)
  AC_INIT([srvx],[1.4.0-rc3],[srvx-bugs@lists.sourceforge.net])
 -CODENAME=surge
 +CODENAME=wgn
  AC_CONFIG_HEADERS(src/config.h)
  AC_CONFIG_SRCDIR(src/opserv.c)
  dnl AM_CANONICAL_TARGET must be before AM_INIT_AUTOMAKE() or autoconf whines
  AC_CANONICAL_TARGET
- AM_INIT_AUTOMAKE([gnu 1.6])
+ AC_CONFIG_MACRO_DIR([m4])
+ AM_INIT_AUTOMAKE([gnu 1.10 silent-rules])
  AM_MAINTAINER_MODE
  LT_INIT
  
@@@ -23,7 -24,6 +24,7 @@@ AC_PROG_INSTAL
  AC_PROG_LN_S
  AC_PROG_MAKE_SET
  AC_PROG_GCC_TRADITIONAL
 +AC_PROG_RANLIB
  
  dnl Look for a git client
  AC_CHECK_PROGS(GIT, [git])
@@@ -333,7 -333,7 +334,7 @@@ AC_ARG_ENABLE(modules
    module_list=""
    dnl Must use a separate file because autoconf can't stand newlines in an AC_SUBSTed variable.
    for module in $enableval ; do
-     module=`echo $module | sed -e s/^mod-// -e s/\.c\$//`
+     module=`echo $module | sed -e s/^mod-// -e s/\\\.c\\\$//`
      EXTRA_MODULE_OBJS="$EXTRA_MODULE_OBJS mod-$module.\$(OBJEXT)"
      module_list="$module_list $module"
      echo "WITH_MODULE($module)" >> $MODULE_DEFINES
diff --combined src/proto-bahamut.c
index 99ffa3bf9800a778a3469e44f0e6f7199494c4c5,af7e5546ea133a75e8915bba9f05532fff64639d..a0c1d98ef102d5f80004c0a05aa3cb8dc21a2f79
@@@ -1105,6 -1105,7 +1105,7 @@@ void init_parse(void) 
  
      userList_init(&dead_users);
      reg_exit_func(parse_cleanup);
+     (void)call_xquery_funcs;
  }
  
  int parse_line(char *line, int recursive) {
@@@ -1230,7 -1231,7 +1231,7 @@@ void mod_usermode(struct userNode *user
  }
  
  struct mod_chanmode *
 -mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
 +mod_chanmode_parse(struct chanNode *channel, struct userNode *user, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
  {
      struct mod_chanmode *change;
      unsigned int ii, in_arg, ch_arg, add;
diff --combined src/proto-common.c
index e690726116d06cbe36b2d30ef9da9fc223fb5fea,6039d759158228af5520aa5ea91ccd8a576723e7..bc37f32a13aa945940a109cba3d627f620120c43
@@@ -23,9 -23,6 +23,9 @@@
  #include "ioset.h"
  #include "log.h"
  #include "nickserv.h"
 +#include "opserv.h"
 +#include "chanserv.h"
 +#include "spamserv.h"
  #include "timeq.h"
  #ifdef HAVE_SYS_SOCKET_H
  #include <sys/socket.h>
@@@ -450,8 -447,6 +450,8 @@@ privmsg_chan_helper(struct chanNode *cn
          && (offchannel_allowed[(unsigned char)pd->text[0]]
              || (GetUserMode(cn, cf->service) && !IsDeaf(cf->service))))
          cf->func(pd->user, cn, pd->text+1, cf->service, pd->is_notice);
 +    else
 +        spamserv_channel_message(cn, pd->user, pd->text);
  
      /* This catches *all* text sent to the channel that the services server sees */
      for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
@@@ -483,8 -478,6 +483,8 @@@ part_helper(struct chanNode *cn, void *
  {
      struct part_desc *desc = data;
      DelChannelUser(desc->user, cn, desc->text, false);
 +    if (IsOper(desc->user))
 +        operpart(cn, desc->user);
  }
  
  static CMD_FUNC(cmd_part)
@@@ -577,6 -570,34 +577,34 @@@ call_oper_funcs(struct userNode *user
      }
  }
  
+ static xquery_func_t *xqf_list;
+ static unsigned int xqf_size = 0, xqf_used = 0;
+ void
+ reg_xquery_func(xquery_func_t handler)
+ {
+     if (xqf_used == xqf_size) {
+         if (xqf_size) {
+             xqf_size <<= 1;
+             xqf_list = realloc(xqf_list, xqf_size*sizeof(xquery_func_t));
+         } else {
+             xqf_size = 8;
+             xqf_list = malloc(xqf_size*sizeof(xquery_func_t));
+         }
+     }
+     xqf_list[xqf_used++] = handler;
+ }
+ static void
+ call_xquery_funcs(struct server *source, const char routing[], const char query[])
+ {
+     unsigned int n;
+     for (n=0; n < xqf_used; n++)
+     {
+         xqf_list[n](source, routing, query);
+     }
+ }
  struct mod_chanmode *
  mod_chanmode_alloc(unsigned int argc)
  {
@@@ -603,9 -624,6 +631,9 @@@ mod_chanmode_dup(struct mod_chanmode *o
          res->modes_set = orig->modes_set;
          res->modes_clear = orig->modes_clear;
          res->new_limit = orig->new_limit;
 +        res->new_access = orig->new_access;
 +        memcpy(res->new_altchan, orig->new_altchan, sizeof(res->new_altchan));
 +        memcpy(res->new_noflood, orig->new_noflood, sizeof(res->new_noflood));
          memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
          memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
          memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
@@@ -625,14 -643,8 +653,14 @@@ mod_chanmode_apply(struct userNode *who
      channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
      if (change->modes_set & MODE_LIMIT)
          channel->limit = change->new_limit;
 +    if (change->modes_set & MODE_ACCESS)
 +        channel->access = change->new_access;
      if (change->modes_set & MODE_KEY)
          strcpy(channel->key, change->new_key);
 +    if (change->modes_set & MODE_ALTCHAN)
 +        strcpy(channel->altchan, change->new_altchan);
 +    if (change->modes_set & MODE_NOFLOOD)
 +        strcpy(channel->noflood, change->new_noflood);
      if (change->modes_set & MODE_UPASS)
         strcpy(channel->upass, change->new_upass);
      if (change->modes_set & MODE_APASS)
@@@ -709,7 -721,7 +737,7 @@@ mod_chanmode(struct userNode *who, stru
          base_oplevel = member->oplevel;
      else
          base_oplevel = MAXOPLEVEL;
 -    if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
 +    if (!(change = mod_chanmode_parse(channel, who, modes, argc, flags, base_oplevel)))
          return 0;
      if (flags & MC_ANNOUNCE)
          mod_chanmode_announce(who, channel, change);
@@@ -729,9 -741,6 +757,9 @@@ irc_make_chanmode(struct chanNode *chan
      mod_chanmode_init(&change);
      change.modes_set = chan->modes;
      change.new_limit = chan->limit;
 +    change.new_access = chan->access;
 +    safestrncpy(change.new_altchan, chan->altchan, sizeof(change.new_altchan));
 +    safestrncpy(change.new_noflood, chan->noflood, sizeof(change.new_noflood));
      safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
      safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
      safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
@@@ -765,12 -774,7 +793,12 @@@ generate_hostmask(struct userNode *user
      }
      hostname = user->hostname;
      if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
 -        hostname = user->fakehost;
 +        if(user->fakehost && user->fakehost[0] == '$') {
 +            hostname = alloca(strlen(user->handle_info->handle) + strlen(user->fakehost));
 +            sprintf(hostname, "%s%s", user->handle_info->handle, user->fakehost+1);
 +        } else {
 +            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);
diff --combined src/proto-p10.c
index c48d21b45125fc6431c1d3413e7b1c2f3c3c60f3,71f6d3766acd5f2ef97de6a6f155b5d1ec31038a..d08fb1804fd82c48aaf5a3f9ffcd624d1a48d05a
@@@ -40,7 -40,6 +40,7 @@@
  #define CMD_EOB_ACK             "EOB_ACK"
  #define CMD_ERROR               "ERROR"
  #define CMD_FAKEHOST            "FAKE"
 +#define CMD_FAKEHOST2           "FAKE2"
  #define CMD_GET                 "GET"
  #define CMD_GLINE               "GLINE"
  #define CMD_HASH                "HASH"
@@@ -73,7 -72,6 +73,7 @@@
  #define CMD_PROTO               "PROTO"
  #define CMD_QUIT                "QUIT"
  #define CMD_REHASH              "REHASH"
 +#define CMD_RELAY               "RELAY"
  #define CMD_RESET               "RESET"
  #define CMD_RESTART             "RESTART"
  #define CMD_RPING               "RPING"
@@@ -88,8 -86,6 +88,8 @@@
  #define CMD_SQUIT               "SQUIT"
  #define CMD_STATS               "STATS"
  #define CMD_SVSNICK             "SVSNICK"
 +#define CMD_SVSMODE             "SVSMODE"
 +#define CMD_SVSJOIN             "SVSJOIN"
  #define CMD_TIME                "TIME"
  #define CMD_TOPIC               "TOPIC"
  #define CMD_TRACE               "TRACE"
  #define CMD_WHO                 "WHO"
  #define CMD_WHOIS               "WHOIS"
  #define CMD_WHOWAS              "WHOWAS"
+ #define CMD_XQUERY              "XQUERY"
+ #define CMD_XRESPONSE           "XRESPONSE"
  
  /* Tokenized commands. */
  #define TOK_ACCOUNT             "AC"
  #define TOK_EOB_ACK             "EA"
  #define TOK_ERROR               "Y"
  #define TOK_FAKEHOST            "FA"
 +#define TOK_FAKEHOST2           "NFH"
  #define TOK_GET                 "GET"
  #define TOK_GLINE               "GL"
  #define TOK_HASH                "HASH"
  #define TOK_PROTO               "PROTO"
  #define TOK_QUIT                "Q"
  #define TOK_REHASH              "REHASH"
 +#define TOK_RELAY               "RL"
  #define TOK_RESET               "RESET"
  #define TOK_RESTART             "RESTART"
  #define TOK_RPING               "RI"
  #define TOK_SQUIT               "SQ"
  #define TOK_STATS               "R"
  #define TOK_SVSNICK             "SN"
 +#define TOK_SVSMODE             "SM"
 +#define TOK_SVSJOIN             "SJ"
  #define TOK_TIME                "TI"
  #define TOK_TOPIC               "T"
  #define TOK_TRACE               "TR"
  #define TOK_WHO                 "H"
  #define TOK_WHOIS               "W"
  #define TOK_WHOWAS              "X"
+ #define TOK_XQUERY              "XQ"
+ #define TOK_XRESPONSE           "XR"
  
  /* Protocol messages; aliased to full commands or tokens depending
     on compile-time configuration. ircu prefers tokens WITH THE
  #define P10_EOB_ACK             TYPE(EOB_ACK)
  #define P10_ERROR               TYPE(ERROR)
  #define P10_FAKEHOST            TYPE(FAKEHOST)
 +#define P10_FAKEHOST2           TYPE(FAKEHOST2)
  #define P10_GET                 TYPE(GET)
  #define P10_GLINE               TYPE(GLINE)
  #define P10_HASH                TYPE(HASH)
  #define P10_PROTO               TYPE(PROTO)
  #define P10_QUIT                TYPE(QUIT)
  #define P10_REHASH              TYPE(REHASH)
 +#define P10_RELAY               TYPE(RELAY)
  #define P10_RESET               TYPE(RESET)
  #define P10_RESTART             TYPE(RESTART)
  #define P10_RPING               TYPE(RPING)
  #define P10_SQUIT               TYPE(SQUIT)
  #define P10_STATS               TYPE(STATS)
  #define P10_SVSNICK             TYPE(SVSNICK)
 +#define P10_SVSMODE             TYPE(SVSMODE)
 +#define P10_SVSJOIN             TYPE(SVSJOIN)
  #define P10_TIME                TYPE(TIME)
  #define P10_TOPIC               TYPE(TOPIC)
  #define P10_TRACE               TYPE(TRACE)
  #define P10_WHO                 TYPE(WHO)
  #define P10_WHOIS               TYPE(WHOIS)
  #define P10_WHOWAS              TYPE(WHOWAS)
+ #define P10_XQUERY              TYPE(XQUERY)
+ #define P10_XRESPONSE           TYPE(XRESPONSE)
  
  /* Servers claiming to have a boot or link time before PREHISTORY
   * trigger errors to the log.  We hope no server has been running
@@@ -305,8 -299,8 +311,8 @@@ static const char *his_servername
  static const char *his_servercomment;
  static struct channelList dead_channels;
  
 -/* These correspond to 1 << X:      012345678901234567 */
 -const char irc_user_mode_chars[] = "o iw dkgn    x   I";
 +/* These correspond to 1 << X:      012345678901234567890123 */
 +const char irc_user_mode_chars[] = "o iw dkgn    x   ISDXHst";
  
  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, unsigned long timestamp, const char *realip);
  
@@@ -505,25 -499,7 +511,25 @@@ irc_account(struct userNode *user, cons
  void
  irc_fakehost(struct userNode *user, const char *host, const char *ident, int force)
  {
 -    putsock("%s " P10_FAKEHOST " %s %s %s%s", self->numeric, user->numeric, ident, host, force ? " FORCE" : "");
 +    /* SRVX added the possibility for FAKE IDENTS
 +     * but this is currently *NOT* supported by our IRCu
 +     *
 +     * edit 24.11.11: 
 +     *  NFH (P10_FAKEHOST2) is now supported by our IRCu (git-65-21592a4)
 +     */
 +    putsock("%s " P10_FAKEHOST2 " %s %s %s%s", self->numeric, user->numeric, ident, host, force ? " FORCE" : "");
 +}
 +
 +void 
 +irc_relay(char *message)
 +{
 +    putsock("%s " P10_RELAY " %s", self->numeric, message);
 +}
 +
 +void 
 +irc_simul(struct userNode *target, char *command)
 +{
 +    putsock("%s " P10_RELAY " %s SI %s :%s", self->numeric, target->numeric, target->numeric, command);
  }
  
  void
@@@ -624,24 -600,6 +630,24 @@@ irc_privmsg(struct userNode *from, cons
          putsock("%s " P10_PRIVMSG " %s :%s", from->numeric, to, message);
  }
  
 +void
 +irc_svsmode(struct userNode *from, struct userNode *user, const char *modes)
 +{
 +putsock("%s " P10_SVSMODE " %s %s", from->numeric, user->numeric, modes);
 +}
 +
 +void
 +irc_svsjoin(struct userNode *from, struct userNode *user, struct chanNode *chan)
 +{
 +putsock("%s " P10_SVSJOIN " %s %s", from->numeric, user->numeric, chan->name);
 +}
 +
 +void
 +irc_svsjoinchan(struct userNode *from, struct userNode *user, const char *chan)
 +{
 +putsock("%s " P10_SVSJOIN " %s %s", from->numeric, user->numeric, chan);
 +}
 +
  void
  irc_eob(void)
  {
@@@ -702,16 -660,12 +708,16 @@@ irc_introduce(const char *passwd
  void
  irc_gline(struct server *srv, struct gline *gline)
  {
 +    //<prefix> GL <target> [!][+|-|>|<]<mask> [<expiration>] [<lastmod>] [<lifetime>] [:<reason>]
 +    //expiration = relative time (seconds)
 +    //lastmod = timestamp
 +    //livetime = timestamp
      if (gline->lastmod)
 -        putsock("%s " P10_GLINE " %s +%s %lu %lu %lu :%s",
 -                self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->lastmod, gline->lifetime, gline->reason);
 +        putsock("%s " P10_GLINE " %s +%s %lu %lu %lu :%s", self->numeric, (srv ? srv->numeric : "*"), 
 +                gline->target, gline->expires-now, gline->lastmod, gline->lifetime, gline->reason);
      else
 -        putsock("%s " P10_GLINE " %s +%s %lu :%s",
 -                self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->reason);
 +        putsock("%s " P10_GLINE " %s +%s %lu :%s", self->numeric, (srv ? srv->numeric : "*"), 
 +                gline->target, gline->expires-now, gline->reason);
  }
  
  void
@@@ -727,7 -681,6 +733,7 @@@ voi
  irc_ungline(const char *mask)
  {
      putsock("%s " P10_GLINE " * -%s %lu", self->numeric, mask, now);
 +    //putsock("%s " P10_GLINE " * %s * %lu", self->numeric, mask, now);
  }
  
  /* Return negative if *(struct modeNode**)pa is "less than" pb,
@@@ -961,6 -914,12 +967,12 @@@ irc_numeric(struct userNode *user, unsi
      putsock(":%s %03d %s %s", self->name, num, user->nick, buffer);
  }
  
+ void
+ irc_xresponse(struct server *target, const char *routing, const char *response)
+ {
+     putsock("%s " P10_XRESPONSE " %s %s :%s", self->numeric, target->numeric, routing, response);
+ }
  static void send_burst(void);
  
  static void
@@@ -1209,8 -1168,6 +1221,8 @@@ create_helper(char *name, void *data
          return;
      }
  
 +    handle_new_channel_created(name, cd->user);
 +
      AddChannelUser(cd->user, AddChannel(name, cd->when, NULL, NULL));
  }
  
@@@ -1344,7 -1301,7 +1356,7 @@@ static CMD_FUNC(cmd_burst
              int n_modes;
              for (pos=argv[next], n_modes = 1; *pos; pos++)
                  if ((*pos == 'k') || (*pos == 'l') || (*pos == 'A')
 -                    || (*pos == 'U'))
 +                    || (*pos == 'U') || (*pos == 'a') || (*pos == 'F'))
                      n_modes++;
              if (next + n_modes > argc)
                  n_modes = argc - next;
@@@ -1599,8 -1556,6 +1611,8 @@@ static CMD_FUNC(cmd_kick
  {
      if (argc < 3)
          return 0;
 +    if (GetUserN(argv[2]) && IsOper(GetUserN(argv[2])))
 +        operpart(GetChannel(argv[1]), GetUserN(argv[2]));
      ChannelUserKicked(GetUserH(origin), GetUserN(argv[2]), GetChannel(argv[1]));
      return 1;
  }
@@@ -1723,105 -1678,16 +1735,115 @@@ static CMD_FUNC(cmd_time
      return 1;
  }
  
 +static CMD_FUNC(cmd_relay)
 +{
 +    struct server *sNode;
 +    unsigned int len;
 +    char buf[3];
 +    //<sender> RELAY <destination> <command>
 +    len = strlen(argv[1]);
 +    buf[2] = 0;
 +    switch(len) {
 +        case 2:
 +            sNode = GetServerN(argv[1]);
 +            break;
 +        case 5:
 +            buf[0] = argv[1][0];
 +            buf[1] = argv[1][1];
 +            sNode = GetServerN(buf);
 +            break;
 +        case 6:
 +            buf[0] = argv[1][1];
 +            buf[1] = argv[1][2];
 +            sNode = GetServerN(buf);
 +            break;
 +        default:
 +            /* Invalid destination. Ignore. */
 +            return 0;
 +    }
 +    if(sNode->numeric == self->numeric) {
 +        //ok  someone relayed something to us!
 +        if(strcmp("LQ", argv[2]) == 0) {
 +            //oooh thats exciting - we've got a LOC Query! :D
 +            //LQ !ABADE pk910 80.153.5.212 server.zoelle1.de ~watchcat :test
 +            //ok  let's check the login datas
 +            struct handle_info *hi;
 +            char tmp[MAXLEN], tmp2[MAXLEN];
 +            sprintf(tmp, "%s@%s",argv[7],argv[6]);
 +            sprintf(tmp2, "%s@%s",argv[7],argv[5]);
 +            if((hi = checklogin(argv[4],argv[argc-1],&argv[3][1],tmp,tmp2))) {
 +             //login ok
 +             struct devnull_class *th;
 +             char devnull[512];
 +             if(hi->devnull && (th = devnull_get(hi->devnull))) {
 +                const char *devnull_modes = DEVNULL_MODES;
 +                int ii, flen;
 +                char flags[50];
 +                for (ii=flen=0; devnull_modes[ii]; ++ii)
 +                    if (th->modes & (1 << ii))
 +                        flags[flen++] = devnull_modes[ii];
 +                flags[flen] = 0;
 +                sprintf(devnull, "+%s %s %lu %lu",flags,th->name,th->maxchan,th->maxsendq);
 +             } else {
 +                devnull[0] = 0;
 +             }
 +             if(!HANDLE_FLAGGED(hi, AUTOHIDE)) {
 +                sprintf(tmp,"%s LA %s 0 %s\n",argv[3],hi->handle,devnull);
 +             } else if(getfakehost(argv[4])) {
 +                sprintf(tmp,"%s LA %s %s %s\n",argv[3],hi->handle,getfakehost(argv[4]),devnull);
 +             } else {
 +                extern const char *hidden_host_suffix;
 +                sprintf(tmp,"%s LA %s %s.%s %s\n",argv[3],hi->handle,hi->handle,hidden_host_suffix,devnull);
 +             }
 +             irc_relay(tmp);
 +            } else {
 +             //login rejected
 +             sprintf(tmp,"%s LR\n",argv[3]);
 +             irc_relay(tmp);
 +            }
 +        } else if(strcmp("UC", argv[2]) == 0) {
 +            char tmp[MAXLEN];
 +            sprintf(tmp,"%s UC %s %s",argv[3],argv[3],argv[4]);
 +            irc_relay(tmp);
 +        } else if(strcmp("JA", argv[2]) == 0) {
 +            struct userData *uData;
 +            struct chanNode *cn;
 +            struct userNode *user;
 +            char tmp[MAXLEN];
 +            cn = GetChannel(argv[4]);
 +            if (!cn) return 0;
 +            if (!(user = GetUserN(argv[3]))) return 0;
 +            if(!cn->channel_info) {
 +                //channel not registered
 +                sprintf(tmp,"%s JAA %s %s\n",argv[3],cn->name,argv[6]);
 +            } else if((uData = GetChannelUser(cn->channel_info, user->handle_info))) {
 +                if(uData->access >= atoi(argv[5])) {
 +                    //we can join
 +                    sprintf(tmp,"%s JAA %s %s\n",argv[3],cn->name,argv[6]);
 +                } else {
 +                    //access too low
 +                    sprintf(tmp,"%s JAR %s %i %i\n",argv[3],cn->name,uData->access,uData->access);
 +                }
 +            } else {
 +                //0 access
 +                sprintf(tmp,"%s JAR %s %s %s\n",argv[3],cn->name,"0","0");
 +            }
 +            irc_relay(tmp);
 +        }
 +    }
 +    return 1;
 +}
 +
+ static CMD_FUNC(cmd_xquery)
+ {
+     struct server *source;
+     if ((argc < 4)
+         || !(source = GetServerH(origin)))
+         return 0;
+     call_xquery_funcs(source, argv[2], argv[3]);
+     return 1;
+ }
  void
  free_user(struct userNode *user)
  {
@@@ -1949,8 -1815,6 +1971,8 @@@ init_parse(void
      dict_insert(irc_func_dict, TOK_STATS, cmd_stats);
      dict_insert(irc_func_dict, CMD_SVSNICK, cmd_svsnick);
      dict_insert(irc_func_dict, TOK_SVSNICK, cmd_svsnick);
 +    dict_insert(irc_func_dict, CMD_RELAY, cmd_relay);
 +    dict_insert(irc_func_dict, TOK_RELAY, cmd_relay);
      dict_insert(irc_func_dict, CMD_WHOIS, cmd_whois);
      dict_insert(irc_func_dict, TOK_WHOIS, cmd_whois);
      dict_insert(irc_func_dict, CMD_GLINE, cmd_gline);
      dict_insert(irc_func_dict, TOK_ADMIN, cmd_admin);
      dict_insert(irc_func_dict, CMD_TIME, cmd_time);
      dict_insert(irc_func_dict, TOK_TIME, cmd_time);
+     /* We don't handle XR or the (not really defined) XQUERY. */
+     dict_insert(irc_func_dict, TOK_XQUERY, cmd_xquery);
  
      /* In P10, DESTRUCT doesn't do anything except be broadcast to servers.
       * Apparently to obliterate channels from any servers that think they
@@@ -2456,10 -2322,8 +2480,10 @@@ void mod_usermode(struct userNode *user
          case 'o':
              do_user_mode(FLAGS_OPER);
              if (!add) {
 +                operdel(user);
                  userList_remove(&curr_opers, user);
              } else if (!userList_contains(&curr_opers, user)) {
 +                operadd(user);
                  userList_append(&curr_opers, user);
                  call_oper_funcs(user);
              }
          case 'g': do_user_mode(FLAGS_GLOBAL); break;
          case 'n': do_user_mode(FLAGS_NOCHAN); break;
          case 'I': do_user_mode(FLAGS_NOIDLE); break;
 +        case 'S': do_user_mode(FLAGS_NETSERV); break;
 +        case 'D': do_user_mode(FLAGS_SECURITYSERV); break;
 +        case 'X': do_user_mode(FLAGS_XTRAOP); break;
 +        case 's': do_user_mode(FLAGS_SERVERNOTICE); break;
 +        case 'H': do_user_mode(FLAGS_HIDDENOPER); break;
 +        case 't': do_user_mode(FLAGS_SEENOIDLE); break;
          case 'x': do_user_mode(FLAGS_HIDDEN_HOST); break;
          case 'r':
              if (*word) {
@@@ -2557,7 -2415,7 +2581,7 @@@ keyncpy(char output[], char input[], si
  }
  
  struct mod_chanmode *
 -mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
 +mod_chanmode_parse(struct chanNode *channel, struct userNode *user, char **modes, unsigned int argc, unsigned int flags, short base_oplevel)
  {
      struct mod_chanmode *change;
      unsigned int ii, in_arg, ch_arg, add;
          case 'C': do_chan_mode(MODE_NOCTCPS); break;
          case 'D': do_chan_mode(MODE_DELAYJOINS); break;
          case 'c': do_chan_mode(MODE_NOCOLORS); break;
 +        case 'M': do_chan_mode(MODE_NOAMSGS); break;
 +        case 'N': do_chan_mode(MODE_NONOTICES); break;
 +        case 'u': do_chan_mode(MODE_AUDITORIUM); break;
 +        case 'S': do_chan_mode(MODE_SSLCHAN); break;
          case 'i': do_chan_mode(MODE_INVITEONLY); break;
          case 'm': do_chan_mode(MODE_MODERATED); break;
          case 'n': do_chan_mode(MODE_NOPRIVMSGS); break;
          case 's': do_chan_mode(MODE_SECRET); break;
          case 't': do_chan_mode(MODE_TOPICLIMIT); break;
          case 'z':
 -          if (!(flags & MCP_REGISTERED))
 -              do_chan_mode(MODE_REGISTERED);
 +          if (!(flags & MCP_REGISTERED) && (!(flags & MCP_IGN_REGISTERED) || add)) {
 +            do_chan_mode(MODE_REGISTERED);
 +          } else if (flags & MCP_IGN_REGISTERED) {
 +            /* ignore the modechange but continue parsing */
 +          } else {
 +            mod_chanmode_free(change);
 +            return NULL;
 +          }
            break;
  #undef do_chan_mode
          case 'l':
                  change->modes_clear |= MODE_LIMIT;
              }
              break;
 +        case 'a':
 +            if (add) {
 +                if (in_arg >= argc)
 +                    goto error;
 +                change->modes_set |= MODE_ACCESS;
 +                change->new_access = atoi(modes[in_arg++]);
 +            } else {
 +                change->modes_set &= ~MODE_ACCESS;
 +                change->modes_clear |= MODE_ACCESS;
 +            }
 +            break;
          case 'k':
              if (add) {
                  if ((in_arg >= argc)
                  }
              }
              break;
 +        case 'f':
 +                  if (add) {
 +                          if (in_arg >= argc)
 +                    goto error;
 +                char *mode = modes[in_arg++];
 +                if(mode[0] == '!' && !(flags & MCP_OPERMODE)) //noflood flag also for overriders
 +                   goto error;//only allow opers
 +                else if(mode[0] == '!')
 +                   mode++;
 +                
 +                if(mode[0] == '+' || mode[0] == '@') {
 +                    mode++;
 +                }
 +                char *p;
 +                int count = 0, time = 0;
 +                for(p = mode; p[0]; p++) {
 +                    if(p[0] == ':') {
 +                        char tmpchar = p[0];
 +                        p[0] = '\0';
 +                        count = strtoul(mode,0,10);
 +                        p[0] = tmpchar;
 +                                              p++;
 +                                              time = strtoul(p,0,10);
 +                                              break;
 +                              }
 +                }
 +                if(count <= 0 || time <= 0 || count > 100 || time > 600)
 +                                  goto error;
 +                              change->modes_set |= MODE_NOFLOOD;
 +                              safestrncpy(change->new_noflood, modes[in_arg - 1], sizeof(change->new_noflood));
 +                      } else {
 +                          change->modes_clear |= MODE_NOFLOOD;
 +                      }
 +            break;
 +        case 'F':
 +            if (add) {
 +                if (in_arg >= argc)
 +                    goto error;
 +                char *altchan = modes[in_arg++];
 +                struct chanNode *target;
 +                if(!IsChannelName(altchan) || !(target = GetChannel(altchan)))
 +                    goto error;
 +                if(!(flags & MCP_OPERMODE)) {
 +                    //check if the user has the permissions to use this channel as target
 +                    struct modeNode *mn;
 +                    struct userData *uData;
 +                    struct chanData *cData;
 +                    if(user && (mn = GetUserMode(target, user)) && (mn->modes & MODE_CHANOP)) {
 +                        //allow - user is opped on target channel
 +                    } else if(user && user->handle_info && 
 +                              (uData = GetChannelUser(channel->channel_info, user->handle_info)) && 
 +                              (cData = uData->channel) && 
 +                              uData->access >= cData->lvlOpts[lvlGiveOps]
 +                             ) {
 +                        //allow - user has access to get op on the channel
 +                    } else 
 +                        goto error;
 +                }
 +                change->modes_set |= MODE_ALTCHAN;
 +                safestrncpy(change->new_altchan, altchan, sizeof(change->new_altchan));
 +            } else {
 +                change->modes_clear |= MODE_ALTCHAN;
 +            }
 +            break;
          case 'U':
              if (flags & MCP_NO_APASS)
                  goto error;
@@@ -2884,17 -2657,10 +2908,17 @@@ mod_chanmode_announce(struct userNode *
          DO_MODE_CHAR(INVITEONLY, 'i');
          DO_MODE_CHAR(NOPRIVMSGS, 'n');
          DO_MODE_CHAR(LIMIT, 'l');
 +        DO_MODE_CHAR(ACCESS, 'a');
 +        DO_MODE_CHAR(ALTCHAN, 'F');
 +        DO_MODE_CHAR(NOFLOOD, 'f');
          DO_MODE_CHAR(DELAYJOINS, 'D');
          DO_MODE_CHAR(REGONLY, 'r');
          DO_MODE_CHAR(NOCOLORS, 'c');
          DO_MODE_CHAR(NOCTCPS, 'C');
 +        DO_MODE_CHAR(NONOTICES, 'N');
 +        DO_MODE_CHAR(NOAMSGS, 'M');
 +        DO_MODE_CHAR(AUDITORIUM, 'u');
 +        DO_MODE_CHAR(SSLCHAN, 'S');
          DO_MODE_CHAR(REGISTERED, 'z');
  #undef DO_MODE_CHAR
          if (change->modes_clear & channel->modes & MODE_KEY)
          DO_MODE_CHAR(REGONLY, 'r');
          DO_MODE_CHAR(NOCOLORS, 'c');
          DO_MODE_CHAR(NOCTCPS, 'C');
 +        DO_MODE_CHAR(NONOTICES, 'N');
 +        DO_MODE_CHAR(NOAMSGS, 'M');
 +        DO_MODE_CHAR(AUDITORIUM, 'u');
 +        DO_MODE_CHAR(SSLCHAN, 'S');
          DO_MODE_CHAR(REGISTERED, 'z');
  #undef DO_MODE_CHAR
          if(change->modes_set & MODE_KEY)
              sprintf(int_buff, "%d", change->new_limit);
              mod_chanmode_append(&chbuf, 'l', int_buff);
          }
 +        if(change->modes_set & MODE_ACCESS) {
 +            sprintf(int_buff, "%d", change->new_access);
 +            mod_chanmode_append(&chbuf, 'a', int_buff);
 +        }
 +        if (change->modes_set & MODE_ALTCHAN)
 +            mod_chanmode_append(&chbuf, 'F', change->new_altchan);
 +        if (change->modes_set & MODE_NOFLOOD)
 +            mod_chanmode_append(&chbuf, 'f', change->new_noflood);
      }
      for (arg = 0; arg < change->argc; ++arg) {
          if (change->args[arg].mode & MODE_REMOVE)
@@@ -3008,9 -2762,6 +3032,9 @@@ mod_chanmode_format(struct mod_chanmod
          DO_MODE_CHAR(INVITEONLY, 'i');
          DO_MODE_CHAR(NOPRIVMSGS, 'n');
          DO_MODE_CHAR(LIMIT, 'l');
 +        DO_MODE_CHAR(ACCESS, 'a');
 +        DO_MODE_CHAR(ALTCHAN, 'F');
 +        DO_MODE_CHAR(NOFLOOD, 'f');
          DO_MODE_CHAR(KEY, 'k');
          DO_MODE_CHAR(UPASS, 'U');
          DO_MODE_CHAR(APASS, 'A');
          DO_MODE_CHAR(REGONLY, 'r');
          DO_MODE_CHAR(NOCOLORS, 'c');
          DO_MODE_CHAR(NOCTCPS, 'C');
 +        DO_MODE_CHAR(NONOTICES, 'N');
 +        DO_MODE_CHAR(NOAMSGS, 'M');
 +        DO_MODE_CHAR(AUDITORIUM, 'u');
 +        DO_MODE_CHAR(SSLCHAN, 'S');
          DO_MODE_CHAR(REGISTERED, 'z');
  #undef DO_MODE_CHAR
      }
          DO_MODE_CHAR(REGONLY, 'r');
          DO_MODE_CHAR(NOCOLORS, 'c');
          DO_MODE_CHAR(NOCTCPS, 'C');
 +        DO_MODE_CHAR(NONOTICES, 'N');
 +              DO_MODE_CHAR(NOAMSGS, 'M');
 +        DO_MODE_CHAR(AUDITORIUM, 'u');
 +        DO_MODE_CHAR(SSLCHAN, 'S');
          DO_MODE_CHAR(REGISTERED, 'z');
          DO_MODE_CHAR(LIMIT, 'l'), args_used += sprintf(args + args_used, " %d", change->new_limit);
          DO_MODE_CHAR(KEY, 'k'), args_used += sprintf(args + args_used, " %s", change->new_key);
 +        DO_MODE_CHAR(ACCESS, 'a'), args_used += sprintf(args + args_used, " %d", change->new_access);
 +        DO_MODE_CHAR(ALTCHAN, 'F'), args_used += sprintf(args + args_used, " %s", change->new_altchan);
 +        DO_MODE_CHAR(NOFLOOD, 'f'), args_used += sprintf(args + args_used, " %s", change->new_noflood);
          DO_MODE_CHAR(UPASS, 'U'), args_used += sprintf(args + args_used, " %s", change->new_upass);
          DO_MODE_CHAR(APASS, 'A'), args_used += sprintf(args + args_used, " %s", change->new_apass);
  #undef DO_MODE_CHAR
@@@ -3072,14 -2812,6 +3096,14 @@@ clear_chanmode(struct chanNode *channel
          case 't': cleared |= MODE_TOPICLIMIT; break;
          case 'i': cleared |= MODE_INVITEONLY; break;
          case 'n': cleared |= MODE_NOPRIVMSGS; break;
 +        case 'F':
 +            cleared |= MODE_ALTCHAN;
 +            channel->altchan[0] = '\0';
 +            break;
 +        case 'f':
 +            cleared |= MODE_NOFLOOD;
 +            channel->noflood[0] = '\0';
 +            break;
          case 'k':
              cleared |= MODE_KEY;
              channel->key[0] = '\0';
              cleared |= MODE_LIMIT;
              channel->limit = 0;
              break;
 +        case 'a':
 +            cleared |= MODE_ACCESS;
 +            channel->access = 0;
 +            break;
          case 'b': cleared |= MODE_BAN; break;
          case 'D': cleared |= MODE_DELAYJOINS; break;
          case 'r': cleared |= MODE_REGONLY; break;
          case 'c': cleared |= MODE_NOCOLORS; break;
          case 'C': cleared |= MODE_NOCTCPS; break;
 +        case 'M': cleared |= MODE_NOAMSGS; break;
 +        case 'u': cleared |= MODE_AUDITORIUM; break;
 +        case 'S': cleared |= MODE_SSLCHAN; break;
 +        case 'N': cleared |= MODE_NONOTICES; break;
          case 'z': cleared |= MODE_REGISTERED; break;
          }
      }
diff --combined src/proto.h
index 54c5e23a420a924f632bf61208e55fb46f422d89,6ded4dfb49463e798ea758455327894312a4c209..2bd70b496e4a41bb42fd86ccae92a3e2c3d8bcfa
@@@ -104,6 -104,9 +104,9 @@@ void unreg_notice_func(struct userNode 
  typedef void (*oper_func_t) (struct userNode *user);
  void reg_oper_func(oper_func_t handler);
  
+ typedef void (*xquery_func_t) (struct server *source, const char routing[], const char query[]);
+ void reg_xquery_func(xquery_func_t handler);
  /* replay silliness */
  void replay_read_line(void);
  void replay_event_loop(void);
@@@ -142,19 -145,13 +145,20 @@@ void irc_kill(struct userNode *from, st
  void irc_raw(const char *what);
  void irc_stats(struct userNode *from, struct server *target, char type);
  void irc_svsnick(struct userNode *from, struct userNode *target, const char *newnick);
+ void irc_xresponse(struct server *target, const char *routing, const char *response);
  
  /* account maintenance */
  void irc_account(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial);
  void irc_regnick(struct userNode *user);
  void irc_fakehost(struct userNode *user, const char *host, const char *ident, int force);
  
 +/* svs maintenance */
 +void irc_svsmode(struct userNode *from, struct userNode *user, const char *modes);
 +void irc_svsjoin(struct userNode *from, struct userNode *user, struct chanNode *chan);
 +void irc_svsjoinchan(struct userNode *from, struct userNode *user, const char *chan);
 +void irc_relay(char *message);
 +void irc_simul(struct userNode *target, char *command);
 +
  /* numeric messages */
  void irc_numeric(struct userNode *user, unsigned int num, const char *format, ...);
  /* RFC1459-compliant numeric responses */
@@@ -188,8 -185,6 +192,8 @@@ unsigned int irc_user_modes(const struc
  
  /* Channel mode manipulation */
  #define KEYLEN          23
 +#define NOFLOODLEN      15
 +#define CHANNELLEN      200
  typedef unsigned long chan_mode_t;
  /* Rules for struct mod_chanmode:
   * For a membership mode change, args[n].mode can contain more than
   */
  struct mod_chanmode {
      chan_mode_t modes_set, modes_clear;
 -    unsigned int new_limit, argc;
 +    unsigned int new_limit, new_access, argc;
  #ifndef NDEBUG
      unsigned int alloc_argc;
  #endif
      char new_key[KEYLEN + 1];
 +    char new_altchan[CHANNELLEN + 1];
 +    char new_noflood[NOFLOODLEN + 1];
      char new_upass[KEYLEN + 1];
      char new_apass[KEYLEN + 1];
      struct {
          } u;
      } args[1];
  };
 -#define MCP_ALLOW_OVB     0x0001 /* allow op, voice, ban manipulation */
 -#define MCP_FROM_SERVER   0x0002 /* parse as from a server */
 -#define MCP_KEY_FREE      0x0004 /* -k without a key argument */
 -#define MCP_REGISTERED    0x0008 /* chan is already registered; do not allow changes to MODE_REGISTERED */
 -#define MCP_UPASS_FREE    0x0010 /* -U without a key argument */
 -#define MCP_APASS_FREE    0x0020 /* -A without a key argument */
 -#define MCP_NO_APASS      0x0040 /* Do not allow +/-A or +/-U */
 -#define MC_ANNOUNCE       0x0100 /* send a mod_chanmode() change out */
 -#define MC_NOTIFY         0x0200 /* make local callbacks to announce */
 +#define MCP_ALLOW_OVB      0x0001 /* allow op, voice, ban manipulation */
 +#define MCP_FROM_SERVER    0x0002 /* parse as from a server */
 +#define MCP_KEY_FREE       0x0004 /* -k without a key argument */
 +#define MCP_REGISTERED     0x0008 /* chan is already registered; do not allow changes to MODE_REGISTERED */
 +#define MCP_UPASS_FREE     0x0010 /* -U without a key argument */
 +#define MCP_APASS_FREE     0x0020 /* -A without a key argument */
 +#define MCP_NO_APASS       0x0040 /* Do not allow +/-A or +/-U */
 +#define MCP_IGN_REGISTERED 0x0080 /* chan is already registered; ignore changes to MODE_REGISTERED */
 +#define MC_ANNOUNCE        0x0100 /* send a mod_chanmode() change out */
 +#define MC_NOTIFY          0x0200 /* make local callbacks to announce */
 +#define MCP_OPERMODE       0x0400
  #ifdef NDEBUG
  #define mod_chanmode_init(CHANMODE) do { memset((CHANMODE), 0, sizeof(*CHANMODE)); } while (0)
  #else
  
  struct mod_chanmode *mod_chanmode_alloc(unsigned int argc);
  struct mod_chanmode *mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra);
 -struct mod_chanmode *mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags, short base_oplevel);
 +struct mod_chanmode *mod_chanmode_parse(struct chanNode *channel, struct userNode *user, char **modes, unsigned int argc, unsigned int flags, short base_oplevel);
  void mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change);
  void mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change);
  char *mod_chanmode_format(struct mod_chanmode *desc, char *buffer);