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