[cvslog] (2002-10-07 22:34:04 UTC) Module eggdrop1.7: Change committed

cvslog cvs at tsss.org
Mon Oct 7 17:35:01 CST 2002


CVSROOT    : /usr/local/cvsroot
Module     : eggdrop1.7
Commit time: 2002-10-07 22:34:04 UTC
Commited by: stdarg <stdarg at techmonkeys.org>

Modified files:
     lib/eggdrop/Makefile.am lib/eggdrop/eggdrop.h
     lib/eggdrop/eggnet.c lib/eggdrop/eggnet.h lib/eggdrop/module.h
     lib/eggdrop/sockbuf.c lib/eggdrop/sockbuf.h

Added files:
     lib/eggdrop/flags.h lib/eggdrop/ircmasks.c lib/eggdrop/ircmasks.h
     lib/eggdrop/match.c lib/eggdrop/match.h lib/eggdrop/throttle.c
     lib/eggdrop/throttle.h lib/eggdrop/users.c lib/eggdrop/users.h
     lib/eggdrop/xml.c lib/eggdrop/xml.h lib/eggdrop/xmlread.c
     lib/eggdrop/xmlwrite.c

Log message:

* Added input/output throttling for sockets (haven't tested input)
* Basic xml read/write stuff. Very forgiving on xml syntax; doesn't do validation. Not done.
* Beginning of new user code. Works well so far.

---------------------- diff included ----------------------
Index: eggdrop1.7/lib/eggdrop/Makefile.am
diff -u eggdrop1.7/lib/eggdrop/Makefile.am:1.9 eggdrop1.7/lib/eggdrop/Makefile.am:1.10
--- eggdrop1.7/lib/eggdrop/Makefile.am:1.9	Thu Sep 19 21:06:25 2002
+++ eggdrop1.7/lib/eggdrop/Makefile.am	Mon Oct  7 17:33:54 2002
@@ -1,4 +1,4 @@
-# $Id: Makefile.am,v 1.9 2002/09/20 02:06:25 stdarg Exp $
+# $Id: Makefile.am,v 1.10 2002/10/07 22:33:54 stdarg Exp $
 
 MAINTAINERCLEANFILES	= Makefile.in
 
@@ -15,15 +15,20 @@
 			eggident.c eggident.h \
 			eggnet.h eggnet.c \
 			eggtimer.c eggtimer.h \
-			fileutil.c \
-			fileutil.h \
+			fileutil.c fileutil.h \
+			hash_table.c hash_table.h \
+			ircmasks.c ircmasks.h \
 			linemode.c linemode.h \
+			match.c match.h \
 			memutil.c \
 			memutil.h \
 			my_socket.c my_socket.h \
 			script.c script.h \
 			sockbuf.c sockbuf.h \
-			stat.h
+			stat.h \
+			throttle.c throttle.h \
+			users.c users.h \
+			xml.c xmlread.c xmlwrite.c xml.h
 
 libeggdrop_la_LIBADD	= $(top_builddir)/lib/compat/libcompat.la \
 		   	$(top_builddir)/lib/egglib/libegg.la \
Index: eggdrop1.7/lib/eggdrop/eggdrop.h
diff -u eggdrop1.7/lib/eggdrop/eggdrop.h:1.10 eggdrop1.7/lib/eggdrop/eggdrop.h:1.11
--- eggdrop1.7/lib/eggdrop/eggdrop.h:1.10	Thu Sep 19 21:06:25 2002
+++ eggdrop1.7/lib/eggdrop/eggdrop.h	Mon Oct  7 17:33:54 2002
@@ -21,7 +21,7 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 /*
- * $Id: eggdrop.h,v 1.10 2002/09/20 02:06:25 stdarg Exp $
+ * $Id: eggdrop.h,v 1.11 2002/10/07 22:33:54 stdarg Exp $
  */
 
 #ifndef _EGGDROP_H
@@ -40,6 +40,9 @@
 #include <eggdrop/eggident.h>
 #include <eggdrop/linemode.h>
 #include <eggdrop/eggtimer.h>
+#include <eggdrop/throttle.h>
+#include <eggdrop/hash_table.h>
+#include <eggdrop/xml.h>
 
 BEGIN_C_DECLS
 
Index: eggdrop1.7/lib/eggdrop/eggnet.c
diff -u eggdrop1.7/lib/eggdrop/eggnet.c:1.2 eggdrop1.7/lib/eggdrop/eggnet.c:1.3
--- eggdrop1.7/lib/eggdrop/eggnet.c:1.2	Sun Sep 22 03:50:46 2002
+++ eggdrop1.7/lib/eggdrop/eggnet.c	Mon Oct  7 17:33:54 2002
@@ -99,7 +99,7 @@
 
 /* Connect to a given host/port. If proxies/firewalls/vhosts are configured, it
  * will automatically use them. Returns an idx for your connection. */
-int egg_connect(const char *host, int port, int timeout)
+int egg_reconnect(int idx, const char *host, int port, int timeout)
 {
 	connect_info_t *connect_info;
 	egg_timeval_t howlong;
@@ -107,7 +107,7 @@
 	/* Resolve the hostname. */
 	connect_info = calloc(1, sizeof(*connect_info));
 	connect_info->port = port;
-	connect_info->idx = sockbuf_new();
+	connect_info->idx = idx;
 	sockbuf_attach_filter(connect_info->idx, &eggnet_connect_filter, connect_info);
 	connect_info->timer_id = -1;
 	connect_info->dns_id = egg_dns_lookup(host, DNS_IPV4, connect_host_resolved, connect_info);
@@ -117,6 +117,15 @@
 		connect_info->timer_id = timer_create_complex(&howlong, egg_connect_timeout, connect_info, 0);
 	}
 	return(connect_info->idx);
+}
+
+int egg_connect(const char *host, int port, int timeout)
+{
+	int idx;
+
+	idx = sockbuf_new();
+	egg_reconnect(idx, host, port, timeout);
+	return(idx);
 }
 
 static int connect_host_resolved(void *client_data, const char *host, const char *ip)
Index: eggdrop1.7/lib/eggdrop/eggnet.h
diff -u eggdrop1.7/lib/eggdrop/eggnet.h:1.2 eggdrop1.7/lib/eggdrop/eggnet.h:1.3
--- eggdrop1.7/lib/eggdrop/eggnet.h:1.2	Sun Sep 22 03:50:46 2002
+++ eggdrop1.7/lib/eggdrop/eggnet.h	Mon Oct  7 17:33:54 2002
@@ -6,6 +6,7 @@
 int egg_client(const char *ip, int port, const char *vip, int vport);
 int egg_listen(int port, int *real_port);
 int egg_connect(const char *host, int port, int timeout);
+int egg_reconnect(int idx, const char *host, int port, int timeout);
 int egg_ident_lookup(const char *ip, int their_port, int our_port, int timeout, int (*callback)(), void *client_data);
 int egg_dns_lookup(const char *host_or_ip, int timeout, int (*callback)(), void *client_data);
 
Index: eggdrop1.7/lib/eggdrop/flags.h
diff -u /dev/null eggdrop1.7/lib/eggdrop/flags.h:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/flags.h	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,10 @@
+#ifndef _FLAGS_H_
+#define _FLAGS_H_
+
+typedef struct {
+	int global;
+	int channel;
+	int udef;
+} flags_t;
+
+#endif
Index: eggdrop1.7/lib/eggdrop/ircmasks.c
diff -u /dev/null eggdrop1.7/lib/eggdrop/ircmasks.c:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/ircmasks.c	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ircmasks.h"
+
+int ircmask_list_add(ircmask_list_t *list, const char *ircmask, void *data)
+{
+	int i, stars, len;
+
+	stars = 0;
+	len = strlen(ircmask);
+	for (i = 0; i < len; i++) {
+		if (ircmask[i] == '*') stars++;
+	}
+	len -= stars;
+	for (i = 0; i < list->len; i++) {
+		if (len > list->list[i].len) break;
+	}
+	list->list = realloc(list->list, sizeof(*list->list) * (list->len+1));
+	memmove(list->list+i+1, list->list+i, sizeof(*list->list) * (list->len-i));
+	list->list[i].ircmask = strdup(ircmask);
+	list->list[i].len = len;
+	list->list[i].data = data;
+	list->len++;
+	return(0);
+}
+
+int ircmask_list_del(ircmask_list_t *list, const char *ircmask, void *data)
+{
+}
+
+int ircmask_list_find(ircmask_list_t *list, const char *irchost, void *dataptr)
+{
+	int i;
+
+	for (i = 0; i < list->len; i++) {
+		if (wild_match(list->list[i].ircmask, irchost) > 0) {
+			*(void **)dataptr = list->list[i].data;
+			return(0);
+		}
+	}
+	*(void **)dataptr = NULL;
+	return(-1);
+}
Index: eggdrop1.7/lib/eggdrop/ircmasks.h
diff -u /dev/null eggdrop1.7/lib/eggdrop/ircmasks.h:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/ircmasks.h	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,19 @@
+#ifndef _IRCMASKS_H_
+#define _IRCMASKS_H_
+
+typedef struct {
+	char *ircmask;
+	int len;
+	void *data;
+} ircmask_list_entry_t;
+
+typedef struct {
+	int len;
+	ircmask_list_entry_t *list;
+} ircmask_list_t;
+
+int ircmask_list_add(ircmask_list_t *list, const char *ircmask, void *data);
+int ircmask_list_del(ircmask_list_t *list, const char *ircmask, void *data);
+int ircmask_list_find(ircmask_list_t *list, const char *irchost, void *dataptr);
+
+#endif
Index: eggdrop1.7/lib/eggdrop/match.c
diff -u /dev/null eggdrop1.7/lib/eggdrop/match.c:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/match.c	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,235 @@
+/*
+ * match.c --
+ *
+ *	wildcard matching functions
+ */
+/*
+ * Once this code was working, I added support for % so that I could
+ * use the same code both in Eggdrop and in my IrcII client.
+ * Pleased with this, I added the option of a fourth wildcard, ~,
+ * which matches varying amounts of whitespace (at LEAST one space,
+ * though, for sanity reasons).
+ *
+ * This code would not have been possible without the prior work and
+ * suggestions of various sources.  Special thanks to Robey for
+ * all his time/help tracking down bugs and his ever-helpful advice.
+ *
+ * 04/09:  Fixed the "*\*" against "*a" bug (caused an endless loop)
+ *
+ *   Chris Fuller  (aka Fred1 at IRC & Fwitz at IRC)
+ *     crf at cfox.bchs.uh.edu
+ *
+ * I hereby release this code into the public domain
+ */
+
+#ifndef lint
+static const char rcsid[] = "$Id: match.c,v 1.1 2002/10/07 22:33:54 stdarg Exp $";
+#endif
+
+/* prototypes */
+#include <ctype.h>
+
+/* The quoting character -- what overrides wildcards */
+#define QUOTE '\\'
+
+/* The "matches ANYTHING" wildcard */
+#define WILDS '*'
+
+/* The "matches ANY NUMBER OF NON-SPACE CHARS" wildcard */
+#define WILDP '%'
+
+/* The "matches EXACTLY ONE CHARACTER" wildcard */
+#define WILDQ '?'
+
+/* The "matches AT LEAST ONE SPACE" wildcard */
+#define WILDT '~'
+
+/* Changing these is probably counter-productive :) */
+#define UNQUOTED (0x7FFF)
+#define QUOTED   (0x8000)
+#define NOMATCH 0
+#define MATCH ((match+sofar)&UNQUOTED)
+#define MATCH_PER (match+saved+sofar)
+
+
+/*
+ * wild_match(char *ma, char *na)
+ *
+ * Features:  Backwards, case-insensitive, ?, *
+ * Best use:  Matching of hostmasks (since they are likely to begin
+ *            with a * rather than end with one).
+ */
+/*
+ * sofar's high bit is used as a flag of whether or not we are quoting.
+ * The other matchers don't need this because when you're going forward,
+ * you just skip over the quote char.
+ * No "saved" is used to track "%" and no special quoting ability is
+ * needed, so we just have (match+sofar) as the result.
+ */
+int wild_match(const unsigned char *m, const unsigned char *n)
+{
+  const unsigned char *ma = m, *na = n, *lsm = 0, *lsn = 0;
+  int match = 1;
+  int sofar = 0;
+
+  /* take care of null strings (should never match) */
+  if ((ma == 0) || (na == 0) || (!*ma) || (!*na))
+    return NOMATCH;
+  /* find the end of each string */
+  while (*(++m));
+    m--;
+  while (*(++n));
+    n--;
+
+  while (n >= na) {
+    if ((m <= ma) || (m[-1] != QUOTE)) {	/* Only look if no quote */
+      switch (*m) {
+      case WILDS:		/* Matches anything */
+	do
+	  m--;			/* Zap redundant wilds */
+	while ((m >= ma) && ((*m == WILDS) || (*m == WILDP)));
+	if ((m >= ma) && (*m == '\\'))
+	  m++;			/* Keep quoted wildcard! */
+	lsm = m;
+	lsn = n;
+	match += sofar;
+	sofar = 0;		/* Update fallback pos */
+	continue;		/* Next char, please */
+      case WILDQ:
+	m--;
+	n--;
+	continue;		/* '?' always matches */
+      }
+      sofar &= UNQUOTED;	/* Remember not quoted */
+    } else
+      sofar |= QUOTED;		/* Remember quoted */
+    if (toupper(*m) == toupper(*n)) {	/* If matching char */
+      m--;
+      n--;
+      sofar++;			/* Tally the match */
+      if (sofar & QUOTED)
+	m--;			/* Skip the quote char */
+      continue;			/* Next char, please */
+    }
+    if (lsm) {			/* To to fallback on '*' */
+      n = --lsn;
+      m = lsm;
+      if (n < na)
+	lsm = 0;		/* Rewind to saved pos */
+      sofar = 0;
+      continue;			/* Next char, please */
+    }
+    return NOMATCH;		/* No fallback=No match */
+  }
+  while ((m >= ma) && ((*m == WILDS) || (*m == WILDP)))
+    m--;			/* Zap leftover %s & *s */
+  return (m >= ma) ? NOMATCH : MATCH;	/* Start of both = match */
+}
+
+
+/*
+ * wild_match_per(char *m, char *n)
+ *
+ * Features:  Forward, case-insensitive, ?, *, %, ~(optional)
+ * Best use:  Generic string matching, such as in IrcII-esque bindings
+ */
+int wild_match_per(const unsigned char *m, const unsigned char *n)
+{
+  const unsigned char *ma = m, *lsm = 0, *lsn = 0, *lpm = 0, *lpn = 0;
+  int match = 1, saved = 0, space;
+  unsigned int sofar = 0;
+
+  /* take care of null strings (should never match) */
+  if ((m == 0) || (n == 0) || (!*n))
+    return NOMATCH;
+  /* (!*m) test used to be here, too, but I got rid of it.  After all,
+   * If (!*n) was false, there must be a character in the name (the
+   * second string), so if the mask is empty it is a non-match.  Since
+   * the algorithm handles this correctly without testing for it here
+   * and this shouldn't be called with null masks anyway, it should be
+   * a bit faster this way */
+
+  while (*n) {
+    /* Used to test for (!*m) here, but this scheme seems to work better */
+    if (*m == WILDT) {		/* Match >=1 space */
+      space = 0;		/* Don't need any spaces */
+      do {
+	m++;
+	space++;
+      }				/* Tally 1 more space ... */
+      while ((*m == WILDT) || (*m == ' '));	/*  for each space or ~ */
+      sofar += space;		/* Each counts as exact */
+      while (*n == ' ') {
+	n++;
+	space--;
+      }				/* Do we have enough? */
+      if (space <= 0)
+	continue;		/* Had enough spaces! */
+    }
+    /* Do the fallback       */
+    else {
+      switch (*m) {
+      case 0:
+	do
+	  m--;			/* Search backwards */
+	while ((m > ma) && (*m == '?'));	/* For first non-? char */
+	if ((m > ma) ? ((*m == '*') && (m[-1] != QUOTE)) : (*m == '*'))
+	  return MATCH_PER;		/* nonquoted * = match */
+	break;
+      case WILDP:
+	while (*(++m) == WILDP);	/* Zap redundant %s */
+	if (*m != WILDS) {	/* Don't both if next=* */
+	  if (*n != ' ') {	/* WILDS can't match ' ' */
+	    lpm = m;
+	    lpn = n;		/* Save '%' fallback spot */
+	    saved += sofar;
+	    sofar = 0;		/* And save tally count */
+	  }
+	  continue;		/* Done with '%' */
+	}
+	/* FALL THROUGH */
+      case WILDS:
+	do
+	  m++;			/* Zap redundant wilds */
+	while ((*m == WILDS) || (*m == WILDP));
+	lsm = m;
+	lsn = n;
+	lpm = 0;		/* Save '*' fallback spot */
+	match += (saved + sofar);	/* Save tally count */
+	saved = sofar = 0;
+	continue;		/* Done with '*' */
+      case WILDQ:
+	m++;
+	n++;
+	continue;		/* Match one char */
+      case QUOTE:
+	m++;			/* Handle quoting */
+      }
+      if (toupper(*m) == toupper(*n)) {		/* If matching */
+	m++;
+	n++;
+	sofar++;
+	continue;		/* Tally the match */
+      }
+    }
+    if (lpm) {			/* Try to fallback on '%' */
+      n = ++lpn;
+      m = lpm;
+      sofar = 0;		/* Restore position */
+      if ((*n | 32) == 32)
+	lpm = 0;		/* Can't match 0 or ' ' */
+      continue;			/* Next char, please */
+    }
+    if (lsm) {			/* Try to fallback on '*' */
+      n = ++lsn;
+      m = lsm;			/* Restore position */
+      /* Used to test for (!*n) here but it wasn't necessary so it's gone */
+      saved = sofar = 0;
+      continue;			/* Next char, please */
+    }
+    return NOMATCH;		/* No fallbacks=No match */
+  }
+  while ((*m == WILDS) || (*m == WILDP))
+    m++;			/* Zap leftover %s & *s */
+  return (*m) ? NOMATCH : MATCH_PER;	/* End of both = match */
+}
Index: eggdrop1.7/lib/eggdrop/match.h
diff -u /dev/null eggdrop1.7/lib/eggdrop/match.h:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/match.h	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,20 @@
+/*
+ * match.h --
+ *
+ *	prototypes for match.c
+ */
+/*
+ * $Id: match.h,v 1.1 2002/10/07 22:33:54 stdarg Exp $
+ */
+
+#ifndef _EGG_MATCH_H
+#define _EGG_MATCH_H
+
+
+/*
+ * Prototypes
+ */
+extern int wild_match(const unsigned char *, const unsigned char *);
+extern int wild_match_per(const unsigned char *, const unsigned char *);
+
+#endif				/* !_EGG_MATCH_H */
Index: eggdrop1.7/lib/eggdrop/module.h
diff -u eggdrop1.7/lib/eggdrop/module.h:1.30 eggdrop1.7/lib/eggdrop/module.h:1.31
--- eggdrop1.7/lib/eggdrop/module.h:1.30	Fri Sep 20 16:41:49 2002
+++ eggdrop1.7/lib/eggdrop/module.h	Mon Oct  7 17:33:54 2002
@@ -20,7 +20,7 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 /*
- * $Id: module.h,v 1.30 2002/09/20 21:41:49 stdarg Exp $
+ * $Id: module.h,v 1.31 2002/10/07 22:33:54 stdarg Exp $
  */
 
 #ifndef _EGG_MOD_MODULE_H
@@ -37,7 +37,6 @@
 #include "src/users.h"
 #include "src/bg.h"
 #include "src/chan.h"
-#include "src/chanprog.h"
 #include "src/cmds.h"
 #include "src/cmdt.h"
 #include "src/core_binds.h"
Index: eggdrop1.7/lib/eggdrop/sockbuf.c
diff -u eggdrop1.7/lib/eggdrop/sockbuf.c:1.3 eggdrop1.7/lib/eggdrop/sockbuf.c:1.4
--- eggdrop1.7/lib/eggdrop/sockbuf.c:1.3	Sun Sep 22 03:50:46 2002
+++ eggdrop1.7/lib/eggdrop/sockbuf.c	Mon Oct  7 17:33:54 2002
@@ -107,7 +107,7 @@
 			}
 			nbytes = 0;
 		}
-		sockbuf_on_written(idx, SOCKBUF_LEVEL_INTERNAL, nbytes, 0);
+
 		if (nbytes == len) return(nbytes);
 		sockbuf_block(idx);
 		data += nbytes;
@@ -241,7 +241,7 @@
 	/* Get the associated error message. */
 	errmsg = strerror(err);
 
-	socket_close(sbuf->sock);
+	sockbuf_close(sbuf->sock);
 	sockbuf_on_eof(idx, SOCKBUF_LEVEL_INTERNAL, err, errmsg);
 }
 
@@ -418,6 +418,40 @@
 	return(idx);
 }
 
+int sockbuf_noread(int idx)
+{
+	int i;
+
+	if (!sockbuf_isvalid(idx)) return(-1);
+
+	/* Find the entry in the pollfds array. */
+	for (i = 0; i < npollfds; i++) {
+		if (idx_array[i] == idx) break;
+	}
+
+	if (i == npollfds) return(-1);
+
+	pollfds[i].events &= (~POLLIN);
+	return(0);
+}
+
+int sockbuf_read(int idx)
+{
+	int i;
+
+	if (!sockbuf_isvalid(idx)) return(-1);
+
+	/* Find the entry in the pollfds array. */
+	for (i = 0; i < npollfds; i++) {
+		if (idx_array[i] == idx) break;
+	}
+
+	if (i == npollfds) return(-1);
+
+	pollfds[i].events |= POLLIN;
+	return(0);
+}
+
 int sockbuf_isvalid(int idx)
 {
 	if (idx >= 0 && idx < nsockbufs && !(sockbufs[idx].flags & (SOCKBUF_AVAIL | SOCKBUF_DELETED))) return(1);
@@ -431,7 +465,7 @@
 	if (!sockbuf_isvalid(idx)) return(-1);
 	sbuf = &sockbufs[idx];
 	if (sbuf->sock >= 0) {
-		close(sbuf->sock);
+		socket_close(sbuf->sock);
 		sockbuf_set_sock(idx, -1, 0);
 	}
 	return(0);
@@ -445,6 +479,7 @@
 	if (!sockbuf_isvalid(idx)) return(-1);
 	sbuf = &sockbufs[idx];
 
+	sbuf->flags |= SOCKBUF_DELETED;
 	/* Call the on_delete handler for all filters. */
 	for (i = 0; i < sbuf->nfilters; i++) {
 		if (sbuf->filters[i]->on_delete) {
@@ -455,7 +490,7 @@
 	if (sbuf->handler->on_delete) sbuf->handler->on_delete(sbuf->client_data, idx);
 
 	/* Close the file descriptor. */
-	if (sbuf->sock >= 0) close(sbuf->sock);
+	if (sbuf->sock >= 0) socket_close(sbuf->sock);
 
 	/* Free the peer ip. */
 	if (sbuf->peer_ip) free(sbuf->peer_ip);
@@ -567,6 +602,22 @@
 	return(0);
 }
 
+int sockbuf_get_filter_data(int idx, sockbuf_filter_t *filter, void *client_data_ptr)
+{
+	int i;
+	sockbuf_t *sbuf;
+
+	if (!sockbuf_isvalid(idx)) return(-1);
+	sbuf = &sockbufs[idx];
+	for (i = 0; i < sbuf->nfilters; i++) {
+		if (sbuf->filters[i] == filter) {
+			*(void **)client_data_ptr = sbuf->filter_client_data[i];
+			return(0);
+		}
+	}
+	return(-1);
+}
+
 /* Detach the specified filter, and return the filter's client data in the
 	client_data pointer (it should be a pointer to a pointer). */
 int sockbuf_detach_filter(int idx, sockbuf_filter_t *filter, void *client_data)
@@ -584,7 +635,7 @@
 	}
 
 	if (client_data) *(void **)client_data = sbuf->filter_client_data[i];
-	memmove(sbuf->filter_client_data+i, sbuf->filter_client_data+i+1, sizeof(void *) * sbuf->nfilters-i-1);
+	memmove(sbuf->filter_client_data+i, sbuf->filter_client_data+i+1, sizeof(void *) * (sbuf->nfilters-i-1));
 	memmove(sbuf->filters+i, sbuf->filters+i+1, sizeof(void *) * (sbuf->nfilters-i-1));
 	sbuf->nfilters--;
 	return(0);
Index: eggdrop1.7/lib/eggdrop/sockbuf.h
diff -u eggdrop1.7/lib/eggdrop/sockbuf.h:1.3 eggdrop1.7/lib/eggdrop/sockbuf.h:1.4
--- eggdrop1.7/lib/eggdrop/sockbuf.h:1.3	Sun Sep 22 03:50:46 2002
+++ eggdrop1.7/lib/eggdrop/sockbuf.h	Mon Oct  7 17:33:54 2002
@@ -20,10 +20,11 @@
 #define SOCKBUF_LEVEL_INTERNAL	-1
 #define SOCKBUF_LEVEL_WRITE_INTERNAL	1000000
 #define SOCKBUF_LEVEL_PROXY	1000
-#define SOCKBUF_LEVEL_ENCRYPTION	2000
-#define SOCKBUF_LEVEL_COMPRESSION	3000
-#define SOCKBUF_LEVEL_TEXT_ALTERATION	4000
-#define SOCKBUF_LEVEL_TEXT_BUFFER	5000
+#define SOCKBUF_LEVEL_THROTTLE	2000
+#define SOCKBUF_LEVEL_ENCRYPTION	3000
+#define SOCKBUF_LEVEL_COMPRESSION	4000
+#define SOCKBUF_LEVEL_TEXT_ALTERATION	5000
+#define SOCKBUF_LEVEL_TEXT_BUFFER	6000
 
 typedef struct {
 	const char *name;
@@ -69,7 +70,10 @@
 int sockbuf_set_handler(int idx, sockbuf_handler_t *handler, void *client_data);
 int sockbuf_get_sock(int idx);
 int sockbuf_set_sock(int idx, int sock, int flags);
+int sockbuf_read(int idx);
+int sockbuf_noread(int idx);
 int sockbuf_attach_listener(int fd);
+int sockbuf_get_filter_data(int idx, sockbuf_filter_t *filter, void *client_data_ptr);
 int sockbuf_detach_listener(int fd);
 int sockbuf_attach_filter(int idx, sockbuf_filter_t *filter, void *client_data);
 int sockbuf_detach_filter(int idx, sockbuf_filter_t *filter, void *client_data);
Index: eggdrop1.7/lib/eggdrop/throttle.c
diff -u /dev/null eggdrop1.7/lib/eggdrop/throttle.c:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/throttle.c	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <eggdrop/eggdrop.h>
+#include "sockbuf.h"
+#include "eggtimer.h"
+
+#define THROTTLE_LEVEL	SOCKBUF_LEVEL_THROTTLE
+
+typedef struct throttle {
+	struct throttle *next, *prev;
+	char *buf;	/* buffered output */
+	int len;	/* buffer length */
+	int idx;	/* idx we're throttling */
+	int bytes_in, bytes_out;	/* i/o this second */
+	int speed_in, speed_out;	/* throttle limits */
+} throttle_t;
+
+static throttle_t *throttle_head = NULL;
+static throttle_t *throttle_next = NULL;
+static int throttle_timer = -1;
+
+static void throttle_remove(throttle_t *t);
+
+static int throttle_on_read(void *client_data, int idx, char *data, int len)
+{
+	throttle_t *t = client_data;
+
+	t->bytes_in += len;
+	if (t->speed_in > 0 && t->bytes_in > t->speed_in) {
+		sockbuf_noread(idx);
+	}
+	return sockbuf_on_read(idx, THROTTLE_LEVEL, data, len);
+}
+
+static int throttle_on_write(void *client_data, int idx, const char *data, int len)
+{
+	throttle_t *t = client_data;
+
+	if (t->speed_out <= 0) return sockbuf_on_write(idx, THROTTLE_LEVEL, data, len);
+
+	t->buf = realloc(t->buf, t->len + len);
+	memcpy(t->buf + t->len, data, len);
+	t->len += len;
+
+	return(0);
+}
+
+static int throttle_on_written(void *client_data, int idx, int len, int remaining)
+{
+	throttle_t *t = client_data;
+
+	return sockbuf_on_written(idx, THROTTLE_LEVEL, len, remaining + t->len);
+}
+
+static int throttle_on_delete(void *client_data, int idx)
+{
+	throttle_off(idx);
+	return(0);
+}
+
+static sockbuf_filter_t throttle_filter = {
+	"throttle",
+	THROTTLE_LEVEL,
+	NULL, NULL, NULL,
+	throttle_on_read, throttle_on_write, throttle_on_written,
+	NULL, throttle_on_delete
+};
+
+static throttle_t *throttle_lookup(int idx)
+{
+	throttle_t *t;
+
+	for (t = throttle_head; t; t = t->next) {
+		if (t->idx == idx) break;
+	}
+	return(t);
+}
+
+static void throttle_add(throttle_t *t)
+{
+	t->next = throttle_head;
+	t->prev = NULL;
+	if (throttle_head) throttle_head->prev = t;
+	throttle_head = t;
+}
+
+static void throttle_remove(throttle_t *t)
+{
+	/* If we get removed during throttle_secondly() we have to update
+	 * the next pointer to a valid one. */
+	if (throttle_next == t) {
+		throttle_next = t->next;
+	}
+
+	if (t->prev) t->prev->next = t->next;
+	else throttle_head = t->next;
+
+	if (t->next) t->next->prev = t->prev;
+}
+
+/* Every second, we reset the i/o counters for throttled sockbufs. */
+static int throttle_secondly(void *ignore)
+{
+	throttle_t *t;
+	int avail, r, out;
+
+	for (t = throttle_head; t; t = throttle_next) {
+		throttle_next = t->next;
+		t->bytes_in -= t->speed_in;
+		if (t->bytes_in < 0) t->bytes_in = 0;
+		else if (t->bytes_in < t->speed_in) sockbuf_read(t->idx);
+
+		out = 0;
+		while (t->len && out < t->speed_out) {
+			/* How many bytes can we send? */
+			avail = t->speed_out - out;
+			if (avail > t->len) avail = t->len;
+
+			r = sockbuf_on_write(t->idx, THROTTLE_LEVEL, t->buf, avail);
+			if (r < 0) break;
+
+			memmove(t->buf, t->buf+avail, t->len-avail);
+			t->len -= avail;
+			out += avail;
+
+			if (r > 0) sockbuf_on_written(t->idx, THROTTLE_LEVEL, r, t->len + avail - r);
+
+		}
+	}
+	return(0);
+}
+
+int throttle_on(int idx)
+{
+	throttle_t *t;
+	egg_timeval_t howlong;
+
+	t = calloc(1, sizeof(*t));
+	t->idx = idx;
+	throttle_add(t);
+	sockbuf_attach_filter(idx, &throttle_filter, t);
+
+	if (throttle_timer < 0) {
+		howlong.sec = 1;
+		howlong.usec = 0;
+		throttle_timer = timer_create_repeater(&howlong, throttle_secondly);
+	}
+	return(0);
+}
+
+int throttle_off(int idx)
+{
+	throttle_t *t;
+
+	t = throttle_lookup(idx);
+	if (!t) return(-1);
+
+	throttle_remove(t);
+	if (sockbuf_isvalid(idx)) {
+		sockbuf_detach_filter(idx, &throttle_filter, NULL);
+		if (t->len) sockbuf_on_write(idx, THROTTLE_LEVEL, t->buf, t->len);
+	}
+
+	if (t->buf) free(t->buf);
+	free(t);
+
+	if (!throttle_head) {
+		timer_destroy(throttle_timer);
+		throttle_timer = -1;
+	}
+	return(0);
+}
+
+int throttle_set(int idx, int dir, int speed)
+{
+	throttle_t *t;
+	int r;
+
+	t = throttle_lookup(idx);
+	if (!t) return(-1);
+	if (dir) {
+		t->speed_out = speed;
+		if (speed <= 0 && t->len) {
+			r = sockbuf_on_write(idx, THROTTLE_LEVEL, t->buf, t->len);
+			if (r > 0) sockbuf_on_written(idx, THROTTLE_LEVEL, r, t->len - r);
+			t->len = 0;
+		}
+	}
+	else t->speed_in = speed;
+	return(0);
+}
Index: eggdrop1.7/lib/eggdrop/throttle.h
diff -u /dev/null eggdrop1.7/lib/eggdrop/throttle.h:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/throttle.h	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,8 @@
+#ifndef _THROTTLE_H_
+#define _THROTTLE_H_
+
+int throttle_on(int idx);
+int throttle_off(int idx);
+int throttle_set(int idx, int dir, int speed);
+
+#endif
Index: eggdrop1.7/lib/eggdrop/users.c
diff -u /dev/null eggdrop1.7/lib/eggdrop/users.c:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/users.c	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,465 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "users.h"
+#include "ircmasks.h"
+#include "hash_table.h"
+#include "xml.h"
+#include "flags.h"
+#include "match.h"
+
+/* When we walk along irchost_cache_ht, we pass along this struct so the
+ * function can keep track of modified entries. */
+typedef struct {
+	user_t *u;
+	const char *ircmask;
+	const char **entries;
+	int nentries;
+} walker_info_t;
+
+/* Keep track of the next available uid. Also keep track of when uid's wrap
+ * around (probably won't happen), so that we know when we can trust g_uid. */
+static int g_uid = 1, uid_wraparound = 0;
+
+/* Hash table to associate irchosts (nick!user at host) with users. */
+static hash_table_t *irchost_cache_ht = NULL;
+
+/* Hash table to associate handles with users. */
+static hash_table_t *handle_ht = NULL;
+
+/* Hash table to associate uid's with users. */
+static hash_table_t *uid_ht = NULL;
+
+/* List to keep all users' ircmasks in. */
+static ircmask_list_t ircmask_list = {0, NULL};
+
+/* Prototypes for internal functions. */
+static user_t *real_user_new(const char *handle, int uid);
+static int user_get_uid();
+static int cache_check_add(const void *key, void *dataptr, void *client_data);
+static int cache_check_del(const void *key, void *dataptr, void *client_data);
+static int cache_user_del(user_t *u, const char *ircmask);
+
+int user_init()
+{
+	handle_ht = hash_table_create(NULL, NULL, USER_HASH_SIZE, HASH_TABLE_STRINGS);
+	uid_ht = hash_table_create(NULL, NULL, USER_HASH_SIZE, HASH_TABLE_INTS);
+	irchost_cache_ht = hash_table_create(NULL, NULL, HOST_HASH_SIZE, HASH_TABLE_STRINGS);
+	return(0);
+}
+
+int user_load(const char *fname)
+{
+	int i, j, k, uid;
+	xml_node_t root, *user_node, *setting_node;
+	user_setting_t *setting;
+	char *handle, *ircmask, *chan, *name, *value;
+	user_t *u;
+
+	memset(&root, 0, sizeof(root));
+	xml_read(&root, fname);
+	for (i = 0; ; i++) {
+		user_node = xml_node_lookup(&root, "user", i, 0);
+		if (!user_node) break;
+		xml_node_get_str(&handle, user_node, "handle", 0, 0);
+		xml_node_get_int(&uid, user_node, "uid", 0, 0);
+		if (!handle || !uid) break;
+		u = real_user_new(handle, uid);
+		xml_node_get_str(&u->pass, user_node, "password", 0, 0);
+		if (u->pass) u->pass = strdup(u->pass);
+		xml_node_get_str(&u->salt, user_node, "salt", 0, 0);
+		if (u->salt) u->salt = strdup(u->salt);
+		for (j = 0; ; j++) {
+			xml_node_get_str(&ircmask, user_node, "ircmask", j, 0);
+			if (!ircmask) break;
+			u->ircmasks = realloc(u->ircmasks, sizeof(char *) * (u->nircmasks+1));
+			u->ircmasks[u->nircmasks] = strdup(ircmask);
+			u->nircmasks++;
+			ircmask_list_add(&ircmask_list, ircmask, u);
+		}
+		for (j = 0; ; j++) {
+			setting_node = xml_node_lookup(user_node, "setting", j, 0);
+			if (!setting_node) break;
+			u->settings = realloc(u->settings, sizeof(*u->settings) * (j+1));
+			u->nsettings++;
+			setting = u->settings+j;
+			xml_node_get_int(&setting->flags, setting_node, "flags", 0, 0);
+			xml_node_get_int(&setting->udef_flags, setting_node, "udef_flags", 0, 0);
+			setting->nextended = 0;
+			setting->extended = NULL;
+			xml_node_get_str(&chan, setting_node, "chan", 0, 0);
+
+			if (chan) setting->chan = strdup(chan);
+			else if (j) continue;
+			else setting->chan = NULL;
+
+			for (k = 0; ; k++) {
+				xml_node_get_str(&name, setting_node, "extended", k, "name", 0, 0);
+				xml_node_get_str(&value, setting_node, "extended", k, "value", 0, 0);
+				if (!name || !value) break;
+				setting->extended = realloc(setting->extended, sizeof(*setting->extended) * (k+1));
+				setting->nextended++;
+				setting->extended[k].name = strdup(name);
+				setting->extended[k].value = strdup(value);
+			}
+		}
+		if (j < 0) {
+			u->settings = calloc(1, sizeof(u->settings));
+			u->nsettings = 1;
+		}
+	}
+	xml_node_destroy(&root);
+	return(0);
+}
+
+static int save_walker(const void *key, void *dataptr, void *param)
+{
+	xml_node_t *root = param;
+	user_t *u = *(void **)dataptr;
+	user_setting_t *setting;
+	xml_node_t *user_node;
+	int i, j;
+
+	user_node = xml_node_new();
+	user_node->name = strdup("user");
+	xml_node_set_str(u->handle, user_node, "handle", 0, 0);
+	xml_node_set_int(u->uid, user_node, "uid", 0, 0);
+	if (u->pass) xml_node_set_str(u->pass, user_node, "password", 0, 0);
+	if (u->salt) xml_node_set_str(u->salt, user_node, "salt", 0, 0);
+	for (i = 0; i < u->nircmasks; i++) {
+		xml_node_set_str(u->ircmasks[i], user_node, "ircmask", i, 0);
+	}
+	for (i = 0; i < u->nsettings; i++) {
+		setting = u->settings+i;
+		if (setting->chan) xml_node_set_str(setting->chan, user_node, "setting", i, "chan", 0, 0);
+		xml_node_set_int(u->settings[i].flags, user_node, "setting", i, "flags", 0, 0);
+		xml_node_set_int(u->settings[i].udef_flags, user_node, "setting", i, "udef_flags", 0, 0);
+		for (j = 0; j < setting->nextended; j++) {
+			xml_node_set_str(setting->extended[j].name, user_node, "setting", i, "extended", j, "name", 0, 0);
+			xml_node_set_str(setting->extended[j].value, user_node, "setting", i, "extended", j, "value", 0, 0);
+		}
+	}
+	xml_node_add(root, user_node);
+	return(0);
+}
+
+int user_save(const char *fname)
+{
+	xml_node_t root;
+	FILE *fp;
+
+	memset(&root, 0, sizeof(root));
+	xml_node_set_int(g_uid, &root, "next_uid", 0, 0);
+	xml_node_set_int(uid_wraparound, &root, "uid_wraparound", 0, 0);
+	hash_table_walk(uid_ht, save_walker, &root);
+	if (!fname) fname = "users.xml";
+	fp = fopen(fname, "w");
+	if (!fp) return(-1);
+	xml_write_node(fp, &root, 0);
+	fclose(fp);
+	xml_node_destroy(&root);
+	return(0);
+}
+
+static int user_get_uid()
+{
+	user_t *u;
+	int uid;
+
+	/* If we've wrapped around on uids, we need to search for a free one. */
+	if (uid_wraparound) {
+		while (!hash_table_find(uid_ht, (void *)g_uid, &u)) g_uid++;
+	}
+	else {
+		if (!g_uid) uid_wraparound++;
+	}
+	uid = g_uid;
+	g_uid++;
+	return(uid);
+}
+
+static user_t *real_user_new(const char *handle, int uid)
+{
+	user_t *u;
+
+	/* Make sure the handle is unique. */
+	u = user_lookup_by_handle(handle);
+	if (u) return(NULL);
+
+	u = calloc(1, sizeof(*u));
+	u->handle = strdup(handle);
+	if (!uid) uid = user_get_uid();
+	u->uid = uid;
+
+	hash_table_insert(handle_ht, u->handle, u);
+	hash_table_insert(uid_ht, (void *)u->uid, u);
+	return(u);
+}
+
+user_t *user_new(const char *handle)
+{
+	int uid;
+
+	uid = user_get_uid();
+	return real_user_new(handle, uid);
+}
+
+int user_delete(user_t *u)
+{
+	int i, j;
+	user_setting_t *setting;
+
+	hash_table_delete(handle_ht, u->handle);
+	hash_table_delete(uid_ht, (void *)u->uid);
+
+	/* Get rid of the ircmasks. */
+	cache_user_del(u, "*");
+	for (i = 0; i < u->nircmasks; i++) {
+		ircmask_list_del(&ircmask_list, u->ircmasks[i], u);
+		free(u->ircmasks[i]);
+	}
+	if (u->ircmasks) free(u->ircmasks);
+	u->ircmasks = NULL;
+	u->nircmasks = 0;
+
+	/* The password. */
+	if (u->pass) free(u->pass);
+	if (u->salt) free(u->salt);
+	u->pass = NULL;
+	u->salt = NULL;
+
+	/* And all of the settings. */
+	for (i = 0; i < u->nsettings; i++) {
+		setting = u->settings+i;
+		for (j = 0; j < setting->nextended; j++) {
+			if (setting->extended[j].name) free(setting->extended[j].name);
+			if (setting->extended[j].value) free(setting->extended[j].value);
+		}
+		if (setting->extended) free(setting->extended);
+	}
+	if (u->settings) free(u->settings);
+	u->settings = NULL;
+	u->nsettings = 0;
+
+	return(0);
+}
+
+user_t *user_lookup_by_handle(const char *handle)
+{
+	user_t *u = NULL;
+
+	hash_table_find(handle_ht, handle, &u);
+	return(u);
+}
+
+user_t *user_lookup_by_uid(int uid)
+{
+	user_t *u = NULL;
+
+	hash_table_find(uid_ht, (void *)uid, &u);
+	return(u);
+}
+
+user_t *user_lookup_by_irchost_nocache(const char *irchost)
+{
+	user_t *u;
+
+	/* Check the ircmask cache. */
+	if (!hash_table_find(irchost_cache_ht, irchost, &u)) return(u);
+
+	/* Look for a match in the ircmask list. We don't cache the result. */
+	ircmask_list_find(&ircmask_list, irchost, &u);
+	return(u);
+}
+
+user_t *user_lookup_by_irchost(const char *irchost)
+{
+	user_t *u;
+
+	/* Check the ircmask cache. */
+	if (!hash_table_find(irchost_cache_ht, irchost, &u)) return(u);
+
+	/* Nope, find it in the ircmask list. */
+	ircmask_list_find(&ircmask_list, irchost, &u);
+
+	/* Cache it, even if it's null. */
+	hash_table_insert(irchost_cache_ht, irchost, u);
+	return(u);
+}
+
+static int cache_check_add(const void *key, void *dataptr, void *client_data)
+{
+	const char *irchost = key;
+	user_t *u = *(user_t **)dataptr;
+	walker_info_t *info = client_data;
+	int i, strength, max_strength;
+
+	/* Get the strength of the current match. */
+	max_strength = 0;
+	if (u) {
+		for (i = 0; i < u->nircmasks; i++) {
+			strength = wild_match(u->ircmasks[i], irchost);
+			if (strength > max_strength) max_strength = strength;
+		}
+	}
+
+	/* And now the strength of the the new mask. */
+	strength = wild_match(info->ircmask, irchost);
+	if (strength > max_strength) {
+		/* Ok, replace it. */
+		*(user_t **)dataptr = info->u;
+	}
+	return(0);
+}
+
+static int cache_check_del(const void *key, void *dataptr, void *client_data)
+{
+	const char *irchost = key;
+	user_t *u = *(user_t **)dataptr;
+	walker_info_t *info = client_data;
+
+	if (u == info->u && wild_match(info->ircmask, irchost)) {
+		info->entries = realloc(info->entries, sizeof(char *) * (info->nentries+1));
+		info->entries[info->nentries] = irchost;
+		info->nentries++;
+	}
+	return(0);
+}
+
+static int cache_user_del(user_t *u, const char *ircmask)
+{
+	walker_info_t info;
+	int i;
+
+	/* Check irchost_cache_ht for changes in the users. */
+	info.u = u;
+	info.ircmask = ircmask;
+	info.entries = NULL;
+	info.nentries = 0;
+	hash_table_walk(irchost_cache_ht, cache_check_del, &info);
+	for (i = 0; i < info.nentries; i++) {
+		hash_table_delete(irchost_cache_ht, info.entries[i]);
+	}
+	if (info.entries) free(info.entries);
+
+	/* And remove it from the ircmask_list. */
+	ircmask_list_del(&ircmask_list, ircmask, u);
+	return(0);
+}
+
+int user_add_ircmask(user_t *u, const char *ircmask)
+{
+	walker_info_t info;
+
+	/* Add the ircmask to the user entry. */
+	u->ircmasks = (char **)realloc(u->ircmasks, sizeof(char *) * (u->nircmasks+1));
+	u->ircmasks[u->nircmasks] = strdup(ircmask);
+
+	/* Put it in the big list. */
+	ircmask_list_add(&ircmask_list, ircmask, u);
+
+	/* Check irchost_cache_ht for changes in the users. */
+	info.u = u;
+	info.ircmask = ircmask;
+	hash_table_walk(irchost_cache_ht, cache_check_add, &info);
+	return(0);
+}
+
+int user_del_ircmask(user_t *u, const char *ircmask)
+{
+	int i;
+
+	/* Find the ircmask in the user entry. */
+	for (i = 0; i < u->nircmasks; i++) {
+		if (!strcasecmp(u->ircmasks[i], ircmask)) break;
+	}
+	if (i == u->nircmasks) return(-1);
+
+	/* Get rid of it. */
+	memmove(u->ircmasks+i, u->ircmasks+i+1, sizeof(char *) * (u->nircmasks - i - 1));
+
+	/* Delete matching entries of this user in the host cache. */
+	cache_user_del(u, ircmask);
+
+	return(0);
+}
+
+int user_get_flags(user_t *u, const char *chan, flags_t *flags)
+{
+	int i;
+	flags->global = u->settings[0].flags;
+	if (chan) for (i = 1; i < u->nsettings; i++) {
+		if (!strcasecmp(chan, u->settings[i].chan)) {
+			flags->channel = u->settings[i].flags;
+			break;
+		}
+	}
+	return(0);
+}
+
+static int find_setting(user_t *u, const char *chan, const char *name, int *row, int *col)
+{
+	user_setting_t *setting;
+	int i = 0;
+
+	*row = -1;
+	*col = -1;
+	if (chan) for (i = 1; i < u->nsettings; i++) {
+		if (!strcasecmp(chan, u->settings[i].chan)) break;
+	}
+	if (i >= u->nsettings) return(-1);
+	*row = i;
+	setting = u->settings+i;
+	for (i = 0; i < setting->nextended; i++) {
+		if (!strcasecmp(name, setting->extended[i].name)) {
+			*col = i;
+			return(i);
+		}
+	}
+	return(-1);
+}
+
+int user_get_setting(user_t *u, const char *chan, const char *setting, char **valueptr)
+{
+	int i, j;
+
+	if (find_setting(u, chan, setting, &i, &j) < 0) {
+		*valueptr = NULL;
+		return(-1);
+	}
+	*valueptr = u->settings[i].extended[j].value;
+	return(0);
+}
+
+int user_set_setting(user_t *u, const char *chan, const char *setting, const char *newvalue)
+{
+	int i, j;
+	char **value;
+	user_setting_t *setptr;
+
+	if (find_setting(u, chan, setting, &i, &j) < 0) {
+		/* See if we need to add the channel. */
+		if (i < 0) {
+			u->settings = realloc(u->settings, sizeof(*u->settings) * (u->nsettings+1));
+			i = u->nsettings;
+			u->nsettings++;
+			memset(u->settings+i, 0, sizeof(*u->settings));
+			u->settings[i].chan = strdup(chan);
+		}
+		setptr = u->settings+i;
+
+		/* And then the setting. */
+		if (j < 0) {
+			setptr->extended = realloc(setptr->extended, sizeof(*setptr->extended) * (setptr->nextended+1));
+			j = setptr->nextended;
+			setptr->nextended++;
+			setptr->extended[j].name = strdup(setting);
+			setptr->extended[j].value = NULL;
+		}
+	}
+	value = &(u->settings[i].extended[j].value);
+	if (*value) free(*value);
+	*value = strdup(newvalue);
+	return(0);
+}
Index: eggdrop1.7/lib/eggdrop/users.h
diff -u /dev/null eggdrop1.7/lib/eggdrop/users.h:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/users.h	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,54 @@
+#ifndef _USERS_H_
+#define _USERS_H_
+
+#define USER_HASH_SIZE	50
+#define HOST_HASH_SIZE	50
+
+typedef struct {
+	char *name;
+	char *value;
+} extended_setting_t;
+
+typedef struct {
+	/* The channel these settings apply to, or NULL for global. */
+	char *chan;
+
+	/* Builtin flags and user defined flags. */
+	int flags, udef_flags;
+
+	/* Extended settings done by modules/scripts. */
+	extended_setting_t *extended;
+	int nextended;
+} user_setting_t;
+
+typedef struct user {
+	/* Each user has a unique handle and a permanent uid. */
+	char *handle;
+	int uid;
+
+	/* The encrypted password and its salt (initialization vector). */
+	char *pass, *salt;
+
+	/* Masks that the user is recognized by on irc (nick!user at host). */
+	char **ircmasks;
+	int nircmasks;
+
+	/* Settings for this user. */
+	user_setting_t *settings;
+	int nsettings;
+} user_t;
+
+int user_init();
+int user_load(const char *fname);
+user_t *user_new(const char *handle);
+int user_delete(user_t *u);
+user_t *user_lookup_by_handle(const char *handle);
+user_t *user_lookup_by_uid(int uid);
+user_t *user_lookup_by_irchost_nocache(const char *irchost);
+user_t *user_lookup_by_irchost(const char *irchost);
+int user_add_ircmask(user_t *u, const char *ircmask);
+int user_del_ircmask(user_t *u, const char *ircmask);
+int user_get_setting(user_t *u, const char *chan, const char *setting, char **valueptr);
+int user_set_setting(user_t *u, const char *chan, const char *setting, const char *newvalue);
+
+#endif
Index: eggdrop1.7/lib/eggdrop/xml.c
diff -u /dev/null eggdrop1.7/lib/eggdrop/xml.c:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/xml.c	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,190 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xml.h"
+
+#ifdef AMP_CHARS
+xml_amp_conversion_t builtin_conversions[] = {
+	{"quot", '"'},
+	{"lt", '<'},
+	{"gt", '>'},
+	{"amp", '&'},
+	{"apos", '\''},
+	{"nbsp", ' '},
+	{0}
+};
+#endif
+
+/* Get a new, blank node. */
+xml_node_t *xml_node_new()
+{
+	xml_node_t *node;
+
+	node = (xml_node_t *)calloc(sizeof(*node), 1);
+	return(node);
+}
+
+/* Delete a node and its children. */
+int xml_node_destroy(xml_node_t *node)
+{
+	int i;
+
+	if (node->text) free(node->text);
+	for (i = 0; i < node->nattributes; i++) {
+		if (node->attributes[i].name) free(node->attributes[i].name);
+		if (node->attributes[i].value) free(node->attributes[i].value);
+	}
+	if (node->attributes) free(node->attributes);
+	for (i = 0; i < node->nchildren; i++) {
+		xml_node_destroy(node->children+i);
+	}
+	if (node->children) free(node->children);
+	return(0);
+}
+
+/* Append a node to another node's children. */
+xml_node_t *xml_node_add(xml_node_t *parent, xml_node_t *child)
+{
+	parent->children = (xml_node_t *)realloc(parent->children, sizeof(*child) * (parent->nchildren+1));
+	memcpy(parent->children+parent->nchildren, child, sizeof(*child));
+	parent->nchildren++;
+	return(parent->children+(parent->nchildren-1));
+}
+
+xml_node_t *xml_node_vlookup(xml_node_t *root, va_list args, int create)
+{
+	char *path;
+	int i, index;
+	xml_node_t *child, newchild;
+
+	for (; root;) {
+		path = va_arg(args, char *);
+		if (!path) break;
+		index = va_arg(args, int);
+		child = NULL;
+		for (i = 0; i < root->nchildren; i++) {
+			if (!strcasecmp(root->children[i].name, path)) {
+				if (index-- > 0) continue;
+				child = root->children+i;
+				break;
+			}
+		}
+		if (!child && create) {
+			memset(&newchild, 0, sizeof(newchild));
+			newchild.name = strdup(path);
+			child = xml_node_add(root, &newchild);
+		}
+		root = child;
+	}
+	return(root);
+}
+
+xml_node_t *xml_node_lookup(xml_node_t *root, ...)
+{
+	va_list args;
+	xml_node_t *node;
+
+	va_start(args, root);
+	node = xml_node_vlookup(root, args, 0);
+	va_end(args);
+	return(node);
+}
+
+/* Just add an attribute to the end of a node's attribute list. */
+xml_attribute_t *xml_attribute_add(xml_node_t *node, xml_attribute_t *attr)
+{
+	node->attributes = (xml_attribute_t *)realloc(node->attributes, sizeof(*attr) * (node->nattributes+1));
+	memcpy(node->attributes+node->nattributes, attr, sizeof(*attr));
+	node->nattributes++;
+	return(node->attributes+(node->nattributes-1));
+}
+
+int xml_node_get_int(int *value, xml_node_t *node, ...)
+{
+	va_list args;
+
+	va_start(args, node);
+	node = xml_node_vlookup(node, args, 0);
+	va_end(args);
+	if (node && node->text) {
+		*value = atoi(node->text);
+		return(0);
+	}
+	*value = 0;
+	return(-1);
+}
+
+int xml_node_get_str(char **str, xml_node_t *node, ...)
+{
+	va_list args;
+
+	va_start(args, node);
+	node = xml_node_vlookup(node, args, 0);
+	va_end(args);
+	if (node && node->text) {
+		*str = node->text;
+		return(0);
+	}
+	*str = NULL;
+	return(-1);
+}
+
+int xml_node_set_int(int value, xml_node_t *node, ...)
+{
+	va_list args;
+	char str[100];
+
+	va_start(args, node);
+	node = xml_node_vlookup(node, args, 1);
+	va_end(args);
+	if (!node) return(-1);
+	if (node->text) free(node->text);
+	snprintf(str, sizeof(str), "%d", value);
+	str[sizeof(str)-1] = 0;
+	node->text = strdup(str);
+	node->len = strlen(str);
+	return(0);
+}
+
+int xml_node_set_str(char *str, xml_node_t *node, ...)
+{
+	va_list args;
+
+	va_start(args, node);
+	node = xml_node_vlookup(node, args, 1);
+	va_end(args);
+	if (!node) return(-1);
+	if (node->text) free(node->text);
+	node->text = strdup(str);
+	node->len = strlen(str);
+	return(0);
+}
+
+xml_attribute_t *xml_attr_lookup(xml_node_t *node, const char *name)
+{
+	int i;
+
+	for (i = 0; i < node->nattributes; i++) {
+		if (!strcasecmp(node->attributes[i].name, name)) break;
+	}
+	if (i < node->nattributes) return(node->attributes+i);
+	return(NULL);
+}
+
+int xml_attr_get_int(xml_node_t *node, const char *name)
+{
+	xml_attribute_t *attr;
+
+	attr = xml_attr_lookup(node, name);
+	if (attr && attr->value) return atoi(attr->value);
+	return(0);
+}
+
+char *xml_attr_get_str(xml_node_t *node, const char *name)
+{
+	xml_attribute_t *attr;
+
+	attr = xml_attr_lookup(node, name);
+	if (attr && attr->value) return(attr->value);
+	return(NULL);
+}
Index: eggdrop1.7/lib/eggdrop/xml.h
diff -u /dev/null eggdrop1.7/lib/eggdrop/xml.h:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/xml.h	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,61 @@
+#ifndef _XML_H_
+#define _XML_H_
+
+#include <stdarg.h>
+
+/* Comment out to disable &char; conversions. */
+#define AMP_CHARS
+
+typedef struct {
+	char *name;
+	char *value;
+	int len;
+} xml_attribute_t;
+
+typedef struct xml_node_b {
+	char *name;
+
+	char *text;
+	int len;
+	int whitespace;
+
+	xml_attribute_t *attributes;
+	int nattributes;
+
+	struct xml_node_b *children;
+	int nchildren;
+
+	int type; /* 0 = normal, 1 = decl (<?xml ... ?>), 2 = CDATA,
+			3 = comment, 4 = .. nothing yet. */
+
+	char *data;
+} xml_node_t;
+
+//#ifdef AMP_CHARS
+typedef struct {
+	char *key;
+	char value;
+} xml_amp_conversion_t;
+//#endif
+
+xml_node_t *xml_node_new();
+int xml_node_destroy(xml_node_t *node);
+xml_node_t *xml_node_add(xml_node_t *parent, xml_node_t *child);
+xml_node_t *xml_node_vlookup(xml_node_t *root, va_list args, int create);
+xml_node_t *xml_node_lookup(xml_node_t *root, ...);
+int xml_node_get_int(int *value, xml_node_t *node, ...);
+int xml_node_get_str(char **str, xml_node_t *node, ...);
+int xml_node_set_int(int value, xml_node_t *node, ...);
+int xml_node_set_str(char *str, xml_node_t *node, ...);
+xml_attribute_t *xml_attribute_add(xml_node_t *node, xml_attribute_t *attr);
+xml_attribute_t *xml_attribute_get(xml_node_t *node, char *name);
+int xml_attribute_set(xml_node_t *node, xml_attribute_t *attr);
+int xml_attr_get_int(xml_node_t *node, const char *name);
+char *xml_attr_get_str(xml_node_t *node, const char *name);
+
+int xml_write_node(FILE *fp, xml_node_t *node, int indent);
+int xml_read_node(xml_node_t *parent, char **data);
+int xml_read(xml_node_t *root, const char *fname);
+
+/* _XML_H_ */
+#endif
Index: eggdrop1.7/lib/eggdrop/xmlread.c
diff -u /dev/null eggdrop1.7/lib/eggdrop/xmlread.c:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/xmlread.c	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,331 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xml.h"
+
+#ifdef AMP_CHARS
+extern xml_amp_conversion_t builtin_conversions[];
+#endif
+
+/* These are pretty much in 'most common' order. */
+static const char *spaces = " \t\n\r\v";
+static const char *name_terminators = "= \t\n\r\v?/>";
+
+/*
+ * Skip over whitespace.
+ * Return number of bytes we used up.
+ */
+
+int skip_whitespace(char **data)
+{
+	int n;
+
+	n = strspn(*data, spaces);
+	*data += n;
+	return(n);
+}
+
+/*
+ * Read in data up to a space, =, >, or ?
+ */
+static void read_name(char **data, char **name)
+{
+	int n;
+
+	n = strcspn(*data, name_terminators);
+	if (!n) {
+		*name = NULL;
+		return;
+	}
+	*name = (char *)malloc(n+1);
+	memcpy(*name, *data, n);
+	(*name)[n] = 0;
+
+	*data += n;
+}
+
+/*
+ * Read in an attribute value.
+ */
+static void read_value(char **data, char **value)
+{
+	const char *terminator;
+	int n;
+
+	/* It's supposed to startwith ' or ", but we'll take no ' or " to mean
+		it's a one word value. */
+
+	if (**data == '\'') terminator = "'";
+	else if (**data == '"') terminator = "\"";
+	else {
+		terminator = name_terminators;
+		(*data)--;
+	}
+
+	/* Skip past first ' or " so we can find the ending one. */
+	(*data)++;
+
+	n = strcspn(*data, terminator);
+	*value = (char *)malloc(n+1);
+	memcpy(*value, *data, n);
+	(*value)[n] = 0;
+
+	/* Skip past closing ' or ". */
+	if (terminator != name_terminators) n++;
+	*data += n;
+}
+
+static void read_text(xml_node_t *node, char **data)
+{
+	char *end;
+	char *text;
+	int len;
+
+	/* Find end-point. */
+	end = strchr(*data, '<');
+	if (!end) end = *data + strlen(*data);
+
+	/* Get length of text. */
+	len = end - *data;
+
+	/* Add it to the node's current text value. */
+	text = (char *)realloc(node->text, len + node->len + 1);
+	memcpy(text + node->len, *data, len);
+	node->text = text;
+	node->len += len;
+	text[node->len] = 0;
+
+	/* Update data to point to < (or \0). */
+	*data = end;
+}
+
+/* Decoded result is guaranteed <= original. */
+int xml_decode_text(char *text, int inlen)
+{
+	char *end, *result, *orig;
+	int n;
+#ifdef AMP_CHARS
+	/* Some variables for &char; conversion. */
+	char *amp, *colon, *next;
+	int i;
+#endif
+
+	/* text = input, result = output */
+	orig = result = text;
+	end = text + inlen;
+
+	/* Can't start with a space. */
+	skip_whitespace(&text);
+
+	while (text < end) {
+		/* Get count of non-spaces. */
+		n = strcspn(text, spaces);
+
+#ifdef AMP_CHARS
+		/* If we're supporting &char; notation, here's where it
+			happens. If we can't find the &char; in the conversions
+			table, then we leave the '&' for it by default. The
+			conversion table is defined in xml.c. */
+
+		amp = text;
+		next = text+n;
+		while (n > 0 && (amp = memchr(amp, '&', n))) {
+			colon = memchr(amp, ';', n);
+			amp++;
+			if (!colon) break;
+			*colon++ = 0;
+			for (i = 0; builtin_conversions[i].key; i++) {
+				if (!strcasecmp(amp, builtin_conversions[i].key)) {
+					*(amp-1) = builtin_conversions[i].value;
+					break;
+				}
+			}
+			n -= (colon-amp);
+
+			/* Shift bytes down, including trailing null. */
+			memmove(amp, colon, n);
+		}
+		memcpy(result, text, n);
+		text = next;
+#else
+		/* If we don't support conversions, just copy the text. */
+		memcpy(result, text, n);
+		text += n;
+#endif
+		result += n;
+
+		/* Skip over whitespace. */
+		n = skip_whitespace(&text);
+
+		/* If there was any, and it's not the end, replace it all with
+			a single space. */
+		if (n && text < end) {
+			*result++ = ' ';
+		}
+	}
+
+	*result = 0;
+	return(result - orig);
+}
+
+/* Parse a string of attributes. */
+static void read_attributes(xml_node_t *node, char **data)
+{
+	xml_attribute_t attr;
+
+	for (;;) {
+		/* Skip over any leading whitespace. */
+		skip_whitespace(data);
+
+		/* Read in the attribute name. */
+		read_name(data, &attr.name);
+		if (!attr.name) return;
+
+		skip_whitespace(data);
+
+		/* Check for '=' sign. */
+		if (**data != '=') {
+			free(attr.name);
+			return;
+		}
+		(*data)++;
+
+		skip_whitespace(data);
+
+		read_value(data, &attr.value);
+
+		xml_attribute_add(node, &attr);
+		//printf("name: '%s'\nvalue: '%s'\n", attr.name, attr.value);
+	}
+}
+
+/*
+ * Read an entire node and all its children.
+ * 0 - read the node successfully
+ * 1 - reached end of input
+ * 2 - reached end of node
+ */
+int xml_read_node(xml_node_t *parent, char **data)
+{
+	xml_node_t node, *ptr;
+	int n;
+	char *end, *name;
+
+	/* Read in any excess data and save it with the parent. */
+	read_text(parent, data);
+
+	/* If read_text() doesn't make us point to an <, we're at the end. */
+	if (**data != '<') return(1);
+
+	/* Skip over the < and any whitespace. */
+	(*data)++;
+	skip_whitespace(data);
+
+	/* If this is a closing tag like </blah> then we're done. */
+	if (**data == '/') {
+		return(2);
+	}
+
+	/* Initialize the node. */
+	memset(&node, 0, sizeof(node));
+
+	/* If this is the start of the xml declaration, <?xml version=...?> then
+		we read it like a normal tag, but set the 'decl' member. */
+	if (**data == '?') {
+		(*data)++;
+		node.type = 1;
+	}
+	else if (**data == '!') {
+		(*data)++;
+		if (**data == '-') {
+			/* Comment <!-- ... --> */
+			int len;
+
+			*data += 2; /* Skip past '--' part. */
+			end = strstr(*data, "-->");
+			len = end - *data;
+			node.text = (char *)malloc(len+1);
+			memcpy(node.text, *data, len);
+			node.text[len] = 0;
+			node.len = len;
+			node.type = 2;
+			xml_node_add(parent, &node);
+			*data = end+3;
+			return(0); /* Skip past '-->' part. */
+		}
+		printf("Unsupported <! type\n");
+	}
+
+	/* Read in the tag name. */
+	read_name(data, &node.name);
+
+	/* Now that we have a name, go ahead and add the node to the tree. */
+	ptr = xml_node_add(parent, &node);
+
+	/* Read in the attributes. */
+	read_attributes(ptr, data);
+
+	/* Now we should be pointing at ?, /, or >. */
+
+	/* '?' and '/' are empty tags with no children or text value. */
+	if (**data == '/' || **data == '?') {
+		end = strchr(*data, '>');
+		if (!end) return(1); /* End of input. */
+		*data = end + 1;
+		return(0);
+	}
+
+	/* Skip over closing '>' after attributes. */
+	(*data)++;
+
+	/* Parse children and text value. */
+	do {
+		n = xml_read_node(ptr, data);
+	} while (n == 0);
+
+	/* Ok, the recursive xml_read_node() has read in all the text value for
+		this node, so decode it now. */
+	ptr->len = xml_decode_text(ptr->text, ptr->len);
+
+	/* Ok, now 1 means we reached end-of-input. */
+	/* 2 means we reached end-of-node. */
+	if (n == 2) {
+		/* We don't validate to make sure the start tag and end tag
+			matches. */
+		end = strchr(*data, '>');
+		if (!end) return(1);
+
+		*data = end+1;
+		return(0);
+		/* End of our node. */
+	}
+
+	/* Otherwise, end-of-input. */
+	return(1);
+}
+
+int xml_read(xml_node_t *root, const char *fname)
+{
+	FILE *fp;
+	int size;
+	char *data, *dataptr;
+
+	fp = fopen(fname, "r");
+	if (!fp) return(NULL);
+
+	fseek(fp, 0l, SEEK_END);
+	size = ftell(fp);
+	fseek(fp, 0l, SEEK_SET);
+
+	data = malloc(size+1);
+	fread(data, size, 1, fp);
+	data[size] = 0;
+	fclose(fp);
+
+	dataptr = data;
+	while (!xml_read_node(root, &dataptr)) {
+		; /* empty */
+	}
+	free(data);
+	return(root);
+}
Index: eggdrop1.7/lib/eggdrop/xmlwrite.c
diff -u /dev/null eggdrop1.7/lib/eggdrop/xmlwrite.c:1.1
--- /dev/null	Mon Oct  7 17:34:04 2002
+++ eggdrop1.7/lib/eggdrop/xmlwrite.c	Mon Oct  7 17:33:54 2002
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include "xml.h"
+
+int xml_write_node(FILE *fp, xml_node_t *node, int indent)
+{
+	char *tabs;
+	int i;
+
+	tabs = (char *)malloc(indent+1);
+	memset(tabs, '\t', indent);
+	tabs[indent] = 0;
+	if (node->name) {
+		if (node->type == 0) fprintf(fp, "%s<%s", tabs, node->name);
+		else fprintf(fp, "%s<?%s", tabs, node->name);
+	}
+	else if (node->type == 2) {
+		/* comment like <!-- ... --> */
+		fprintf(fp, "%s<!--%s-->\n", tabs, node->text);
+		return(0);
+	}
+
+	for (i = 0; i < node->nattributes; i++) {
+		fprintf(fp, " %s='%s'", node->attributes[i].name, node->attributes[i].value);
+	}
+	if (node->len > 50 || node->nchildren) {
+		if (node->name) fprintf(fp, ">\n");
+	}
+	else if (node->len > 0) {
+		/* If it's just small text and no children... */
+		fprintf(fp, ">%s</%s>\n", node->text, node->name);
+		return(0);
+	}
+	else {
+		free(tabs);
+		if (node->type == 0) fprintf(fp, " />\n");
+		else fprintf(fp, " ?>\n");
+		return(0);
+	}
+
+	if (node->len) {
+		fprintf(fp, "%s\t%s\n", tabs, node->text);
+	}
+	if (node->name) indent++;
+	for (i = 0; i < node->nchildren; i++) {
+		xml_write_node(fp, &node->children[i], indent);
+	}
+	if (node->name) {
+		fprintf(fp, "%s</%s>\n", tabs, node->name);
+	}
+	return(0);
+}
----------------------- End of diff -----------------------



More information about the Changes mailing list