]> jfr.im git - irc/rqf/shadowircd.git/blob - bandb/bantool.c
Recommend EFNet's RBL instead of DroneBL due to trustworthiness issues.
[irc/rqf/shadowircd.git] / bandb / bantool.c
1 /**
2 * ircd-ratbox: A slightly useful ircd.
3 * bantool.c: The ircd-ratbox database managment tool.
4 *
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>
9 *
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.
14 *
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.
19 *
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
23 * USA
24 *
25 * $Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $
26 *
27 *
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
34 *
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
38 *
39 */
40
41 #define _XOPEN_SOURCE
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <time.h>
45
46 #include "stdinc.h"
47 #include "common.h"
48 #include "rsdb.h"
49
50 #define EmptyString(x) ((x == NULL) || (*(x) == '\0'))
51 #define CheckEmpty(x) EmptyString(x) ? "" : x
52
53 #define BT_VERSION "0.4.1"
54
55 typedef enum
56 {
57 BANDB_KLINE,
58 BANDB_KLINE_PERM,
59 BANDB_DLINE,
60 BANDB_DLINE_PERM,
61 BANDB_XLINE,
62 BANDB_XLINE_PERM,
63 BANDB_RESV,
64 BANDB_RESV_PERM,
65 LAST_BANDB_TYPE
66 } bandb_type;
67
68
69 static char bandb_letter[LAST_BANDB_TYPE] = {
70 'K', 'K', 'D', 'D', 'X', 'X', 'R', 'R'
71 };
72
73 static const char *bandb_table[LAST_BANDB_TYPE] = {
74 "kline", "kline", "dline", "dline", "xline", "xline", "resv", "resv"
75 };
76
77 static const char *bandb_suffix[LAST_BANDB_TYPE] = {
78 "", ".perm",
79 "", ".perm",
80 "", ".perm",
81 "", ".perm"
82 };
83
84 static char me[PATH_MAX];
85
86 /* *INDENT-OFF* */
87 /* report counters */
88 struct counter
89 {
90 unsigned int klines;
91 unsigned int dlines;
92 unsigned int xlines;
93 unsigned int resvs;
94 unsigned int error;
95 } count = {0, 0, 0, 0, 0};
96
97 /* flags set by command line options */
98 struct flags
99 {
100 int none;
101 int export;
102 int import;
103 int verify;
104 int vacuum;
105 int pretend;
106 int verbose;
107 int wipe;
108 int dupes_ok;
109 } flag = {YES, NO, NO, NO, NO, NO, NO, NO, NO};
110 /* *INDENT-ON* */
111
112 static int table_has_rows(const char *table);
113 static int table_exists(const char *table);
114
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);
121
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);
130
131 /**
132 * swing your pants
133 */
134 int
135 main(int argc, char *argv[])
136 {
137 char etc[PATH_MAX];
138 char conf[PATH_MAX];
139 int opt;
140 int i;
141
142 rb_strlcpy(me, argv[0], sizeof(me));
143
144 while((opt = getopt(argc, argv, "hieuspvwd")) != -1)
145 {
146 switch (opt)
147 {
148 case 'h':
149 print_help(EXIT_SUCCESS);
150 break;
151 case 'i':
152 flag.none = NO;
153 flag.import = YES;
154 break;
155 case 'e':
156 flag.none = NO;
157 flag.export = YES;
158 break;
159 case 'u':
160 flag.none = NO;
161 flag.verify = YES;
162 break;
163 case 's':
164 flag.none = NO;
165 flag.vacuum = YES;
166 break;
167 case 'p':
168 flag.pretend = YES;
169 break;
170 case 'v':
171 flag.verbose = YES;
172 break;
173 case 'w':
174 flag.wipe = YES;
175 break;
176 case 'd':
177 flag.dupes_ok = YES;
178 break;
179 default: /* '?' */
180 print_help(EXIT_FAILURE);
181 }
182 }
183
184 /* they should really read the help. */
185 if(flag.none)
186 print_help(EXIT_FAILURE);
187
188 if((flag.import && flag.export) || (flag.export && flag.wipe)
189 || (flag.verify && flag.pretend) || (flag.export && flag.pretend))
190 {
191 fprintf(stderr, "* Error: Conflicting flags.\n");
192 if(flag.export && flag.pretend)
193 fprintf(stderr, "* There is nothing to 'pretend' when exporting.\n");
194
195 fprintf(stderr, "* For an explination of commands, run: %s -h\n", me);
196 exit(EXIT_FAILURE);
197 }
198
199 if(argv[optind] != NULL)
200 rb_strlcpy(etc, argv[optind], sizeof(etc));
201 else
202 rb_strlcpy(etc, ETCPATH, sizeof(ETCPATH));
203
204 fprintf(stdout,
205 "* ircd-ratbox bantool v.%s ($Id: bantool.c 26164 2008-10-26 19:52:43Z androsyn $)\n",
206 BT_VERSION);
207
208 if(flag.pretend == NO)
209 {
210 if(rsdb_init(db_error_cb) == -1)
211 {
212 fprintf(stderr, "* Error: Unable to open database\n");
213 exit(EXIT_FAILURE);
214 }
215 check_schema();
216
217 if(flag.vacuum)
218 db_reclaim_slack();
219
220 if(flag.import && flag.wipe)
221 {
222 flag.dupes_ok = YES; /* dont check for dupes if we are wiping the db clean */
223 for(i = 0; i < 3; i++)
224 fprintf(stdout,
225 "* WARNING: YOU ARE ABOUT TO WIPE YOUR DATABASE!\n");
226
227 fprintf(stdout, "* Press ^C to abort! ");
228 fflush(stdout);
229 rb_sleep(10, 0);
230 fprintf(stdout, "Carrying on...\n");
231 wipe_schema();
232 }
233 }
234 if(flag.verbose && flag.dupes_ok == YES)
235 fprintf(stdout, "* Allowing duplicate bans...\n");
236
237 /* checking for our files to import or export */
238 for(i = 0; i < LAST_BANDB_TYPE; i++)
239 {
240 rb_snprintf(conf, sizeof(conf), "%s/%s.conf%s",
241 etc, bandb_table[i], bandb_suffix[i]);
242
243 if(flag.import && flag.pretend == NO)
244 rsdb_transaction(RSDB_TRANS_START);
245
246 if(flag.import)
247 import_config(conf, i);
248
249 if(flag.export)
250 export_config(conf, i);
251
252 if(flag.import && flag.pretend == NO)
253 rsdb_transaction(RSDB_TRANS_END);
254 }
255
256 if(flag.import)
257 {
258 if(count.error && flag.verbose)
259 fprintf(stderr, "* I was unable to locate %i config files to import.\n",
260 count.error);
261
262 fprintf(stdout, "* Import Stats: Klines: %i, Dlines: %i, Xlines: %i, Resvs: %i \n",
263 count.klines, count.dlines, count.xlines, count.resvs);
264
265 fprintf(stdout,
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");
267
268 if(flag.pretend)
269 fprintf(stdout,
270 "* Pretend mode engaged. Nothing was actually entered into the database.\n");
271 }
272
273 return 0;
274 }
275
276
277 /**
278 * export the database to old-style flat files
279 */
280 static void
281 export_config(const char *conf, int id)
282 {
283 struct rsdb_table table;
284 static char sql[BUFSIZE * 2];
285 static char buf[512];
286 FILE *fd = NULL;
287 int j;
288
289 /* for sanity sake */
290 const int mask1 = 0;
291 const int mask2 = 1;
292 const int reason = 2;
293 const int oper = 3;
294 const int ts = 4;
295 /* const int perm = 5; */
296
297 if(!table_has_rows(bandb_table[id]))
298 return;
299
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",
303 bandb_table[id]);
304 else
305 rb_snprintf(sql, sizeof(sql),
306 "SELECT DISTINCT mask1,mask2,reason,oper,time FROM %s WHERE perm = 0 ORDER BY time",
307 bandb_table[id]);
308
309 rsdb_exec_fetch(&table, sql);
310 if(table.row_count <= 0)
311 {
312 rsdb_exec_fetch_end(&table);
313 return;
314 }
315
316 if(flag.verbose)
317 fprintf(stdout, "* checking for %s: ", conf); /* debug */
318
319 /* open config for reading, or skip to the next */
320 if(!(fd = fopen(conf, "w")))
321 {
322 if(flag.verbose)
323 fprintf(stdout, "\tmissing.\n");
324 count.error++;
325 return;
326 }
327
328 for(j = 0; j < table.row_count; j++)
329 {
330 switch (id)
331 {
332 case BANDB_DLINE:
333 case BANDB_DLINE_PERM:
334 rb_snprintf(buf, sizeof(buf),
335 "\"%s\",\"%s\",\"\",\"%s\",\"%s\",%s\n",
336 table.row[j][mask1],
337 mangle_reason(table.row[j][reason]),
338 bt_smalldate(table.row[j][ts]),
339 table.row[j][oper], table.row[j][ts]);
340 break;
341
342 case BANDB_XLINE:
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]);
349 break;
350
351 case BANDB_RESV:
352 case BANDB_RESV_PERM:
353 rb_snprintf(buf, sizeof(buf),
354 "\"%s\",\"%s\",\"%s\",%s\n",
355 table.row[j][mask1],
356 mangle_reason(table.row[j][reason]),
357 table.row[j][oper], table.row[j][ts]);
358 break;
359
360
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],
367 table.row[j][ts]);
368 break;
369 }
370
371 fprintf(fd, "%s", buf);
372 }
373
374 rsdb_exec_fetch_end(&table);
375 if(flag.verbose)
376 fprintf(stdout, "\twritten.\n");
377 fclose(fd);
378 }
379
380 /**
381 * attempt to condense the individual conf functions into one
382 */
383 static void
384 import_config(const char *conf, int id)
385 {
386 FILE *fd;
387
388 char line[BUFSIZE];
389 char *p;
390 int i = 0;
391
392 char f_perm = 0;
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];
400
401 if(flag.verbose)
402 fprintf(stdout, "* checking for %s: ", conf); /* debug */
403
404 /* open config for reading, or skip to the next */
405 if(!(fd = fopen(conf, "r")))
406 {
407 if(flag.verbose)
408 fprintf(stdout, "%*s", strlen(bandb_suffix[id]) > 0 ? 10 : 15,
409 "missing.\n");
410 count.error++;
411 return;
412 }
413
414 if(strstr(conf, ".perm") != 0)
415 f_perm = 1;
416
417
418 /* xline
419 * "SYSTEM","0","banned","stevoo!stevoo@efnet.port80.se{stevoo}",1111080437
420 * resv
421 * "OseK","banned nickname","stevoo!stevoo@efnet.port80.se{stevoo}",1111031619
422 * dline
423 * "194.158.192.0/19","laptop scammers","","2005/3/17 05.33","stevoo!stevoo@efnet.port80.se{stevoo}",1111033988
424 */
425 while(fgets(line, sizeof(line), fd))
426 {
427 if((p = strpbrk(line, "\r\n")) != NULL)
428 *p = '\0';
429
430 if((*line == '\0') || (*line == '#'))
431 continue;
432
433 /* mask1 */
434 f_mask1 = getfield(line);
435
436 if(EmptyString(f_mask1))
437 continue;
438
439 /* mask2 */
440 switch (id)
441 {
442 case BANDB_XLINE:
443 case BANDB_XLINE_PERM:
444 f_mask1 = escape_quotes(clean_gecos_field(f_mask1));
445 getfield(NULL); /* empty field */
446 break;
447
448 case BANDB_RESV:
449 case BANDB_RESV_PERM:
450 case BANDB_DLINE:
451 case BANDB_DLINE_PERM:
452 break;
453
454 default:
455 f_mask2 = getfield(NULL);
456 if(EmptyString(f_mask2))
457 continue;
458 break;
459 }
460
461 /* reason */
462 f_reason = getfield(NULL);
463 if(EmptyString(f_reason))
464 continue;
465
466 /* oper comment */
467 switch (id)
468 {
469 case BANDB_KLINE:
470 case BANDB_KLINE_PERM:
471 case BANDB_DLINE:
472 case BANDB_DLINE_PERM:
473 f_oreason = getfield(NULL);
474 getfield(NULL);
475 break;
476
477 default:
478 break;
479 }
480
481 f_oper = getfield(NULL);
482 f_time = strip_quotes(f_oper + strlen(f_oper) + 2);
483 if(EmptyString(f_oper))
484 f_oper = "unknown";
485
486 /* meh */
487 if(id == BANDB_KLINE || id == BANDB_KLINE_PERM)
488 {
489 if(strstr(f_mask1, "!") != NULL)
490 {
491 fprintf(stderr,
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");
495 continue;
496 }
497 }
498
499 /* append operreason_field to reason_field */
500 if(!EmptyString(f_oreason))
501 rb_snprintf(newreason, sizeof(newreason), "%s | %s", f_reason, f_oreason);
502 else
503 rb_snprintf(newreason, sizeof(newreason), "%s", f_reason);
504
505 if(flag.pretend == NO)
506 {
507 if(flag.dupes_ok == NO)
508 drop_dupes(f_mask1, f_mask2, bandb_table[id]);
509
510 rsdb_exec(NULL,
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,
513 newreason);
514 }
515
516 if(flag.pretend && flag.verbose)
517 fprintf(stdout,
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,
520 f_time);
521
522 i++;
523 }
524
525 switch (bandb_letter[id])
526 {
527 case 'K':
528 count.klines += i;
529 break;
530 case 'D':
531 count.dlines += i;
532 break;
533 case 'X':
534 count.xlines += i;
535 break;
536 case 'R':
537 count.resvs += i;
538 break;
539 default:
540 break;
541 }
542
543 if(flag.verbose)
544 fprintf(stdout, "%*s\n", strlen(bandb_suffix[id]) > 0 ? 10 : 15, "imported.");
545
546 return;
547 }
548
549 /**
550 * getfield
551 *
552 * inputs - input buffer
553 * output - next field
554 * side effects - field breakup for ircd.conf file.
555 */
556 char *
557 getfield(char *newline)
558 {
559 static char *line = NULL;
560 char *end, *field;
561
562 if(newline != NULL)
563 line = newline;
564
565 if(line == NULL)
566 return (NULL);
567
568 field = line;
569
570 /* XXX make this skip to first " if present */
571 if(*field == '"')
572 field++;
573 else
574 return (NULL); /* mal-formed field */
575
576 end = strchr(line, ',');
577
578 while(1)
579 {
580 /* no trailing , - last field */
581 if(end == NULL)
582 {
583 end = line + strlen(line);
584 line = NULL;
585
586 if(*end == '"')
587 {
588 *end = '\0';
589 return field;
590 }
591 else
592 return NULL;
593 }
594 else
595 {
596 /* look for a ", to mark the end of a field.. */
597 if(*(end - 1) == '"')
598 {
599 line = end + 1;
600 end--;
601 *end = '\0';
602 return field;
603 }
604
605 /* search for the next ',' */
606 end++;
607 end = strchr(end, ',');
608 }
609 }
610
611 return NULL;
612 }
613
614 /**
615 * strip away "quotes" from around strings
616 */
617 static char *
618 strip_quotes(const char *string)
619 {
620 static char buf[14]; /* int(11) + 2 + \0 */
621 char *str = buf;
622
623 if(string == NULL)
624 return NULL;
625
626 while(*string)
627 {
628 if(*string != '"')
629 {
630 *str++ = *string;
631 }
632 string++;
633 }
634 *str = '\0';
635 return buf;
636 }
637
638 /**
639 * escape quotes in a string
640 */
641 static char *
642 escape_quotes(const char *string)
643 {
644 static char buf[BUFSIZE * 2];
645 char *str = buf;
646
647 if(string == NULL)
648 return NULL;
649
650 while(*string)
651 {
652 if(*string == '"')
653 {
654 *str++ = '\\';
655 *str++ = '"';
656 }
657 else
658 {
659 *str++ = *string;
660 }
661 string++;
662 }
663 *str = '\0';
664 return buf;
665 }
666
667
668 static char *
669 mangle_reason(const char *string)
670 {
671 static char buf[BUFSIZE * 2];
672 char *str = buf;
673
674 if(string == NULL)
675 return NULL;
676
677 while(*string)
678 {
679 switch (*string)
680 {
681 case '"':
682 *str = '\'';
683 break;
684 case ':':
685 *str = ' ';
686 break;
687 default:
688 *str = *string;
689 }
690 string++;
691 str++;
692
693 }
694 *str = '\0';
695 return buf;
696 }
697
698
699 /**
700 * change spaces to \s in gecos field
701 */
702 static const char *
703 clean_gecos_field(const char *gecos)
704 {
705 static char buf[BUFSIZE * 2];
706 char *str = buf;
707
708 if(gecos == NULL)
709 return NULL;
710
711 while(*gecos)
712 {
713 if(*gecos == ' ')
714 {
715 *str++ = '\\';
716 *str++ = 's';
717 }
718 else
719 *str++ = *gecos;
720 gecos++;
721 }
722 *str = '\0';
723 return buf;
724 }
725
726 /**
727 * verify the database integrity, and if necessary create apropriate tables
728 */
729 static void
730 check_schema(void)
731 {
732 int i, j;
733 char type[8]; /* longest string is 'INTEGER\0' */
734
735 if(flag.verify || flag.verbose)
736 fprintf(stdout, "* Verifying database.\n");
737
738 const char *columns[] = {
739 "perm",
740 "mask1",
741 "mask2",
742 "oper",
743 "time",
744 "reason",
745 NULL
746 };
747
748 for(i = 0; i < LAST_BANDB_TYPE; i++)
749 {
750 if(!table_exists(bandb_table[i]))
751 {
752 rsdb_exec(NULL,
753 "CREATE TABLE %s (mask1 TEXT, mask2 TEXT, oper TEXT, time INTEGER, perm INTEGER, reason TEXT)",
754 bandb_table[i]);
755 }
756
757 /*
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
760 */
761 else
762 {
763 for(j = 0; columns[j] != NULL; j++)
764 {
765 if(!strcmp(columns[j], "time") && !strcmp(columns[j], "perm"))
766 rb_strlcpy(type, "INTEGER", sizeof(type));
767 else
768 rb_strlcpy(type, "TEXT", sizeof(type));
769
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],
772 columns[j], type);
773 }
774 }
775
776 i++; /* skip over .perm */
777 }
778 }
779
780 static void
781 db_reclaim_slack(void)
782 {
783 fprintf(stdout, "* Reclaiming free space.\n");
784 rsdb_exec(NULL, "VACUUM");
785 }
786
787
788 /**
789 * check that appropriate tables exist.
790 */
791 static int
792 table_exists(const char *dbtab)
793 {
794 struct rsdb_table table;
795 rsdb_exec_fetch(&table, "SELECT name FROM sqlite_master WHERE type='table' AND name='%s'",
796 dbtab);
797 rsdb_exec_fetch_end(&table);
798 return table.row_count;
799 }
800
801 /**
802 * check that there are actual entries in a table
803 */
804 static int
805 table_has_rows(const char *dbtab)
806 {
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;
811 }
812
813 /**
814 * completly wipes out an existing ban.db of all entries.
815 */
816 static void
817 wipe_schema(void)
818 {
819 int i;
820 rsdb_transaction(RSDB_TRANS_START);
821 for(i = 0; i < LAST_BANDB_TYPE; i++)
822 {
823 rsdb_exec(NULL, "DROP TABLE %s", bandb_table[i]);
824 i++; /* double increment to skip over .perm */
825 }
826 rsdb_transaction(RSDB_TRANS_END);
827
828 check_schema();
829 }
830
831 /**
832 * remove pre-existing duplicate bans from the database.
833 * we favor the new, imported ban over the one in the database
834 */
835 void
836 drop_dupes(const char *user, const char *host, const char *t)
837 {
838 rsdb_exec(NULL, "DELETE FROM %s WHERE mask1='%Q' AND mask2='%Q'", t, user, host);
839 }
840
841 static void
842 db_error_cb(const char *errstr)
843 {
844 return;
845 }
846
847
848 /**
849 * convert unix timestamp to human readable (small) date
850 */
851 static char *
852 bt_smalldate(const char *string)
853 {
854 static char buf[MAX_DATE_STRING];
855 struct tm *lt;
856 time_t t;
857 t = strtol(string, NULL, 10);
858 lt = gmtime(&t);
859 if(lt == NULL)
860 return NULL;
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);
863 return buf;
864 }
865
866 /**
867 * you are here ->.
868 */
869 void
870 print_help(int i_exit)
871 {
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");
879
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");
884 fprintf(stderr,
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");
888 fprintf(stderr,
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");
891 fprintf(stderr,
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");
894 fprintf(stderr,
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");
898 fprintf(stderr,
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");
901 exit(i_exit);
902 }