]>
jfr.im git - solanum.git/blob - bandb/bantool.c
2 * ircd-ratbox: A slightly useful ircd.
3 * bantool.c: The ircd-ratbox database managment tool.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2008 ircd-ratbox development team
8 * Copyright (C) 2008 Daniel J Reidy <dubkat@gmail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
25 * $Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $
28 * The following server admins have either contributed various configs to test against,
29 * or helped with debugging and feature requests. Many thanks to them.
30 * stevoo / efnet.port80.se
31 * AndroSyn / irc2.choopa.net, irc.igs.ca
32 * Salvation / irc.blessed.net
33 * JamesOff / efnet.demon.co.uk
35 * Thanks to AndroSyn for challenging me to learn C on the fly :)
36 * BUGS Direct Question, Bug Reports, and Feature Requests to #ratbox on EFnet.
37 * BUGS Complaints >/dev/null
50 #define EmptyString(x) ((x == NULL) || (*(x) == '\0'))
51 #define CheckEmpty(x) EmptyString(x) ? "" : x
53 #define BT_VERSION "0.4.1"
69 static char bandb_letter
[LAST_BANDB_TYPE
] = {
70 'K', 'K', 'D', 'D', 'X', 'X', 'R', 'R'
73 static const char *bandb_table
[LAST_BANDB_TYPE
] = {
74 "kline", "kline", "dline", "dline", "xline", "xline", "resv", "resv"
77 static const char *bandb_suffix
[LAST_BANDB_TYPE
] = {
84 static char me
[PATH_MAX
];
95 } count
= {0, 0, 0, 0, 0};
97 /* flags set by command line options */
109 } flag
= {YES
, NO
, NO
, NO
, NO
, NO
, NO
, NO
, NO
};
112 static int table_has_rows(const char *table
);
113 static int table_exists(const char *table
);
115 static const char *clean_gecos_field(const char *gecos
);
116 static char *bt_smalldate(const char *string
);
117 static char *getfield(char *newline
);
118 static char *strip_quotes(const char *string
);
119 static char *mangle_reason(const char *string
);
120 static char *escape_quotes(const char *string
);
122 static void db_error_cb(const char *errstr
);
123 static void db_reclaim_slack(void);
124 static void export_config(const char *conf
, int id
);
125 static void import_config(const char *conf
, int id
);
126 static void check_schema(void);
127 static void print_help(int i_exit
);
128 static void wipe_schema(void);
129 static void drop_dupes(const char *user
, const char *host
, const char *t
);
135 main(int argc
, char *argv
[])
142 rb_strlcpy(me
, argv
[0], sizeof(me
));
144 while((opt
= getopt(argc
, argv
, "hieuspvwd")) != -1)
149 print_help(EXIT_SUCCESS
);
180 print_help(EXIT_FAILURE
);
184 /* they should really read the help. */
186 print_help(EXIT_FAILURE
);
188 if((flag
.import && flag
.export
) || (flag
.export
&& flag
.wipe
)
189 || (flag
.verify
&& flag
.pretend
) || (flag
.export
&& flag
.pretend
))
191 fprintf(stderr
, "* Error: Conflicting flags.\n");
192 if(flag
.export
&& flag
.pretend
)
193 fprintf(stderr
, "* There is nothing to 'pretend' when exporting.\n");
195 fprintf(stderr
, "* For an explination of commands, run: %s -h\n", me
);
199 if(argv
[optind
] != NULL
)
200 rb_strlcpy(etc
, argv
[optind
], sizeof(etc
));
202 rb_strlcpy(etc
, ETCPATH
, sizeof(ETCPATH
));
205 "* ircd-ratbox bantool v.%s ($Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $)\n",
208 if(flag
.pretend
== NO
)
210 if(rsdb_init(db_error_cb
) == -1)
212 fprintf(stderr
, "* Error: Unable to open database\n");
220 if(flag
.import && flag
.wipe
)
222 flag
.dupes_ok
= YES
; /* dont check for dupes if we are wiping the db clean */
223 for(i
= 0; i
< 3; i
++)
225 "* WARNING: YOU ARE ABOUT TO WIPE YOUR DATABASE!\n");
227 fprintf(stdout
, "* Press ^C to abort! ");
230 fprintf(stdout
, "Carrying on...\n");
234 if(flag
.verbose
&& flag
.dupes_ok
== YES
)
235 fprintf(stdout
, "* Allowing duplicate bans...\n");
237 /* checking for our files to import or export */
238 for(i
= 0; i
< LAST_BANDB_TYPE
; i
++)
240 rb_snprintf(conf
, sizeof(conf
), "%s/%s.conf%s",
241 etc
, bandb_table
[i
], bandb_suffix
[i
]);
243 if(flag
.import && flag
.pretend
== NO
)
244 rsdb_transaction(RSDB_TRANS_START
);
247 import_config(conf
, i
);
250 export_config(conf
, i
);
252 if(flag
.import && flag
.pretend
== NO
)
253 rsdb_transaction(RSDB_TRANS_END
);
258 if(count
.error
&& flag
.verbose
)
259 fprintf(stderr
, "* I was unable to locate %i config files to import.\n",
262 fprintf(stdout
, "* Import Stats: Klines: %i, Dlines: %i, Xlines: %i, Resvs: %i \n",
263 count
.klines
, count
.dlines
, count
.xlines
, count
.resvs
);
266 "*\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");
270 "* Pretend mode engaged. Nothing was actually entered into the database.\n");
278 * export the database to old-style flat files
281 export_config(const char *conf
, int id
)
283 struct rsdb_table table
;
284 static char sql
[BUFSIZE
* 2];
285 static char buf
[512];
289 /* for sanity sake */
292 const int reason
= 2;
295 /* const int perm = 5; */
297 if(!table_has_rows(bandb_table
[id
]))
300 if(strstr(conf
, ".perm") != 0)
301 rb_snprintf(sql
, sizeof(sql
),
302 "SELECT DISTINCT mask1,mask2,reason,oper,time FROM %s WHERE perm = 1 ORDER BY time",
305 rb_snprintf(sql
, sizeof(sql
),
306 "SELECT DISTINCT mask1,mask2,reason,oper,time FROM %s WHERE perm = 0 ORDER BY time",
309 rsdb_exec_fetch(&table
, sql
);
310 if(table
.row_count
<= 0)
312 rsdb_exec_fetch_end(&table
);
317 fprintf(stdout
, "* checking for %s: ", conf
); /* debug */
319 /* open config for reading, or skip to the next */
320 if(!(fd
= fopen(conf
, "w")))
323 fprintf(stdout
, "\tmissing.\n");
328 for(j
= 0; j
< table
.row_count
; j
++)
333 case BANDB_DLINE_PERM
:
334 rb_snprintf(buf
, sizeof(buf
),
335 "\"%s\",\"%s\",\"\",\"%s\",\"%s\",%s\n",
337 mangle_reason(table
.row
[j
][reason
]),
338 bt_smalldate(table
.row
[j
][ts
]),
339 table
.row
[j
][oper
], table
.row
[j
][ts
]);
343 case BANDB_XLINE_PERM
:
344 rb_snprintf(buf
, sizeof(buf
),
345 "\"%s\",\"0\",\"%s\",\"%s\",%s\n",
346 escape_quotes(table
.row
[j
][mask1
]),
347 mangle_reason(table
.row
[j
][reason
]),
348 table
.row
[j
][oper
], table
.row
[j
][ts
]);
352 case BANDB_RESV_PERM
:
353 rb_snprintf(buf
, sizeof(buf
),
354 "\"%s\",\"%s\",\"%s\",%s\n",
356 mangle_reason(table
.row
[j
][reason
]),
357 table
.row
[j
][oper
], table
.row
[j
][ts
]);
361 default: /* Klines */
362 rb_snprintf(buf
, sizeof(buf
),
363 "\"%s\",\"%s\",\"%s\",\"\",\"%s\",\"%s\",%s\n",
364 table
.row
[j
][mask1
], table
.row
[j
][mask2
],
365 mangle_reason(table
.row
[j
][reason
]),
366 bt_smalldate(table
.row
[j
][ts
]), table
.row
[j
][oper
],
371 fprintf(fd
, "%s", buf
);
374 rsdb_exec_fetch_end(&table
);
376 fprintf(stdout
, "\twritten.\n");
381 * attempt to condense the individual conf functions into one
384 import_config(const char *conf
, int id
)
393 const char *f_mask1
= NULL
;
394 const char *f_mask2
= NULL
;
395 const char *f_oper
= NULL
;
396 const char *f_time
= NULL
;
397 const char *f_reason
= NULL
;
398 const char *f_oreason
= NULL
;
399 char newreason
[REASONLEN
];
402 fprintf(stdout
, "* checking for %s: ", conf
); /* debug */
404 /* open config for reading, or skip to the next */
405 if(!(fd
= fopen(conf
, "r")))
408 fprintf(stdout
, "%*s", strlen(bandb_suffix
[id
]) > 0 ? 10 : 15,
414 if(strstr(conf
, ".perm") != 0)
419 * "SYSTEM","0","banned","stevoo!stevoo@efnet.port80.se{stevoo}",1111080437
421 * "OseK","banned nickname","stevoo!stevoo@efnet.port80.se{stevoo}",1111031619
423 * "194.158.192.0/19","laptop scammers","","2005/3/17 05.33","stevoo!stevoo@efnet.port80.se{stevoo}",1111033988
425 while(fgets(line
, sizeof(line
), fd
))
427 if((p
= strpbrk(line
, "\r\n")) != NULL
)
430 if((*line
== '\0') || (*line
== '#'))
434 f_mask1
= getfield(line
);
436 if(EmptyString(f_mask1
))
443 case BANDB_XLINE_PERM
:
444 f_mask1
= escape_quotes(clean_gecos_field(f_mask1
));
445 getfield(NULL
); /* empty field */
449 case BANDB_RESV_PERM
:
451 case BANDB_DLINE_PERM
:
455 f_mask2
= getfield(NULL
);
456 if(EmptyString(f_mask2
))
462 f_reason
= getfield(NULL
);
463 if(EmptyString(f_reason
))
470 case BANDB_KLINE_PERM
:
472 case BANDB_DLINE_PERM
:
473 f_oreason
= getfield(NULL
);
481 f_oper
= getfield(NULL
);
482 f_time
= strip_quotes(f_oper
+ strlen(f_oper
) + 2);
483 if(EmptyString(f_oper
))
487 if(id
== BANDB_KLINE
|| id
== BANDB_KLINE_PERM
)
489 if(strstr(f_mask1
, "!") != NULL
)
492 "* SKIPPING INVALID KLINE %s@%s set by %s\n",
493 f_mask1
, f_mask2
, f_oper
);
494 fprintf(stderr
, " You may wish to re-apply it correctly.\n");
499 /* append operreason_field to reason_field */
500 if(!EmptyString(f_oreason
))
501 rb_snprintf(newreason
, sizeof(newreason
), "%s | %s", f_reason
, f_oreason
);
503 rb_snprintf(newreason
, sizeof(newreason
), "%s", f_reason
);
505 if(flag
.pretend
== NO
)
507 if(flag
.dupes_ok
== NO
)
508 drop_dupes(f_mask1
, f_mask2
, bandb_table
[id
]);
511 "INSERT INTO %s (mask1, mask2, oper, time, perm, reason) VALUES('%Q','%Q','%Q','%Q','%d','%Q')",
512 bandb_table
[id
], f_mask1
, f_mask2
, f_oper
, f_time
, f_perm
,
516 if(flag
.pretend
&& flag
.verbose
)
518 "%s: perm(%d) mask1(%s) mask2(%s) oper(%s) reason(%s) time(%s)\n",
519 bandb_table
[id
], f_perm
, f_mask1
, f_mask2
, f_oper
, newreason
,
525 switch (bandb_letter
[id
])
544 fprintf(stdout
, "%*s\n", strlen(bandb_suffix
[id
]) > 0 ? 10 : 15, "imported.");
552 * inputs - input buffer
553 * output - next field
554 * side effects - field breakup for ircd.conf file.
557 getfield(char *newline
)
559 static char *line
= NULL
;
570 /* XXX make this skip to first " if present */
574 return (NULL
); /* mal-formed field */
576 end
= strchr(line
, ',');
580 /* no trailing , - last field */
583 end
= line
+ strlen(line
);
596 /* look for a ", to mark the end of a field.. */
597 if(*(end
- 1) == '"')
605 /* search for the next ',' */
607 end
= strchr(end
, ',');
615 * strip away "quotes" from around strings
618 strip_quotes(const char *string
)
620 static char buf
[14]; /* int(11) + 2 + \0 */
639 * escape quotes in a string
642 escape_quotes(const char *string
)
644 static char buf
[BUFSIZE
* 2];
669 mangle_reason(const char *string
)
671 static char buf
[BUFSIZE
* 2];
700 * change spaces to \s in gecos field
703 clean_gecos_field(const char *gecos
)
705 static char buf
[BUFSIZE
* 2];
727 * verify the database integrity, and if necessary create apropriate tables
733 char type
[8]; /* longest string is 'INTEGER\0' */
735 if(flag
.verify
|| flag
.verbose
)
736 fprintf(stdout
, "* Verifying database.\n");
738 const char *columns
[] = {
748 for(i
= 0; i
< LAST_BANDB_TYPE
; i
++)
750 if(!table_exists(bandb_table
[i
]))
753 "CREATE TABLE %s (mask1 TEXT, mask2 TEXT, oper TEXT, time INTEGER, perm INTEGER, reason TEXT)",
758 * i can't think of any better way to do this, other then attempt to
759 * force the creation of column that may, or may not already exist. --dubkat
763 for(j
= 0; columns
[j
] != NULL
; j
++)
765 if(!strcmp(columns
[j
], "time") && !strcmp(columns
[j
], "perm"))
766 rb_strlcpy(type
, "INTEGER", sizeof(type
));
768 rb_strlcpy(type
, "TEXT", sizeof(type
));
770 /* attempt to add a column with extreme prejudice, errors are ignored */
771 rsdb_exec(NULL
, "ALTER TABLE %s ADD COLUMN %s %s", bandb_table
[i
],
776 i
++; /* skip over .perm */
781 db_reclaim_slack(void)
783 fprintf(stdout
, "* Reclaiming free space.\n");
784 rsdb_exec(NULL
, "VACUUM");
789 * check that appropriate tables exist.
792 table_exists(const char *dbtab
)
794 struct rsdb_table table
;
795 rsdb_exec_fetch(&table
, "SELECT name FROM sqlite_master WHERE type='table' AND name='%s'",
797 rsdb_exec_fetch_end(&table
);
798 return table
.row_count
;
802 * check that there are actual entries in a table
805 table_has_rows(const char *dbtab
)
807 struct rsdb_table table
;
808 rsdb_exec_fetch(&table
, "SELECT * FROM %s", dbtab
);
809 rsdb_exec_fetch_end(&table
);
810 return table
.row_count
;
814 * completly wipes out an existing ban.db of all entries.
820 rsdb_transaction(RSDB_TRANS_START
);
821 for(i
= 0; i
< LAST_BANDB_TYPE
; i
++)
823 rsdb_exec(NULL
, "DROP TABLE %s", bandb_table
[i
]);
824 i
++; /* double increment to skip over .perm */
826 rsdb_transaction(RSDB_TRANS_END
);
832 * remove pre-existing duplicate bans from the database.
833 * we favor the new, imported ban over the one in the database
836 drop_dupes(const char *user
, const char *host
, const char *t
)
838 rsdb_exec(NULL
, "DELETE FROM %s WHERE mask1='%Q' AND mask2='%Q'", t
, user
, host
);
842 db_error_cb(const char *errstr
)
849 * convert unix timestamp to human readable (small) date
852 bt_smalldate(const char *string
)
854 static char buf
[MAX_DATE_STRING
];
857 t
= strtol(string
, NULL
, 10);
861 rb_snprintf(buf
, sizeof(buf
), "%d/%d/%d %02d.%02d",
862 lt
->tm_year
+ 1900, lt
->tm_mon
+ 1, lt
->tm_mday
, lt
->tm_hour
, lt
->tm_min
);
870 print_help(int i_exit
)
872 fprintf(stderr
, "bantool v.%s - the ircd-ratbox database tool.\n", BT_VERSION
);
873 fprintf(stderr
, "Copyright (C) 2008 Daniel J Reidy <dubkat@gmail.com>\n");
874 fprintf(stderr
, "$Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $\n\n");
875 fprintf(stderr
, "This program is distributed in the hope that it will be useful,\n"
876 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
877 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
878 "GNU General Public License for more details.\n\n");
880 fprintf(stderr
, "Usage: %s <-i|-e> [-p] [-v] [-h] [-d] [-w] [path]\n", me
);
881 fprintf(stderr
, " -h : Display some slightly useful help.\n");
882 fprintf(stderr
, " -i : Actually import configs into your database.\n");
883 fprintf(stderr
, " -e : Export your database to old-style flat files.\n");
885 " This is suitable for redistrubuting your banlists, or creating backups.\n");
886 fprintf(stderr
, " -s : Reclaim empty slack space the database may be taking up.\n");
887 fprintf(stderr
, " -u : Update the database tables to support any new features.\n");
889 " This is automaticlly done if you are importing or exporting\n");
890 fprintf(stderr
, " but should be run whenever you upgrade the ircd.\n");
892 " -p : pretend, checks for the configs, and parses them, then tells you some data...\n");
893 fprintf(stderr
, " but does not touch your database.\n");
895 " -v : Be verbose... and it *is* very verbose! (intended for debugging)\n");
896 fprintf(stderr
, " -d : Enable checking for redunant entries.\n");
897 fprintf(stderr
, " -w : Completly wipe your database clean. May be used with -i \n");
899 " path : An optional directory containing old ratbox configs for import, or export.\n");
900 fprintf(stderr
, " If not specified, it looks in PREFIX/etc.\n");