From vulture@iastate.edu Sat Feb 16 10:24:39 2002
Date: Sat, 5 Jan 2002 01:04:54 -0600
From: Sean Stanek <vulture@iastate.edu>
Reply-To: ircd-coders@the-project.org
To: ircd-coders@blackened.com
Subject: Persistent Clients


Andrew already has some persistent client code written, but
it needs some updating to be integrated into the current
hyb7 source or something like that...

So in the meanwhile I wrote a (useable) persistent client
*module* (/modload m_persist.so) that needs only a few
lines extra in client.c and client.h, so persistent client
support can be loaded/unloaded at will. This is only very
basic (and definately could be optimized), so whenever
Andrew gets his stuff together it would be nice to share
code and ideas. :)

Sean Stanek


==[ /src/client.c
diff -u ]==[ begin ]==================================

--- /src/client.c  Fri Jan  4 23:04:42 2002
+++ /src/client.c  Sat Dec 15 16:20:07 2001
@@ -153,8 +153,6 @@

   client_p->status = STAT_UNKNOWN;
   client_p->fd = -1;
-  client_p->persistpw[0] = '\0';
-
 #ifndef HAVE_SOCKETPAIR
   client_p->fd_r = -1;
 #endif
@@ -268,8 +266,6 @@
       next_ptr = ptr->next;
       client_p = ptr->data;

-      if(client_p->flags & FLAGS_PERSISTENT) continue;
-
       /*
       ** Note: No need to notify opers here. It's
       ** already done when "FLAGS_DEADSOCKET" is set.
@@ -1253,13 +1249,6 @@
   struct Client        *next;
   char comment1[HOSTLEN + HOSTLEN + 2];
   dlink_node *m;
-
-  if(source_p->persistpw[0] && (!(source_p->flags & FLAGS_KILLED))) {
-    source_p->flags |= FLAGS_PERSISTENT;
-    fd_close(source_p->fd);
-    source_p->fd = -1;
-    return 0;
-  }

   /* source_p->flags |= FLAGS_DEADSOCKET; */

==[ /src/client.c
diff -u ]==[ end ]====================================

==[ /src/client.h
diff -u ]==[ begin ]==================================

--- client.h    Fri Jan  4 23:08:53 2002
+++ /usr/local/ircd/ircd-hybrid-7/include/client.h      Thu Nov 29
14:23:57 2001
@@ -197,8 +197,6 @@
    */
   char              info[REALLEN + 1]; /* Free form additional client
info */

-  char              persistpw[PASSWDLEN + 1];
-
 /* cache table of mappings between top level chan and sub vchan
client
  * is on.
  */
@@ -396,7 +394,6 @@
 #define FLAGS_WAITAUTH     0x8000 /* waiting for CRYPTLINK AUTH
command */
 #define FLAGS_SERVLINK     0x10000 /* servlink has servlink process
*/
 #define FLAGS_MARK        0x20000 /* marked client */
-#define FLAGS_PERSISTENT   0x100000 /* persistent client IN USE */
 /* umodes, settable flags */

 #define FLAGS_SERVNOTICE   0x0001 /* server notices such as kill */

==[ /src/client.h
diff -u ]==[ end ]====================================

==[ /modules/m_persist.c
raw ]==[ begin ]===============================

/*********************************************************************
***
 *   IRC - Internet Relay Chat, modules/m_persist.c
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   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_persist.c,v 1.00 2002/01/04 01:05:52 vulture Exp $
 */
#include <string.h>
#include "handlers.h"
#include "packet.h"
#include "client.h"
#include "ircd.h"
#include "numeric.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_serv.h"
#include "s_user.h"
#include "send.h"
#include "msg.h"
#include "parse.h"
#include "modules.h"
#include "fdlist.h"
#include "channel_mode.h"

static void m_persist(struct Client*, struct Client*, int, char**);

struct Message persist_msgtab = {
  "PERSIST", 0, 0, 0, 0, MFLG_SLOW, 0,
  {m_unregistered, m_persist, m_persist, m_persist }
};

#ifndef STATIC_MODULES
void
_modinit(void)
{
  mod_add_cmd(&persist_msgtab);
}

void
_moddeinit(void)
{
  mod_del_cmd(&persist_msgtab);
}

char *_version = "20020104";
#endif

/*
 * m_persist - PERSIST command handler
 *      parv[0] = sender prefix
 *      parv[1] = subcommand
 *
 * Usage in its current state:
 *
 *   /persist pass <password> - set a password to log back in as
 *   /persist reset - reset password to blank
 *   /persist attach <nick> <password> - reattach to a persistent
client
 *
 * As long as a password is set, disconnecting from the ircd will
 * simply result in client being put into a persistent state, where
 * no i/o can occur, and client cannot be disconnected (except by
 * being /kill'ed). To actually quit a client, do a "/persist reset"
 * to reset the password, after which /quit'ing or simply pinging
 * off will result in a normal disconnection (blank password means
 * no persistent client). Abnormal termination of the connection
 * (i.e. being packetted) after a password has been set will result
 * in a persistent client. The password still stay the same after
 * reattaching and does not need to be re-entered after attaching.
 *                                     -- vulture
 *
 */

static void m_persist(struct Client* client_p, struct Client*
source_p,
                      int parc, char* parv[])
{
  struct Client* target_p;
  struct Channel *chptr;
  dlink_node *ptr;
  static char modebuf[MODEBUFLEN];
  static char parabuf[MODEBUFLEN];
  static char* namepointer[2];

  if(parc < 2) {
    sendto_one(source_p, ":%s NOTICE %s :PERSIST commands available:
PASS, ATTACH, RESET",
               me.name, source_p->name);
    return;
  }

  if(strcasecmp(parv[1],"PASS") == 0) {

    if(parc < 3) {
      sendto_one(source_p, ":%s NOTICE %s :PERSIST PASS usage:
/PERSIST PASS <password>",
                 me.name, source_p->name);
      return;
    }

    strncpy(source_p->persistpw, parv[2], PASSWDLEN-1);
    source_p->persistpw[PASSWDLEN-1] = '\0';

    sendto_one(source_p, ":%s NOTICE %s :PERSIST PASS: password set
to: %s",
               me.name, source_p->name, source_p->persistpw);

  } else if(strcasecmp(parv[1],"ATTACH") == 0) {

    if(parc < 4) {
      sendto_one(source_p, ":%s NOTICE %s :PERSIST ATTACH usage:
/PERSIST ATTACH <nick> <password>",
                 me.name, source_p->name);
      return;
    }

    for(target_p = GlobalClientList; target_p; target_p =
target_p->next) {
      if(strcasecmp(target_p->name, parv[2])) continue;
      if(!target_p->flags & FLAGS_PERSISTENT) {
        sendto_one(source_p, ":%s NOTICE %s :PERSIST ATTACH: Incorrect
nick/password", me.name, source_p->name);
        return;
      }
      if(strcmp(target_p->persistpw, parv[3])) {
        sendto_one(source_p, ":%s NOTICE %s :PERSIST ATTACH: Incorrect
nick/password", me.name, source_p->name);
        return;
      }



      /*
      ** If we make it here, then persistent reattachment nick/pass
was correct,
      ** proceed with reattaching the client.
      */


      /* Client will think he changed nicks to the persistent client's
nick */

      sendto_one(source_p, ":%s!%s@%s NICK :%s",
                   source_p->name, source_p->username, source_p->host,
                   target_p->name);


      /* Client is no longer persistent, also swap fd's for
reattachment */

      target_p->flags &= ~FLAGS_PERSISTENT;
      target_p->fd = source_p->fd;
      source_p->fd = -1;

      fd_note(target_p->fd, "Nick: %s", target_p->name);


      /* mark socket to be looked at for read events again */

      comm_setselect(target_p->fd, FDLIST_IDLECLIENT,
COMM_SELECT_READ, read_packet,
        target_p, 0);


      /* send JOIN messages to channels client wasn't in */

      for (ptr = target_p->user->channel.head; ptr; ptr = ptr->next) {
        chptr = ptr->data;
        if(!IsMember(source_p, chptr)) {
          sendto_one(target_p, ":%s!%s@%s JOIN :%s", target_p->name,
target_p->username, target_p->host, chptr->chname);
        }
        channel_member_names( target_p, chptr, chptr->chname, 1);

        channel_modes(chptr, target_p, modebuf, parabuf);
        sendto_one(target_p, form_str(RPL_CHANNELMODEIS), me.name,
parv[0], chptr->chname, modebuf, parabuf);
        sendto_one(target_p, form_str(RPL_CREATIONTIME), me.name,
parv[0], chptr->chname, chptr->channelts);
      }


      /* send PART messages to channels persistent client wasn't in */

      for (ptr = source_p->user->channel.head; ptr; ptr = ptr->next) {
        chptr = ptr->data;
        if(!IsMember(target_p, chptr)) {
          sendto_one(target_p, ":%s!%s@%s PART :%s", target_p->name,
target_p->username, target_p->host, chptr->chname);
        }
      }


      /* update user mode */

      namepointer[0] = namepointer[1] = target_p->name;
      user_mode(target_p, target_p, 2, namepointer);


      /* Quit older client, since it's no longer needed after
reattaching */

      exit_client(client_p, source_p, source_p, "User ate a cookie!");
      return;
    }

    sendto_one(source_p, ":%s NOTICE %s :PERSIST ATTACH: Incorrect
nick/password",
               me.name, source_p->name);
    return;

  } else if(strcasecmp(parv[1],"RESET") == 0) {

    source_p->persistpw[0] = '\0';
    source_p->flags &= ~FLAGS_PERSISTENT;

    sendto_one(source_p, ":%s NOTICE %s :PERSIST RESET: Password
cleared; client will no longer persist",
                 me.name, source_p->name);

  } else {

    sendto_one(source_p, ":%s NOTICE %s :PERSIST commands available:
PASS, ATTACH, RESET",
               me.name, source_p->name);
    return;

  }

  return;
}

==[ /modules/m_persist.c
raw ]==[ end ]=================================

