[merge] trunk
authorJelmer Vernooij <jelmer@samba.org>
Fri, 16 Jun 2006 14:08:07 +0000 (16:08 +0200)
committerJelmer Vernooij <jelmer@samba.org>
Fri, 16 Jun 2006 14:08:07 +0000 (16:08 +0200)
72 files changed:
.bzrignore
BUGS [new file with mode: 0644]
ChangeLog [deleted file]
Makefile
Makefile.settings.in
NEWS [new file with mode: 0644]
README
TODO [deleted file]
UPGRADING
config.default
config.example
config.network [new file with mode: 0644]
configure.ac
debian/docs
doc/Makefile
doc/ctrlproxy.1.xml
doc/ctrlproxy_config.5.xml [new file with mode: 0644]
doc/ctrlproxyrc.5.xml [deleted file]
doc/install.xml
doc/logging.xml
doc/syntax.xml [deleted file]
doc/user-guide.xml
mods/linestack_sqlite.c [new file with mode: 0644]
mods/listener.c
mods/log_custom.c
scripts/upgrade.py
src/admin.c [moved from admin.c with 97% similarity]
src/admin.h [moved from admin.h with 100% similarity]
src/cache.c [moved from cache.c with 89% similarity]
src/client.c [moved from client.c with 100% similarity]
src/client.h [moved from client.h with 100% similarity]
src/ctcp.c [moved from ctcp.c with 100% similarity]
src/ctcp.h [moved from ctcp.h with 100% similarity]
src/ctrlproxy.h [moved from ctrlproxy.h with 99% similarity]
src/gen_config.c [moved from gen_config.c with 100% similarity]
src/hooks.c [moved from hooks.c with 100% similarity]
src/hooks.h [moved from hooks.h with 100% similarity]
src/internals.h [moved from internals.h with 96% similarity]
src/irc.h [moved from irc.h with 100% similarity]
src/isupport.c [moved from isupport.c with 100% similarity]
src/line.c [moved from line.c with 100% similarity]
src/line.h [moved from line.h with 100% similarity]
src/linestack.c [moved from linestack.c with 97% similarity]
src/linestack.h [moved from linestack.h with 100% similarity]
src/linestack_file.c [moved from linestack_file.c with 97% similarity]
src/log.c [moved from log.c with 100% similarity]
src/main.c [moved from main.c with 80% similarity]
src/motd.c [moved from motd.c with 100% similarity]
src/network.c [moved from network.c with 97% similarity]
src/network.h [moved from network.h with 95% similarity]
src/nickserv.c [moved from nickserv.c with 97% similarity]
src/plugins.c [moved from plugins.c with 100% similarity]
src/plugins.h [moved from plugins.h with 100% similarity]
src/posix.c [moved from posix.c with 100% similarity]
src/redirect.c [moved from redirect.c with 100% similarity]
src/repl.c [moved from repl.c with 100% similarity]
src/repl.h [moved from repl.h with 100% similarity]
src/settings.c [moved from settings.c with 89% similarity]
src/settings.h [moved from settings.h with 100% similarity]
src/state.c [moved from state.c with 99% similarity]
src/state.h [moved from state.h with 97% similarity]
src/user.c [new file with mode: 0644]
src/util.c [moved from util.c with 100% similarity]
testsuite/Makefile [deleted file]
testsuite/test-cmp.c
testsuite/test-isupport.c
testsuite/test-parser.c
testsuite/test-ssl.c [deleted file]
testsuite/test-state.c
testsuite/test-util.c
testsuite/torture.c
testsuite/torture.h [deleted file]

index c0cdba17d91dbc320b7c5e5914db451ec133b2f5..a147de4507885e95859b3987041a30f701bc6fec 100644 (file)
@@ -53,3 +53,5 @@ doc/*.html
 debian/files
 debian/*.substvars
 doc/user-guide
+*.d
+testsuite/check
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..985cde9
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,4 @@
+- SSL support is broken
+- Some of the documentation is outdated
+
+see http://bugs.bitlbee.org/ctrlproxy/ for a full list of known bugs.
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644 (file)
index 0c8159b..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,201 +0,0 @@
- Otherwise indicated differently, all changes made by 
-       Jelmer Vernooij <jelmer@vernstok.nl>
-
-Ctrlproxy 3.0:
- * Removed dependency on popt
- * Move to bzr
- * Make linestack_file the default
- * Remove linestack_memory
- * Add log levels (-d switch)
- * NetBSD portability fixes (Adrian Portelli)
- * Properly support strict-rfc1459 comparisons
- * Show SVN revision in version for SVN checkouts
- * Add support for %B and %Y (Korbinian Rosenegger)
- * Use separate include files
- * Move <autosend> to a seperate module
- * No longer depend on specific order of USER and NICK commands
- * Use new CtrlProxy-specific logging system. Logs go 
- to ~/.ctrlproxy/log by default now when in daemon mode.
- * Fixed 100% CPU memory usage bug
- * Add inetd-style client support
- * Support CONNECT proxy command as used by irssi
- * Fix crash bug in log_custom (Korbinian Rosenegger)
- * admin module can now also work as a seperate ('virtual') network
- * get rid of complicated filter class system
- * nickserv is now "self-learning"
- * Reduce number of files generated by configure
- * Modules can now be compiled into ctrlproxy as well as being built as shared
- libraries
- * Fix large number of memory leaks
- * Use libxml less internally
- * Moved documentation to seperate repository (version-independant)
- * Add socks module
- * Add Mozilla NSS support
- * Enhance custom_log (Alexander Wild <wild@te-elektronik.com>)
- * Use ctrlproxyrc.default if ~/.ctrlproxyrc does not exist
- * Make reconnect_timeout changeable (Alexander Wild <wild@te-elektronik.com>)
- * Add scripts/ directory and framework
- * Add default ctrlproxyrc and install it
- * Add some tests to the testsuite
- * Extend linestack interface
- * Add test-filter utility
- * Fix bug in unloading modules
- * Fix dependency system for modules
- * Add testsuite
- * Make CONNECT command force a reconnect if one is waiting to happen
- * Restructure API for server management inside networks
- * Add DELNETWORK command
- * Introduce functions to use filter classes
- * Support different NICK sent by client in replication
- * Support non-blocking connects (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Add 'NEXTSERVER' command (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Unset autojoin on KICK (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Add ability to change NICK when away
- * Make repl_* work when NICK sent by client differs from current NICK
- * Build with -Wall
- * Add dircproxyrc-to-ctrlproxyrc script
- * Add TOS support (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Support mIRC and ksirc
- * Don't crash on non-existing <networks> or <plugins>
- * Add 'debug' module for easier debugging
- * Make sure modules are never loaded twice (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Support binding on a specific IP in socket module (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Add support for unloading crashing modules
- * Use correct string comparison when necessary
- * Support 'true' RFC1459 string comparison
- * Respect CASEMAPPING sent by server
- * Use NETWORK sent by server
- * Add autosave module (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Use PREFIX sent by server
- * Add get_prefix_by_mode()
- * Use CHANTYPES sent by server
- * Add way to get network features
- * Keep track of channels nick is on in network_nick structure
- * New, more advanced filter system supporting classes and priorities
- * Add help support in admin module (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Make name and hostmask attribute of nick on network shared (Daniel Poelzleithner <ctrlproxy@poelzi.org>)
- * Do the actual NICK after GHOST'ing another nick
- * Fix bug with setting PRIVATE too often
- * Fix issue with NickServ IDENTIFY'ing when connecting to server
- * Add cisg utility
- * Update items kept track of by stats by default
- * Put some modules in their own directory
- * Add linestack_file
- * Add repl_lastdisconnect.so
- * Add server_connect, server_disconnect and initialized hooks
- * Add gnutls support
- * Fix problems with incorrectly added colons
- * Add %e to log_custom (Korbinian Rosenegger)
- * And much, much more...
-
-Ctrlproxy 2.6:
- * Configuration utility (Kyoshiro)
- * Add bind to specific IP only support
- * Fix some bugs in parsing line structs
- * Fix MODE handling
- * Lots of memory leaks fixed
- * Added MOTD support
- * Added quickstart
- * Update example RC file
- * Add repl_simple module
- * Add DETACH command to admin module (Stephane Lacoin)
- * Fix for immediately IDENTIFY'ing when connecting to server
- * Add backwards compatibility module 'repl_memory'
- * Fix for better RFC compliance
- * Fixed bug when crashing on exit
- * Fixed daemon mode
- * Start FAQ
- * Several bugs in log_custom fixed (thanks, igla)
- * Fix build without libssl
- * Add support for building PDF using db2latex
- * Add attribute only_noclient
- * Split repl_memory into linestack_memory and repl_simple
- * Add linestack support
- * Add linestack_memory
- * Add repl_command
- * Add repl_highlight
- * Add repl_simple
- * Add support for adding new admin commands
- * Added function plugin_loaded()
- * Admin commands can now be executed by /CTRLPROXY
- * Executing admin commands by sending them to the nick 'ctrlproxy' is now
- optional
-
-Ctrlproxy 2.5:
- * Added configuration example for log_custom
- * Fixed log_custom (works correct and with multiple files now!)
- * Fix in personal nick MODE replication
- * Fix bug in ignore_first_nickchange
- * Add DIE command to admin module
- * Correctly handle MODE +o and +v
- * Correctly disconnect from server on exit
- * Only add mode if mode is either @ or + in replication
- * Sockets are now correctly closed when exiting
- * Fix double loading bug in stats module
- * Add ability to ignore first NICK command the user executes
- * Fix daemon mode
- * Print backtrace on crash
- * Fix parsing of MODE +b and -b
- * Add antiflood module
- * Add function to unregister transports
- * Add log_custom module that can be used for logging in a self-defined
- format.
- * Update plugin API to include data pointer that can be used by backend
- * Add fini_plugin() function to all modules (RELOADMODULE and UNLOADMODULE
-    should work correctly now)
- * Fix bug in strip.c when client disconnects before answer to query arrives
- * Don't send MODE if nothing set
- * Fix synchronisation bug in strip module
- * Support multiple arguments to KICK
- * Add nickserv module
- * Add support to log_irssi for TOPIC, QUIT, NICK and KICK
- * Fix support in log_irssi for MODE
- * Fix bug where xchat does not show first letter of last argument
- * Fix documentation build and installation system
- * Fix bug in reconnect after the connection to the server was lost
- * Only send PRIVMSG and NOTICE messages to fellow clients
- * Don't crash when nick can't be found in channel when setting MODE
-
-Ctrlproxy 2.4:
- * Better handling of comma-seperated arguments (e.g. multiple channels 
-   for JOIN or PART)
- * Support listening on IPv6
- * Support MODE
- * Handle own nick changes correctly
- * Add logging support to admin module
- * Implement admin module
- * Handle disconnected clients more cleanly
- * Listening on multiple ports is now possible
- * Client SSL support added
- * Add first docs
- * Do smart rebinding to ports to prevent 'address in use' errors
-   (SO_REUSEADDR)
- * Server SSL support added
- * Don't freak out on errors when JOINing or PARTing channels
- * Send 'real' list of supported MODE's
- * Cleanups in headers and functions
- * Support channel keys
- * Support environment variable MODULESDIR
-
-Ctrlproxy 2.3:
- * Use general glib functions to retrieve homedir, username and fullname
-   instead of getenv() and getpwuid()
- * Improve Makefile
- * Throw out debian directory (maintained downstream now)
- * Don't echo nick when changing it to exactly the same nick
- * Several segfaults have been fixed
- * Direct interfacing with inetd-style IRC servers
- * Added stats module, which logs the frequency of certain patterns in a tdb 
-   database
- * Added printstats utility that prints contents of tdb database
- * Added strip module that prevents responses to a query from one client 
-   to go to all clients
- * New interface for 'transports', and migrated transport-specific stuff 
-   (TCP/IP) to seperate module
- * Added DTD file for ctrlproxyrc file
- * Added example stats PHP script
- * Support saving configuration when receiving USR1 signal.
- * Added autoconf support
-
-Ctrlproxy previously didn't have a ChangeLog file, check CVS history 
-for the changes.
index 6342096cc65f12ab6a41c7395266e62f3d76cf2d..2ebab48f1873b072c62cdd54e8eff07c0130f61b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -17,15 +17,40 @@ CFLAGS+=-ansi -Wall -DMODULESDIR=\"$(modulesdir)\" -DSTRICT_MEMORY_ALLOCS=
 
 all: $(BINS) $(MODS_SHARED_FILES) 
 
-objs = network.o posix.o client.o cache.o line.o state.o util.o hooks.o linestack.o plugins.o settings.o isupport.o log.o redirect.o gen_config.o repl.o linestack_file.o ctcp.o motd.o nickserv.o admin.o
-
-ctrlproxy$(EXEEXT): main.o $(objs)
+objs = src/network.o \
+          src/posix.o \
+          src/client.o \
+          src/cache.o \
+          src/line.o \
+          src/state.o \
+          src/util.o \
+          src/hooks.o \
+          src/linestack.o \
+          src/plugins.o \
+          src/settings.o \
+          src/isupport.o \
+          src/log.o \
+          src/redirect.o \
+          src/gen_config.o \
+          src/repl.o \
+          src/linestack_file.o \
+          src/ctcp.o \
+          src/motd.o \
+          src/nickserv.o \
+          src/admin.o \
+          src/user.o
+dep_files = $(patsubst %.o, %.d, $(objs)) $(patsubst %.c, %.d, $(wildcard mods/*.c))
+
+ctrlproxy$(EXEEXT): src/main.o $(objs)
        @echo Linking $@
        @$(CC) $(LIBS) -rdynamic -o $@ $^
 
 .c.o:
        @echo Compiling $<
-       @$(CC) -I. $(CFLAGS) $(GCOV_CFLAGS) -c $< -o $@
+       @$(CC) -I. -Isrc $(CFLAGS) $(GCOV_CFLAGS) -c $< -o $@
+
+%.d: %.c
+       @$(CC) -I. -Isrc -M -MG -MP -MT $(<:.c=.o) $(CFLAGS) $< -o $@
 
 configure: autogen.sh configure.ac acinclude.m4 $(wildcard mods/*/*.m4)
        ./$<
@@ -44,12 +69,13 @@ install-dirs:
 
 install-bin:
        $(INSTALL) ctrlproxy$(EXEEXT) $(DESTDIR)$(bindir)
+       $(INSTALL) scripts/upgrade.py $(DESTDIR)$(bindir)/ctrlproxy-upgrade 
 
 install-doc: doc
        $(INSTALL) -m 644 ctrlproxy.h $(DESTDIR)$(destincludedir)
        $(INSTALL) AUTHORS $(DESTDIR)$(docdir)
        $(INSTALL) COPYING $(DESTDIR)$(docdir)
-       $(INSTALL) TODO $(DESTDIR)$(docdir)
+       $(INSTALL) BUGS $(DESTDIR)$(docdir)
        $(INSTALL) UPGRADING $(DESTDIR)$(docdir)
        $(MAKE) -C doc install PACKAGE_VERSION=$(PACKAGE_VERSION)
 
@@ -70,14 +96,19 @@ install-pkgconfig:
 gcov:
        $(GCOV) -po . *.c 
 
-mods/lib%.so: mods/%.o
+mods/lib%.$(SHLIBEXT): mods/%.o
        @echo Linking $@
        @$(CC) $(LDFLAGS) -fPIC -shared -o $@ $^
 
 clean::
-       rm -f $(MODS_SHARED_FILES)
-       rm -f *.$(OBJEXT) ctrlproxy$(EXEEXT) printstats *~
-       rm -f *.gcov *.gcno *.gcda mods/*.o
+       @echo Removing .so files
+       @rm -f $(MODS_SHARED_FILES)
+       @echo Removing dependency files
+       @rm -f $(dep_files)
+       @echo Removing object files and executables
+       @rm -f src/*.o testsuite/check ctrlproxy$(EXEEXT) testsuite/*.o *~ mods/*.o
+       @echo Removing gcov output
+       @rm -f *.gcov *.gcno *.gcda 
 
 dist: distclean
        $(MAKE) -C doc dist
@@ -95,6 +126,8 @@ mods/gnutls.o: CFLAGS+=$(GNUTLS_CFLAGS)
 mods/libgnutls.$(SHLIBEXT): LDFLAGS+=$(GNUTLS_LDFLAGS)
 mods/openssl.o: CFLAGS+=$(OPENSSL_CFLAGS)
 mods/libopenssl.$(SHLIBEXT): LDFLAGS+=$(OPENSSL_LDFLAGS)
+mods/liblinestack_sqlite.$(SHLIBEXT): LDFLAGS+=$(SQLITE_LDFLAGS)
+mods/linestack_sqlite.o: CFLAGS+=$(SQLITE_CFLAGS)
 
 # Python specific stuff below this line
 mods/python2.o ctrlproxy_wrap.o: CFLAGS+=$(PYTHON_CFLAGS)
@@ -127,18 +160,14 @@ testsuite/ctrlproxyrc.torture: testsuite/ctrlproxyrc.torture.in
 rfctest: testsuite/ctrlproxyrc.torture
        @$(IRCDTORTURE) -- ./ctrlproxy -d 0 -i TEST -r $<
 
-# Regular testsuite
+# Unit tests
 
-$(patsubst testsuite/%.c,testsuite/lib%.$(SHLIBEXT),$(wildcard testsuite/test-*.c)): testsuite/lib%.so: testsuite/%.o $(objs)
-
-testsuite/lib%.so: 
+testsuite/check: testsuite/test-cmp.o testsuite/test-isupport.o testsuite/test-parser.o testsuite/test-state.o testsuite/test-util.o testsuite/torture.o $(objs)
        @echo Linking $@
-       @$(CC) $(LIBS) $(CFLAGS) -shared -o $@ $^
+       @$(CC) $(LIBS) -o $@ $^ -lcheck
 
-test: testsuite/torture $(patsubst testsuite/%.c,testsuite/lib%.$(SHLIBEXT),$(wildcard testsuite/test-*.c)) 
+check: testsuite/check
        @echo Running testsuite
-       @$(VALGRIND) ./$< $(patsubst %,./%,$(filter testsuite/libtest-%.$(SHLIBEXT),$^))
+       @$(VALGRIND) ./testsuite/check
 
-testsuite/torture: testsuite/torture.o 
-       @echo Linking $@
-       @$(CC) $(LIBS) -o $@ $^ 
+-include $(dep_files)
index c64aaa9a352997b63e9aaea65a392ffdf9661f92..ef48237bb2fa26a1f7935fbf6edd85e981be66e7 100644 (file)
@@ -3,6 +3,7 @@ LIBS = @LIBS@ @COMMON_LIBS@
 CC = @CC@
 prefix = @prefix@
 exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
 localedir = @datadir@/locale
 INSTALL = @INSTALL@
 bindir = @bindir@
@@ -30,6 +31,8 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LDFLAGS = @GNUTLS_LIBS@
 OPENSSL_CFLAGS = @OPENSSL_CFLAGS@
 OPENSSL_LDFLAGS = @OPENSSL_LIBS@
+SQLITE_CFLAGS = @SQLITE_CFLAGS@
+SQLITE_LDFLAGS = @SQLITE_LIBS@
 PYTHON = @PYTHON@
 PYTHON_PREFIX = @PYTHON_PREFIX@
 SWIG = @SWIG@
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..4826822
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,94 @@
+ Otherwise indicated differently, all changes made by 
+       Jelmer Vernooij.
+
+Ctrlproxy 3.0  UNRELEASED
+
+  This list is not complete. Several subsystems in ctrlproxy have been 
+  rewritten.
+
+  BUG FIXES
+
+    * NetBSD portability fixes. (Adrian Portelli)
+
+    * Properly support strict-rfc1459 comparisons.
+
+       * No longer depend on specific order of USER and NICK commands.
+
+       * Fixed 100% CPU usage bug.
+
+       * Fix large number of memory leaks.
+
+       * Fix crash bug in log_custom (Korbinian Rosenegger)
+
+       * Support 'true' RFC1459 string comparison.
+
+       * Respect CASEMAPPING sent by server.
+
+       * Support different NICK sent by client in replication.
+
+       * Make repl_* work when NICK sent by client differs from current NICK.
+
+       * Support mIRC and ksirc.
+
+       * Fix bug with setting PRIVATE too often.
+
+       * Fix issue with NickServ IDENTIFY'ing when connecting to server.
+       
+       * Fix problems with incorrectly added colons.
+
+  INTERNALS
+
+       * Reduce number of files generated by configure.
+
+       * Moved source control system to Bazaar (http://www.bazaar-vcs.org/)
+
+       * Use separate include files rather than one large one.
+
+       * Get rid of complicated filter class system.
+
+       * Several modules have been integrated into the ControlProxy binary 
+         to avoid (engineering) overhead.
+
+       * There now is a testsuite that tests some of the internal functions.
+
+       * Add 'debug' module for easier debugging.
+
+  NEW FEATURES
+
+       * Removed dependency on popt and libxml.
+
+    * Enhance custom_log (Alexander Wild).
+
+       * Add support for %B, %e and %Y in log_custom (Korbinian Rosenegger)
+
+       * Support CONNECT proxy command as used by irssi
+
+       * Use new CtrlProxy-specific logging system. 
+         Logs go to ~/.ctrlproxy/log by default now when in daemon mode.
+
+       * nickserv is now "self-learning".
+
+       * Add socks module to allow connecting using SOCKS.
+
+       * Install default configuration that is used when the user 
+         does not have a local configuration.
+
+       * Support autogenerating a configuration using `ctrlproxy --init'.
+
+       * Add Mozilla NSS and GNUTLS support.
+
+       * Remove linestack_memory and make linestack_file the default.
+
+       * Admin module can now also work as a seperate ('virtual') network.
+
+       * Make reconnect_timeout changeable (Alexander Wild)
+
+       * Add inetd-style client support.
+
+       * Support non-blocking connects. (Daniel Poelzleithner)
+
+       * Parse 005 line sent by server for PREFIX, CHANTYPES, NETWORK.
+
+       * Add ability to change NICK when away.
+
+       * Add help support in admin module (Daniel Poelzleithner).
diff --git a/README b/README
index c6e0054b432fd3b0b11866201ff6217c009ccf2f..cec0fd74afa93a4398b283d88b5e4a92d676a0be 100644 (file)
--- a/README
+++ b/README
@@ -16,9 +16,10 @@ of course run irssi on my workstation, but my workstation isn't on
 24/7 and some people depend on the channel logs I generate.
 
 Running ctrlproxy requires much less bandwidth then running a 
-remote irssi inside screen.
+remote irssi inside screen and it works much better over high-latency 
+connections.
 
-The structure of CtrlProxy is very modular and it is easily extendible.
+The structure of CtrlProxy is modular and it is easily extendible.
 
 Features
 --------
@@ -36,6 +37,7 @@ Features
  * SSL support
  * Custom logging in any format you specify
  * Flood protection
+ * IPv6 Support
 
 Requirements
 ------------
@@ -56,28 +58,18 @@ $ autoreconf
 
 before you run ./configure, make and make install
 
-Documentation
--------------
-Most documentation is in the manual and the 
-manpages: ctrlproxy(1) and ctrlproxyrc(5).
-The example ctrlproxyrc file might be of some use..
-
-After you've installed, configured and started ctrlproxy you can connect to 
-it on port 6668 and up.
-
-To build the documentation, run ./configure --enable-docs. Please note that 
-a pregenerated version of the documentation is available in the tarball. So, 
-in most cases, you shouldn't need to rebuild.
-
 Quick start
 -----------
-1. Install ctrlproxy (either from debian, or from source, see above)
+1. Install ctrlproxy
 
-2. Run ctrlproxy --daemon
+2. Run ctrlproxy --init 
 
-3. Surf to localhost:8888 with your webbrowser and configure ctrlproxy.
+2. Run ctrlproxy --daemon or ctrlproxy
 
-4. Connect to ctrlproxy from your IRC client. By default, you can connect 
-to port 6670.
+3. Connect to ctrlproxy from your IRC client.
 
-5. Simply disconnect by typing /QUIT
+Documentation
+-------------
+Most documentation is in the manual and the 
+manpages: ctrlproxy(1) and ctrlproxyrc(5).
+The example ctrlproxyrc file might be of some use..
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index b445758..0000000
--- a/TODO
+++ /dev/null
@@ -1,51 +0,0 @@
-current release (3.0.0):
-- test and fix SSL support
-- update documentation
-- fix issue with loading state from linestack_file
-- finish upgrade script
-- fix strange bug with 'reconnect' interaction
-- support automatic setup of listeners
-- integrate antiflood
-
-next minor release (3.0.1):
-- better handling of log_network_state()
-- properly expand second argument of PRIVMSG in log_irssi and log_custom
-- <allow/> rules in socks and listener based on ip addresses/ranges
- - auth mechanism like the abandoned 3.0 has?
-- support passwords in listener for selecting between networks
-- some sort of cascading filters, remove options from line struct
-- more advanced repl backends, rules-based perhaps?
-- use more GLib functions (e.g. g_spawn() rather then fork(), 
-   g_swap_async_with_pipes() instead of piped_child(), etc)
-- warn about unclaimed options!
-- hierarchical structure for admin commands
- - NETWORK
- - NETWORK ADD <NAME>
- - NETWORK REMOVE <NAME>
- - NETWORK LIST
- - NETWORK DISCONNECT <NAME>
- - NETWORK INFO <NAME>
- - NETWORK NEXTSERVER <NAME>
- - NETWORK SET <NAME> <VAR> <VAL>
- - CHANNEL <NETWORK> LIST
- - CHANNEL <NETWORK> SET <VAR> <VAL>
- - LISTENER ADD <binding> <port> [<network>]
- - LISTENER DEL <binding> <port>
- - LISTENER LIST
- - SAVECONFIG
- - SET <name> <value>
-- python module:
- - pass line arguments correctly
- - examples
- - easy mechanism to test python scripts
-- doxygen
-- support /PASS being send after USER and NICK
-
-next major release (3.1.0):
-- merge report_time into the repl_ modules
-- import dcc support
-- re-add merge support
-- import anti-idle
-- import authenticators
-- support for multiple users at the same time (same process)?
-- support for the icecap protocol?
index dd940b9c862027b9597f9002a042524ac0908e4c..caabcf8b82a933a528d8a312f8a8146990ad89bd 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -1,27 +1,7 @@
 Information for users upgrading from 2.6 or 2.7-preXX:
 ======================================================
 
-Any existing configuration files can be upgraded by running the upgrade 
-utility avaiable in the directory 'scripts' in the source tarball.
-
-- GNUTLS is now used by default when it is available instead of SSL. GNUTLS 
-  needs a cafile next to the key and cert files. 
-- The "socket" module has been removed, it is now partially part of the core,
-  and part of it's functionality has been moved to the "listener" module
-- <listen> and client_pass are now below the "liblistener" <plugin> node 
-  rather then under a network. For example, to listen on port 6669 for
-  connections for network MyNetwork and password MyPass:
-
-  <listener port="6669" network="MyNetwork" password="MyPass"/>
-- <nickserv> is now below the "nickserv" <plugin> node as 
-  <nick> rather then under a network. For example:
-
-  <nick nick="mynick" password="mypass"/>
-
-  The network attribute for this element is optional.
-- the "strip", "report_time", "linestack_file", "ctcp" and "repl_none" 
-  modules have been merged into the main program and should no longer be 
-  included in any configuration files
-- the <autosend> element is now handled by the new "autosend" module. Move 
-  any elements from <network> to the configuration of this module, 
-  with an option attribute for the network name.
+The configuration is now maintained as a set of flat-text files in 
+~/.ctrlproxy/. Any existing configuration files can be upgraded by 
+running the upgrade utility available in the directory 'scripts' in 
+the source tarball.
index 7f4d9e3a89536ec5818fef5cefa595d061c5bc5e..9a20697c59efc50c3b41c1415587693e5abbb5bc 100644 (file)
@@ -1,12 +1,17 @@
 # Example RC file for ctrlproxy
-# (C) 2003-2006 Jelmer Vernooij <jelmer@vernstok.nl>
 # Read the documentation for more information about specifics
 # Please adapt to your needs!
 
 [global]
 
-# Replication mechanism to use
+# Replication mechanism to use (some other IRC proxies call this backlog)
 # Possible values: none, simple, highlight, disconnect
+# Meanings:
+#      none: No backlog
+#      simple: Send backlog since the user last said something
+#      disconnect: Send backlog since the users' last disconnect
+#      highlight: Send backlog since last connect, but only lines 
+#                         containing 'matches' (see below)
 replication = none
 
 # Override motd-file location
@@ -17,8 +22,9 @@ report-time = false
 
 autosave = true
 
-# Networks to connect to on startup
+# Networks to connect to on startup. Seperate by semicolons
 autoconnect = admin
+# autoconnect = admin;irc.oftc.net;irc.freenode.net;
 
 # Support for interfacing to ctrlproxy 
 # using /MSG ctrlproxy or /CTRLPROXY -->
@@ -49,4 +55,9 @@ logfile = /home/jelmer/tmp/ctrlproxy
 
 #[nickserv]
 # Learn new nickserv user/password combinations by interpreting traffic to server
-#learn = True
+#learn = true
+
+#[listen]
+# Listen for network connections start port 6667
+# auto = true
+# autoport = 6667
index a20047e5c263c5cd42504985ba7cc2b2af149379..ef9d50a1e1c51b50f4722c7b84c185c168bfb36d 100644 (file)
@@ -1,5 +1,4 @@
 # Example RC file for ctrlproxy
-# (C) 2003-2006 Jelmer Vernooij <jelmer@vernstok.nl>
 # Read the documentation for more information about specifics
 # Please adapt to your needs!
 
@@ -49,9 +48,10 @@ logfile = /home/jelmer/tmp/ctrlproxy
 
 #[nickserv]
 # Learn new nickserv user/password combinations by interpreting traffic to server
-#learn = True
+#learn = true
 
 [listener]
 # Listen for network connections start port 6667
-ports = 6667..6675
+# auto = true
+# autoport = 6667
 password = secret
diff --git a/config.network b/config.network
new file mode 100644 (file)
index 0000000..6dd034d
--- /dev/null
@@ -0,0 +1,10 @@
+# This is an example network configuration
+# Network configurations live in ~/.ctrlproxy/networks/<NAME>
+[global]
+fullname=Somebody
+nick=mynick
+username=myuser
+reconnect-interval=60
+servers=irc.ipv6.freenode.net:6667;
+[irc.ipv6.freenode.net:6667]
+ssl=false
index 3f422351bdae706b60d17864c1ee7137bcca371a..78207149d07b31c532833c64be63926719c9133c 100644 (file)
@@ -22,7 +22,7 @@ fi
 AC_DEFINE_UNQUOTED(VERSION,"$VERSION$BZRVERSION", [ Package version])
 AC_SUBST(PACKAGE)
 AC_SUBST(VERSION)
-AC_CONFIG_SRCDIR([line.c])
+AC_CONFIG_SRCDIR([src/line.c])
 AC_CONFIG_HEADER([config.h])
 
 # Checks for programs.
@@ -58,6 +58,7 @@ AC_TYPE_SIGNAL
 AC_CHECK_FUNCS([gethostbyname gethostname memset strchr strerror strstr uname backtrace_symbols gettimeofday strrchr daemon])
 
 PKG_CHECK_MODULES(COMMON, glib-2.0 gmodule-2.0, , AC_MSG_ERROR([glib is required]))
+PKG_CHECK_MODULES(SQLITE, sqlite3, [DEFMODULE(linestack_sqlite)])
 
 ###############################################################################
 # NSS support
index f27f168d0aa3715a7285af096a1d01972173e411..92324f4310d4f2be7802d99de359967f9038348c 100644 (file)
@@ -1,4 +1,4 @@
-TODO
+BUGS
 README
 AUTHORS
 UPGRADING
index bafe2d63315675b8a1ee78297e6896e289dd6420..86f4de7b43447e44bc47477b7054e79ffa9f8d69 100644 (file)
@@ -1,5 +1,5 @@
 include ../Makefile.settings
-DOCS = ctrlproxy.1 ctrlproxyrc.5 user-guide.html user-guide/index.html ctrlproxy.1.html ctrlproxyrc.5.html
+DOCS = ctrlproxy.1 ctrlproxy_config.5 user-guide.html user-guide/index.html ctrlproxy.1.html ctrlproxy_config.5.html
 
 .PHONY: all manpages clean distclean
 
@@ -7,7 +7,7 @@ all: $(DOCS)
 
 dist: all
 
-manpages: ctrlproxy.1 ctrlproxyrc.5 
+manpages: ctrlproxy.1 ctrlproxy_config.5 
 
 %/index.html: %.xml
        $(XSLTPROC) --stringparam base.dir "$(@D)/" http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl $<
@@ -33,5 +33,5 @@ install: all
        $(INSTALL) -d $(DESTDIR)$(mandir)/man5
        $(INSTALL) -d $(DESTDIR)$(docdir)
        $(INSTALL) ctrlproxy.1 $(DESTDIR)$(mandir)/man1
-       $(INSTALL) ctrlproxyrc.5 $(DESTDIR)$(mandir)/man5
+       $(INSTALL) ctrlproxy_config.5 $(DESTDIR)$(mandir)/man5
        $(INSTALL) user-guide.html $(DESTDIR)$(docdir)
index 25bc1c3a2d3c56e402a5ab9e7323f06fc43d98a4..1a4612836d30024f2894ab93d37ee21f788bed0a 100644 (file)
@@ -64,8 +64,8 @@ on the number of servers it is connected to.
        <listitem><para>Go to the background after the program has been started (daemon mode).</para></listitem>
        </varlistentry>
 
-       <varlistentry><term>-r, --rc-file=RCFILE</term>
-       <listitem><para>Read configuration file from specified location <option>RCFILE</option> instead of from <filename>.ctrlproxyrc</filename> in the users' homedirectory.</para></listitem>
+       <varlistentry><term>-c, --config-dir=DIR</term>
+               <listitem><para>Read configuration files from the specified directory <option>DIR</option> instead of from <filename>.ctrlproxy</filename> in the users' homedirectory.</para></listitem>
        </varlistentry>
 
        <varlistentry><term>-v, --version</term>
@@ -92,8 +92,7 @@ on the number of servers it is connected to.
        <title>SIGNALS</title>
 
 <para>
-       When ctrlproxy receives a <constant>USR1</constant> signal, it will save it's current state
-to the configuration file (usually ~/.ctrlproxyrc).
+       When ctrlproxy receives a <constant>USR1</constant> signal, it will save it's current state. 
 </para>
 
 </refsect1>
@@ -113,7 +112,7 @@ to the configuration file (usually ~/.ctrlproxyrc).
 <refsect1>
        <title>SEE ALSO</title>
 
-<para>irssi (1), ctrlproxyrc (5), http://www.nl.linux.org/~jelmer/ctrlproxy/, ctrlproxyrc.example</para>
+<para>irssi (1), ctrlproxy_config(5), http://ctrlproxy.vernstok.nl/, config.example</para>
 
 </refsect1>
 
diff --git a/doc/ctrlproxy_config.5.xml b/doc/ctrlproxy_config.5.xml
new file mode 100644 (file)
index 0000000..d3c9a03
--- /dev/null
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<refentry id="ctrlproxy_config.5">
+
+<refmeta>
+       <refentrytitle>ctrlproxy_config</refentrytitle>
+       <manvolnum>5</manvolnum>
+</refmeta>
+
+<refnamediv>
+       <refname>~/ctrlproxy/config</refname>
+       <refpurpose>Configuration file for ctrlproxy</refpurpose>
+</refnamediv>
+
+<refsect1>
+       <title>DESCRIPTION</title>
+<para>
+Ctrlproxy uses a configuration directory called <filename>.ctrlproxy</filename>
+that lives inside the users' home directory. The main configuration 
+file lives in this directory and is called <filename>config</filename>. It 
+is an ini-style configuration file with sections and key-value pairs.
+</para>
+
+<para>
+This manpage discusses the variables that can be set in this file 
+in a standard ControlProxy installation.
+</para>
+
+</refsect1>
+
+<refsect1>
+       <title>SETTINGS</title>
+
+<refsect2>
+       <title>[global]</title>
+
+       <para>The [global] section contains some of the most important settings.</para>
+
+       <variablelist>
+               <varlistentry>
+                       <term>replication</term>
+               </varlistentry>
+               
+               <varlistentry>
+                       <term>report-time</term>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>autosave</term>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>autoconnect</term>
+               </varlistentry>
+
+               <varlistentry>
+                       <term>motd-file</term>
+               </varlistentry>
+       
+       </variablelist>
+
+</refsect2>
+
+<refsect2>
+       <title>[admin]</title>
+
+       <para>If the [admin] section is present, ControlProxy will 
+               create a fake network with a control channel that can 
+               be used for administration.
+       </para>
+
+       <variablelist>
+               <varlistentry>
+                       <term>no_privmsg</term>
+               </varlistentry>
+       </variablelist>
+</refsect2>
+
+<refsect2>
+       <title>[log-irssi]</title>
+
+       <para>If the [log-irssi] section is present, ControlProxy 
+               will write irssi-style log files to <filename>~/.ctrlproxy/log-irssi/NETWORK/CHANNEL</filename>.
+       </para>
+
+       <variablelist>
+               <varlistentry>
+                       <term>logfile</term>
+               </varlistentry>
+       </variablelist>
+</refsect2>
+
+<refsect2>
+       <title>[socks]</title>
+
+       <para>
+               If the [socks] section is present, ControlProxy will listen 
+               for SOCKS connections.</para>
+
+       <variablelist>
+               <varlistentry>
+                       <term>port</term>
+               </varlistentry>
+               <varlistentry>
+                       <term>allow</term>
+               </varlistentry>
+       </variablelist>
+
+</refsect2>
+
+<refsect2>
+       <title>[nickserv]</title>
+
+       <variablelist>
+               <varlistentry>
+                       <term>learn</term>
+               </varlistentry>
+       </variablelist>
+</refsect2>
+
+<refsect2>
+       <title>[listener]</title>
+
+       <variablelist>
+               <varlistentry>
+                       <term>auto</term>
+               </varlistentry>
+               <varlistentry>
+                       <term>autoport</term>
+               </varlistentry>
+               <varlistentry>
+                       <term>password</term>
+               </varlistentry>
+       </variablelist>
+</refsect2>    
+
+</refsect1>
+
+<refsect1>
+       <title>SEE ALSO</title>
+
+       <para>ctrlproxy (1), config.example, http://ctrlproxy.vernstok.nl/</para>
+
+</refsect1>
+
+<refsect1>
+       <title>LICENSE</title>
+
+<para>
+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 2 of the License, or
+(at your option) any later version.
+</para>
+
+<para>
+This program is distributed in the hope that it will be useful, but
+\fBWITHOUT ANY WARRANTY\fR; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
+General Public License for more details.
+</para>
+
+<para>
+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., 59 Temple PLace, Suite 330, Boston, MA  02111-1307  USA
+</para>
+
+</refsect1>
+
+<refsect1>
+       <title>BUGS</title>
+
+       <para>
+               CtrlProxy currently does not warn about unknown configuration 
+               parameters.
+       </para>
+</refsect1>
+
+<refsect1>
+       <title>AUTHOR</title>
+
+<para>
+<ulink url="mailto:jelmer@nl.linux.org">Jelmer Vernooij</ulink>
+</para>
+</refsect1>
+</refentry>
diff --git a/doc/ctrlproxyrc.5.xml b/doc/ctrlproxyrc.5.xml
deleted file mode 100644 (file)
index 470d765..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-
-<refentry id="ctrlproxyrc.5">
-
-<refmeta>
-       <refentrytitle>ctrlproxyrc</refentrytitle>
-       <manvolnum>5</manvolnum>
-</refmeta>
-
-<refnamediv>
-       <refname>ctrlproxyrc</refname>
-       <refpurpose>Configuration file for ctrlproxy</refpurpose>
-</refnamediv>
-
-<refsect1>
-       <title>DESCRIPTION</title>
-<para>
-Ctrlproxy uses an XML file for configuration. Check the 
-ctrlproxyrc.example file for a good example. There are a few 
-so-called 'sections' in the file that are discussed below.
-</para>
-
-<refsect2>
-       <title>plugins</title>
-
-<para>
-       This section contains a list of all the plugins that are to be loaded. A &lt;plugin&gt;
-tag specifies a plugin. The 'file' attribute specifies the location the 
-plugin can be found. Additional child elements can be specified, 
-depending on the plugin.
-</para>
-</refsect2>
-
-<refsect2>
-       <title>networks</title>
-
-<para>
-This section contains a list of all the servers to join. The &lt;network&gt; element
-supports the attributes: nick, username, fullname, type, pass. 
-</para>
-
-<para>
-pass contains the password to join the server. client_pass contains 
-the password any client that connects to the proxy should send.
-</para>
-
-<para>
-The &lt;network&gt; element supports the child element &lt;channel&gt;. Attributes 
-of this element are name (indicating the name of the channel) and 
-autojoin (whether or not to join the channel on start).
-</para>
-
-<para>
-Each &lt;network&gt; element contains a &lt;servers&gt; element which contains 
-several transport-specific elements. 
-</para>
-
-</refsect2>
-
-</refsect1>
-
-<refsect1>
-       <title>SEE ALSO</title>
-
-<para>ctrlproxy (1), ctrlproxyrc.example, http://jelmer.vernstok.nl/oss/ctrlproxy, http://xmlsoft.org/</para>
-
-</refsect1>
-
-<refsect1>
-       <title>LICENSE</title>
-
-<para>
-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 2 of the License, or
-(at your option) any later version.
-</para>
-
-<para>
-This program is distributed in the hope that it will be useful, but
-\fBWITHOUT ANY WARRANTY\fR; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
-General Public License for more details.
-</para>
-
-<para>
-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., 59 Temple PLace, Suite 330, Boston, MA  02111-1307  USA
-</para>
-
-</refsect1>
-
-<refsect1>
-       <title>BUGS</title>
-
-       <para>XML isn't really the most suitable format for configuration files that
-               are supposed to be edited by hand. The current configuration file 
-               format also forces the user to know more about the ctrlproxy internals
-               then (s)he has to. For example, it should not matter whether something
-               is loaded from a plugin or part of the ctrlproxy core.
-       </para>
-</refsect1>
-
-<refsect1>
-       <title>AUTHOR</title>
-
-<para>
-<ulink url="mailto:jelmer@nl.linux.org">Jelmer Vernooij</ulink>
-</para>
-</refsect1>
-</refentry>
index 1b3b8d3b7f2fca1e727afe94aff3864aeacf767f..732a260e554f143e3f2059eb60309c30cd8a9c23 100644 (file)
@@ -20,7 +20,7 @@
 
                <para>CtrlProxy should run on pretty much all POSIX-compatible 
                        platforms. A version for Windows will be released in the future
-                       (for 2.7).
+                       (for 3.0).
                </para>
        </sect1>
 
                        source files available there can be unpacked using tar and gzip:</para>
 
                <para><screen>
-<prompt>$ </prompt><userinput>tar xvgz ctrlproxy-2.7.tar.gz</userinput>
-ctrlproxy-2.7/AUTHORS
+<prompt>$ </prompt><userinput>tar xvgz ctrlproxy-3.0.tar.gz</userinput>
+ctrlproxy-3.0/AUTHORS
 ...
 </screen></para>
 
                <para>If you wish to use the bleeding-edge version of ctrlproxy, you can 
-                       download the sources from Subversion. </para>
+                       download the sources from Bazaar. </para>
 
                <sect2>
-                       <title>Downloading from Subversion</title>
+                       <title>Downloading from Bazaar</title>
 
-                       <para>Ctrlproxy Subversion can be accessed by running: </para>
+                       <para>Ctrlproxy Bazaar can be accessed by running: </para>
 
                        <para><screen>
-                                       <prompt>$ </prompt><userinput>svn co http://ctrlproxy.vernstok.nl/svn/trunk ctrlproxy-trunk</userinput>
-ctrlproxy-trunk
-ctrlproxy-trunk/AUTHORS
-ctrlproxy-trunk/README
+                                       <prompt>$ </prompt><userinput>bzr get http://ctrlproxy.vernstok.nl/bzr/trunk ctrlproxy-trunk</userinput>
 ...
 </screen>
                        </para>
index 58aa0deaa6f9c72abaa8bc9af1aeeb56279b33b4..399ec97b0ce01875d84f73df0e08d12c50af0042 100644 (file)
        network.</para>
 
        <para>If no directory is specified, data will be logged to <filename>$HOME/.ctrlproxy/log_irssi/$NETWORK/$CHANNEL</filename>.</para>
-
-       <example>
-               <![CDATA[
-               <ctrlproxy>
-                       <plugins>
-                               <plugin autoload="1" file="log_irssi">
-                                       <logfile>/home/jelmer/log/ctrlproxy</logfile>
-                               </plugin>
-                       </plugins>
-
-                       <networks>
-                               <network name="OFTC">
-                                       <servers><ipv4 host="irc.oftc.net"/></servers>
-                                       <channel name="#flood"/>
-                               </network>
-                       </networks>
-               </ctrlproxy>
-               ]]>
-       </example>
 </sect1>
 
 <sect1>
                </sect3>
        </sect2>
 
-       <sect2>
-               <title>Example</title>
-
-       <example>
-               <![CDATA[
-               <ctrlproxy>
-                       <plugins>
-                               <plugin autoload="1" file="log_custom">
-                                       <logfile>/home/jelmer/log/ctrlproxy/%@</logfile>
-                                       <join>%h%M%s -!- User %n [%u] has joined %c</join>
-                                       <part>%h%M%s -!- User %n [%u] has left %c [%m]</part>
-                                       <quit>%h%M%s -!- User %n [%u] has quit [%m]</quit>
-                                       <action>%h%M%s * %n %m</action>
-                               </plugin>
-                       </plugins>
-
-                       <networks>
-                               <network name="OFTC">
-                                       <servers><ipv4 host="irc.oftc.net"/></servers>
-                                       <channel name="#flood"/>
-                               </network>
-                       </networks>
-               </ctrlproxy>
-               ]]>
-       </example>
-</sect2>
 </sect1>
 
 </chapter>
diff --git a/doc/syntax.xml b/doc/syntax.xml
deleted file mode 100644 (file)
index 1aa0c82..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-<chapter id="syntax">
-       <title>Configuration file syntax</title>
-
-<!-- Introduction -->
-<para>Ctrlproxy uses XML as the format of it's RC file. The syntax of XML 
-files is described much better in other documents on the web and 
-is beyond the scope of this document. </para>
-
-<para>Take a look at the <filename>ctrlproxyrc.example</filename> file that 
-is distributed with ctrlproxy. It should give you a good impression of what 
-a ctrlproxyrc file is supposed to look like.</para>
-
-<para>The root element contains 2 elements: plugins and networks. These are discussed below.</para>
-
-<sect1><title>Plugins</title>
-
-       <para>Contains various &lt;plugin&gt; elements, which 
-       each represent a plugin that can be loaded. When the 
-       autoload attribute is set, the plugin will be loaded 
-       when ctrlproxy starts. </para>
-
-       <para>The <constant>file</constant> attribute is required and 
-       should specify either an absolute path to a plugin or the name of a 
-       plugin in the default modules dir (usually something like 
-       <filename>/usr/lib/ctrlproxy</filename>).</para>
-
-       <para>The &lt;plugin&gt; element should contain plugin-specific elements. See the documentation for the individual plugins for details.</para>
-</sect1>
-
-<sect1><title>Networks</title>
-
-       <para>The &lt;networks&gt; element contains several &lt;network&gt; 
-               elements, each representing an IRC network that CtrlProxy 
-               can connect to.</para>
-
-       <para>Attributes that can be specified on a network element are:</para>
-
-       <variablelist>
-       <varlistentry><term>name</term>
-       <listitem><para>Name of the network. Something like <quote>OPN</quote>,  <quote>OFTC</quote> or <quote>IRCNet</quote>. The name of the first 
-       server is used if this is not specified.</para></listitem>
-       </varlistentry>
-
-       <varlistentry><term>nick</term>
-       <listitem><para>Initial nick name to use on this network. Defaults to UNIX user name.</para></listitem>
-       </varlistentry>
-
-       <varlistentry><term>username</term>
-       <listitem><para>User name to report in hostmask. Defaults to UNIX user name.</para></listitem>
-       </varlistentry>
-
-       <varlistentry><term>ignore_first_nickchange</term>
-               <listitem><para>IRC clients always send a NICK command to the IRC server 
-                               after they have connected. Ctrlproxy happily passes this 
-                               new nick name on to the real server. If you want ctrlproxy 
-                               to ignore the first nick change that a client sends, 
-                               add this attribute.</para></listitem>
-       </varlistentry>
-
-       <varlistentry><term>fullname</term>
-       <listitem><para>Full name to report (for example in <command>/WHOIS</command> information). Defaults to the full name specified in the gecos field of your NSS passwd backend (usually the file <filename>/etc/passwd</filename>.</para></listitem>
-       </varlistentry>
-
-       <varlistentry><term>autoconnect</term>
-               <listitem><para>Specifies whether to connect to this network at start-up. 
-                               If this parameter is set to 0 ctrlproxy might, for example, 
-                               connect to the network when a client tries to use the network.
-               </para></listitem>
-       </varlistentry>
-
-       </variablelist>
-       
-       <sect2><title>Channels</title>
-       
-               <para>A &lt;network&gt; element can also contain 
-               several &lt;channel&gt; elements. Each channel 
-               should have a <quote>name</quote> attribute which 
-               should contain the name of the channel.</para>
-
-               <para>The <quote>autojoin</quote> attribute is optional
-               and specifies whether the channel should be joined automatically
-               when ctrlproxy connects to the network. </para>
-
-               <para>Example:</para>
-
-               <programlisting>
-               <![CDATA[
-               <ctrlproxy>
-                       <networks>
-                               <network name="Freenode">
-                                       <channel name="#samba"/>
-                                       <channel name="#samba-technical" autojoin="1"/>
-                               </network>
-                       </networks>
-               </ctrlproxy>
-               ]]>
-               </programlisting>
-       </sect2>
-       
-       <sect2><title>Servers</title>
-       
-       <para>CtrlProxy can connect to networks via TCP/IP, 
-               it can connect to program (such as BitlBee) or it can 
-               &quot;simulate&quot; a server (such as the Admin network 
-               that allows you to change CtrlProxy settings on the fly).</para>
-
-               <para>Note that ctrlproxy always connects to exactly <emphasis>one</emphasis> server at a time. </para>
-
-       <sect3>
-               <title>TCP/IP Connection to a server</title>
-
-               <para>
-                       TCP/IP is the main transport for IRC traffic. You can specify 
-                       a list of servers that to connect to, in case one of them goes down.
-                       CtrlProxy will automatically connect to the next server in the 
-                       list if the current one goes down.
-               </para>
-
-       <para>Example:</para>
-
-       <programlisting>
-       <![CDATA[
-       <ctrlproxy>
-               <plugins>
-               </plugins>
-               <networks>
-                       <network name="Freenode" autoconnect="1">
-                               <servers>
-                                       <server host="irc.freenode.net"/>
-                                       <server host="irc.ipv6.freenode.net"/>
-                               </servers>
-                       </network>
-               </networks>
-       </ctrlproxy>
-       ]]>
-       </programlisting>
-
-       </sect3>
-
-       <sect3>
-               <title>Virtual servers</title>
-
-               <para>Virtual servers are provided by plugins to CtrlProxy. A 
-                       good example is the <constant>admin</constant> module that 
-                       provides a virtual network with exactly one channel you can 
-                       use for administrating CtrlProxy.
-               </para>
-       </sect3>
-
-       <sect3>
-               <title>Programs</title>
-
-               <para>This type of network is used for connecting to 
-                       local IRC-servers that support inetd-style communication. One 
-                       of the examples of such a program is 
-                       <ulink url="http://www.bitlbee.org/">BitlBee</ulink>
-               </para>
-       </sect3>
-
-       </sect2>
-
-</sect1>
-
-</chapter>
index c70b94053b374a51af15a1cd33039211b6819849..076b44598f8bd1c4043cd827712b9c9e1fb80e56 100644 (file)
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="iso-8859-1"?>
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
 <!ENTITY install SYSTEM "install.xml">
-<!ENTITY syntax SYSTEM "syntax.xml">
 <!ENTITY clients SYSTEM "clients.xml">
 <!ENTITY introduction SYSTEM "introduction.xml">
 ]>
@@ -36,7 +35,6 @@
 
 <part id="configuration">
        <title>Configuration</title>
-       &syntax;
        &clients;
 </part>
 
diff --git a/mods/linestack_sqlite.c b/mods/linestack_sqlite.c
new file mode 100644 (file)
index 0000000..904ae4a
--- /dev/null
@@ -0,0 +1,594 @@
+/* 
+       ctrlproxy: A modular IRC proxy
+       (c) 2006 Jelmer Vernooij <jelmer@nl.linux.org>
+
+       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 2 of the License, 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.
+*/
+
+#include "internals.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <sqlite3.h>
+
+#define STATE_DUMP_INTERVAL 1000
+
+struct linestack_sqlite_network {
+       int last_state_id;
+       int last_state_line_id;
+       int last_line_id;
+};
+
+struct linestack_sqlite_data {
+       GHashTable *networks;
+       sqlite3 *db;
+};
+
+static gboolean insert_state_data(struct linestack_sqlite_data *data, const struct network *n);
+
+static const char *tables[] = {
+       "CREATE TABLE IF NOT EXISTS line(network text, state_id int, time int, data text)",
+       "CREATE TABLE IF NOT EXISTS network_state ("
+               "name text,"
+               "nick text,"
+               "line_id int"
+       ")",
+
+       "CREATE TABLE IF NOT EXISTS banlist_entry (channel int, time int, hostmask text, byhostmask text)",
+       "CREATE TABLE IF NOT EXISTS invitelist_entry (channel int, hostmask text)",
+       "CREATE TABLE IF NOT EXISTS exceptlist_entry (channel int, hostmask text)",
+       "CREATE TABLE IF NOT EXISTS channel_state ("
+               "name text,"
+               "topic text,"
+               "namreply_started int,"
+               "banlist_started int,"
+               "invitelist_started int,"
+               "exceptlist_started int,"
+               "nicklimit int,"
+               "modes text,"
+               "mode text,"
+               "key text,"
+               "state_id int"
+       ")",
+
+       "CREATE TABLE IF NOT EXISTS network_nick ("
+               "nick text,"
+               "fullname text,"
+               "query int,"
+               "modes text,"
+               "hostmask text,"
+               "state_id int"
+       ")",
+
+       "CREATE TABLE IF NOT EXISTS channel_nick (nick text, channel text, mode text)",
+
+       NULL
+};
+
+static struct linestack_sqlite_network *get_network_data(struct linestack_sqlite_data *data, const struct network *n)
+{
+       struct linestack_sqlite_network *ret = g_hash_table_lookup(data->networks, n);
+
+       if (ret) 
+               return ret;
+
+       ret = g_new0(struct linestack_sqlite_network, 1);
+
+       g_hash_table_insert(data->networks, n, ret);
+       
+       insert_state_data(data, n);
+
+       return ret;
+}
+
+static char **get_table(struct linestack_sqlite_data *data, int *nrow, int *ncol, const char *fmt, ...)
+{
+       char *query;
+       int rc;
+       va_list ap;
+       char **ret;
+       char *err;
+
+       va_start(ap, fmt);
+       query = sqlite3_vmprintf(fmt, ap);
+       va_end(ap);
+
+       rc = sqlite3_get_table(data->db, query, &ret, nrow, ncol, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error running query '%s' %s", query, err);
+               return NULL;
+       }
+
+       return ret;
+}
+
+static gboolean run_query(struct linestack_sqlite_data *data, const char *fmt, ...)
+{
+       va_list ap;
+       int rc;
+       char *query;
+       char *err;
+
+       va_start(ap, fmt);
+       query = sqlite3_vmprintf(fmt, ap);
+       va_end(ap);
+
+       rc = sqlite3_exec(data->db, query, NULL, NULL, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error running query '%s' %s", fmt, err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
+static gboolean sqlite_init(struct linestack_context *ctx, struct ctrlproxy_config *config)
+{
+       int rc;
+       struct linestack_sqlite_data *data = g_new0(struct linestack_sqlite_data, 1);
+       char *fname, *err;
+       int i;
+
+       fname = g_build_filename(config->config_dir, "linestack_sqlite", NULL);
+
+       rc = sqlite3_open(fname, &data->db);
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error opening linestack database '%s': %s", fname, sqlite3_errmsg(data->db));
+               return FALSE;
+       }
+
+       for (i = 0; tables[i]; i++) {
+               rc = sqlite3_exec(data->db, tables[i], NULL, NULL, &err);
+               if (rc != SQLITE_OK) {
+                       log_global(NULL, LOG_WARNING, "Error creating table: %s", err);
+                       return FALSE;
+               }
+       }
+
+       data->networks = g_hash_table_new(g_str_hash, g_str_equal);
+
+       ctx->backend_data = data;
+
+       return TRUE;
+}
+
+static gboolean sqlite_fini(struct linestack_context *ctx)
+{
+       struct linestack_sqlite_data *data = ctx->backend_data;
+       sqlite3_close(data->db);
+       return TRUE;
+}
+
+static struct network_state *get_network_state(struct linestack_sqlite_data *data, int state_id, int *line_id)
+{
+       char *query;
+       int rc, i, j;
+       char **ret, *err;
+       int nrow, ncolumn;
+       struct network_state *state;
+       char *mynick;
+
+       g_assert(state_id);
+
+       query = sqlite3_mprintf("SELECT nick, line_id FROM network_state WHERE ROWID = %d", state_id);
+
+       rc = sqlite3_get_table(data->db, query, &ret, &nrow, &ncolumn, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error state data: %s", err);
+               return NULL;
+       }
+
+       if (nrow == 0) {
+               log_global(NULL, LOG_WARNING, "State data not found: %s", err);
+               sqlite3_free_table(ret);
+               return NULL;
+       }
+
+       mynick = g_strdup(ret[2]);
+       *line_id = atoi(ret[3]);
+       sqlite3_free_table(ret);
+       
+       state = g_new0(struct network_state, 1);
+
+       query = sqlite3_mprintf("SELECT nick, fullname, query, modes, hostmask FROM network_nick WHERE state_id = %d", state_id);
+       rc = sqlite3_get_table(data->db, query, &ret, &nrow, &ncolumn, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error network nicks: %s", err);
+               g_free(state);
+               g_free(mynick);
+               return NULL;
+       }
+
+       if (nrow == 0) {
+               log_global(NULL, LOG_WARNING, "Error: no network nicks returned");
+               sqlite3_free_table(ret);
+               g_free(state);
+               g_free(mynick);
+               return NULL;
+       }
+
+       for (i = 1; i <= nrow; i++) {
+               struct network_nick *nn;
+               
+               if (!strcmp(mynick, ret[5*i])) {
+                       nn = &state->me;
+               } else
+                       nn = g_new0(struct network_nick, 1);
+
+               nn->fullname = g_strdup(ret[5*i+1]);
+               network_nick_set_hostmask(nn, ret[5*i+2]);
+
+               g_free(nn->nick);
+               nn->nick = g_strdup(ret[5*i]);
+
+               /*nn->modes = NULL; /* FIXME: ret[5*i+3] */
+
+               if (strcmp(mynick, ret[5*i]) != 0) {
+                       state->nicks = g_list_append(state->nicks, nn);
+                       nn->query = atoi(ret[5*i+4]);
+               } else
+                       nn->query = 1;
+       }
+
+       g_assert(state->me.nick);
+       sqlite3_free_table(ret);
+       g_free(mynick);
+
+       query = sqlite3_mprintf("SELECT ROWID, name, topic, namreply_started, banlist_started, invitelist_started, exceptlist_started, nicklimit, key FROM channel_state WHERE state_id = %d", state_id);
+       rc = sqlite3_get_table(data->db, query, &ret, &nrow, &ncolumn, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error channel data: %s", err);
+               g_free(state);
+               return NULL;
+       }
+
+       if (nrow == 0) {
+               log_global(NULL, LOG_WARNING, "Error channel data: %s", err);
+               sqlite3_free_table(ret);
+               g_free(state);
+               return NULL;
+       }
+
+       for (i = 1; i <= nrow; i++) {
+               char **ret1;
+               int nrow1, ncol1;
+               struct channel_state *cs = g_new0(struct channel_state, 1);
+               
+               cs->name = g_strdup(ret[9*i+1]);
+               cs->topic = g_strdup(ret[9*i+2]);
+               cs->namreply_started = atoi(ret[9*i+3]);
+               cs->banlist_started = atoi(ret[9*i+4]);
+               cs->invitelist_started = atoi(ret[9*i+5]);
+               cs->exceptlist_started = atoi(ret[9*i+6]);
+               cs->limit = atoi(ret[9*i+7]);
+               cs->key = g_strdup(ret[9*i+8]);
+               cs->network = state;
+
+               state->channels = g_list_append(state->channels, cs);
+
+               ret1 = get_table(data, &nrow1, &ncol1, "SELECT hostmask FROM invitelist_entry WHERE channel = %s", ret[9*i]);
+
+               for (j = 1; j <= nrow1; j++) {
+                       cs->invitelist = g_list_append(cs->invitelist, g_strdup(ret1[j]));
+               }
+
+               sqlite3_free_table(ret1);
+
+               ret1 = get_table(data, &nrow1, &ncol1, "SELECT hostmask FROM exceptlist_entry WHERE channel = %s", ret[9*i]);
+
+               for (j = 1; j <= nrow1; j++) {
+                       cs->exceptlist = g_list_append(cs->exceptlist, g_strdup(ret1[j]));
+               }
+
+               sqlite3_free_table(ret1);
+
+               ret1 = get_table(data, &nrow1, &ncol1, "SELECT time, hostmask, byhostmask FROM banlist_entry WHERE channel = %s", ret[9*i]);
+
+               for (j = 1; j <= nrow1; j++) {
+                       struct banlist_entry *be = g_new0(struct banlist_entry, 1);
+
+                       be->hostmask = g_strdup(ret1[j*3+1]);
+                       be->by = g_strdup(ret1[j*3+2]);
+                       be->time_set = atol(ret1[j*3]);
+
+                       cs->banlist = g_list_append(cs->banlist, be);
+               }
+
+               sqlite3_free_table(ret1);
+
+               ret1 = get_table(data, &nrow1, &ncol1, "SELECT nick, mode FROM channel_nick WHERE channel = '%q'", ret[9*i+1]);
+
+               g_assert(nrow1 > 1);
+
+               for (j = 1; j <= nrow1; j++) {
+                       struct channel_nick *cn;
+
+                       cn = find_add_channel_nick(cs, ret1[j*2]);
+                       g_assert(cn);
+
+                       cn->mode = ret1[j*2+1][0];
+               }
+
+               sqlite3_free_table(ret1);
+       }
+
+       sqlite3_free_table(ret);
+
+
+       return state;
+}
+
+static gboolean insert_state_data(struct linestack_sqlite_data *data, const struct network *n)
+{
+       struct linestack_sqlite_network *nd = get_network_data(data, n);
+       GList *gl, *gl1;
+
+       log_network("sqlite", LOG_TRACE, n, "Inserting state");
+
+       if (!run_query(data, "INSERT INTO network_state (name, nick, line_id) VALUES ('%q','%q',%d)", n->name, n->state->me.nick, nd->last_line_id))
+               return FALSE;
+
+       nd->last_state_id = sqlite3_last_insert_rowid(data->db);
+
+       for (gl = n->state->nicks; gl; gl = gl->next) {
+               struct network_nick *nn = gl->data;
+
+               if (!run_query(data, "INSERT INTO network_nick (nick,state_id,query,fullname,modes,hostmask) VALUES ('%q',%d,%d,'%q','%q','%q')", nn->nick, nd->last_state_id, nn->query, nn->fullname, mode2string(nn->modes), nn->hostmask))
+                       return FALSE;
+       }
+
+       if (!run_query(data, "INSERT INTO network_nick (nick,state_id,query,fullname,modes,hostmask) VALUES ('%q',%d,%d,'%q','%q','%q')", n->state->me.nick, nd->last_state_id, n->state->me.query, n->state->me.fullname, mode2string(n->state->me.modes), n->state->me.hostmask))
+                       return FALSE;
+
+       for (gl = n->state->channels; gl; gl = gl->next) {
+               int channel_id;
+               char *modestring;
+               struct channel_state *cs = gl->data;
+               modestring = mode2string(cs->modes);
+
+               if (!run_query(data, "INSERT INTO channel_state (state_id, name, topic, namreply_started, banlist_started, invitelist_started, exceptlist_started, nicklimit, key, modes, mode) VALUES (%d,'%q','%q',%d,%d,%d,%d,%d,'%q','%q','%s')", nd->last_state_id, cs->name, cs->topic, cs->namreply_started, cs->banlist_started, cs->invitelist_started, cs->exceptlist_started, cs->limit, cs->key, modestring, cs->mode)) {
+                       g_free(modestring);
+                       return FALSE;
+               }
+               g_free(modestring);
+               
+               channel_id = sqlite3_last_insert_rowid(data->db);
+
+               for (gl1 = cs->nicks; gl1; gl1 = gl1->next) {
+                       struct channel_nick *cn = gl1->data;
+
+                       if (!run_query(data, "INSERT INTO channel_nick (nick, channel, mode) VALUES ('%q','%q','%c')", cn->global_nick->nick, cn->channel->name, cn->mode))
+                               return FALSE;
+
+               }
+
+               for (gl1 = cs->banlist; gl1; gl1 = gl1->next) {
+                       struct banlist_entry *be = gl1->data;
+
+                       if (!run_query(data, "INSERT INTO banlist_entry (channel, hostmask, by_hostmask, time) VALUES (%d, '%q', '%q', %d)", channel_id, be->hostmask, be->by, be->time_set))
+                               return FALSE;
+               }
+
+               for (gl1 = cs->exceptlist; gl1; gl1 = gl1->next) {
+                       if (!run_query(data, "INSERT INTO exceptlist_entry (channel, hostmask) VALUES (%d, '%q')", channel_id, gl1->data))
+                               return FALSE;
+               }
+
+               for (gl1 = cs->invitelist; gl1; gl1 = gl1->next) {
+                       if (!run_query(data, "INSERT INTO invitelist_entry (channel, hostmask) VALUES (%d, '%q')", channel_id, gl1->data))
+                               return FALSE;
+               }
+       }
+
+       nd->last_state_line_id = nd->last_line_id;
+
+       return TRUE;
+}
+
+static gboolean sqlite_insert_line(struct linestack_context *ctx, const struct network *n, const struct line *l)
+{
+       struct linestack_sqlite_data *data = ctx->backend_data;
+       struct linestack_sqlite_network *nd = get_network_data(data, n);
+       char *raw;
+       time_t now = time(NULL);
+
+       g_assert(n);
+       g_assert(data->db);
+
+       if (nd->last_line_id > nd->last_state_line_id + STATE_DUMP_INTERVAL)
+               insert_state_data(data, n);
+
+       raw = irc_line_string(l);
+
+       if (!run_query(data, "INSERT INTO line (state_id, network, time, data) VALUES (%d, '%q',%lu,'%q')", nd->last_state_id, n->name, now, raw)) {
+               g_free(raw);
+               return FALSE;
+       }
+
+       g_free(raw);
+
+       nd->last_line_id = sqlite3_last_insert_rowid(data->db);
+
+       return TRUE;
+}
+
+static void *sqlite_get_marker(struct linestack_context *ctx, struct network *n)
+{
+       struct linestack_sqlite_data *data = ctx->backend_data;
+       char *err;
+       int rc;
+       char *query;
+       char **ret;
+       int nrow, ncol;
+       int *result = g_new0(int, 1);
+
+       query = sqlite3_mprintf("SELECT ROWID FROM line WHERE network='%q' ORDER BY ROWID DESC LIMIT 1", n->name);
+
+       rc = sqlite3_get_table(data->db, query, &ret, &nrow, &ncol, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Get marker: %s", err);
+               return NULL;
+       }
+
+       *result = atoi(ret[0]);
+
+       sqlite3_free_table(ret);
+
+       return result;
+}
+
+static struct network_state * sqlite_get_state (
+               struct linestack_context *ctx, 
+               struct network *n, 
+               void *m)
+{
+       struct linestack_sqlite_data *backend_data = ctx->backend_data;
+       int rc;
+       char *err;
+       int id;
+       int state_id, line_id;
+       struct network_state *state;
+       struct linestack_marker m1, m2;
+       char *query;
+       char **ret;
+       int ncol, nrow;
+       struct linestack_sqlite_network *nd = get_network_data(backend_data, n);
+
+       if (m != NULL) 
+               id = *(int *)m;
+       else 
+               id = nd->last_line_id;
+
+       query = sqlite3_mprintf("SELECT state_id FROM line WHERE ROWID=%d", id);
+
+       /* Find line for marker */
+       rc = sqlite3_get_table(backend_data->db, query, &ret, &nrow, &ncol, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error getting line related to row: %s", err);
+               return NULL;
+       }
+
+       if (nrow != 1) {
+               log_global("sqlite", LOG_WARNING, "Expected 1 row, got %d (%d)", nrow, id);
+               return NULL;
+       }
+
+       state_id = atoi(ret[1]);
+       g_assert(state_id);
+
+       sqlite3_free_table(ret);
+       
+       state = get_network_state(backend_data, state_id, &line_id);
+
+       m1.ctx = m2.ctx = ctx;
+       m1.data = &line_id;
+       m2.data = &id;
+       linestack_replay(ctx, n, &m1, &m2, state);
+
+       return state;
+}
+
+struct traverse_data {
+       linestack_traverse_fn fn;
+       void *userdata;
+};
+
+static int traverse_fn(void *_ret, int n, char **names, char **values)
+{
+       struct traverse_data *data = _ret;
+       struct line *l = irc_parse_line(values[1]);
+
+       data->fn(l, atoi(values[0]), data->userdata);
+
+       return 0;
+}
+
+static gboolean sqlite_traverse(struct linestack_context *ctx,
+               struct network *n, 
+               void *mf,
+               void *mt,
+               linestack_traverse_fn tf, 
+               void *userdata)
+{
+       struct linestack_sqlite_data *backend_data = ctx->backend_data;
+       int rc;
+       char *err;
+       int from, to;
+       struct traverse_data data;
+       char *query;
+       struct linestack_sqlite_network *nd = get_network_data(backend_data, n);
+
+       data.fn = tf;
+       data.userdata = userdata;
+
+       if (mf != NULL)
+               from = *(int *)mf;
+       else
+               from = nd->last_line_id;
+
+       if (mt != NULL)
+               to = *(int *)mt;
+       else
+               to = nd->last_line_id;
+
+       query = sqlite3_mprintf("SELECT time,data FROM line WHERE network='%q' AND ROWID >= %d AND ROWID <= %d", n->name, from, to);
+
+       rc = sqlite3_exec(backend_data->db, query, traverse_fn, &data, &err);
+       sqlite3_free(query);
+
+       if (rc != SQLITE_OK) {
+               log_global(NULL, LOG_WARNING, "Error traversing lines between %d and %dd: %s", from, to, err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+const struct linestack_ops linestack_sqlite = {
+       .name = "sqlite",
+       .init = sqlite_init,
+       .fini = sqlite_fini,
+       .insert_line = sqlite_insert_line,
+       .get_marker = sqlite_get_marker,
+       .get_state = sqlite_get_state, 
+       .free_marker = g_free,
+       .traverse = sqlite_traverse
+};
+
+static gboolean init_plugin(void)
+{
+       register_linestack(&linestack_sqlite);
+       return TRUE;
+}
+
+struct plugin_ops plugin = {
+       .name = "linestack_sqlite",
+       .version = 0,
+       .init = init_plugin,
+};
index 5a12c48c01988a9ec58b0e3a02b5862b3b0cdff1..0b95fe395316ca77098824fb47514ed755eac811 100644 (file)
@@ -1,6 +1,6 @@
 /* 
        ctrlproxy: A modular IRC proxy
-       (c) 2005 Jelmer Vernooij <jelmer@nl.linux.org>
+       (c) 2005-2006 Jelmer Vernooij <jelmer@nl.linux.org>
        
        Manual listen on ports
 
@@ -121,7 +121,16 @@ static gboolean handle_new_client(GIOChannel *c_server, GIOCondition condition,
        return TRUE;
 }
 
+/* FIXME: Store in global struct somehow */
 static GList *listeners = NULL;
+static int autoport = 6667;
+static gboolean auto_listener = FALSE;
+static GKeyFile *keyfile = NULL;
+
+static int next_autoport()
+{
+       return ++autoport;
+}
 
 gboolean start_listener(struct listener *l)
 {
@@ -194,7 +203,7 @@ gboolean start_listener(struct listener *l)
 
 gboolean stop_listener(struct listener *l)
 {
-       log_global ( "listener", LOG_INFO, "Stopping listener at %s:%s", l->address?l->address:"", l->port);
+       log_global ( "listener", LOG_INFO, "Stopped listening at %s:%s", l->address?l->address:"", l->port);
        g_source_remove(l->incoming_id);
        return TRUE;
 }
@@ -233,11 +242,16 @@ static void update_config(struct global *global, const char *path)
 
        default_password = g_key_file_get_string(global->config->keyfile, "listener", "password", NULL);
 
+       g_key_file_set_boolean(global->config->keyfile, "listener", "auto", auto_listener);
+
+       g_key_file_set_integer(global->config->keyfile, "listener", "autoport", autoport);
+
        filename = g_build_filename(path, "listener", NULL);
        
-       /* FIXME: Store old GKeyFile somewhere, so we can keep comments... */
+       if (!keyfile)
+               keyfile = g_key_file_new();
 
-       kf = g_key_file_new();
+       kf = keyfile;
 
        for (gl = listeners; gl; gl = gl->next) {
                struct listener *l = gl->data;
@@ -264,7 +278,26 @@ static void update_config(struct global *global, const char *path)
        }
        
        g_free(filename);
-       g_key_file_free(kf);
+}
+
+static void auto_add_listener(struct network *n, void *private_data)
+{
+       GList *gl;
+       char *port;
+       struct listener *l;
+
+       /* See if there is already a listener for n */
+       for (gl = listeners; gl; gl = gl->next) {
+               l = gl->data;
+
+               if (l->network == n || l->network == NULL)
+                       return;
+       }
+
+       port = g_strdup_printf("%d", next_autoport());
+       l = listener_init(NULL, port);
+       l->network = n;
+       start_listener(l);
 }
 
 static void load_config(struct global *global)
@@ -277,12 +310,19 @@ static void load_config(struct global *global)
        char *default_password;
 
        default_password = g_key_file_get_string(global->config->keyfile, "listener", "password", NULL);
+       if (g_key_file_has_key(global->config->keyfile, "listener", "auto", NULL))
+               auto_listener = g_key_file_get_boolean(global->config->keyfile, "listener", "auto", NULL);
+
+       if (g_key_file_has_key(global->config->keyfile, "listener", "autoport", NULL))
+               autoport = g_key_file_get_integer(global->config->keyfile, "listener", "autoport", NULL);
 
-       kf = g_key_file_new();
+       if (auto_listener)
+               register_new_network_notify(global, auto_add_listener, NULL);
+
+       keyfile = kf = g_key_file_new();
 
        if (!g_key_file_load_from_file(kf, filename, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
                g_free(filename);
-               g_key_file_free(kf);
                return;
        }
                
@@ -326,7 +366,6 @@ static void load_config(struct global *global)
 
        g_strfreev(groups);
        g_free(filename);
-       g_key_file_free(kf);
 }
 
 static void fini_plugin(void)
@@ -350,6 +389,11 @@ void cmd_start_listener(struct client *c, char **args, void *userdata)
                return;
        }
 
+       if (!args[2]) {
+               admin_out(c, "No password specified");
+               return;
+       }
+
        b = g_strdup(args[1]);
        if ((p = strchr(b, ':'))) {
                *p = '\0';
@@ -363,10 +407,12 @@ void cmd_start_listener(struct client *c, char **args, void *userdata)
 
        if (b) g_free(b); else g_free(p);
 
-       if (args[2]) {
-               l->network = find_network(c->network->global, args[2]);
+       l->password = g_strdup(args[2]);
+
+       if (args[3]) {
+               l->network = find_network(c->network->global, args[3]);
                if (l->network == NULL) {
-                       admin_out(c, "No such network `%s'", args[2]);
+                       admin_out(c, "No such network `%s'", args[3]);
                        free_listener(l);
                        return;
                }
@@ -429,7 +475,7 @@ void cmd_list_listener(struct client *c, char **args, void *userdata)
 }
 
 const static struct admin_command listener_commands[] = {
-       { "STARTLISTENER", cmd_start_listener, "[<address>:]<port> [<network>]", "Add listener on specified port" },
+       { "STARTLISTENER", cmd_start_listener, "[<address>:]<port> <password> [<network>]", "Add listener on specified port" },
        { "STOPLISTENER", cmd_stop_listener, "[<address>:]<port>", "Stop listener on specified port" },
        { "LISTLISTENER", cmd_list_listener, "", "Add new network with specified name" },
        { NULL }
index a2f0c832a7d78ed4aa9b9cfbfff79cea5fa13405..2ec791f7049e54f48a16e7dc3beb840c673eeb99 100644 (file)
@@ -35,7 +35,7 @@
 
 struct log_custom_data {
     char *logfilename;
-    GHashTable *fmts;
+       GKeyFile *kf;
     GHashTable *files;
 };
 
@@ -48,48 +48,56 @@ struct log_mapping {
        char *(*callback) (struct network *, const struct line *l, gboolean case_sensitive);
 };
 
-static char *get_hours(struct network *n, const struct line *l, gboolean case_sensitive) { 
+static char *get_hours(struct network *n, const struct line *l, gboolean case_sensitive) 
+{ 
        time_t ti = time(NULL);
        struct tm *t = localtime(&ti);
        return g_strdup_printf("%02d", t->tm_hour);
 }
 
-static char *get_minutes(struct network *n, const struct line *l, gboolean case_sensitive) { 
+static char *get_minutes(struct network *n, const struct line *l, gboolean case_sensitive) 
+{ 
        time_t ti = time(NULL);
        struct tm *t = localtime(&ti);
        return g_strdup_printf("%02d", t->tm_min);
 }
 
-static char *get_seconds(struct network *n, const struct line *l, gboolean case_sensitive) { 
+static char *get_seconds(struct network *n, const struct line *l, gboolean case_sensitive) 
+{ 
        time_t ti = time(NULL);
        struct tm *t = localtime(&ti);
        return g_strdup_printf("%02d", t->tm_sec);
 }
 
-static char *get_seconds_since_1970(struct network *n, const struct line *l, gboolean case_sensitive) {
+static char *get_seconds_since_1970(struct network *n, const struct line *l, gboolean case_sensitive) 
+{
        time_t ti = time(NULL);
        return g_strdup_printf("%ld", ti);
 }
 
-static char *get_day(struct network *n, const struct line *l, gboolean case_sensitive) { 
+static char *get_day(struct network *n, const struct line *l, gboolean case_sensitive) 
+{ 
        time_t ti = time(NULL);
        struct tm *t = localtime(&ti);
        return g_strdup_printf("%02d", t->tm_mday);
 }
 
-static char *get_month(struct network *n, const struct line *l, gboolean case_sensitive) { 
+static char *get_month(struct network *n, const struct line *l, gboolean case_sensitive) 
+{ 
        time_t ti = time(NULL);
        struct tm *t = localtime(&ti);
        return g_strdup_printf("%02d", t->tm_mon + 1);
 }
 
-static char *get_year(struct network *n, const struct line *l, gboolean case_sensitive) { 
+static char *get_year(struct network *n, const struct line *l, gboolean case_sensitive) 
+{ 
        time_t ti = time(NULL);
        struct tm *t = localtime(&ti);
        return g_strdup_printf("%04d", t->tm_year + 1900);
 }
 
-static char *get_user(struct network *n, const struct line *l, gboolean case_sensitive) {
+static char *get_user(struct network *n, const struct line *l, gboolean case_sensitive) 
+{
        char *nick = NULL;
        char *user = NULL;
 
@@ -101,14 +109,16 @@ static char *get_user(struct network *n, const struct line *l, gboolean case_sen
        else return g_strdup(user);
 }
 
-static char *get_monthname(struct network *n, const struct line *l, gboolean case_sensitive) { 
+static char *get_monthname(struct network *n, const struct line *l, gboolean case_sensitive) 
+{ 
        char stime[512];
        time_t ti = time(NULL);
        strftime(stime, sizeof(stime), "%b", localtime(&ti));
        return g_strdup_printf("%s", stime);
 }
 
-static char *get_nick(struct network *n, const struct line *l, gboolean case_sensitive) {
+static char *get_nick(struct network *n, const struct line *l, gboolean case_sensitive) 
+{
        if (l->origin) {
                char *n = line_get_nick(l);
                if(case_sensitive) {
@@ -327,11 +337,17 @@ static void file_write_target(struct log_custom_data *data, struct network *netw
 {
        char *t, *s, *fmt;
        FILE *f;
-
-       fmt = g_hash_table_lookup(data->fmts, n);
+       
+       fmt = g_key_file_get_string(data->kf, "log-custom", n, NULL);
        if(!fmt) return;
+       
+       g_assert(l->args[0]);
+       g_assert(l->args[1]);
+       g_assert(network->state);
+       g_assert(network->state->me.nick);
+       g_assert(network->state->info);
 
-       if(!irccmp(network->state->info, network->state->me.nick, l->args[1])) {
+       if (!irccmp(network->state->info, network->state->me.nick, l->args[1])) {
                if (l->origin) t = line_get_nick(l);
                else t = g_strdup("_messages_");
        } else {
@@ -356,7 +372,7 @@ static void file_write_channel_only(struct log_custom_data *data, struct network
        char *s, *fmt;
        FILE *f;
 
-       fmt = g_hash_table_lookup(data->fmts, n);
+       fmt = g_key_file_get_string(data->kf, "log-custom", n, NULL);
        if(!fmt) return;
 
        f = find_add_channel_file(data, network, l, l->args[1], TRUE);
@@ -381,7 +397,7 @@ static void file_write_channel_query(struct log_custom_data *data, struct networ
        if (!l->origin) return;
        nick = line_get_nick(l);
 
-       fmt = g_hash_table_lookup(data->fmts, n);
+       fmt = g_key_file_get_string(data->kf, "log-custom", n, NULL);
        if(!fmt) {
                g_free(nick);
                return;
@@ -499,8 +515,6 @@ static void load_config(struct global *global)
 {
     GKeyFile *kf = global->config->keyfile;
     struct log_custom_data *data;
-    char **varnames;
-    int i; gsize len;
     
     if (!g_key_file_has_group(kf, "log-custom")) {
            del_log_filter("log_custom");
@@ -512,14 +526,8 @@ static void load_config(struct global *global)
        add_log_filter("log_custom", log_custom_data, data, 1000);
 
        data->files = g_hash_table_new(g_str_hash, g_str_equal);
-       data->fmts = g_hash_table_new(g_str_hash, g_str_equal);
     data->logfilename = g_key_file_get_string(kf, "log-custom", "logfilename", NULL);
-
-    varnames = g_key_file_get_keys(kf, "log-custom", &len, NULL);
-    for (i = 0; i < len; i++) {
-           g_hash_table_insert(data->fmts, varnames[i], g_key_file_get_string(kf, "log-custom", varnames[i], NULL));
-    }
-    g_free(varnames);
+       data->kf = kf;
 }
 
 static gboolean init_plugin(void)
index d1ba65ec3187f83593aa2a3fdbfd54ca7b67aa0e..5ad4a39f8f0aeaa1826d078580014e8551edb135 100755 (executable)
@@ -240,7 +240,7 @@ class IniFile(object):
         for var in self.conf[section]:
             print "%s = %s" % (var, self.conf[section][var])
 
-conf = IniFile({'global':{}})
+conf = IniFile({'global':{'autoconnect':[]}})
 networks = {}
 listeners = IniFile()
 warnings = []
similarity index 97%
rename from admin.c
rename to src/admin.c
index 51c8d38f7c48a26bd0392c4c3f4e30638168105f..17863842eb1797644f3e6d52efc2832fc0a1ae02 100644 (file)
--- a/admin.c
@@ -64,7 +64,7 @@ static void add_network (struct client *c, char **args, void *userdata)
        g_free(nc->name); nc->name = g_strdup(args[1]);
        load_network(c->network->global, nc);
 
-       admin_out(c, "Network `%s' added", args[1]);
+       admin_out(c, "Network `%s' added. Use ADDSERVER to add a server to this network.", args[1]);
 }
 
 static void del_network (struct client *c, char **args, void *userdata)
@@ -321,6 +321,11 @@ static void dump_joined_channels(struct client *c, char **args, void *userdata)
                n = c->network;
        }
 
+       if (!n->state) {
+               admin_out(c, "Network '%s' not connected", n->name);
+               return;
+       }
+
        for (gl = n->state->channels; gl; gl = gl->next) {
                struct channel_state *ch = (struct channel_state *)gl->data;
                admin_out(c, "%s", ch->name);
@@ -469,11 +474,20 @@ void admin_log(const char *module, enum log_level level, const struct network *n
        struct line *l;
        char *tmp, *hostmask;
        GList *gl;
+       static gboolean entered = FALSE;
 
        if (!my_global || !my_global->config || 
                !my_global->config->admin_log) 
                return;
 
+       if (level < LOG_INFO)
+               return;
+
+       if (entered)
+               return; /* Prevent inifinite recursion.. */
+
+       entered = TRUE;
+
        tmp = g_strdup_printf("%s%s%s%s%s%s", 
                                                  data, 
                                                  n?" (":"",
@@ -498,6 +512,8 @@ void admin_log(const char *module, enum log_level level, const struct network *n
        }
 
        g_free(tmp);
+
+       entered = FALSE;
 }
 
 const static struct admin_command builtin_commands[] = {
similarity index 100%
rename from admin.h
rename to src/admin.h
similarity index 89%
rename from cache.c
rename to src/cache.c
index 3c79013d4d2bb5c7bc870de30f0062aa1bb07f2a..435a359351fa124a2961ee93fb0c0cb493e0347d 100644 (file)
--- a/cache.c
@@ -172,29 +172,31 @@ static gboolean client_try_cache_names(struct client *c, struct line *l)
        return TRUE;
 }
 
+struct cache_command {
+       const char *name;
+       /* Should return FALSE if command couldn't be cached */
+       gboolean (*try_cache) (struct client *c, struct line *l);
+} cache_commands[] = {
+       { "JOIN", client_try_cache_join },
+       { "MODE", client_try_cache_mode },
+       { "NAMES", client_try_cache_names },
+       { "TOPIC", client_try_cache_topic },
+       { NULL, NULL }
+};
+
 /* Try to answer a client query from cache */
 gboolean client_try_cache(struct client *c, struct line *l)
 {
        g_assert(l);
+       int i;
 
        if (l->argc == 0) 
                return TRUE;
 
-       if (!g_strcasecmp(l->args[0], "JOIN")) {
-               return client_try_cache_join(c, l);
+       for (i = 0; cache_commands[i].name; i++) {
+               if (!g_strcasecmp(l->args[0], cache_commands[i].name))
+                       return cache_commands[i].try_cache(c, l);
        }
        
-       if (!g_strcasecmp(l->args[0], "MODE")) {
-               return client_try_cache_mode(c, l);
-       }
-
-       if (!g_strcasecmp(l->args[0], "NAMES")) {
-               return client_try_cache_names(c, l);
-       }
-
-       if (!g_strcasecmp(l->args[0], "TOPIC")) {
-               return client_try_cache_topic(c, l);
-       }
-
        return FALSE;
 }
similarity index 100%
rename from client.c
rename to src/client.c
similarity index 100%
rename from client.h
rename to src/client.h
similarity index 100%
rename from ctcp.c
rename to src/ctcp.c
similarity index 100%
rename from ctcp.h
rename to src/ctcp.h
similarity index 99%
rename from ctrlproxy.h
rename to src/ctrlproxy.h
index 5ea4c1be09ccc81af8582a0e887df129810246e0..41f6ae4e141d322dbdbadab447da55509e346164 100644 (file)
@@ -56,6 +56,7 @@
 struct global {
        struct ctrlproxy_config *config;
        struct linestack_context *linestack;
+       GList *new_network_notifiers;
        GList *networks;
        GList *nickserv_nicks;
 };
similarity index 100%
rename from gen_config.c
rename to src/gen_config.c
similarity index 100%
rename from hooks.c
rename to src/hooks.c
similarity index 100%
rename from hooks.h
rename to src/hooks.h
similarity index 96%
rename from internals.h
rename to src/internals.h
index e003743d370c04248ff8783e0a43a2eb163a6cd9..23d175e8533dc244a90330092329175c84a61753 100644 (file)
@@ -92,11 +92,13 @@ void channel_update_config(struct channel_state *ns, struct channel_config *nc);
 
 /* repl.c */
 void client_replicate(struct client *);
+char *mode2string(char modes[255]);
 
 /* main.c */
 void free_global(struct global *);
 void config_load_notify(struct global *global);
 void config_save_notify(struct global *global, const char *);
+struct global *new_global(const char *config_dir);
 
 /* nickserv.c */
 void init_nickserv(void);
@@ -109,4 +111,7 @@ void init_admin(void);
 gboolean admin_process_command(const struct client *c, struct line *l, int cmdoffset);
 void admin_log(const char *module, enum log_level level, const struct network *n, const struct client *c, const char *data);
 
+/* settings.c */
+gboolean create_configuration(const char *config_dir);
+
 #endif /* __INTERNALS_H__ */
diff --git a/irc.h b/src/irc.h
similarity index 100%
rename from irc.h
rename to src/irc.h
similarity index 100%
rename from isupport.c
rename to src/isupport.c
similarity index 100%
rename from line.c
rename to src/line.c
similarity index 100%
rename from line.h
rename to src/line.h
similarity index 97%
rename from linestack.c
rename to src/linestack.c
index b8c6b64f556c433c0e87fe19f626becda67e5964..b35e71a6d5957cf3da0de15841db8e29a12e18d3 100644 (file)
@@ -53,7 +53,7 @@ struct linestack_context *new_linestack(struct ctrlproxy_config *cfg)
        }
 
        if (!current_backend) {
-               current_backend = linestack_backends->data;
+               current_backend = &linestack_file;
        }
 
        ctx = g_new0(struct linestack_context, 1);
@@ -96,10 +96,17 @@ struct network_state *linestack_get_state(
                struct network *n, 
                struct linestack_marker *lm)
 {
+       struct network_state *st;
        if (!ctx->ops) return NULL;
        if (!ctx->ops->get_state) return NULL;
 
-       return ctx->ops->get_state(ctx, n, lm?lm->data:NULL);
+       st = ctx->ops->get_state(ctx, n, lm?lm->data:NULL);
+       if (st == NULL)
+               return NULL;
+
+       g_assert(st->me.nick);
+       g_assert(st->me.query);
+       return st;
 }
 
 gboolean linestack_traverse(
similarity index 100%
rename from linestack.h
rename to src/linestack.h
similarity index 97%
rename from linestack_file.c
rename to src/linestack_file.c
index 91fd46c62ea15b868dc7c5745a6edd394ed0ca04..429f39b42f02440a5172a22bd1b41d9f2a24f491 100644 (file)
@@ -118,6 +118,7 @@ static struct lf_network_data *get_data(struct linestack_context *ctx, const str
                        return NULL;
                }
                nd = g_new0(struct lf_network_data, 1);
+               nd->lines_since_last_state = STATE_DUMP_INTERVAL;
                nd->file = file;
                log_network(NULL, LOG_TRACE, n, "Creating new linestack file '%s'", path);
                file_insert_state(nd, n);
@@ -156,7 +157,7 @@ static gboolean file_insert_line(struct linestack_context *ctx, const struct net
        return (ret != EOF);
 }
 
-void *file_get_marker(struct linestack_context *ctx, struct network *n)
+static void *file_get_marker(struct linestack_context *ctx, struct network *n)
 {
        long *pos;
        struct lf_network_data *nd = get_data(ctx, n);
@@ -168,7 +169,7 @@ void *file_get_marker(struct linestack_context *ctx, struct network *n)
        return pos;
 }
 
-static         struct network_state * file_get_state (
+static struct network_state * file_get_state (
                struct linestack_context *ctx, 
                struct network *n, 
                void *m)
diff --git a/log.c b/src/log.c
similarity index 100%
rename from log.c
rename to src/log.c
similarity index 80%
rename from main.c
rename to src/main.c
index bc6890794096d8d07484f441cc86f3e5fb925628..f87e349271e2fe722e64853cbe342bf541b6f623 100644 (file)
--- a/main.c
@@ -38,8 +38,7 @@
 /* globals */
 static GMainLoop *main_loop;
 extern char my_hostname[];
-
-struct global *my_global;
+extern struct global *my_global;
 
 static void signal_crash(int sig) 
 {
@@ -129,68 +128,6 @@ static void signal_save(int sig)
        nickserv_save(my_global, my_global->config->config_dir);
 }
 
-struct global *new_global(const char *config_dir)
-{
-       struct global *global = g_new0(struct global, 1);
-
-       global->config = load_configuration(config_dir);
-
-       load_networks(global, global->config);
-
-       nickserv_load(global);
-
-       if (!global->config) {
-               g_free(global);
-               return NULL;
-       }
-
-       global->linestack = new_linestack(global->config);
-
-       config_load_notify(global);
-       
-       return global;
-}
-
-void free_global(struct global *global)
-{
-       fini_networks(global);
-       free_config(global->config);
-       global->config = NULL;
-       free_linestack_context(global->linestack); global->linestack = NULL;
-       fini_networks(global);
-}
-
-static GList *load_notifies = NULL, *save_notifies = NULL;
-
-void register_load_config_notify(config_load_notify_fn fn)
-{
-       load_notifies = g_list_append(load_notifies, fn);
-}
-
-void register_save_config_notify(config_save_notify_fn fn)
-{
-       save_notifies = g_list_append(save_notifies, fn);
-}
-
-void config_load_notify(struct global *global)
-{
-       GList *gl;
-       for (gl = load_notifies; gl; gl = gl->next) {
-               config_load_notify_fn fn = gl->data;
-
-               fn(global);
-       }
-}
-
-void config_save_notify(struct global *global, const char *dest)
-{
-       GList *gl;
-       for (gl = save_notifies; gl; gl = gl->next) {
-               config_save_notify_fn fn = gl->data;
-
-               fn(global, dest);
-       }
-}
 
 int main(int argc, char **argv)
 {
@@ -200,6 +137,7 @@ int main(int argc, char **argv)
        extern gboolean no_log_timestamp;
        const char *config_dir = NULL;
        char *tmp;
+       gboolean init = FALSE;
        const char *inetd_client = NULL;
        gboolean version = FALSE;
        GOptionContext *pc;
@@ -208,9 +146,10 @@ int main(int argc, char **argv)
                {"debug-level", 'd', 'd', G_OPTION_ARG_INT, &current_log_level, ("Debug level [0-5]"), "LEVEL" },
                {"no-timestamp", 'n', FALSE, G_OPTION_ARG_NONE, &no_log_timestamp, "No timestamps in logs" },
                {"daemon", 'D', 0, G_OPTION_ARG_NONE, &isdaemon, ("Run in the background (as a daemon)")},
+               {"init", 0, 0, G_OPTION_ARG_NONE, &init, "Create configuration" },
                {"log", 'l', 0, G_OPTION_ARG_STRING, &logfile, ("Log messages to specified file"), ("FILE")},
                {"config-dir", 'c', 0, G_OPTION_ARG_STRING, &config_dir, ("Override configuration directory"), ("DIR")},
-               {"version", 'v', 'v', G_OPTION_ARG_NONE, &version, ("Show version information")},
+               {"version", 'v', 0, G_OPTION_ARG_NONE, &version, ("Show version information")},
                { NULL }
        };
 
@@ -250,6 +189,13 @@ int main(int argc, char **argv)
                logfile = g_build_filename(config_dir, "log", NULL);
        }
 
+       if (init) {
+               if (!create_configuration(config_dir))
+                       return 1;
+               printf("Configuration created in %s. \n", config_dir);
+               return 0;
+       }
+
        init_log(logfile);
 
        log_global(NULL, LOG_INFO, "CtrlProxy %s starting", VERSION);
@@ -293,12 +239,12 @@ int main(int argc, char **argv)
                char *rcfile = g_build_filename(g_get_home_dir(), ".ctrlproxyrc", NULL);
                
                if(g_file_test(rcfile, G_FILE_TEST_EXISTS)) {
-                       log_global(NULL, LOG_INFO, "Pre-3.0 style .ctrlproxyrc found, starting upgrade");
-                       /* FIXME: Upgrade script */
+                       log_global(NULL, LOG_INFO, "Pre-3.0 style .ctrlproxyrc found");
+                       log_global(NULL, LOG_INFO, "Run ctrlproxy-upgrade to update configuration");
+                       return 1;
                } else {
-                       log_global(NULL, LOG_INFO, "No configuration found, loading default");
-                       my_global = new_global(DEFAULT_CONFIG_DIR);     
-                       my_global->config->config_dir = g_strdup(config_dir);
+                       log_global(NULL, LOG_INFO, "No configuration found. Maybe you would like to create one by running with --init ?");
+                       return 1;
                }
 
                g_free(rcfile);
similarity index 100%
rename from motd.c
rename to src/motd.c
similarity index 97%
rename from network.c
rename to src/network.c
index 0d9fe4db206eb69501a86e5ac4c659f0ffb1062c..e0c42040fbd8143627e5c8f5b1d32ae906aa32ce 100644 (file)
--- a/network.c
@@ -1,6 +1,6 @@
 /*
        ctrlproxy: A modular IRC proxy
-       (c) 2002-2005 Jelmer Vernooij <jelmer@nl.linux.org>
+       (c) 2002-2006 Jelmer Vernooij <jelmer@nl.linux.org>
 
        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
@@ -29,6 +29,19 @@ static gboolean connect_server(struct network *s);
 static gboolean close_server(struct network *s);
 static void reconnect(struct network *server, gboolean rm_source);
 
+struct new_network_notify_data {
+       new_network_notify_fn fn;
+       void *data;
+};
+
+void register_new_network_notify(struct global *global, new_network_notify_fn fn, void *userdata)
+{
+       struct new_network_notify_data *p = g_new0(struct new_network_notify_data, 1);
+       p->fn = fn;
+       p->data = userdata;
+       global->new_network_notifiers = g_list_append(global->new_network_notifiers, p);
+}
+
 static void server_send_login (struct network *s) 
 {
        g_assert(s);
@@ -675,6 +688,7 @@ static gboolean delayed_connect_server(struct network *s)
 struct network *load_network(struct global *global, struct network_config *sc)
 {
        struct network *s;
+       GList *gl;
 
        g_assert(sc);
 
@@ -691,6 +705,13 @@ struct network *load_network(struct global *global, struct network_config *sc)
        s->global = global;
 
        global->networks = g_list_append(global->networks, s);
+
+       for (gl = global->new_network_notifiers; gl; gl = gl->next) {
+               struct new_network_notify_data *p = gl->data;
+
+               p->fn(s, p->data);
+       }
+
        return s;
 }
 
@@ -904,3 +925,4 @@ void network_select_next_server(struct network *n)
        log_network(NULL, LOG_INFO, n, "Trying next server");
        n->connection.data.tcp.current_server = network_get_next_tcp_server(n);
 }
+
similarity index 95%
rename from network.h
rename to src/network.h
index e11fdda775fb6e43893a68a03a463375242e2587..66f60a4c90a815bac673b9b1ea170cb08ab110dd 100644 (file)
--- a/network.h
@@ -102,5 +102,7 @@ G_MODULE_EXPORT void register_virtual_network(struct virtual_network_ops *);
 G_MODULE_EXPORT struct network *find_network(struct global *, const char *);
 G_MODULE_EXPORT gboolean virtual_network_recv_line(struct network *l, struct line *);
 G_MODULE_EXPORT gboolean virtual_network_recv_args(struct network *l, const char *origin, ...); 
+typedef void (*new_network_notify_fn) (struct network *, void *);
+G_MODULE_EXPORT void register_new_network_notify(struct global *, new_network_notify_fn, void *userdata);
 
 #endif /* __CTRLPROXY_NETWORK_H__ */
similarity index 97%
rename from nickserv.c
rename to src/nickserv.c
index dce39ba51ec35506a53a79969790574fc1ff69f1..0d4d53555fde0e04c8fae714f44ace73f8c44cbc 100644 (file)
@@ -53,7 +53,10 @@ void nickserv_identify_me(struct network *network, char *nick)
        const char *pass;
 
        /* Don't try to identify if we're already identified */
-       if (network->state->me.modes['R']) return;
+       /* FIXME: Apparently, +e indicates being registered on Freenode,
+        * +R is only used on OFTC */
+       if (network->state->me.modes['R']) 
+               return;
        
        pass = nickserv_find_nick(network, nick);
        
@@ -220,6 +223,7 @@ gboolean nickserv_load(struct global *global)
 
        g_free(filename);
 
+       g_io_channel_shutdown(gio, TRUE, NULL);
        g_io_channel_unref(gio);
 
        return TRUE;
similarity index 100%
rename from plugins.c
rename to src/plugins.c
similarity index 100%
rename from plugins.h
rename to src/plugins.h
similarity index 100%
rename from posix.c
rename to src/posix.c
similarity index 100%
rename from redirect.c
rename to src/redirect.c
similarity index 100%
rename from repl.c
rename to src/repl.c
similarity index 100%
rename from repl.h
rename to src/repl.h
similarity index 89%
rename from settings.c
rename to src/settings.c
index 1eb33c3866c5aa88782fdeffcd27745f00372794..1b41419b1caf2ed86b5c362a5a81e48ced06448b 100644 (file)
@@ -28,6 +28,8 @@
 #include <sys/socket.h>
 #include <glib/gstdio.h>
 
+#define DEFAULT_ADMIN_PORT 6680
+
 gboolean g_key_file_save_to_file(GKeyFile *kf, const gchar *file, GError **error)
 {
        gsize length, nr;
@@ -550,3 +552,56 @@ void free_config(struct ctrlproxy_config *cfg)
        g_free(cfg);
 }
 
+gboolean create_configuration(const char *config_dir)
+{
+       GKeyFile *kf;
+       struct global *global;
+       char port[250];
+       char *pass, *listenerfile;
+       GError *error = NULL;
+
+       if (g_file_test(config_dir, G_FILE_TEST_IS_DIR)) {
+               fprintf(stderr, "%s already exists\n", config_dir);
+               return FALSE;
+       }
+
+       if (g_mkdir(config_dir, 0700) != 0) {
+               fprintf(stderr, "Can't create config directory '%s': %s\n", config_dir, strerror(errno));
+               return FALSE;
+       }
+
+       global = new_global(DEFAULT_CONFIG_DIR);        
+       global->config->config_dir = g_strdup(config_dir);
+       save_configuration(global->config, config_dir);
+
+       kf = g_key_file_new();
+
+       snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
+       printf("Please specify port the administration interface should listen on.\n"
+                  "Prepend with a colon to listen on a specific address.\n"
+                  "Example: localhost:6668\n\nPort [%s]: ", port); fflush(stdout);
+       fgets(port, sizeof(port), stdin);
+
+       if (port[strlen(port)-1] == '\n')
+               port[strlen(port)-1] = '\0';
+
+       if (strlen(port) == 0) 
+               snprintf(port, sizeof(port), "%d", DEFAULT_ADMIN_PORT);
+
+       pass = getpass("Please specify a password for the administration interface: "); 
+       g_key_file_set_string(kf, port, "network", "admin");
+       if (!strcmp(pass, "")) {
+               fprintf(stderr, "Warning: no password specified. Authentication disabled!\n");
+       } else {
+               g_key_file_set_string(kf, port, "password", pass);
+       }
+
+       listenerfile = g_build_filename(config_dir, "listener", NULL);
+
+       if (!g_key_file_save_to_file(kf, listenerfile, &error)) {
+               fprintf(stderr, "Error saving %s: %s\n", listenerfile, error->message);
+               return FALSE;
+       }
+
+       return TRUE;
+}
similarity index 100%
rename from settings.h
rename to src/settings.h
similarity index 99%
rename from state.c
rename to src/state.c
index 18bc28b4968d81bc6bc036d049963c6ab4e389a1..5868e6b526dfba37fa2f8ed6d8a25d3c70d7225c 100644 (file)
--- a/state.c
@@ -186,6 +186,7 @@ static void free_channel(struct channel_state *c)
        g_free(c->name);
        g_free(c->topic);
        g_free(c->key);
+       g_assert(c->network);
        c->network->channels = g_list_remove(c->network->channels, c);
        g_free(c);
 }
@@ -843,9 +844,7 @@ void free_network_state(struct network_state *state)
                return;
 
        while(state->channels)
-       {
                free_channel((struct channel_state *)state->channels->data);
-       }
 
        g_free(state->me.nick);
        g_free(state->me.username);
@@ -1070,9 +1069,11 @@ static gboolean marshall_network_state (struct network_state *nst, enum marshall
 {
        gboolean ret = TRUE;
 
+       ret &= marshall_network_nick(n, m, t, &n->me);
        ret &= marshall_GList(n, m, t, &n->nicks, (marshall_fn_t)marshall_network_nick_p);
        ret &= marshall_GList(n, m, t, &n->channels, (marshall_fn_t)marshall_channel_state);
-       ret &= marshall_network_nick(n, m, t, &n->me);
+
+       g_assert(n->me.nick);
 
        return ret;
 }
similarity index 97%
rename from state.h
rename to src/state.h
index 23a47e0db60275b1c3a247f38cf7ed6bb2a10750..26d1b7f9afbd730921e88a4d8ec84a09d3475a14 100644 (file)
--- a/state.h
@@ -104,6 +104,7 @@ G_MODULE_EXPORT gboolean state_handle_data(struct network_state *s, struct line
 
 G_MODULE_EXPORT struct channel_state *find_channel(struct network_state *st, const char *name);
 G_MODULE_EXPORT struct channel_nick *find_channel_nick(struct channel_state *c, const char *name);
+G_MODULE_EXPORT struct channel_nick *find_add_channel_nick(struct channel_state *c, const char *name);
 G_MODULE_EXPORT struct network_nick *find_network_nick(struct network_state *c, const char *name);
 G_MODULE_EXPORT gboolean network_nick_set_hostmask(struct network_nick *n, const char *hm);
 G_MODULE_EXPORT gboolean client_send_state(struct client *, struct network_state *);
diff --git a/src/user.c b/src/user.c
new file mode 100644 (file)
index 0000000..9d1d633
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+       ctrlproxy: A modular IRC proxy
+       (c) 2002-2006 Jelmer Vernooij <jelmer@nl.linux.org>
+
+       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 2 of the License, 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "internals.h"
+
+static GList *load_notifies = NULL, *save_notifies = NULL;
+
+void register_load_config_notify(config_load_notify_fn fn)
+{
+       load_notifies = g_list_append(load_notifies, fn);
+}
+
+void register_save_config_notify(config_save_notify_fn fn)
+{
+       save_notifies = g_list_append(save_notifies, fn);
+}
+
+void config_load_notify(struct global *global)
+{
+       GList *gl;
+       for (gl = load_notifies; gl; gl = gl->next) {
+               config_load_notify_fn fn = gl->data;
+
+               fn(global);
+       }
+}
+
+void config_save_notify(struct global *global, const char *dest)
+{
+       GList *gl;
+       for (gl = save_notifies; gl; gl = gl->next) {
+               config_save_notify_fn fn = gl->data;
+
+               fn(global, dest);
+       }
+}
+
+/* globals */
+struct global *my_global;
+
+struct global *new_global(const char *config_dir)
+{
+       struct global *global = g_new0(struct global, 1);
+
+       global->config = load_configuration(config_dir);
+
+       load_networks(global, global->config);
+
+       nickserv_load(global);
+
+       if (!global->config) {
+               g_free(global);
+               return NULL;
+       }
+
+       global->linestack = new_linestack(global->config);
+
+       config_load_notify(global);
+       
+       return global;
+}
+
+void free_global(struct global *global)
+{
+       fini_networks(global);
+       free_config(global->config);
+       global->config = NULL;
+       free_linestack_context(global->linestack); global->linestack = NULL;
+       fini_networks(global);
+}
+
similarity index 100%
rename from util.c
rename to src/util.c
diff --git a/testsuite/Makefile b/testsuite/Makefile
deleted file mode 100644 (file)
index 2649e34..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-all: torture $(patsubst %.c,lib%.so,$(wildcard test-*.c))
-
-
index 9a6d64717647a4a34de06f308125c5ac8fad22b9..9a99fc18e804dab5f3ea885151103476515d280f 100644 (file)
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include "torture.h"
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
-#include "../ctrlproxy.h"
+#include <check.h>
+#include "ctrlproxy.h"
 
-int test_rfccmp(void)
-{
-       if (str_rfc1459cmp("abcde", "ABCDE") != 0) return -1;
-       if (str_rfc1459cmp("abcde~{}", "ABCDE^[]") != 0) return -2;
-       if (str_asciicmp("abcde", "ABCDE") != 0) return -3;
-       if (str_strictrfc1459cmp("abcde{}", "ABCDE[]") != 0) return -4;
-       if (str_strictrfc1459cmp("abcde{}^", "ABCDE[]~") == 0) return -5;
-       if (str_strictrfc1459cmp("abcde{}", "abcde{}") != 0) return -6;
-       if (str_strictrfc1459cmp("abcde{}^", "abcde{}") == 0) return -7;
-
-       return 0;
-}
+START_TEST(test_rfccmp)
+       fail_if (str_rfc1459cmp("abcde", "ABCDE") != 0);
+       fail_if (str_rfc1459cmp("abcde~{}", "ABCDE^[]") != 0);
+       fail_if (str_asciicmp("abcde", "ABCDE") != 0);
+       fail_if (str_strictrfc1459cmp("abcde{}", "ABCDE[]") != 0);
+       fail_if (str_strictrfc1459cmp("abcde{}^", "ABCDE[]~") == 0);
+       fail_if (str_strictrfc1459cmp("abcde{}", "abcde{}") != 0);
+       fail_if (str_strictrfc1459cmp("abcde{}^", "abcde{}") == 0);
+END_TEST
 
-void torture_init(void)
+Suite *cmp_suite()
 {
-       register_test("TEST-IRCCMP1459", test_rfccmp);
+       Suite *s = suite_create("cmp");
+       TCase *tc_core = tcase_create("core");
+       suite_add_tcase(s, tc_core);
+       tcase_add_test(tc_core, test_rfccmp);
+       return s;
 }
index 1d4c210f9aaf77244fd3c861ad77904a523a18fb..dc0c8366b225f48027a7a7396bfba82a5cdc1910 100644 (file)
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include "torture.h"
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
+#include <check.h>
 #include "ctrlproxy.h"
 
-static int isupport_isprefix(void)
-{
-       if (!is_prefix('@', NULL)) return -1;
-       if (is_prefix('a', NULL)) return -2;
+START_TEST(isupport_isprefix)
+       fail_if (!is_prefix('@', NULL));
+       fail_if (is_prefix('a', NULL));
+END_TEST
 
-       return 0;
-}
+START_TEST(isupport_ischannelname)
+       fail_if (!is_channelname("#bla", NULL));
+       fail_if (!is_channelname("&bla", NULL));
+       fail_if (is_channelname("bla", NULL));
+END_TEST
 
-static int isupport_ischannelname(void)
-{
-       if (!is_channelname("#bla", NULL)) return -1;
-       if (!is_channelname("&bla", NULL)) return -2;
-       if (is_channelname("bla", NULL)) return -3;
-
-       return 0;
-}
-
-static int isupport_prefixbymode(void)
-{
-       if (get_prefix_by_mode('o',NULL) != '@') return -1;
-       if (get_prefix_by_mode('v',NULL) != '+') return -2;
-       if (get_prefix_by_mode('x',NULL) != ' ') return -3;
-
-       return 0;
-}
+START_TEST(isupport_prefixbymode)
+       fail_if (get_prefix_by_mode('o',NULL) != '@');
+       fail_if (get_prefix_by_mode('v',NULL) != '+');
+       fail_if (get_prefix_by_mode('x',NULL) != ' ');
+END_TEST
 
-void torture_init(void)
+Suite *isupport_suite(void)
 {
-       register_test("ISUPPORT-ISPREFIX", isupport_isprefix);
-       register_test("ISUPPORT-ISCHANNELNAME", isupport_ischannelname);
-       register_test("ISUPPORT-PREFIXBYMODE", isupport_prefixbymode);
+       Suite *s = suite_create("isupport");
+       TCase *tc_core = tcase_create("core");
+       suite_add_tcase(s, tc_core);
+       tcase_add_test(tc_core, isupport_isprefix);
+       tcase_add_test(tc_core, isupport_ischannelname);
+       tcase_add_test(tc_core, isupport_prefixbymode);
+       return s;
 }
index c3c619ddb4f785be67be0214b72b6d8917fc18c7..58a43e4592e2385041970eb7a23b9bee00550b2d 100644 (file)
@@ -1,8 +1,8 @@
 #include <stdio.h>
 #include <malloc.h>
 #include <string.h>
-#include "torture.h"
-#include "../line.h"
+#include <check.h>
+#include "line.h"
 
 static const char *malformed[] = {
        "PRIVMSG :foo :bar",
@@ -13,11 +13,11 @@ static const char *malformed[] = {
        NULL
 };
 
-static int parser_malformed(void)
-{
+START_TEST(parser_malformed)
        struct line *l;
        char *raw;
        int i;
+
        for (i = 0; malformed[i]; i++) {
                l = irc_parse_line(malformed[i]);
                if (!l) continue;
@@ -25,30 +25,21 @@ static int parser_malformed(void)
                free(raw);
                free_line(l);
        }
-
-       return 0;
-}
+END_TEST
 
 #define NUM_RUNS 200
 
-static int parser_random(void)
-{
+START_TEST(parser_random)
        struct line *l;
        char *raw;
        char buf[4096];
        FILE *f = fopen("/dev/urandom", "r");
        int i;
 
-       if (!f) {
-               perror("Couldn't open /dev/urandom");
-               return -1;
-       }
+       fail_if (!f, "Couldn't open /dev/urandom");
 
        for (i = 0; i < 200; i++) {
-               if (!fgets(buf, sizeof(buf)-2, f)) {
-                       perror("error reading random data");
-                       return -1;
-               }
+               fail_if (!fgets(buf, sizeof(buf)-2, f), "error reading random data");
        
                l = irc_parse_line(buf);
                if (!l) continue;
@@ -58,25 +49,19 @@ static int parser_random(void)
        }
 
        fclose(f);
+END_TEST
 
-       return 0;
-}
-
-static int parser_vargs(void)
-{
+START_TEST(parser_vargs)
        struct line *l = irc_parse_line_args( "FOO", "x", "y", NULL);
 
-       if (!l) return -1;
-       if (strcmp(l->origin, "FOO") != 0) return -2;
-       if (l->argc != 2) return -3;
-       if (strcmp(l->args[0], "x") != 0) return -4;
-       if (strcmp(l->args[1], "y") != 0) return -5;
+       fail_if (!l);
+       fail_if (strcmp(l->origin, "FOO") != 0);
+       fail_if (l->argc != 2);
+       fail_if (strcmp(l->args[0], "x") != 0);
+       fail_if (strcmp(l->args[1], "y") != 0);
+END_TEST
 
-       return 0;
-}
-
-static int parser_stringnl(void)
-{
+START_TEST( parser_stringnl)
        struct line l;
        char *ret;
        char *args[] = { "x", "y", "z", NULL };
@@ -87,29 +72,24 @@ static int parser_stringnl(void)
 
        ret = irc_line_string_nl(&l);
 
-       if (strcmp(ret, ":foobar x y z\r\n") != 0) return -1;
+       fail_if (strcmp(ret, ":foobar x y z\r\n") != 0);
+END_TEST
 
-       return 0;
-}
-
-static int parser_get_nick(void)
-{
+START_TEST(parser_get_nick)
        struct line l;
        char *nick;
 
        l.origin = "foobar";
        nick = line_get_nick(&l);
-       if (strcmp(nick, "foobar") != 0) { g_free(nick); return -3; }
+       fail_if (strcmp(nick, "foobar") != 0);
        l.origin = "foobar!~username@userhost";
        g_free(nick);
        nick = line_get_nick(&l);
-       if (strcmp(nick, "foobar") != 0) { g_free(nick); return -4; }
+       fail_if (strcmp(nick, "foobar") != 0);
        g_free(nick);
-       return 0;
-}
+END_TEST
 
-static int parser_dup(void)
-{
+START_TEST(parser_dup)
        struct line l, *m;
        char *args[] = { "x", "y", "z", NULL };
        l.is_private = 1;
@@ -120,26 +100,28 @@ static int parser_dup(void)
 
        m = linedup(&l);
 
-       if (l.is_private != m->is_private) return -1;
-       if (strcmp(l.origin, m->origin)) return -3;
-       if (l.argc != m->argc) return -4;
-       if (strcmp(l.args[0], m->args[0])) return -5;
-       if (strcmp(l.args[1], m->args[1])) return -6;
-       if (strcmp(l.args[2], m->args[2])) return -7;
+       fail_if (l.is_private != m->is_private);
+       fail_if (strcmp(l.origin, m->origin));
+       fail_if (l.argc != m->argc);
+       fail_if (strcmp(l.args[0], m->args[0]));
+       fail_if (strcmp(l.args[1], m->args[1]));
+       fail_if (strcmp(l.args[2], m->args[2]));
 
        l.origin = NULL;
        m = linedup(&l);
-       if (m->origin) return -8;
-
-       return 0;
-}
+       fail_if (m->origin);
+END_TEST
 
-void torture_init(void)
+Suite *parser_suite(void)
 {
-       register_test("PARSER-VARGS", parser_vargs);
-       register_test("PARSER-STRINGNL", parser_stringnl);
-       register_test("PARSER-MALFORMED", parser_malformed);
-       register_test("PARSER-RANDOM", parser_random);
-       register_test("PARSER-GETNICK", parser_get_nick);
-       register_test("PARSER-DUP", parser_dup);
+       Suite *s = suite_create("parser");
+       TCase *tcase = tcase_create("core");
+       suite_add_tcase(s, tcase);
+       tcase_add_test(tcase, parser_vargs);
+       tcase_add_test(tcase, parser_stringnl);
+       tcase_add_test(tcase, parser_malformed);
+       tcase_add_test(tcase, parser_random);
+       tcase_add_test(tcase, parser_get_nick);
+       tcase_add_test(tcase, parser_dup);
+       return s;
 }
diff --git a/testsuite/test-ssl.c b/testsuite/test-ssl.c
deleted file mode 100644 (file)
index 81d99b1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-
-/*
-       ircdtorture: an IRC RFC compliancy tester
-       (c) 2005 Jelmer Vernooij <jelmer@nl.linux.org>
-
-       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 2 of the License, 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.
-*/
-
-#include "torture.h"
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-
-int test;
-
-void torture_init(void)
-{
-}
index 34fe370d1457b7b84e1e117aa48cb2d64b50c205..61e54a352b35b355e8aec33834400a9e8387560e 100644 (file)
@@ -1,4 +1,3 @@
-
 /*
     ircdtorture: an IRC RFC compliancy tester
        (c) 2005 Jelmer Vernooij <jelmer@nl.linux.org>
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include "torture.h"
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
+#include <check.h>
 #include "ctrlproxy.h"
 
 gboolean network_nick_set_nick(struct network_nick *, const char *);
 gboolean network_nick_set_hostmask(struct network_nick *, const char *);
 
-static int state_init(void)
-{
+START_TEST(state_init)
        struct network_state *ns = network_state_init(NULL, "bla", "Gebruikersnaam", "Computernaam");
 
-       if (!ns) return -1;
-
-       if (strcmp(ns->me.nick, "bla") != 0) return -2;
-       if (strcmp(ns->me.username, "Gebruikersnaam") != 0) return -3;
-       if (strcmp(ns->me.hostname, "Computernaam") != 0) return -4;
+       fail_if(ns == NULL, "network_state_init returned NULL");
 
-       if (g_list_length(ns->channels) != 0)
-               return -5;
+       fail_unless (strcmp(ns->me.nick, "bla") == 0);
+       fail_unless (strcmp(ns->me.username, "Gebruikersnaam") == 0);
+       fail_unless (strcmp(ns->me.hostname, "Computernaam") == 0);
 
-       return 0;
-}
+       fail_unless (g_list_length(ns->channels) == 0);
+END_TEST
 
 static void state_process(struct network_state *ns, const char *line)
 {
@@ -52,95 +47,76 @@ static void state_process(struct network_state *ns, const char *line)
        free_line(l);
 }
 
-static int state_join(void)
-{
+START_TEST(state_join)
        struct network_state *ns = network_state_init(NULL, "bla", "Gebruikersnaam", "Computernaam");
        struct channel_state *cs;
 
-       if (!ns) return -1;
+       fail_if (!ns);
 
        state_process(ns, ":bla!user@host JOIN #examplechannel");
 
-       if (g_list_length(ns->channels) != 1)
-               return -1;
+       fail_unless (g_list_length(ns->channels) == 1);
        
        cs = ns->channels->data;
 
-       if (strcmp(cs->name, "#examplechannel") != 0)
-               return -2;
+       fail_unless (strcmp(cs->name, "#examplechannel") == 0);
+END_TEST
 
-       return 0;
-}
-
-static int state_part(void)
-{
+START_TEST(state_part)
        struct network_state *ns = network_state_init(NULL, "bla", "Gebruikersnaam", "Computernaam");
        struct channel_state *cs;
 
-       if (!ns) return -1;
+       fail_if (!ns);
 
        state_process(ns, ":bla!user@host JOIN #examplechannel");
 
-       if (g_list_length(ns->channels) != 1)
-               return -1;
+       fail_unless (g_list_length(ns->channels) == 1);
        
        cs = ns->channels->data;
 
-       if (strcmp(cs->name, "#examplechannel") != 0)
-               return -2;
+       fail_unless (strcmp(cs->name, "#examplechannel") == 0);
        
        state_process(ns, ":bla!user@host PART #examplechannel");
 
-       if (g_list_length(ns->channels) != 0)
-               return -3;
+       fail_unless (g_list_length(ns->channels) == 0);
+END_TEST
 
-       return 0;
-}
-
-static int state_nick_change(void)
-{
+START_TEST(state_nick_change)
        struct network_state *ns = network_state_init(NULL, "bla", "Gebruikersnaam", "Computernaam");
 
-       if (!ns) return -1;
+       fail_if(ns == NULL);
 
        state_process(ns, ":bla!user@host NICK blie");
 
-       return strcmp(ns->me.nick, "blie");
-}
+       fail_unless(strcmp(ns->me.nick, "blie") == 0);
+END_TEST
 
-static int state_set_nick(void)
-{
+START_TEST(state_set_nick)
        struct network_nick nn;
        memset(&nn, 0, sizeof(nn));
        
-       if (!network_nick_set_nick(&nn, "mynick")) return -5;
-       if (strcmp(nn.nick, "mynick") != 0) return -1;
-       if (strcmp(nn.hostmask, "mynick!~(null)@(null)") != 0) return -2;
-       if (!network_nick_set_nick(&nn, "mynick")) return -6;
-       if (strcmp(nn.nick, "mynick") != 0) return -3;
-       if (strcmp(nn.hostmask, "mynick!~(null)@(null)") != 0) return -4;
-
-       return 0;
-}
-
-static int state_set_hostmask(void)
-{
+       fail_if (!network_nick_set_nick(&nn, "mynick"));
+       fail_unless (strcmp(nn.nick, "mynick") == 0);
+       fail_unless (strcmp(nn.hostmask, "mynick!~(null)@(null)") == 0);
+       fail_if (!network_nick_set_nick(&nn, "mynick"));
+       fail_unless (strcmp(nn.nick, "mynick") == 0);
+       fail_unless (strcmp(nn.hostmask, "mynick!~(null)@(null)") == 0);
+END_TEST
+
+START_TEST(state_set_hostmask)
        struct network_nick nn;
        memset(&nn, 0, sizeof(nn));
 
-       if (!network_nick_set_hostmask(&nn, "ikke!~uname@uhost")) return -3;
-       if (!nn.nick || strcmp(nn.nick, "ikke") != 0) return -4;
-       if (!nn.username || strcmp(nn.username, "~uname") != 0) return -5;
-       if (!nn.hostname || strcmp(nn.hostname, "uhost") != 0) return -6;
-       if (!network_nick_set_hostmask(&nn, "ikke!~uname@uhost")) return -7;
+       fail_if (!network_nick_set_hostmask(&nn, "ikke!~uname@uhost"));
+       fail_if (!nn.nick || strcmp(nn.nick, "ikke") != 0);
+       fail_if (!nn.username || strcmp(nn.username, "~uname") != 0);
+       fail_if (!nn.hostname || strcmp(nn.hostname, "uhost") != 0);
+       fail_if (!network_nick_set_hostmask(&nn, "ikke!~uname@uhost"));
 
-       if (network_nick_set_hostmask(&nn, "ikkeongeldig")) return -8;
+       fail_if (network_nick_set_hostmask(&nn, "ikkeongeldig"));
+END_TEST
 
-       return 0;
-}
-
-static int state_marshall_simple(void)
-{
+START_TEST(state_marshall_simple)
        struct network_state *s, *t;
        size_t len1, len2;
        char *data1, *data2;
@@ -150,29 +126,32 @@ static int state_marshall_simple(void)
        t = network_state_decode(data1, len1, NULL);
        data2 = network_state_encode(s, &len2);
 
-       if (len1 != len2) return -1;
+       fail_unless (len1 == len2);
 
-       if (memcmp(data1, data2, len1) != 0) return -2;
+       fail_unless (memcmp(data1, data2, len1) == 0);
 
-       if (strcmp(s->me.nick, t->me.nick) != 0) return -3;
-       if (strcmp(s->me.username, t->me.username) != 0) return -3;
-       if (strcmp(s->me.hostname, t->me.hostname) != 0) return -4;
+       fail_unless (strcmp(s->me.nick, t->me.nick) == 0);
+       fail_unless (strcmp(s->me.username, t->me.username) == 0);
+       fail_unless (strcmp(s->me.hostname, t->me.hostname) == 0);
 
        free_network_state(t);
-
-       return 0;
-}
+END_TEST
 
 gboolean init_log(const char *lf);
 
-void torture_init(void)
+Suite *state_suite(void)
 {
+       Suite *s = suite_create("state");
+       TCase *tc_core = tcase_create("Core");
+       suite_add_tcase(s, tc_core);
+       tcase_add_test(tc_core, state_init);
+       /* FIXME: Setup */
        init_log("test-state");
-       register_test("STATE-INIT", state_init);
-       register_test("STATE-JOIN", state_join);
-       register_test("STATE-PART", state_part);
-       register_test("STATE-SETNICK", state_set_nick);
-       register_test("STATE-SETHOSTMASK", state_set_hostmask);
-       register_test("STATE-NICK-CHANGE", state_nick_change);
-       register_test("MARSHALL-SIMPLE", state_marshall_simple);
+       tcase_add_test(tc_core, state_join);
+       tcase_add_test(tc_core, state_part);
+       tcase_add_test(tc_core, state_set_nick);
+       tcase_add_test(tc_core, state_set_hostmask);
+       tcase_add_test(tc_core, state_nick_change);
+       tcase_add_test(tc_core, state_marshall_simple);
+       return s;
 }
index cf34c9814580f80c3244e3baa8d68bd3f27bdb06..7a20246b48edf5ad4a1329405511f4c8aa1788fc 100644 (file)
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#include "torture.h"
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <glib.h>
-#include "../ctrlproxy.h"
+#include <check.h>
+#include "ctrlproxy.h"
 
-static int test_list_make_string(void)
-{
+START_TEST(test_list_make_string)
        GList *gl = NULL;
        char *ret;
 
        ret = list_make_string(NULL);
-       if (strcmp(ret, "") != 0)
-               return -1;
+       fail_unless (strcmp(ret, "") == 0);
 
        gl = g_list_append(gl, "bla");
        gl = g_list_append(gl, "bloe");
        
        ret = list_make_string(gl);
-       if (strcmp(ret, "bla bloe") != 0)
-               return -2;
+       fail_unless (strcmp(ret, "bla bloe") == 0);
        
-       return 0;
-}
+END_TEST
 
-void torture_init(void)
+Suite *util_suite(void)
 {
-       register_test("UTIL-LIST-STRING", test_list_make_string);
+       Suite *s = suite_create("util");
+       TCase *tc_core = tcase_create("Core");
+       suite_add_tcase(s, tc_core);
+       tcase_add_test(tc_core, test_list_make_string);
+       return s;
 }
index 1a072f4ad2094fc33c2583633190884e09a71a89..308920543ec7d042b713c85eda6c3167b51f725e 100644 (file)
 #include "config.h"
 #endif
 
-#include "torture.h"
-
 #include <stdio.h>
 #include <glib.h>
 #include <gmodule.h>
-#include "../ctrlproxy.h"
+#include "ctrlproxy.h"
+#include <check.h>
 
 #define DEFAULT_TIMEOUT 1000
 
-struct torture_test {
-       const char *name;
-       int (*test) (void);
-};
-
-GList *tests = NULL;
-
-void register_test(const char *name, int (*data) (void)) 
-{
-       struct torture_test *test = g_new0(struct torture_test, 1);
-       test->name = g_strdup(name);
-       test->test = data;
-       tests = g_list_append(tests, test);
-}
-
-int run_test(struct torture_test *test)
-{
-       int ret;
-       printf("Running %s... ", test->name);
-       fflush(stdout);
-       ret = test->test();
-       if (ret) printf("failed (%d)!\n", ret); else printf("ok\n");
-       return ret;
-}
-
-gboolean load_module(const char *name)
-{
-       GModule *m;
-       void (*init_func) (void);
-       char *path;
-
-       if (g_file_test(name, G_FILE_TEST_EXISTS))
-               path = g_strdup(name);
-       else
-               path = g_module_build_path(g_get_current_dir(), name);
-
-       m = g_module_open(path, G_MODULE_BIND_LAZY);
-
-       g_free(path);
-
-       if(!m) {
-               fprintf(stderr, "Can't open module %s: %s\n", name, g_module_error());
-               return FALSE;
-       }
-
-       if(!g_module_symbol(m, "torture_init", (gpointer)&init_func)) {
-               fprintf(stderr, "Can't find symbol \"torture_init\" in %s: %s\n", name, g_module_error());
-               return FALSE;
-       }
+Suite *util_suite(void);
+Suite *state_suite(void);
+Suite *isupport_suite(void);
+Suite *cmp_suite(void);
+Suite *parser_suite(void);
 
-       init_func();
-       return TRUE;
-}
-
-int main(int argc, const char *argv[])
+int main (void)
 {
-       GList *gl;
-       int ret = 0, i;
-
-       if (argc == 1) {
-               fprintf(stderr, "Usage: %s <so-file> [<so-file>...]\n", argv[0]);
-               return 1;
-       }
-
-       for (i = 1; i < argc; i++) {
-               if (!load_module(argv[i]))
-                       ret = -1;
-       }
-       
-       for (gl = tests; gl; gl = gl->next) {
-               if (run_test(gl->data)) 
-                       ret = -1;
-       }
-
-       return ret;
+       int nf;
+       SRunner *sr = srunner_create(util_suite());
+       srunner_add_suite(sr, state_suite());
+       srunner_add_suite(sr, isupport_suite());
+       srunner_add_suite(sr, cmp_suite());
+       srunner_add_suite(sr, parser_suite());
+       srunner_run_all (sr, CK_NORMAL);
+       nf = srunner_ntests_failed(sr);
+       srunner_free(sr);
+       return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/testsuite/torture.h b/testsuite/torture.h
deleted file mode 100644 (file)
index 3ebbf1b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __TORTURE_H__
-#define __TORTURE_H__
-
-#include <glib.h>
-
-void register_test(const char *name, int (*test) (void));
-
-#endif /* __TORTURE_H__ */