Merge branch 'master' into KeepConn
authorpk910 <philipp@zoelle1.de>
Fri, 11 May 2012 03:36:12 +0000 (05:36 +0200)
committerpk910 <philipp@zoelle1.de>
Fri, 11 May 2012 03:36:12 +0000 (05:36 +0200)
18 files changed:
include/client.h
include/handlers.h
include/msg.h
include/numeric.h
include/s_bsd.h
include/s_misc.h
ircd/Makefile.in
ircd/ircd.c
ircd/m_quit.c
ircd/m_recover.c [new file with mode: 0644]
ircd/m_unzombie.c [new file with mode: 0644]
ircd/m_zombie.c [new file with mode: 0644]
ircd/parse.c
ircd/s_bsd.c
ircd/s_err.c
ircd/s_misc.c
ircd/s_user.c
ircd/send.c

index 5d754bf1cfff2a706660a6b5036128f970aa9dcb..a86633d2652d3c0aece42f9a0dfb0dff575747f1 100644 (file)
@@ -179,6 +179,7 @@ enum Flag
     FLAG_DEBUG,                     /**< send global debug/anti-hack info */
     FLAG_ACCOUNT,                   /**< account name has been set */
     FLAG_HIDDENHOST,                /**< user's host is hidden */
+    FLAG_NOTCONN,                   /**< nobody is connected to this client */
     FLAG_FAKEHOST,                  /**< user has a fakehost */
     FLAG_FAKEIDENT,                  /**< user has a fakeident */
     FLAG_NOCHAN,                    /**< hide user's channels for non-opers */
@@ -626,6 +627,8 @@ struct Client {
 #define IsAccount(x)            HasFlag(x, FLAG_ACCOUNT)
 /** Return non-zero if the client has set mode +x (hidden host). */
 #define IsHiddenHost(x)         HasFlag(x, FLAG_HIDDENHOST)
+/** Return non-zero if nobody is connected to this client structure */
+#define IsNotConn(x)            HasFlag(x, FLAG_NOTCONN)
 /** Return non-zero if the client has an active PING request. */
 #define IsPingSent(x)           HasFlag(x, FLAG_PINGSENT)
 /** Return non-zero if the client's channels are hidden. */
@@ -696,6 +699,8 @@ struct Client {
 #define SetAccount(x)           SetFlag(x, FLAG_ACCOUNT)
 /** Mark a client as having mode +x (hidden host). */
 #define SetHiddenHost(x)        SetFlag(x, FLAG_HIDDENHOST)
+/** Mark a client as not having anyone connected to it */
+#define SetNotConn(x)           SetFlag(x, FLAG_NOTCONN)
 /** Mark a client as having a fakehost. */
 #define SetFakeHost(x)          SetFlag(x, FLAG_FAKEHOST)
 #define SetFakeIdent(x)          SetFlag(x, FLAG_FAKEIDENT)
@@ -752,6 +757,8 @@ struct Client {
 #define ClearServNotice(x)      ClrFlag(x, FLAG_SERVNOTICE)
 /** Remove mode +x (hidden host) from the client. */
 #define ClearHiddenHost(x)      ClrFlag(x, FLAG_HIDDENHOST)
+/** Mark client as having someone connected to it */
+#define ClearNotConn(x)         ClrFlag(x, FLAG_NOTCONN)
 /** Remove fakehost flag from the flient. */
 #define ClearFakeHost(x)        ClrFlag(x, FLAG_FAKEHOST)
 #define ClearFakeIdent(x)        ClrFlag(x, FLAG_FAKEIDENT)
index 34d9be02d8bf6f64a65b70eb2e71be4ec4f5d393..19b64ceb1d8adab8b81701c8d3b5ef471279cdd3 100644 (file)
@@ -125,6 +125,7 @@ extern int m_privs(struct Client*, struct Client*, int, char*[]);
 extern int m_proto(struct Client*, struct Client*, int, char*[]);
 extern int m_pseudo(struct Client*, struct Client*, int, char*[]);
 extern int m_quit(struct Client*, struct Client*, int, char*[]);
+extern int m_recover(struct Client*, struct Client*, int, char*[]);
 extern int m_registered(struct Client*, struct Client*, int, char*[]);
 extern int m_silence(struct Client*, struct Client*, int, char*[]);
 extern int m_stats(struct Client*, struct Client*, int, char*[]);
@@ -235,6 +236,7 @@ extern int ms_svsjoin(struct Client*, struct Client*, int, char*[]);
 extern int ms_svspart(struct Client*, struct Client*, int, char*[]);
 extern int ms_topic(struct Client*, struct Client*, int, char*[]);
 extern int ms_trace(struct Client*, struct Client*, int, char*[]);
+extern int ms_unzombie(struct Client*, struct Client*, int, char*[]);
 extern int ms_uping(struct Client*, struct Client*, int, char*[]);
 extern int ms_version(struct Client*, struct Client*, int, char*[]);
 extern int ms_wallchops(struct Client*, struct Client*, int, char*[]);
@@ -242,6 +244,7 @@ extern int ms_wallops(struct Client*, struct Client*, int, char*[]);
 extern int ms_wallusers(struct Client*, struct Client*, int, char*[]);
 extern int ms_wallvoices(struct Client*, struct Client*, int, char*[]);
 extern int ms_whois(struct Client*, struct Client*, int, char*[]);
+extern int ms_zombie(struct Client*, struct Client*, int, char*[]);
 
 #endif /* INCLUDED_handlers_h */
 
index 59f921cf769dbbac4fca0818000c87eecaa3a141..4afde55c7ee02a2babee5679359acc57b447f622 100644 (file)
@@ -336,6 +336,18 @@ struct Client;
 #define TOK_ACCOUNT            "AC"
 #define CMD_ACCOUNT            MSG_ACCOUNT, TOK_ACCOUNT
 
+#define MSG_RECOVER            "RECOVER"       /* RECOVER */
+#define TOK_RECOVER            "RC"
+#define CMD_RECOVER            MSG_RECOVER, TOK_RECOVER
+
+#define MSG_ZOMBIE             "ZOMBIE"        /* ZOMB */
+#define TOK_ZOMBIE             "ZO"
+#define CMD_ZOMBIE             MSG_ZOMBIE, TOK_ZOMBIE
+
+#define MSG_UNZOMBIE           "UNZOMBIE"      /* UZMB */
+#define TOK_UNZOMBIE           "ZU"
+#define CMD_UNZOMBIE           MSG_UNZOMBIE, TOK_UNZOMBIE
+
 #define MSG_ASLL               "ASLL"          /* ASLL */
 #define TOK_ASLL               "LL"
 #define CMD_ASLL               MSG_ASLL, TOK_ASLL
index 30e54d936cdbad663bac4d1887e561bb273cf9cb..6de836346df470b0c6f6c2a3a1dd463ea5886f6d 100644 (file)
@@ -357,6 +357,7 @@ extern const struct Numeric* get_error_numeric(int err);
 #define ERR_NONICKNAMEGIVEN  431
 #define ERR_ERRONEUSNICKNAME 432
 #define ERR_NICKNAMEINUSE    433
+#define ERR_RECOVERDENIED    434
 /*      ERR_SERVICENAMEINUSE 434 ? */
 /*     ERR_NORULES          434   unreal */
 /*      ERR_SERVICECONFUSED  435 ? */
index 5bf4e675be9c32d9217a45f6d6d1c741d329ca4d..d842a4420a0bce551aa6cfa5b6ada398f357de8c 100644 (file)
@@ -66,6 +66,7 @@ extern unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf);
 extern int connect_server(struct ConfItem* aconf, struct Client* by);
 extern int  net_close_unregistered_connections(struct Client* source);
 extern void close_connection(struct Client *cptr);
+extern void connection_switch_to_client(struct Client *source, struct Client *target);
 extern void add_connection(struct Listener* listener, int fd, ssl_session_t *ssl);
 extern int  read_message(time_t delay);
 extern void init_server_identity(void);
index 13d7e2d982708e43c8bf409f1f94ebc2c1da3f1c..59dc5e45d1b730c3c1c1ed315e815b771d3c7010 100644 (file)
@@ -76,6 +76,10 @@ struct ServerStatistics {
 
 extern int check_registered(struct Client *sptr);
 extern int check_registered_user(struct Client *sptr);
+extern void zombie_client(struct Client *cptr, struct Client *killer,
+                          struct Client *victim);
+extern void unzombie_client(struct Client *cptr, struct Client *sptr,
+                            struct Client *acptr, struct Client *victim);
 extern int exit_client(struct Client *cptr, struct Client *bcptr,
     struct Client *sptr, const char *comment);
 extern char *myctime(time_t value);
index 2e3cf13002608e9efb866ecbc1898d776e4277ce..c3361889b420c1336be6d78935c9f45baba34910 100644 (file)
@@ -160,7 +160,8 @@ IRCD_SRC = \
        m_proto.c \
        m_pseudo.c \
        m_quit.c \
-       m_rehash.c \
+       m_recover.c \
+    m_rehash.c \
        m_relay.c \
        m_reset.c \
        m_restart.c \
@@ -179,6 +180,7 @@ IRCD_SRC = \
        m_time.c \
        m_topic.c \
        m_trace.c \
+       m_unzombie.c \
        m_uping.c \
        m_user.c \
        m_userhost.c \
@@ -192,6 +194,7 @@ IRCD_SRC = \
        m_who.c \
        m_whois.c \
        m_whowas.c \
+       m_zombie.c \
        match.c \
        memdebug.c \
        motd.c \
index f5f0eeb54d513403c0885f549129d52cb851895c..ad6ae5fb5f9699e4f7e521575496efa005eb9e8a 100644 (file)
@@ -339,7 +339,19 @@ static void check_pings(struct Event* ev) {
    
     if (!cptr)
       continue;
-     
+    
+    /* We don't need to check zombies here */
+    if (IsNotConn(cptr)) {
+      assert(IsUser(cptr));
+      /* for now: reap after fixed time (5 minutes) */
+      if ((CurrentTime - cli_user(cptr)->last) >= 300) {
+        SetFlag(cptr, FLAG_DEADSOCKET);
+        /* this will be used as exit message */
+        ircd_strncpy(cli_info(cptr), "Ping timeout", REALLEN);
+      } else
+        continue;
+    }
+    
     assert(&me != cptr);  /* I should never be in the local client array! */
    
 
@@ -420,6 +432,14 @@ static void check_pings(struct Event* ev) {
         sendto_opmask_butone(0, SNO_OLDSNO,
                              "No response from %s, closing link",
                              cli_name(cptr));
+      /*
+       * Keep client structure around when a user pings out, so that they can
+       * reconnect to it later
+       */
+      if (IsUser(cptr) && IsAccount(cptr)) {
+        zombie_client(&me, &me, cptr);
+        continue;
+      }
       exit_client_msg(cptr, cptr, &me, "Ping timeout");
       continue;
     }
index 16376e42a4406b33988dc96d649ae2577bf54a72..9c06b31ccc78d29684a9622f9c2f20ebbff76eba 100644 (file)
@@ -105,6 +105,11 @@ int m_quit(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   assert(0 != sptr);
   assert(cptr == sptr);
 
+  if (!ircd_strcmp(parv[parc - 1], "ZOMBIE")) {
+    zombie_client(&me, &me, sptr);
+    return 0;
+  }
+
   if (cli_user(sptr)) {
     struct Membership* chan;
     for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) {
diff --git a/ircd/m_recover.c b/ircd/m_recover.c
new file mode 100644 (file)
index 0000000..51c9fae
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * IRC - Internet Relay Chat, ircd/m_recover.c
+ * Copyright (C) 2012 Philipp Kreil <srvx@pk910.de>
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program 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 1, or (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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+/*
+ * m_functions execute protocol messages on this server:
+ *
+ *    cptr    is always NON-NULL, pointing to a *LOCAL* client
+ *            structure (with an open socket connected!). This
+ *            identifies the physical socket where the message
+ *            originated (or which caused the m_function to be
+ *            executed--some m_functions may call others...).
+ *
+ *    sptr    is the source of the message, defined by the
+ *            prefix part of the message if present. If not
+ *            or prefix not found, then sptr==cptr.
+ *
+ *            (!IsServer(cptr)) => (cptr == sptr), because
+ *            prefixes are taken *only* from servers...
+ *
+ *            (IsServer(cptr))
+ *                    (sptr == cptr) => the message didn't
+ *                    have the prefix.
+ *
+ *                    (sptr != cptr && IsServer(sptr) means
+ *                    the prefix specified servername. (?)
+ *
+ *                    (sptr != cptr && !IsServer(sptr) means
+ *                    that message originated from a remote
+ *                    user (not local).
+ *
+ *            combining
+ *
+ *            (!IsServer(sptr)) means that, sptr can safely
+ *            taken as defining the target structure of the
+ *            message in this server.
+ *
+ *    *Always* true (if 'parse' and others are working correct):
+ *
+ *    1)      sptr->from == cptr  (note: cptr->from == cptr)
+ *
+ *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+ *            *cannot* be a local connection, unless it's
+ *            actually cptr!). [MyConnect(x) should probably
+ *            be defined as (x == x->from) --msa ]
+ *
+ *    parc    number of variable parameter strings (if zero,
+ *            parv is allowed to be NULL)
+ *
+ *    parv    a NULL terminated list of parameter pointers,
+ *
+ *                    parv[0], sender (prefix string), if not present
+ *                            this points to an empty string.
+ *                    parv[1]...parv[parc-1]
+ *                            pointers to additional parameters
+ *                    parv[parc] == NULL, *always*
+ *
+ *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
+ *                    non-NULL pointers.
+ */
+#include "config.h"
+
+#include "client.h"
+#include "hash.h"
+#include "ircd.h"
+#include "ircd_log.h"
+#include "ircd_reply.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numeric.h"
+#include "numnicks.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "send.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/** m_recover - Try to recover a disconnected Client (Zombie)
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nick of the zombie to be recovered
+ */
+int m_recover(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
+{
+  struct Client *victim;
+
+  if (parc < 2)
+    return need_more_params(sptr, "RECOVER");
+
+  //check Auth
+  if(!IsAccount(sptr)) {
+    send_reply(sptr, ERR_NOTREGISTERED);
+    return 0;
+  }
+
+  if (!(victim = FindUser(parv[1]))) {
+    send_reply(sptr, ERR_NOSUCHNICK, parv[1]);
+    return 0;
+  }
+
+  if (!IsNotConn(victim)) {
+    send_reply(sptr, ERR_RECOVERDENIED, cli_name(victim), "not zombie");
+    return 0;
+  }
+  assert(IsAccount(victim));
+
+  const struct User* sptr_user = cli_user(sptr);
+  const struct User* victim_user = cli_user(victim);
+  if(ircd_strcmp(sptr_user->account, victim_user->account)) {
+    send_reply(sptr, ERR_RECOVERDENIED, cli_name(victim), "auth missmatch");
+    return 0;
+  }
+
+  unzombie_client(&me, &me, sptr, victim);
+  return 0;
+}
diff --git a/ircd/m_unzombie.c b/ircd/m_unzombie.c
new file mode 100644 (file)
index 0000000..32adefa
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * IRC - Internet Relay Chat, ircd/m_unzombie.c
+ * Copyright (C) 2011 Jan Krueger <jk@jk.gs>
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program 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 1, or (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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "ircd.h"
+#include "ircd_log.h"
+#include "ircd_reply.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numnicks.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "send.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/** Handle an UNZOMBIE message from a server connection.
+ *
+ * \a parv has the following elements:
+ * \li \a parv[1] is the numnick of the client attaching to the zombie
+ * \li \a parv[2] is the numnick of the zombie
+ *
+ * See @ref m_functions for discussion of the arguments.
+ * @param[in] cptr Client that sent us the message.
+ * @param[in] sptr Original source of message.
+ * @param[in] parc Number of arguments.
+ * @param[in] parv Argument vector.
+ */
+int ms_unzombie(struct Client* cptr, struct Client* sptr, int parc,
+              char* parv[])
+{
+  struct Client *acptr;
+  struct Client *victim;
+
+  if (parc < 3)
+    return need_more_params(sptr, "UNZOMBIE");
+
+  if (!IsServer(sptr))
+    return protocol_violation(cptr, "UNZOMBIE from non-server %s",
+                             cli_name(sptr));
+
+  if (!(acptr = findNUser(parv[1])))
+    return 0; /* If this is colliding with a QUIT, let the QUIT win */
+
+  if (!(victim = findNUser(parv[2])))
+    /* TODO send error */
+    ;
+
+  if (!IsNotConn(victim))
+    return protocol_violation(cptr, "UNZOMBIE trying to attach to non-zombie %s",
+                             cli_name(victim));
+  assert(IsAccount(victim));
+
+  unzombie_client(cptr, sptr, acptr, victim);
+  return 0;
+}
diff --git a/ircd/m_zombie.c b/ircd/m_zombie.c
new file mode 100644 (file)
index 0000000..cd4b26d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * IRC - Internet Relay Chat, ircd/m_zombie.c
+ * Copyright (C) 2011 Jan Krueger <jk@jk.gs>
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * This program 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 1, or (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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "ircd.h"
+#include "ircd_log.h"
+#include "ircd_reply.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numnicks.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "send.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/** Handle a ZOMBIE message from a server connection.
+ *
+ * \a parv has the following elements:
+ * \li \a parv[1] is the numnick of the client to act on
+ *
+ * See @ref m_functions for discussion of the arguments.
+ * @param[in] cptr Client that sent us the message.
+ * @param[in] sptr Original source of message.
+ * @param[in] parc Number of arguments.
+ * @param[in] parv Argument vector.
+ */
+int ms_zombie(struct Client* cptr, struct Client* sptr, int parc,
+              char* parv[])
+{
+  struct Client *acptr;
+
+  if (parc < 2)
+    return need_more_params(sptr, "ZOMBIE");
+
+  if (!IsServer(sptr))
+    return protocol_violation(cptr, "ZOMBIE from non-server %s",
+                             cli_name(sptr));
+
+  if (!(acptr = findNUser(parv[1])))
+    return 0; /* Ignore for a user that QUIT; probably crossed (however unlikely) */
+
+  if (!IsAccount(acptr))
+    return protocol_violation(cptr, "ZOMBIE for user without account (%s)",
+                             cli_name(acptr));
+
+  zombie_client(cptr, sptr, acptr);
+  return 0;
+}
index 0a0d88da293f0c1a23c13868aba7697f47baaaed..1e1ebdd1536d02f4015bac395341b44e0001d1c8 100644 (file)
@@ -610,6 +610,27 @@ struct Message msgtab[] = {
     /* UNREG, CLIENT, SERVER, OPER, SERVICE */
     { m_ignore, m_ignore, ms_account, m_ignore, m_ignore }
   },
+  {
+    MSG_RECOVER,
+    TOK_RECOVER,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
+    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+    { m_unregistered, m_recover, m_ignore, m_ignore, m_ignore }
+  },
+  {
+    MSG_ZOMBIE,
+    TOK_ZOMBIE,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
+    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+    { m_ignore, m_ignore, ms_zombie, m_ignore, m_ignore }
+  },
+  {
+    MSG_UNZOMBIE,
+    TOK_UNZOMBIE,
+    0, MAXPARA, MFLG_SLOW, 0, NULL,
+    /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+    { m_ignore, m_ignore, ms_unzombie, m_ignore, m_ignore }
+  },
   {
     MSG_ASLL,
     TOK_ASLL,
index 00f7d8edc42ddba7005a0037bb8def01a6e72bed..fb842764a4152c36de12749765db47110bcb908e 100644 (file)
@@ -459,6 +459,34 @@ void close_connection(struct Client *cptr)
   }
 }
 
+/**
+ * Switches a client's connection over to a different, zombied client.
+ *
+ * @param source client to attach to zombied client
+ * @param target zombied client
+ */
+void connection_switch_to_client(struct Client *source, struct Client *target)
+{
+  assert(IsNotConn(target));
+  assert(MyConnect(source));
+  assert(-1 < cli_fd(source));
+  cli_connect(target) = cli_connect(source);
+  /* conveniently, this makes it much easier to get rid of source later on */
+  cli_from(target) = target;
+  LocalClientArray[cli_fd(source)] = target;
+
+  /* make main loop remove source soonish */
+  SetFlag(source, FLAG_DEADSOCKET);
+
+  /* need to copy over data from old client */
+  cli_user(target)->server = cli_user(source)->server;
+  strcpy(cli_yxx(target), cli_yxx(source));
+  cli_hopcount(target) = cli_hopcount(source);
+  cli_ip(target) = cli_ip(source);
+  strcpy(cli_username(target), cli_username(source));
+  strcpy(cli_user(target)->realhost, cli_user(source)->realhost);
+}
+
 /** Close all unregistered connections.
  * @param source Oper who requested the close.
  * @return Number of closed connections.
index cc9ccf88f933c8970d6cd932819339d175532a11..bc7d0ffbd4347d677789b550fc1aa3ccc3d53537 100644 (file)
@@ -900,7 +900,7 @@ static Numeric replyTable[] = {
 /* 433 */
   { ERR_NICKNAMEINUSE, "%s :Nickname is already in use.", "433" },
 /* 434 */
-  { 0 },
+  { ERR_RECOVERDENIED, "%s :You may not recover this connection. (%s)", "434" },
 /* 435 */
   { 0 },
 /* 436 */
index 48b2dd94a93c92183b9cb16d182b48a7027d7bec..2dd7b8296860862f5298b333873f51327c73cdcc 100644 (file)
@@ -320,6 +320,95 @@ static void exit_downlinks(struct Client *cptr, struct Client *sptr, char *comme
   }
 }
 
+/**
+ * Marks a local client as disconnected, and close its link if it is a local client.
+ *
+ * @param cptr server that notified us
+ * @param killer origin of decision to zombie \a victim
+ * @param victim zombied client
+ */
+void zombie_client(struct Client *cptr, struct Client *killer, struct Client *victim)
+{
+  assert(IsServer(cptr) || IsMe(cptr));
+  assert(IsServer(killer) || IsMe(killer));
+  assert(IsUser(victim));
+
+  /*
+   * Stop a running /LIST clean
+   */
+  if (MyUser(victim) && cli_listing(victim)) {
+    MyFree(cli_listing(victim));
+    cli_listing(victim) = NULL;
+  }
+
+  if (MyConnect(victim))
+    close_connection(victim);
+  /* need this so that main loop doesn't exit the client */
+  ClrFlag(victim, FLAG_DEADSOCKET);
+
+  SetNotConn(victim);
+  sendcmdto_serv_butone(killer, CMD_ZOMBIE, cptr, "%C", victim);
+}
+
+/**
+ * Attaches a client to a zombied client, removing the superfluous client in the process.
+ *
+ * @param cptr server that notified us
+ * @param sptr origin server of unzombie operation
+ * @param acptr client that is attaching to \a victim
+ * @param victim zombied client that someone is attaching to
+ */
+void unzombie_client(struct Client *cptr, struct Client *sptr, struct Client *acptr, struct Client *victim)
+{
+  assert(IsServer(cptr) || IsMe(cptr));
+  assert(IsServer(sptr) || IsMe(sptr));
+  assert(IsUser(acptr));
+  assert(IsNotConn(victim));
+
+  if (MyConnect(acptr))
+    connection_switch_to_client(acptr, victim);
+
+  ClearOper(victim);
+  ClearNotConn(victim);
+
+  /* announce "disconnect" of the source client */
+  sendcmdto_common_channels_butone_audit(acptr, CMD_QUIT, NULL, ":Switched to %s", cli_name(victim));
+  remove_user_from_all_channels(acptr);
+
+  if (MyConnect(victim)) {
+    /* inform client about "new" modes */
+    struct Flags setflags = cli_flags(acptr);
+    struct Membership *chan;
+    sendcmdto_one(acptr, CMD_NICK, victim, "%C", victim);
+    send_umode(victim, victim, &setflags, ALL_UMODES, 0);
+
+    /*
+     * mark current client as zombie on all channels so that it does not show
+     * up in the memberships we'll resend below
+     */
+    for (chan = cli_user(acptr)->channel; chan; chan = chan->next_channel) {
+      SetZombie(chan);
+    }
+
+    /* resend channel memberships */
+    for (chan = cli_user(victim)->channel; chan; chan = chan->next_channel) {
+      struct Channel *chptr = chan->channel;
+      /* pretty unlikely to happen but let's handle this anyway */
+      if (IsZombie(chan))
+        continue;
+      sendcmdto_one(victim, CMD_JOIN, victim, ":%H", chptr);
+      if (chptr->topic[0]) {
+        send_reply(victim, RPL_TOPIC, chptr->chname, chptr->topic);
+        send_reply(victim, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick,
+                  chptr->topic_time);
+      }
+      do_names(victim, chptr, NAMES_ALL|NAMES_EON); /* send /names list */
+    }
+  }
+
+  sendcmdto_serv_butone(sptr, CMD_UNZOMBIE, cptr, "%C %C", acptr, victim);
+}
+
 /* exit_client, rewritten 25-9-94 by Run */
 /**
  * Exits a client of *any* type (user, server, etc)
index 53cebf2602df7e646dd136b4238b39635972a014..f42cf9a0130438611eb59540029820c7ee176904 100644 (file)
@@ -589,7 +589,8 @@ static const struct UserMode {
   { FLAG_WEBIRC,      'W' },
   { FLAG_SEE_IDLETIME,'t' },
   { FLAG_SECURITY_SERV,'D' },
-  { FLAG_HIDDENHOST,  'x' }
+  { FLAG_HIDDENHOST,  'x' },
+  { FLAG_NOTCONN,     'Z' }
 };
 
 /** Length of #userModeList. */
@@ -1299,6 +1300,12 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
       case 'z': /* Formerly SSL mode; we ignore it. */
         break;
 #endif
+      case 'Z':
+        if (what == MODE_ADD)
+          SetNotConn(sptr);
+        else
+          ClearNotConn(sptr);
+        break;
       default:
         send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m);
         break;
@@ -1321,6 +1328,8 @@ int set_user_mode(struct Client *cptr, struct Client *sptr, int parc,
       ClrFlag(sptr, FLAG_FAKEHOST);
     if (!FlagHas(&setflags, FLAG_SEE_IDLETIME) && IsSeeIdletime(sptr))
       ClrFlag(sptr, FLAG_SEE_IDLETIME);
+    if (!FlagHas(&setflags, FLAG_NOTCONN) && IsNotConn(sptr))
+      ClrFlag(sptr, FLAG_NOTCONN);
     /*
      * new umode; servers and privileged opers can set it, local users cannot;
      * prevents users from /kick'ing or /mode -o'ing
index ff8d1158b0f0b06e25fbdaae4c539c4b0d3c64a1..921b37db28559e11c4e8ccd33bb05e6618964286 100644 (file)
@@ -108,7 +108,7 @@ static void dead_link(struct Client *to, char *notice)
 static int can_send(struct Client* to)
 {
   assert(0 != to);
-  return (IsDead(to) || IsMe(to) || -1 == cli_fd(to)) ? 0 : 1;
+  return (IsDead(to) || IsMe(to) || IsNotConn(to) || -1 == cli_fd(to)) ? 0 : 1;
 }
 
 /** Close the connection with the highest sendq.