debian/files
debian/*.substvars
doc/user-guide
+*.d
+testsuite/check
--- /dev/null
+- SSL support is broken
+- Some of the documentation is outdated
+
+see http://bugs.bitlbee.org/ctrlproxy/ for a full list of known bugs.
+++ /dev/null
- 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.
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)
./$<
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)
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
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)
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)
CC = @CC@
prefix = @prefix@
exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
localedir = @datadir@/locale
INSTALL = @INSTALL@
bindir = @bindir@
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@
--- /dev/null
+ 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).
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
--------
* SSL support
* Custom logging in any format you specify
* Flood protection
+ * IPv6 Support
Requirements
------------
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..
+++ /dev/null
-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?
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.
# 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
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 -->
#[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
# 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!
#[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
--- /dev/null
+# 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
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.
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
-TODO
+BUGS
README
AUTHORS
UPGRADING
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
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 $<
$(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)
<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>
<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>
<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>
--- /dev/null
+<?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>
+++ /dev/null
-<?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 <plugin>
-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 <network> 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 <network> element supports the child element <channel>. 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 <network> element contains a <servers> 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>
<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>
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>
+++ /dev/null
-<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 <plugin> 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 <plugin> element should contain plugin-specific elements. See the documentation for the individual plugins for details.</para>
-</sect1>
-
-<sect1><title>Networks</title>
-
- <para>The <networks> element contains several <network>
- 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 <network> element can also contain
- several <channel> 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
- "simulate" 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>
<?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">
]>
<part id="configuration">
<title>Configuration</title>
- &syntax;
&clients;
</part>
--- /dev/null
+/*
+ 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,
+};
/*
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
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)
{
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;
}
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;
}
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)
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;
}
g_strfreev(groups);
g_free(filename);
- g_key_file_free(kf);
}
static void fini_plugin(void)
return;
}
+ if (!args[2]) {
+ admin_out(c, "No password specified");
+ return;
+ }
+
b = g_strdup(args[1]);
if ((p = strchr(b, ':'))) {
*p = '\0';
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;
}
}
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 }
struct log_custom_data {
char *logfilename;
- GHashTable *fmts;
+ GKeyFile *kf;
GHashTable *files;
};
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;
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) {
{
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 {
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);
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;
{
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");
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)
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 = []
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)
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);
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?" (":"",
}
g_free(tmp);
+
+ entered = FALSE;
}
const static struct admin_command builtin_commands[] = {
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;
}
struct global {
struct ctrlproxy_config *config;
struct linestack_context *linestack;
+ GList *new_network_notifiers;
GList *networks;
GList *nickserv_nicks;
};
/* 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);
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__ */
}
if (!current_backend) {
- current_backend = linestack_backends->data;
+ current_backend = &linestack_file;
}
ctx = g_new0(struct linestack_context, 1);
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(
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);
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);
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)
/* globals */
static GMainLoop *main_loop;
extern char my_hostname[];
-
-struct global *my_global;
+extern struct global *my_global;
static void signal_crash(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)
{
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;
{"debug-level", 'd', 'd', G_OPTION_ARG_INT, ¤t_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 }
};
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);
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);
/*
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
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);
struct network *load_network(struct global *global, struct network_config *sc)
{
struct network *s;
+ GList *gl;
g_assert(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;
}
log_network(NULL, LOG_INFO, n, "Trying next server");
n->connection.data.tcp.current_server = network_get_next_tcp_server(n);
}
+
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__ */
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);
g_free(filename);
+ g_io_channel_shutdown(gio, TRUE, NULL);
g_io_channel_unref(gio);
return TRUE;
#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;
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;
+}
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);
}
return;
while(state->channels)
- {
free_channel((struct channel_state *)state->channels->data);
- }
g_free(state->me.nick);
g_free(state->me.username);
{
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;
}
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 *);
--- /dev/null
+/*
+ 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);
+}
+
+++ /dev/null
-all: torture $(patsubst %.c,lib%.so,$(wildcard test-*.c))
-
-
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;
}
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;
}
#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",
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;
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;
}
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 };
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;
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;
}
+++ /dev/null
-
-/*
- 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)
-{
-}
-
/*
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)
{
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;
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;
}
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;
}
#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;
}
+++ /dev/null
-#ifndef __TORTURE_H__
-#define __TORTURE_H__
-
-#include <glib.h>
-
-void register_test(const char *name, int (*test) (void));
-
-#endif /* __TORTURE_H__ */