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