diff --git a/bandb/Makefile.in b/bandb/Makefile.in new file mode 100644 index 000000000..6d0a27c2e --- /dev/null +++ b/bandb/Makefile.in @@ -0,0 +1,92 @@ +# +# Makefile.in for bandb/src +# +# $Id: Makefile.in 1285 2006-05-05 15:03:53Z nenolod $ +# + +CC = @CC@ +INSTALL = @INSTALL@ +INSTALL_BIN = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SUID = @INSTALL_PROGRAM@ -o root -m 4755 +RM = @RM@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +CFLAGS = @IRC_CFLAGS@ -DIRCD_PREFIX=\"@prefix@\" +LDFLAGS = @LDFLAGS@ +MKDEP = @MKDEP@ -DIRCD_PREFIX=\"@prefix@\" +MV = @MV@ +RM = @RM@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +libexecdir = @libexecdir@ +confdir = @confdir@ +localstatedir = @localstatedir@ + +ZIP_LIB = @ZLIB_LD@ + +IRCDLIBS = @MODULES_LIBS@ -L../libratbox/src/.libs -lratbox @LIBS@ $(SSL_LIBS) $(ZIP_LIB) + +INCLUDES = -I. -I../include -I../libratbox/include $(SSL_INCLUDES) +CPPFLAGS = ${INCLUDES} @CPPFLAGS@ + +PROGS = bandb bantool + +BANDB_SOURCES = \ + bandb.c \ + rsdb_sprintf.c \ + rsdb_sqlite3.c + +BANDB_OBJECTS = ${BANDB_SOURCES:.c=.o} + +BANTOOL_SOURCES = \ + bantool.c \ + rsdb_sprintf.c \ + rsdb_sqlite3.c + +BANTOOL_OBJECTS = ${BANDB_SOURCES:.c=.o} + +all: bandb bantool + +build: all + +bandb: ${BANDB_OBJECTS} + ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJECTS} ${IRCDLIBS} + +bantool: ${BANTOOL_OBJECTS} + ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJECTS} ${IRCDLIBS} + +install: build + @echo "ircd: installing bandb ($(PROGS))" + @for i in $(PROGS); do \ + if test -f $(DESTDIR)$(bindir)/$$i; then \ + $(MV) $(DESTDIR)$(bindir)/$$i $(DESTDIR)$(bindir)/$$i.old; \ + fi; \ + $(INSTALL_BIN) $$i $(DESTDIR)$(bindir); \ + done + +.c.o: + ${CC} ${CPPFLAGS} ${CFLAGS} -c $< + +.PHONY: depend clean distclean +depend: + @${MKDEP} ${CPPFLAGS} ${SOURCES} > .depend.tmp + @sed -e '/^# DO NOT DELETE THIS LINE/,$$d' Makefile.depend + @echo '# DO NOT DELETE THIS LINE!!!' >>Makefile.depend + @echo '# make depend needs it.' >>Makefile.depend + @cat .depend.tmp >>Makefile.depend + @mv Makefile.depend Makefile + @rm -f .depend.tmp + +clean: + ${RM} -f *.o *~ *.core core bandb + +lint: + lint -aacgprxhH $(CPPFLAGS) -DIRCD_PREFIX=\"@prefix@\" $(SOURCES) >>../lint.out + +distclean: clean + ${RM} -f Makefile + +# End of Makefile diff --git a/bandb/bandb.c b/bandb/bandb.c new file mode 100644 index 000000000..cce310c4b --- /dev/null +++ b/bandb/bandb.c @@ -0,0 +1,309 @@ +/* bandb/bandb.c + * + * Copyright (C) 2006 Lee Hardy + * Copyright (C) 2006-2008 ircd-ratbox development team + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1.Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2.Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3.The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: bandb.c 26094 2008-09-19 15:33:46Z androsyn $ + */ +#include "setup.h" +#include +#include +#include "rsdb.h" +#include "common.h" + + +#define MAXPARA 10 + +typedef enum +{ + BANDB_KLINE, + BANDB_DLINE, + BANDB_XLINE, + BANDB_RESV, + LAST_BANDB_TYPE +} bandb_type; + +static char bandb_letter[LAST_BANDB_TYPE] = { + 'K', 'D', 'X', 'R' +}; + +static const char *bandb_table[LAST_BANDB_TYPE] = { + "kline", "dline", "xline", "resv" +}; + + +static rb_helper *bandb_helper; + +static void check_schema(void); + +static void +parse_ban(bandb_type type, char *parv[], int parc) +{ + const char *mask1 = NULL; + const char *mask2 = NULL; + const char *oper = NULL; + const char *curtime = NULL; + const char *reason = NULL; + const char *perm = NULL; + int para = 1; + + if(type == BANDB_KLINE) + { + if(parc != 7) + return; + } + else if(parc != 6) + return; + + mask1 = parv[para++]; + + if(type == BANDB_KLINE) + mask2 = parv[para++]; + + oper = parv[para++]; + curtime = parv[para++]; + perm = parv[para++]; + reason = parv[para++]; + + rsdb_exec(NULL, + "INSERT INTO %s (mask1, mask2, oper, time, perm, reason) VALUES('%Q', '%Q', '%Q', %s, %s, '%Q')", + bandb_table[type], mask1, mask2 ? mask2 : "", oper, curtime, perm, reason); +} + +static void +parse_unban(bandb_type type, char *parv[], int parc) +{ + const char *mask1 = NULL; + const char *mask2 = NULL; + + if(type == BANDB_KLINE) + { + if(parc != 3) + return; + } + else if(parc != 2) + return; + + mask1 = parv[1]; + + if(type == BANDB_KLINE) + mask2 = parv[2]; + + rsdb_exec(NULL, "DELETE FROM %s WHERE mask1='%Q' AND mask2='%Q'", + bandb_table[type], mask1, mask2 ? mask2 : ""); +} + +static void +list_bans(void) +{ + static char buf[512]; + struct rsdb_table table; + int i, j; + + /* schedule a clear of anything already pending */ + rb_helper_write_queue(bandb_helper, "C"); + + for(i = 0; i < LAST_BANDB_TYPE; i++) + { + rsdb_exec_fetch(&table, "SELECT mask1,mask2,oper,reason FROM %s WHERE 1", + bandb_table[i]); + + for(j = 0; j < table.row_count; j++) + { + if(i == BANDB_KLINE) + rb_snprintf(buf, sizeof(buf), "%c %s %s %s :%s", + bandb_letter[i], table.row[j][0], + table.row[j][1], table.row[j][2], table.row[j][3]); + else + rb_snprintf(buf, sizeof(buf), "%c %s %s :%s", + bandb_letter[i], table.row[j][0], + table.row[j][2], table.row[j][3]); + + rb_helper_write_queue(bandb_helper, "%s", buf); + } + + rsdb_exec_fetch_end(&table); + } + + rb_helper_write(bandb_helper, "F"); +} + +static void +parse_request(rb_helper *helper) +{ + static char *parv[MAXPARA + 1]; + static char readbuf[READBUF_SIZE]; + int parc; + int len; + + + while((len = rb_helper_read(helper, readbuf, sizeof(readbuf))) > 0) + { + parc = rb_string_to_array(readbuf, parv, MAXPARA); + + if(parc < 1) + continue; + + switch (parv[0][0]) + { + case 'K': + parse_ban(BANDB_KLINE, parv, parc); + break; + + case 'D': + parse_ban(BANDB_DLINE, parv, parc); + break; + + case 'X': + parse_ban(BANDB_XLINE, parv, parc); + break; + + case 'R': + parse_ban(BANDB_RESV, parv, parc); + break; + + case 'k': + parse_unban(BANDB_KLINE, parv, parc); + break; + + case 'd': + parse_unban(BANDB_DLINE, parv, parc); + break; + + case 'x': + parse_unban(BANDB_XLINE, parv, parc); + break; + + case 'r': + parse_unban(BANDB_RESV, parv, parc); + break; + + case 'L': + list_bans(); + break; + default: + break; + } + } +} + + +static void +error_cb(rb_helper *helper) +{ + exit(1); +} + +#ifndef WINDOWS +static void +dummy_handler(int sig) +{ + return; +} +#endif + +static void +setup_signals() +{ +#ifndef WINDOWS + struct sigaction act; + + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask, SIGPIPE); + sigaddset(&act.sa_mask, SIGALRM); +#ifdef SIGTRAP + sigaddset(&act.sa_mask, SIGTRAP); +#endif + +#ifdef SIGWINCH + sigaddset(&act.sa_mask, SIGWINCH); + sigaction(SIGWINCH, &act, 0); +#endif + sigaction(SIGPIPE, &act, 0); +#ifdef SIGTRAP + sigaction(SIGTRAP, &act, 0); +#endif + + act.sa_handler = dummy_handler; + sigaction(SIGALRM, &act, 0); +#endif +} + + +static void +db_error_cb(const char *errstr) +{ + char buf[256]; + rb_snprintf(buf, sizeof(buf), "! :%s", errstr); + rb_helper_write(bandb_helper, buf); + rb_sleep(2 << 30, 0); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + setup_signals(); + bandb_helper = rb_helper_child(parse_request, error_cb, NULL, NULL, NULL, 256, 256, 256, 256); /* XXX fix me */ + if(bandb_helper == NULL) + { + fprintf(stderr, + "This is ircd-ratbox bandb. You aren't supposed to run me directly. Maybe you want bantool?\n"); + fprintf(stderr, + "However I will print my Id tag $Id: bandb.c 26094 2008-09-19 15:33:46Z androsyn $\n"); + fprintf(stderr, "Have a nice day\n"); + exit(1); + } + rsdb_init(db_error_cb); + check_schema(); + rb_helper_loop(bandb_helper, 0); + + return 0; +} + +static void +check_schema(void) +{ + struct rsdb_table table; + int i; + + for(i = 0; i < LAST_BANDB_TYPE; i++) + { + rsdb_exec_fetch(&table, + "SELECT name FROM sqlite_master WHERE type='table' AND name='%s'", + bandb_table[i]); + + rsdb_exec_fetch_end(&table); + + if(!table.row_count) + rsdb_exec(NULL, + "CREATE TABLE %s (mask1 TEXT, mask2 TEXT, oper TEXT, time INTEGER, perm INTEGER, reason TEXT)", + bandb_table[i]); + } +} diff --git a/bandb/bantool.c b/bandb/bantool.c new file mode 100644 index 000000000..871474209 --- /dev/null +++ b/bandb/bantool.c @@ -0,0 +1,899 @@ +/** + * ircd-ratbox: A slightly useful ircd. + * bantool.c: The ircd-ratbox database managment tool. + * + * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center + * Copyright (C) 1996-2002 Hybrid Development Team + * Copyright (C) 2002-2008 ircd-ratbox development team + * Copyright (C) 2008 Daniel J Reidy + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * $Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $ + * + * + * The following server admins have either contributed various configs to test against, + * or helped with debugging and feature requests. Many thanks to them. + * stevoo / efnet.port80.se + * AndroSyn / irc2.choopa.net, irc.igs.ca + * Salvation / irc.blessed.net + * JamesOff / efnet.demon.co.uk + * + * Thanks to AndroSyn for challenging me to learn C on the fly :) + * BUGS Direct Question, Bug Reports, and Feature Requests to #ratbox on EFnet. + * BUGS Complaints >/dev/null + * + */ + +#define _XOPEN_SOURCE +#include +#include +#include + +#include "stdinc.h" +#include "rsdb.h" + +#define EmptyString(x) ((x == NULL) || (*(x) == '\0')) +#define CheckEmpty(x) EmptyString(x) ? "" : x + +#define BT_VERSION "0.4.1" + +typedef enum +{ + BANDB_KLINE, + BANDB_KLINE_PERM, + BANDB_DLINE, + BANDB_DLINE_PERM, + BANDB_XLINE, + BANDB_XLINE_PERM, + BANDB_RESV, + BANDB_RESV_PERM, + LAST_BANDB_TYPE +} bandb_type; + + +static char bandb_letter[LAST_BANDB_TYPE] = { + 'K', 'K', 'D', 'D', 'X', 'X', 'R', 'R' +}; + +static const char *bandb_table[LAST_BANDB_TYPE] = { + "kline", "kline", "dline", "dline", "xline", "xline", "resv", "resv" +}; + +static const char *bandb_suffix[LAST_BANDB_TYPE] = { + "", ".perm", + "", ".perm", + "", ".perm", + "", ".perm" +}; + +static char me[PATH_MAX]; + +/* *INDENT-OFF* */ +/* report counters */ +struct counter +{ + unsigned int klines; + unsigned int dlines; + unsigned int xlines; + unsigned int resvs; + unsigned int error; +} count = {0, 0, 0, 0, 0}; + +/* flags set by command line options */ +struct flags +{ + int none; + int export; + int import; + int verify; + int vacuum; + int pretend; + int verbose; + int wipe; + int dupes_ok; +} flag = {YES, NO, NO, NO, NO, NO, NO, NO, NO}; +/* *INDENT-ON* */ + +static int table_has_rows(const char *table); +static int table_exists(const char *table); + +static const char *clean_gecos_field(const char *gecos); +static char *bt_smalldate(const char *string); +static char *getfield(char *newline); +static char *strip_quotes(const char *string); +static char *mangle_reason(const char *string); +static char *escape_quotes(const char *string); + +static void db_error_cb(const char *errstr); +static void db_reclaim_slack(void); +static void export_config(const char *conf, int id); +static void import_config(const char *conf, int id); +static void check_schema(void); +static void print_help(int i_exit); +static void wipe_schema(void); +static void drop_dupes(const char *user, const char *host, const char *t); + +/** + * swing your pants + */ +int +main(int argc, char *argv[]) +{ + char etc[PATH_MAX]; + char conf[PATH_MAX]; + int opt; + int i; + + rb_strlcpy(me, argv[0], sizeof(me)); + + while((opt = getopt(argc, argv, "hieuspvwd")) != -1) + { + switch (opt) + { + case 'h': + print_help(EXIT_SUCCESS); + break; + case 'i': + flag.none = NO; + flag.import = YES; + break; + case 'e': + flag.none = NO; + flag.export = YES; + break; + case 'u': + flag.none = NO; + flag.verify = YES; + break; + case 's': + flag.none = NO; + flag.vacuum = YES; + break; + case 'p': + flag.pretend = YES; + break; + case 'v': + flag.verbose = YES; + break; + case 'w': + flag.wipe = YES; + break; + case 'd': + flag.dupes_ok = YES; + break; + default: /* '?' */ + print_help(EXIT_FAILURE); + } + } + + /* they should really read the help. */ + if(flag.none) + print_help(EXIT_FAILURE); + + if((flag.import && flag.export) || (flag.export && flag.wipe) + || (flag.verify && flag.pretend) || (flag.export && flag.pretend)) + { + fprintf(stderr, "* Error: Conflicting flags.\n"); + if(flag.export && flag.pretend) + fprintf(stderr, "* There is nothing to 'pretend' when exporting.\n"); + + fprintf(stderr, "* For an explination of commands, run: %s -h\n", me); + exit(EXIT_FAILURE); + } + + if(argv[optind] != NULL) + rb_strlcpy(etc, argv[optind], sizeof(etc)); + else + rb_strlcpy(etc, ETCPATH, sizeof(ETCPATH)); + + fprintf(stdout, + "* ircd-ratbox bantool v.%s ($Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $)\n", + BT_VERSION); + + if(flag.pretend == NO) + { + if(rsdb_init(db_error_cb) == -1) + { + fprintf(stderr, "* Error: Unable to open database\n"); + exit(EXIT_FAILURE); + } + check_schema(); + + if(flag.vacuum) + db_reclaim_slack(); + + if(flag.import && flag.wipe) + { + flag.dupes_ok = YES; /* dont check for dupes if we are wiping the db clean */ + for(i = 0; i < 3; i++) + fprintf(stdout, + "* WARNING: YOU ARE ABOUT TO WIPE YOUR DATABASE!\n"); + + fprintf(stdout, "* Press ^C to abort! "); + fflush(stdout); + rb_sleep(10, 0); + fprintf(stdout, "Carrying on...\n"); + wipe_schema(); + } + } + if(flag.verbose && flag.dupes_ok == YES) + fprintf(stdout, "* Allowing duplicate bans...\n"); + + /* checking for our files to import or export */ + for(i = 0; i < LAST_BANDB_TYPE; i++) + { + rb_snprintf(conf, sizeof(conf), "%s/%s.conf%s", + etc, bandb_table[i], bandb_suffix[i]); + + if(flag.import && flag.pretend == NO) + rsdb_transaction(RSDB_TRANS_START); + + if(flag.import) + import_config(conf, i); + + if(flag.export) + export_config(conf, i); + + if(flag.import && flag.pretend == NO) + rsdb_transaction(RSDB_TRANS_END); + } + + if(flag.import) + { + if(count.error && flag.verbose) + fprintf(stderr, "* I was unable to locate %i config files to import.\n", + count.error); + + fprintf(stdout, "* Import Stats: Klines: %i, Dlines: %i, Xlines: %i, Resvs: %i \n", + count.klines, count.dlines, count.xlines, count.resvs); + + fprintf(stdout, + "*\n* If your IRC server is currently running, newly imported bans \n* will not take effect until you issue the command: /quote rehash bans\n"); + + if(flag.pretend) + fprintf(stdout, + "* Pretend mode engaged. Nothing was actually entered into the database.\n"); + } + + return 0; +} + + +/** + * export the database to old-style flat files + */ +static void +export_config(const char *conf, int id) +{ + struct rsdb_table table; + static char sql[BUFSIZE * 2]; + static char buf[512]; + FILE *fd = NULL; + int j; + + /* for sanity sake */ + const int mask1 = 0; + const int mask2 = 1; + const int reason = 2; + const int oper = 3; + const int ts = 4; + /* const int perm = 5; */ + + if(!table_has_rows(bandb_table[id])) + return; + + if(strstr(conf, ".perm") != 0) + rb_snprintf(sql, sizeof(sql), + "SELECT DISTINCT mask1,mask2,reason,oper,time FROM %s WHERE perm = 1 ORDER BY time", + bandb_table[id]); + else + rb_snprintf(sql, sizeof(sql), + "SELECT DISTINCT mask1,mask2,reason,oper,time FROM %s WHERE perm = 0 ORDER BY time", + bandb_table[id]); + + rsdb_exec_fetch(&table, sql); + if(table.row_count <= 0) + { + rsdb_exec_fetch_end(&table); + return; + } + + if(flag.verbose) + fprintf(stdout, "* checking for %s: ", conf); /* debug */ + + /* open config for reading, or skip to the next */ + if(!(fd = fopen(conf, "w"))) + { + if(flag.verbose) + fprintf(stdout, "\tmissing.\n"); + count.error++; + return; + } + + for(j = 0; j < table.row_count; j++) + { + switch (id) + { + case BANDB_DLINE: + case BANDB_DLINE_PERM: + rb_snprintf(buf, sizeof(buf), + "\"%s\",\"%s\",\"\",\"%s\",\"%s\",%s\n", + table.row[j][mask1], + mangle_reason(table.row[j][reason]), + bt_smalldate(table.row[j][ts]), + table.row[j][oper], table.row[j][ts]); + break; + + case BANDB_XLINE: + case BANDB_XLINE_PERM: + rb_snprintf(buf, sizeof(buf), + "\"%s\",\"0\",\"%s\",\"%s\",%s\n", + escape_quotes(table.row[j][mask1]), + mangle_reason(table.row[j][reason]), + table.row[j][oper], table.row[j][ts]); + break; + + case BANDB_RESV: + case BANDB_RESV_PERM: + rb_snprintf(buf, sizeof(buf), + "\"%s\",\"%s\",\"%s\",%s\n", + table.row[j][mask1], + mangle_reason(table.row[j][reason]), + table.row[j][oper], table.row[j][ts]); + break; + + + default: /* Klines */ + rb_snprintf(buf, sizeof(buf), + "\"%s\",\"%s\",\"%s\",\"\",\"%s\",\"%s\",%s\n", + table.row[j][mask1], table.row[j][mask2], + mangle_reason(table.row[j][reason]), + bt_smalldate(table.row[j][ts]), table.row[j][oper], + table.row[j][ts]); + break; + } + + fprintf(fd, "%s", buf); + } + + rsdb_exec_fetch_end(&table); + if(flag.verbose) + fprintf(stdout, "\twritten.\n"); + fclose(fd); +} + +/** + * attempt to condense the individual conf functions into one + */ +static void +import_config(const char *conf, int id) +{ + FILE *fd; + + char line[BUFSIZE]; + char *p; + int i = 0; + + char f_perm = 0; + char *f_mask1 = NULL; + char *f_mask2 = NULL; + char *f_oper = NULL; + char *f_time = NULL; + char *f_reason = NULL; + char *f_oreason = NULL; + char newreason[REASONLEN]; + + if(flag.verbose) + fprintf(stdout, "* checking for %s: ", conf); /* debug */ + + /* open config for reading, or skip to the next */ + if(!(fd = fopen(conf, "r"))) + { + if(flag.verbose) + fprintf(stdout, "%*s", strlen(bandb_suffix[id]) > 0 ? 10 : 15, + "missing.\n"); + count.error++; + return; + } + + if(strstr(conf, ".perm") != 0) + f_perm = 1; + + + /* xline + * "SYSTEM","0","banned","stevoo!stevoo@efnet.port80.se{stevoo}",1111080437 + * resv + * "OseK","banned nickname","stevoo!stevoo@efnet.port80.se{stevoo}",1111031619 + * dline + * "194.158.192.0/19","laptop scammers","","2005/3/17 05.33","stevoo!stevoo@efnet.port80.se{stevoo}",1111033988 + */ + while(fgets(line, sizeof(line), fd)) + { + if((p = strpbrk(line, "\r\n")) != NULL) + *p = '\0'; + + if((*line == '\0') || (*line == '#')) + continue; + + /* mask1 */ + f_mask1 = getfield(line); + + if(EmptyString(f_mask1)) + continue; + + /* mask2 */ + switch (id) + { + case BANDB_XLINE: + case BANDB_XLINE_PERM: + f_mask1 = escape_quotes(clean_gecos_field(f_mask1)); + getfield(NULL); /* empty field */ + break; + + case BANDB_RESV: + case BANDB_RESV_PERM: + case BANDB_DLINE: + case BANDB_DLINE_PERM: + break; + + default: + f_mask2 = getfield(NULL); + if(EmptyString(f_mask2)) + continue; + break; + } + + /* reason */ + f_reason = getfield(NULL); + if(EmptyString(f_reason)) + continue; + + /* oper comment */ + switch (id) + { + case BANDB_KLINE: + case BANDB_KLINE_PERM: + case BANDB_DLINE: + case BANDB_DLINE_PERM: + f_oreason = getfield(NULL); + getfield(NULL); + break; + + default: + break; + } + + f_oper = getfield(NULL); + f_time = strip_quotes(f_oper + strlen(f_oper) + 2); + + /* meh */ + if(id == BANDB_KLINE || id == BANDB_KLINE_PERM) + { + if(strstr(f_mask1, "!") != NULL) + { + fprintf(stderr, + "* SKIPPING INVALID KLINE %s@%s set by %s\n", + f_mask1, f_mask2, f_oper); + fprintf(stderr, " You may wish to re-apply it correctly.\n"); + continue; + } + } + + /* append operreason_field to reason_field */ + if(!EmptyString(f_oreason)) + rb_snprintf(newreason, sizeof(newreason), "%s | %s", f_reason, f_oreason); + else + rb_snprintf(newreason, sizeof(newreason), "%s", f_reason); + + if(flag.pretend == NO) + { + if(flag.dupes_ok == NO) + drop_dupes(f_mask1, f_mask2, bandb_table[id]); + + rsdb_exec(NULL, + "INSERT INTO %s (mask1, mask2, oper, time, perm, reason) VALUES('%Q','%Q','%Q','%Q','%d','%Q')", + bandb_table[id], f_mask1, f_mask2, f_oper, f_time, f_perm, + newreason); + } + + if(flag.pretend && flag.verbose) + fprintf(stdout, + "%s: perm(%d) mask1(%s) mask2(%s) oper(%s) reason(%s) time(%s)\n", + bandb_table[id], f_perm, f_mask1, f_mask2, f_oper, newreason, + f_time); + + i++; + } + + switch (bandb_letter[id]) + { + case 'K': + count.klines += i; + break; + case 'D': + count.dlines += i; + break; + case 'X': + count.xlines += i; + break; + case 'R': + count.resvs += i; + break; + default: + break; + } + + if(flag.verbose) + fprintf(stdout, "%*s\n", strlen(bandb_suffix[id]) > 0 ? 10 : 15, "imported."); + + return; +} + +/** + * getfield + * + * inputs - input buffer + * output - next field + * side effects - field breakup for ircd.conf file. + */ +char * +getfield(char *newline) +{ + static char *line = NULL; + char *end, *field; + + if(newline != NULL) + line = newline; + + if(line == NULL) + return (NULL); + + field = line; + + /* XXX make this skip to first " if present */ + if(*field == '"') + field++; + else + return (NULL); /* mal-formed field */ + + end = strchr(line, ','); + + while(1) + { + /* no trailing , - last field */ + if(end == NULL) + { + end = line + strlen(line); + line = NULL; + + if(*end == '"') + { + *end = '\0'; + return field; + } + else + return NULL; + } + else + { + /* look for a ", to mark the end of a field.. */ + if(*(end - 1) == '"') + { + line = end + 1; + end--; + *end = '\0'; + return field; + } + + /* search for the next ',' */ + end++; + end = strchr(end, ','); + } + } + + return NULL; +} + +/** + * strip away "quotes" from around strings + */ +static char * +strip_quotes(const char *string) +{ + static char buf[14]; /* int(11) + 2 + \0 */ + char *str = buf; + + if(string == NULL) + return NULL; + + while(*string) + { + if(*string != '"') + { + *str++ = *string; + } + string++; + } + *str = '\0'; + return buf; +} + +/** + * escape quotes in a string + */ +static char * +escape_quotes(const char *string) +{ + static char buf[BUFSIZE * 2]; + char *str = buf; + + if(string == NULL) + return NULL; + + while(*string) + { + if(*string == '"') + { + *str++ = '\\'; + *str++ = '"'; + } + else + { + *str++ = *string; + } + string++; + } + *str = '\0'; + return buf; +} + + +static char * +mangle_reason(const char *string) +{ + static char buf[BUFSIZE * 2]; + char *str = buf; + + if(string == NULL) + return NULL; + + while(*string) + { + switch (*string) + { + case '"': + *str = '\''; + break; + case ':': + *str = ' '; + break; + default: + *str = *string; + } + string++; + str++; + + } + *str = '\0'; + return buf; +} + + +/** + * change spaces to \s in gecos field + */ +static const char * +clean_gecos_field(const char *gecos) +{ + static char buf[BUFSIZE * 2]; + char *str = buf; + + if(gecos == NULL) + return NULL; + + while(*gecos) + { + if(*gecos == ' ') + { + *str++ = '\\'; + *str++ = 's'; + } + else + *str++ = *gecos; + gecos++; + } + *str = '\0'; + return buf; +} + +/** + * verify the database integrity, and if necessary create apropriate tables + */ +static void +check_schema(void) +{ + int i, j; + char type[8]; /* longest string is 'INTEGER\0' */ + + if(flag.verify || flag.verbose) + fprintf(stdout, "* Verifying database.\n"); + + const char *columns[] = { + "perm", + "mask1", + "mask2", + "oper", + "time", + "reason", + NULL + }; + + for(i = 0; i < LAST_BANDB_TYPE; i++) + { + if(!table_exists(bandb_table[i])) + { + rsdb_exec(NULL, + "CREATE TABLE %s (mask1 TEXT, mask2 TEXT, oper TEXT, time INTEGER, perm INTEGER, reason TEXT)", + bandb_table[i]); + } + + /* + * i can't think of any better way to do this, other then attempt to + * force the creation of column that may, or may not already exist. --dubkat + */ + else + { + for(j = 0; columns[j] != NULL; j++) + { + if(!strcmp(columns[j], "time") && !strcmp(columns[j], "perm")) + rb_strlcpy(type, "INTEGER", sizeof(type)); + else + rb_strlcpy(type, "TEXT", sizeof(type)); + + /* attempt to add a column with extreme prejudice, errors are ignored */ + rsdb_exec(NULL, "ALTER TABLE %s ADD COLUMN %s %s", bandb_table[i], + columns[j], type); + } + } + + i++; /* skip over .perm */ + } +} + +static void +db_reclaim_slack(void) +{ + fprintf(stdout, "* Reclaiming free space.\n"); + rsdb_exec(NULL, "VACUUM"); +} + + +/** + * check that appropriate tables exist. + */ +static int +table_exists(const char *dbtab) +{ + struct rsdb_table table; + rsdb_exec_fetch(&table, "SELECT name FROM sqlite_master WHERE type='table' AND name='%s'", + dbtab); + rsdb_exec_fetch_end(&table); + return table.row_count; +} + +/** + * check that there are actual entries in a table + */ +static int +table_has_rows(const char *dbtab) +{ + struct rsdb_table table; + rsdb_exec_fetch(&table, "SELECT * FROM %s", dbtab); + rsdb_exec_fetch_end(&table); + return table.row_count; +} + +/** + * completly wipes out an existing ban.db of all entries. + */ +static void +wipe_schema(void) +{ + int i; + rsdb_transaction(RSDB_TRANS_START); + for(i = 0; i < LAST_BANDB_TYPE; i++) + { + rsdb_exec(NULL, "DROP TABLE %s", bandb_table[i]); + i++; /* double increment to skip over .perm */ + } + rsdb_transaction(RSDB_TRANS_END); + + check_schema(); +} + +/** + * remove pre-existing duplicate bans from the database. + * we favor the new, imported ban over the one in the database + */ +void +drop_dupes(const char *user, const char *host, const char *t) +{ + rsdb_exec(NULL, "DELETE FROM %s WHERE mask1='%Q' AND mask2='%Q'", t, user, host); +} + +static void +db_error_cb(const char *errstr) +{ + return; +} + + +/** + * convert unix timestamp to human readable (small) date + */ +static char * +bt_smalldate(const char *string) +{ + static char buf[MAX_DATE_STRING]; + struct tm *lt; + time_t t; + t = strtol(string, NULL, 10); + lt = gmtime(&t); + if(lt == NULL) + return NULL; + rb_snprintf(buf, sizeof(buf), "%d/%d/%d %02d.%02d", + lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min); + return buf; +} + +/** + * you are here ->. + */ +void +print_help(int i_exit) +{ + fprintf(stderr, "bantool v.%s - the ircd-ratbox database tool.\n", BT_VERSION); + fprintf(stderr, "Copyright (C) 2008 Daniel J Reidy \n"); + fprintf(stderr, "$Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $\n\n"); + fprintf(stderr, "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n"); + + fprintf(stderr, "Usage: %s <-i|-e> [-p] [-v] [-h] [-d] [-w] [path]\n", me); + fprintf(stderr, " -h : Display some slightly useful help.\n"); + fprintf(stderr, " -i : Actually import configs into your database.\n"); + fprintf(stderr, " -e : Export your database to old-style flat files.\n"); + fprintf(stderr, + " This is suitable for redistrubuting your banlists, or creating backups.\n"); + fprintf(stderr, " -s : Reclaim empty slack space the database may be taking up.\n"); + fprintf(stderr, " -u : Update the database tables to support any new features.\n"); + fprintf(stderr, + " This is automaticlly done if you are importing or exporting\n"); + fprintf(stderr, " but should be run whenever you upgrade the ircd.\n"); + fprintf(stderr, + " -p : pretend, checks for the configs, and parses them, then tells you some data...\n"); + fprintf(stderr, " but does not touch your database.\n"); + fprintf(stderr, + " -v : Be verbose... and it *is* very verbose! (intended for debugging)\n"); + fprintf(stderr, " -d : Enable checking for redunant entries.\n"); + fprintf(stderr, " -w : Completly wipe your database clean. May be used with -i \n"); + fprintf(stderr, + " path : An optional directory containing old ratbox configs for import, or export.\n"); + fprintf(stderr, " If not specified, it looks in PREFIX/etc.\n"); + exit(i_exit); +} diff --git a/bandb/rsdb.h b/bandb/rsdb.h new file mode 100644 index 000000000..52d03afd3 --- /dev/null +++ b/bandb/rsdb.h @@ -0,0 +1,42 @@ +/* $Id: rsdb.h 26164 2008-10-26 19:52:43Z androsyn $ */ +#ifndef INCLUDED_rsdb_h +#define INCLUDED_rsdb_h + +/* error handler callback */ +typedef void rsdb_error_cb(const char *); + +typedef int (*rsdb_callback) (int, const char **); + +typedef enum rsdb_transtype +{ + RSDB_TRANS_START, + RSDB_TRANS_END +} +rsdb_transtype; + +struct rsdb_table +{ + char ***row; + int row_count; + int col_count; + void *arg; +}; + +int rsdb_init(rsdb_error_cb *); +void rsdb_shutdown(void); + +const char *rsdb_quote(const char *src); + +void rsdb_exec(rsdb_callback cb, const char *format, ...); + +void rsdb_exec_fetch(struct rsdb_table *data, const char *format, ...); +void rsdb_exec_fetch_end(struct rsdb_table *data); + +void rsdb_transaction(rsdb_transtype type); +/* rsdb_snprintf.c */ + +int rs_vsnprintf(char *dest, const size_t bytes, const char *format, va_list args); +int rs_snprintf(char *dest, const size_t bytes, const char *format, ...); + + +#endif diff --git a/bandb/rsdb_snprintf.c b/bandb/rsdb_snprintf.c new file mode 100644 index 000000000..749221953 --- /dev/null +++ b/bandb/rsdb_snprintf.c @@ -0,0 +1,628 @@ +/* + * libString, Copyright (C) 1999 Patrick Alken + * This library comes with absolutely NO WARRANTY + * + * Should you choose to use and/or modify this source code, please + * do so under the terms of the GNU General Public License under which + * this library is distributed. + * + * $Id: rsdb_snprintf.c 26094 2008-09-19 15:33:46Z androsyn $ + */ +#include "stdinc.h" +#include "rsdb.h" + +/* + * This table is arranged in chronological order from 0-999, + * however the numbers are written backwards, so the number 100 + * is expressed in this table as "001". + * It's purpose is to ensure fast conversions from integers to + * ASCII strings. When an integer variable is encountered, a + * simple hash algorithm is used to determine where to look + * in this array for the corresponding string. + * This outperforms continually dividing by 10 and using the + * digit obtained as a character, because we can now divide by + * 1000 and use the remainder directly, thus cutting down on + * the number of costly divisions needed. For an integer's worst + * case, 2 divisions are needed because it can only go up to + * 32767, so after 2 divisions by 1000, and some algebra, we will + * be left with 327 which we can get from this table. This is much + * better than the 5 divisions by 10 that we would need if we did + * it the conventional way. Of course, if we made this table go + * from 0-9999, only 1 division would be needed. + * Longs and unsigned ints of course, are another matter :-). + * + * Patrick Alken + */ + +/* + * Set this to the number of indices (numbers) in our table + */ +#define TABLE_MAX 1000 + +static const char *IntTable[] = { + "000", "100", "200", "300", "400", + "500", "600", "700", "800", "900", + "010", "110", "210", "310", "410", + "510", "610", "710", "810", "910", + "020", "120", "220", "320", "420", + "520", "620", "720", "820", "920", + "030", "130", "230", "330", "430", + "530", "630", "730", "830", "930", + "040", "140", "240", "340", "440", + "540", "640", "740", "840", "940", + "050", "150", "250", "350", "450", + "550", "650", "750", "850", "950", + "060", "160", "260", "360", "460", + "560", "660", "760", "860", "960", + "070", "170", "270", "370", "470", + "570", "670", "770", "870", "970", + "080", "180", "280", "380", "480", + "580", "680", "780", "880", "980", + "090", "190", "290", "390", "490", + "590", "690", "790", "890", "990", + "001", "101", "201", "301", "401", + "501", "601", "701", "801", "901", + "011", "111", "211", "311", "411", + "511", "611", "711", "811", "911", + "021", "121", "221", "321", "421", + "521", "621", "721", "821", "921", + "031", "131", "231", "331", "431", + "531", "631", "731", "831", "931", + "041", "141", "241", "341", "441", + "541", "641", "741", "841", "941", + "051", "151", "251", "351", "451", + "551", "651", "751", "851", "951", + "061", "161", "261", "361", "461", + "561", "661", "761", "861", "961", + "071", "171", "271", "371", "471", + "571", "671", "771", "871", "971", + "081", "181", "281", "381", "481", + "581", "681", "781", "881", "981", + "091", "191", "291", "391", "491", + "591", "691", "791", "891", "991", + "002", "102", "202", "302", "402", + "502", "602", "702", "802", "902", + "012", "112", "212", "312", "412", + "512", "612", "712", "812", "912", + "022", "122", "222", "322", "422", + "522", "622", "722", "822", "922", + "032", "132", "232", "332", "432", + "532", "632", "732", "832", "932", + "042", "142", "242", "342", "442", + "542", "642", "742", "842", "942", + "052", "152", "252", "352", "452", + "552", "652", "752", "852", "952", + "062", "162", "262", "362", "462", + "562", "662", "762", "862", "962", + "072", "172", "272", "372", "472", + "572", "672", "772", "872", "972", + "082", "182", "282", "382", "482", + "582", "682", "782", "882", "982", + "092", "192", "292", "392", "492", + "592", "692", "792", "892", "992", + "003", "103", "203", "303", "403", + "503", "603", "703", "803", "903", + "013", "113", "213", "313", "413", + "513", "613", "713", "813", "913", + "023", "123", "223", "323", "423", + "523", "623", "723", "823", "923", + "033", "133", "233", "333", "433", + "533", "633", "733", "833", "933", + "043", "143", "243", "343", "443", + "543", "643", "743", "843", "943", + "053", "153", "253", "353", "453", + "553", "653", "753", "853", "953", + "063", "163", "263", "363", "463", + "563", "663", "763", "863", "963", + "073", "173", "273", "373", "473", + "573", "673", "773", "873", "973", + "083", "183", "283", "383", "483", + "583", "683", "783", "883", "983", + "093", "193", "293", "393", "493", + "593", "693", "793", "893", "993", + "004", "104", "204", "304", "404", + "504", "604", "704", "804", "904", + "014", "114", "214", "314", "414", + "514", "614", "714", "814", "914", + "024", "124", "224", "324", "424", + "524", "624", "724", "824", "924", + "034", "134", "234", "334", "434", + "534", "634", "734", "834", "934", + "044", "144", "244", "344", "444", + "544", "644", "744", "844", "944", + "054", "154", "254", "354", "454", + "554", "654", "754", "854", "954", + "064", "164", "264", "364", "464", + "564", "664", "764", "864", "964", + "074", "174", "274", "374", "474", + "574", "674", "774", "874", "974", + "084", "184", "284", "384", "484", + "584", "684", "784", "884", "984", + "094", "194", "294", "394", "494", + "594", "694", "794", "894", "994", + "005", "105", "205", "305", "405", + "505", "605", "705", "805", "905", + "015", "115", "215", "315", "415", + "515", "615", "715", "815", "915", + "025", "125", "225", "325", "425", + "525", "625", "725", "825", "925", + "035", "135", "235", "335", "435", + "535", "635", "735", "835", "935", + "045", "145", "245", "345", "445", + "545", "645", "745", "845", "945", + "055", "155", "255", "355", "455", + "555", "655", "755", "855", "955", + "065", "165", "265", "365", "465", + "565", "665", "765", "865", "965", + "075", "175", "275", "375", "475", + "575", "675", "775", "875", "975", + "085", "185", "285", "385", "485", + "585", "685", "785", "885", "985", + "095", "195", "295", "395", "495", + "595", "695", "795", "895", "995", + "006", "106", "206", "306", "406", + "506", "606", "706", "806", "906", + "016", "116", "216", "316", "416", + "516", "616", "716", "816", "916", + "026", "126", "226", "326", "426", + "526", "626", "726", "826", "926", + "036", "136", "236", "336", "436", + "536", "636", "736", "836", "936", + "046", "146", "246", "346", "446", + "546", "646", "746", "846", "946", + "056", "156", "256", "356", "456", + "556", "656", "756", "856", "956", + "066", "166", "266", "366", "466", + "566", "666", "766", "866", "966", + "076", "176", "276", "376", "476", + "576", "676", "776", "876", "976", + "086", "186", "286", "386", "486", + "586", "686", "786", "886", "986", + "096", "196", "296", "396", "496", + "596", "696", "796", "896", "996", + "007", "107", "207", "307", "407", + "507", "607", "707", "807", "907", + "017", "117", "217", "317", "417", + "517", "617", "717", "817", "917", + "027", "127", "227", "327", "427", + "527", "627", "727", "827", "927", + "037", "137", "237", "337", "437", + "537", "637", "737", "837", "937", + "047", "147", "247", "347", "447", + "547", "647", "747", "847", "947", + "057", "157", "257", "357", "457", + "557", "657", "757", "857", "957", + "067", "167", "267", "367", "467", + "567", "667", "767", "867", "967", + "077", "177", "277", "377", "477", + "577", "677", "777", "877", "977", + "087", "187", "287", "387", "487", + "587", "687", "787", "887", "987", + "097", "197", "297", "397", "497", + "597", "697", "797", "897", "997", + "008", "108", "208", "308", "408", + "508", "608", "708", "808", "908", + "018", "118", "218", "318", "418", + "518", "618", "718", "818", "918", + "028", "128", "228", "328", "428", + "528", "628", "728", "828", "928", + "038", "138", "238", "338", "438", + "538", "638", "738", "838", "938", + "048", "148", "248", "348", "448", + "548", "648", "748", "848", "948", + "058", "158", "258", "358", "458", + "558", "658", "758", "858", "958", + "068", "168", "268", "368", "468", + "568", "668", "768", "868", "968", + "078", "178", "278", "378", "478", + "578", "678", "778", "878", "978", + "088", "188", "288", "388", "488", + "588", "688", "788", "888", "988", + "098", "198", "298", "398", "498", + "598", "698", "798", "898", "998", + "009", "109", "209", "309", "409", + "509", "609", "709", "809", "909", + "019", "119", "219", "319", "419", + "519", "619", "719", "819", "919", + "029", "129", "229", "329", "429", + "529", "629", "729", "829", "929", + "039", "139", "239", "339", "439", + "539", "639", "739", "839", "939", + "049", "149", "249", "349", "449", + "549", "649", "749", "849", "949", + "059", "159", "259", "359", "459", + "559", "659", "759", "859", "959", + "069", "169", "269", "369", "469", + "569", "669", "769", "869", "969", + "079", "179", "279", "379", "479", + "579", "679", "779", "879", "979", + "089", "189", "289", "389", "489", + "589", "689", "789", "889", "989", + "099", "199", "299", "399", "499", + "599", "699", "799", "899", "999" +}; + +/* + * Since we calculate the right-most digits for %d %u etc first, + * we need a temporary buffer to store them in until we get + * to the left-most digits + */ + +#define TEMPBUF_MAX 20 + +static char TempBuffer[TEMPBUF_MAX]; + +/* +vSnprintf() + Backend to Snprintf() - performs the construction of 'dest' +using the string 'format' and the given arguments. Also makes sure +not more than 'bytes' characters are copied to 'dest' + + We always allow room for a terminating \0 character, so at most, +bytes - 1 characters will be written to dest. + +Return: Number of characters written, NOT including the terminating + \0 character which is *always* placed at the end of the string + +NOTE: This function handles the following flags only: + %s %d %c %u %ld %lu + In addition, this function performs *NO* precision, padding, + or width formatting. If it receives an unknown % character, + it will call vsprintf() to complete the remainder of the + string. +*/ + +int +rs_vsnprintf(char *dest, const size_t bytes, const char *format, va_list args) +{ + char ch; + int written = 0; /* bytes written so far */ + int maxbytes = bytes - 1; + + while((ch = *format++) && (written < maxbytes)) + { + if(ch == '%') + { + /* + * Advance past the % + */ + ch = *format++; + + /* + * Put the most common cases first - %s %d etc + */ + + if(ch == 's') + { + const char *str = va_arg(args, const char *); + + while((*dest = *str)) + { + ++dest; + ++str; + + if(++written >= maxbytes) + break; + } + + continue; + } + + if(ch == 'd') + { + int num = va_arg(args, int); + int quotient; + const char *str; + char *digitptr = TempBuffer; + + /* + * We have to special-case "0" unfortunately + */ + if(num == 0) + { + *dest++ = '0'; + ++written; + continue; + } + + if(num < 0) + { + *dest++ = '-'; + if(++written >= maxbytes) + continue; + + num = -num; + } + + do + { + quotient = num / TABLE_MAX; + + /* + * We'll start with the right-most digits of 'num'. + * Dividing by TABLE_MAX cuts off all but the X + * right-most digits, where X is such that: + * + * 10^X = TABLE_MAX + * + * For example, if num = 1200, and TABLE_MAX = 1000, + * quotient will be 1. Multiplying this by 1000 and + * subtracting from 1200 gives: 1200 - (1 * 1000) = 200. + * We then go right to slot 200 in our array and behold! + * The string "002" (200 backwards) is conveniently + * waiting for us. Then repeat the process with the + * digits left. + * + * The reason we need to have the integers written + * backwards, is because we don't know how many digits + * there are. If we want to express the number 12130 + * for example, our first pass would leave us with 130, + * whose slot in the array yields "031", which we + * plug into our TempBuffer[]. The next pass gives us + * 12, whose slot yields "21" which we append to + * TempBuffer[], leaving us with "03121". This is the + * exact number we want, only backwards, so it is + * a simple matter to reverse the string. If we used + * straightfoward numbers, we would have a TempBuffer + * looking like this: "13012" which would be a nightmare + * to deal with. + */ + + str = IntTable[num - (quotient * TABLE_MAX)]; + + while((*digitptr = *str)) + { + ++digitptr; + ++str; + } + } + while((num = quotient) != 0); + + /* + * If the last quotient was a 1 or 2 digit number, there + * will be one or more leading zeroes in TempBuffer[] - + * get rid of them. + */ + while(*(digitptr - 1) == '0') + --digitptr; + + while(digitptr != TempBuffer) + { + *dest++ = *--digitptr; + if(++written >= maxbytes) + break; + } + + continue; + } /* if (ch == 'd') */ + + if(ch == 'c') + { + *dest++ = va_arg(args, int); + + ++written; + + continue; + } /* if (ch == 'c') */ + + if(ch == 'u') + { + unsigned int num = va_arg(args, unsigned int); + unsigned int quotient; + const char *str; + char *digitptr = TempBuffer; + + if(num == 0) + { + *dest++ = '0'; + ++written; + continue; + } + + do + { + quotient = num / TABLE_MAX; + + /* + * Very similar to case 'd' + */ + + str = IntTable[num - (quotient * TABLE_MAX)]; + + while((*digitptr = *str)) + { + ++digitptr; + ++str; + } + } + while((num = quotient) != 0); + + while(*(digitptr - 1) == '0') + --digitptr; + + while(digitptr != TempBuffer) + { + *dest++ = *--digitptr; + if(++written >= maxbytes) + break; + } + + continue; + } /* if (ch == 'u') */ + + if(ch == 'Q') + { + const char *arg = va_arg(args, const char *); + + if(arg == NULL) + continue; + + const char *str = rsdb_quote(arg); + + while((*dest = *str)) + { + ++dest; + ++str; + + if(++written >= maxbytes) + break; + } + + continue; + } + + if(ch == 'l') + { + if(*format == 'u') + { + unsigned long num = va_arg(args, unsigned long); + unsigned long quotient; + const char *str; + char *digitptr = TempBuffer; + + ++format; + + if(num == 0) + { + *dest++ = '0'; + ++written; + continue; + } + + do + { + quotient = num / TABLE_MAX; + + /* + * Very similar to case 'u' + */ + + str = IntTable[num - (quotient * TABLE_MAX)]; + + while((*digitptr = *str)) + { + ++digitptr; + ++str; + } + } + while((num = quotient) != 0); + + while(*(digitptr - 1) == '0') + --digitptr; + + while(digitptr != TempBuffer) + { + *dest++ = *--digitptr; + if(++written >= maxbytes) + break; + } + + continue; + } + else + /* if (*format == 'u') */ if(*format == 'd') + { + long num = va_arg(args, long); + long quotient; + const char *str; + char *digitptr = TempBuffer; + + ++format; + + if(num == 0) + { + *dest++ = '0'; + ++written; + continue; + } + + if(num < 0) + { + *dest++ = '-'; + if(++written >= maxbytes) + continue; + + num = -num; + } + + do + { + quotient = num / TABLE_MAX; + + str = IntTable[num - (quotient * TABLE_MAX)]; + + while((*digitptr = *str)) + { + ++digitptr; + ++str; + } + } + while((num = quotient) != 0); + + while(*(digitptr - 1) == '0') + --digitptr; + + while(digitptr != TempBuffer) + { + *dest++ = *--digitptr; + if(++written >= maxbytes) + break; + } + + continue; + } + else /* if (*format == 'd') */ + { + /* XXX error */ + exit(1); + } + + + } /* if (ch == 'l') */ + + if(ch != '%') + { + /* XXX error */ + exit(1); + } /* if (ch != '%') */ + } /* if (ch == '%') */ + + *dest++ = ch; + ++written; + } /* while ((ch = *format++) && (written < maxbytes)) */ + + /* + * Terminate the destination buffer with a \0 + */ + *dest = '\0'; + + return (written); +} /* vSnprintf() */ + +/* +rs_snprintf() + Optimized version of snprintf(). + +Inputs: dest - destination string + bytes - number of bytes to copy + format - formatted string + args - args to 'format' + +Return: number of characters copied, NOT including the terminating + NULL which is always placed at the end of the string +*/ + +int +rs_snprintf(char *dest, const size_t bytes, const char *format, ...) +{ + va_list args; + int count; + + va_start(args, format); + + count = rs_vsnprintf(dest, bytes, format, args); + + va_end(args); + + return (count); +} /* Snprintf() */ diff --git a/bandb/rsdb_sqlite3.c b/bandb/rsdb_sqlite3.c new file mode 100644 index 000000000..2df2bf6cc --- /dev/null +++ b/bandb/rsdb_sqlite3.c @@ -0,0 +1,265 @@ +/* src/rsdb_sqlite.h + * Contains the code for the sqlite database backend. + * + * Copyright (C) 2003-2006 Lee Hardy + * Copyright (C) 2003-2006 ircd-ratbox development team + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1.Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2.Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3.The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: rsdb_sqlite3.c 26182 2008-11-11 02:52:41Z androsyn $ + */ +#include "stdinc.h" +#include "rsdb.h" + +#include + +struct sqlite3 *rb_bandb; + +rsdb_error_cb *error_cb; + +static void +mlog(const char *errstr, ...) +{ + if(error_cb != NULL) + { + char buf[256]; + va_list ap; + va_start(ap, errstr); + rb_vsnprintf(buf, sizeof(buf), errstr, ap); + va_end(ap); + error_cb(buf); + } + else + exit(1); +} + +int +rsdb_init(rsdb_error_cb * ecb) +{ + const char *bandb_dpath; + char dbpath[PATH_MAX]; + char errbuf[128]; + error_cb = ecb; + + /* try a path from the environment first, useful for basedir overrides */ + bandb_dpath = getenv("BANDB_DPATH"); + + if(bandb_dpath != NULL) + rb_snprintf(dbpath, sizeof(dbpath), "%s/etc/ban.db", bandb_dpath); + else + rb_strlcpy(dbpath, DBPATH, sizeof(dbpath)); + + if(sqlite3_open(dbpath, &rb_bandb) != SQLITE_OK) + { + rb_snprintf(errbuf, sizeof(errbuf), "Unable to open sqlite database: %s", + sqlite3_errmsg(rb_bandb)); + mlog(errbuf); + return -1; + } + if(access(dbpath, W_OK)) + { + rb_snprintf(errbuf, sizeof(errbuf), "Unable to open sqlite database for write: %s", strerror(errno)); + mlog(errbuf); + return -1; + } + return 0; +} + +void +rsdb_shutdown(void) +{ + if(rb_bandb) + sqlite3_close(rb_bandb); +} + +const char * +rsdb_quote(const char *src) +{ + static char buf[BUFSIZE * 4]; + char *p = buf; + + /* cheap and dirty length check.. */ + if(strlen(src) >= (sizeof(buf) / 2)) + return NULL; + + while(*src) + { + if(*src == '\'') + *p++ = '\''; + + *p++ = *src++; + } + + *p = '\0'; + return buf; +} + +static int +rsdb_callback_func(void *cbfunc, int argc, char **argv, char **colnames) +{ + rsdb_callback cb = (rsdb_callback)((uintptr_t)cbfunc); + (cb) (argc, (const char **)argv); + return 0; +} + +void +rsdb_exec(rsdb_callback cb, const char *format, ...) +{ + static char buf[BUFSIZE * 4]; + va_list args; + char *errmsg; + unsigned int i; + int j; + + va_start(args, format); + i = rs_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + if(i >= sizeof(buf)) + { + mlog("fatal error: length problem with compiling sql"); + } + + if((i = sqlite3_exec(rb_bandb, buf, (cb ? rsdb_callback_func : NULL), (void *)((uintptr_t)cb), &errmsg))) + { + switch (i) + { + case SQLITE_BUSY: + for(j = 0; j < 5; j++) + { + rb_sleep(0, 500000); + if(!sqlite3_exec + (rb_bandb, buf, (cb ? rsdb_callback_func : NULL), (void *)((uintptr_t)cb), &errmsg)) + return; + } + + /* failed, fall through to default */ + mlog("fatal error: problem with db file: %s", errmsg); + break; + + default: + mlog("fatal error: problem with db file: %s", errmsg); + break; + } + } +} + +void +rsdb_exec_fetch(struct rsdb_table *table, const char *format, ...) +{ + static char buf[BUFSIZE * 4]; + va_list args; + char *errmsg; + char **data; + int pos; + unsigned int retval; + int i, j; + + va_start(args, format); + retval = rs_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + if(retval >= sizeof(buf)) + { + mlog("fatal error: length problem with compiling sql"); + } + + if((retval = + sqlite3_get_table(rb_bandb, buf, &data, &table->row_count, &table->col_count, &errmsg))) + { + int success = 0; + + switch (retval) + { + case SQLITE_BUSY: + for(i = 0; i < 5; i++) + { + rb_sleep(0, 500000); + if(!sqlite3_get_table + (rb_bandb, buf, &data, &table->row_count, &table->col_count, + &errmsg)) + { + success++; + break; + } + } + + if(success) + break; + + mlog("fatal error: problem with db file: %s", errmsg); + break; + + default: + mlog("fatal error: problem with db file: %s", errmsg); + break; + } + } + + /* we need to be able to free data afterward */ + table->arg = data; + + if(table->row_count == 0) + { + table->row = NULL; + return; + } + + /* sqlite puts the column names as the first row */ + pos = table->col_count; + table->row = rb_malloc(sizeof(char **) * table->row_count); + for(i = 0; i < table->row_count; i++) + { + table->row[i] = rb_malloc(sizeof(char *) * table->col_count); + + for(j = 0; j < table->col_count; j++) + { + table->row[i][j] = data[pos++]; + } + } +} + +void +rsdb_exec_fetch_end(struct rsdb_table *table) +{ + int i; + + for(i = 0; i < table->row_count; i++) + { + rb_free(table->row[i]); + } + rb_free(table->row); + + sqlite3_free_table((char **)table->arg); +} + +void +rsdb_transaction(rsdb_transtype type) +{ + if(type == RSDB_TRANS_START) + rsdb_exec(NULL, "BEGIN TRANSACTION"); + else if(type == RSDB_TRANS_END) + rsdb_exec(NULL, "COMMIT TRANSACTION"); +}