From: pk910 Date: Mon, 12 Nov 2012 00:20:49 +0000 (+0100) Subject: Merge branch 'master' into KeepConn X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=commitdiff_plain;h=fa4486fe3e166152a57a42320fd1f2faee37b0c3;hp=2fec862674d5b33145529ab0397640a2e4d516c7 Merge branch 'master' into KeepConn Conflicts: include/handlers.h ircd/Makefile.in --- diff --git a/include/client.h b/include/client.h index 5d754bf..a86633d 100644 --- a/include/client.h +++ b/include/client.h @@ -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) diff --git a/include/handlers.h b/include/handlers.h index 2cb2954..79a052d 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -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*[]); @@ -236,6 +237,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_uninvite(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*[]); @@ -244,6 +246,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 */ diff --git a/include/msg.h b/include/msg.h index e5ed340..f615110 100644 --- a/include/msg.h +++ b/include/msg.h @@ -340,6 +340,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 diff --git a/include/numeric.h b/include/numeric.h index 1d8e372..d979e5f 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -358,6 +358,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 ? */ diff --git a/include/s_bsd.h b/include/s_bsd.h index 5bf4e67..d842a44 100644 --- a/include/s_bsd.h +++ b/include/s_bsd.h @@ -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); diff --git a/include/s_misc.h b/include/s_misc.h index 13d7e2d..59dc5e4 100644 --- a/include/s_misc.h +++ b/include/s_misc.h @@ -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); diff --git a/ircd/Makefile.in b/ircd/Makefile.in index d007a8c..c9057a2 100644 --- a/ircd/Makefile.in +++ b/ircd/Makefile.in @@ -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_uninvite.c \ m_uping.c \ m_user.c \ @@ -193,6 +195,7 @@ IRCD_SRC = \ m_who.c \ m_whois.c \ m_whowas.c \ + m_zombie.c \ match.c \ memdebug.c \ motd.c \ diff --git a/ircd/ircd.c b/ircd/ircd.c index f5f0eeb..ad6ae5f 100644 --- a/ircd/ircd.c +++ b/ircd/ircd.c @@ -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; } diff --git a/ircd/m_quit.c b/ircd/m_quit.c index 16376e4..9c06b31 100644 --- a/ircd/m_quit.c +++ b/ircd/m_quit.c @@ -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 index 0000000..51c9fae --- /dev/null +++ b/ircd/m_recover.c @@ -0,0 +1,137 @@ +/* + * IRC - Internet Relay Chat, ircd/m_recover.c + * Copyright (C) 2012 Philipp Kreil + * + * 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 +#include + +/** 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 index 0000000..32adefa --- /dev/null +++ b/ircd/m_unzombie.c @@ -0,0 +1,81 @@ +/* + * IRC - Internet Relay Chat, ircd/m_unzombie.c + * Copyright (C) 2011 Jan Krueger + * + * 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 +#include + +/** 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 index 0000000..cd4b26d --- /dev/null +++ b/ircd/m_zombie.c @@ -0,0 +1,74 @@ +/* + * IRC - Internet Relay Chat, ircd/m_zombie.c + * Copyright (C) 2011 Jan Krueger + * + * 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 +#include + +/** 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; +} diff --git a/ircd/parse.c b/ircd/parse.c index fab2743..519049c 100644 --- a/ircd/parse.c +++ b/ircd/parse.c @@ -617,6 +617,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_recover, 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, diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c index 00f7d8e..fb84276 100644 --- a/ircd/s_bsd.c +++ b/ircd/s_bsd.c @@ -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. diff --git a/ircd/s_err.c b/ircd/s_err.c index 1a31ebf..c8a3e58 100644 --- a/ircd/s_err.c +++ b/ircd/s_err.c @@ -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 */ diff --git a/ircd/s_misc.c b/ircd/s_misc.c index 48b2dd9..2dd7b82 100644 --- a/ircd/s_misc.c +++ b/ircd/s_misc.c @@ -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) diff --git a/ircd/s_user.c b/ircd/s_user.c index 9f45018..2c9a9bc 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -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. */ @@ -1318,6 +1319,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; @@ -1340,6 +1347,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 diff --git a/ircd/send.c b/ircd/send.c index ff8d115..921b37d 100644 --- a/ircd/send.c +++ b/ircd/send.c @@ -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.