]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/convert-conf.c
1 /* convert-conf.c - Convert ircu2.10.11 ircd.conf to ircu2.10.12 format.
2 * Copyright 2005 Michael Poole
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 #include <ctype.h> /* tolower() */
21 #include <stdio.h> /* *printf(), fgets() */
22 #include <stdlib.h> /* free(), strtol() */
23 #include <string.h> /* strlen(), memcpy(), strchr(), strspn() */
27 const char *admin_names
[] = { "location", "contact", "contact", 0 },
28 *connect_names
[] = { "host", "password", "name", "#port", "class", 0 },
29 *crule_names
[] = { "server", "", "rule", 0 },
30 *general_names
[] = { "name", "vhost", "description", "", "#numeric", 0 },
31 *motd_names
[] = { "host", "file", 0 },
32 *class_names
[] = { "name", "#pingfreq", "#connectfreq", "#maxlinks", "#sendq", 0 },
33 *removed_features
[] = { "VIRTUAL_HOST", "TIMESEC", "OPERS_SEE_IN_SECRET_CHANNELS", "LOCOP_SEE_IN_SECRET_CHANNELS", "HIS_STATS_h", "HIS_DESYNCS", "AUTOHIDE", 0 };
34 char orig_line
[512], line
[512], dbuf
[512];
35 char *fields
[MAX_FIELDS
+ 1];
39 /*** GENERIC SUPPORT CODE ***/
41 static int split_line(char *input
, char **output
)
43 size_t quoted
= 0, jj
;
44 char *dest
= dbuf
, ch
;
48 while (*input
!= '\0' && *input
!= '#') switch (ch
= *input
++) {
54 if (nfields
>= MAX_FIELDS
)
56 output
[nfields
++] = dest
;
60 switch (ch
= *input
++) {
61 case 'b': *dest
++ = '\b'; break;
62 case 'f': *dest
++ = '\f'; break;
63 case 'n': *dest
++ = '\n'; break;
64 case 'r': *dest
++ = '\r'; break;
65 case 't': *dest
++ = '\t'; break;
66 case 'v': *dest
++ = '\v'; break;
67 default: *dest
++ = ch
; break;
70 case '"': quoted
= !quoted
; break;
71 default: *dest
++ = ch
; break;
75 for (jj
= nfields
; jj
< MAX_FIELDS
; ++jj
)
80 static void simple_line(const char *block
, const char **names
, const char *extra
)
84 /* Print the current line and start the new block. */
85 fprintf(stdout
, "# %s\n%s {\n", orig_line
, block
);
87 /* Iterate over fields in input line, formatting each. */
88 for (ii
= 0; ii
< nfields
&& names
[ii
]; ++ii
) {
89 if (!fields
[ii
][0] || !names
[ii
][0])
91 else if (names
[ii
][0] == '#')
92 fprintf(stdout
, "\t%s = %s;\n", names
[ii
] + 1, fields
[ii
]);
94 fprintf(stdout
, "\t%s = \"%s\";\n", names
[ii
], fields
[ii
]);
97 /* Close the new block (including any fixed-form text). */
99 fprintf(stdout
, "\t%s\n", extra
);
100 fputs("};\n", stdout
);
103 #define dupstring(TARGET, SOURCE) do { free(TARGET); if (SOURCE) { size_t len = strlen(SOURCE) + 1; (TARGET) = malloc(len); memcpy((TARGET), (SOURCE), len); } else (TARGET) = 0; } while(0)
105 /*** MANAGING LISTS OF STRINGS ***/
108 struct string_list
*next
;
114 /** Find or insert the element from \a list that contains \a value.
115 * If an element of \a list already contains \a value, return it.
116 * Otherwise, append a new element to \a list containing \a value and
118 * @param[in,out] list A list of strings.
119 * @param[in] value A string to search for.
120 * @return A string list element from \a list containing \a value.
122 static struct string_list
*string_get(struct string_list
**list
, const char *value
)
124 struct string_list
*curr
;
125 size_t len
= strlen(value
), ii
;
127 while ((curr
= *list
)) {
128 for (ii
= 0; tolower(curr
->value
[ii
]) == tolower(value
[ii
]) && ii
< len
; ++ii
) ;
129 if (curr
->value
[ii
] == '\0' && value
[ii
] == '\0')
134 *list
= calloc(1, sizeof(**list
) + len
);
135 memcpy((*list
)->value
, value
, len
);
139 /*** SERVER CONNECTION RELATED CODE ***/
148 struct connect
*next
;
149 struct string_list
*origins
;
153 static struct connect
*get_connect(const char *name
)
155 struct connect
*conn
;
158 /* Look for a pre-existing connection with the same name. */
160 for (conn
= connects
; conn
; conn
= conn
->next
)
162 for (ii
= 0; tolower(name
[ii
]) == tolower(conn
->name
[ii
]) && ii
< nlen
; ++ii
) ;
163 if (conn
->name
[ii
] == '\0' && name
[ii
] == '\0')
167 /* If none was found, create a new one. */
170 conn
= calloc(1, sizeof(*conn
) + nlen
);
171 for (ii
= 0; ii
< nlen
; ++ii
)
172 conn
->name
[ii
] = name
[ii
];
173 conn
->next
= connects
;
177 /* Return the connection. */
181 static void do_connect(void)
183 struct connect
*conn
= get_connect(fields
[2]);
184 dupstring(conn
->host
, fields
[0]);
185 dupstring(conn
->password
, fields
[1]);
186 dupstring(conn
->port
, fields
[3]);
187 dupstring(conn
->class, fields
[4]);
188 string_get(&conn
->origins
, orig_line
);
191 static void do_hub(void)
193 struct connect
*conn
= get_connect(fields
[2]);
194 dupstring(conn
->hub
, fields
[0]);
195 dupstring(conn
->maximum
, fields
[3]);
196 string_get(&conn
->origins
, orig_line
);
199 static void do_leaf(void)
201 struct connect
*conn
= get_connect(fields
[2]);
204 string_get(&conn
->origins
, orig_line
);
207 static void finish_connects(void)
209 struct connect
*conn
;
210 struct string_list
*sl
;
212 for (conn
= connects
; conn
; conn
= conn
->next
)
214 for (sl
= conn
->origins
; sl
; sl
= sl
->next
)
215 fprintf(stdout
, "# %s\n", sl
->value
);
216 if (conn
->name
== NULL
217 || conn
->host
== NULL
218 || conn
->password
== NULL
219 || conn
->class == NULL
)
221 fprintf(stderr
, "H:line missing C:line for %s\n",sl
->value
);
226 "Connect {\n\tname =\"%s\";\n\thost = \"%s\";\n"
227 "\tpassword = \"%s\";\n\tclass = \"%s\";\n",
228 conn
->name
, conn
->host
, conn
->password
, conn
->class);
229 if (conn
->port
&& conn
->port
[0] != '\0')
230 fprintf(stdout
, "\tport = %s;\n", conn
->port
);
233 "# Every Connect block should have a port number.\n"
234 "# To prevent autoconnects, set autoconnect = no.\n"
236 "\tautoconnect = no;\n");
237 if (conn
->maximum
&& conn
->maximum
[0] != '\0')
238 fprintf(stdout
, "\tmaxhops = %s;\n", conn
->maximum
);
239 if (conn
->hub
&& conn
->hub
[0] != '\0')
240 fprintf(stdout
, "\thub = \"%s\";\n", conn
->hub
);
241 fprintf(stdout
, "};\n\n");
246 /*** FEATURE MANAGEMENT CODE ***/
249 struct string_list
*values
;
250 struct string_list
*origins
;
251 struct feature
*next
;
255 struct remapped_feature
{
257 const char *privilege
;
258 int flags
; /* 2 = global, 1 = local */
259 struct feature
*feature
;
260 } remapped_features
[] = {
261 /* Specially handled privileges: If you change the index of
262 * anything with NULL privilege, change the code in
263 * finish_operators() to match!
265 { "CRYPT_OPER_PASSWORD", NULL
, 0, 0 }, /* default: true */
266 { "OPER_KILL", NULL
, 2, 0 }, /* default: true */
267 { "LOCAL_KILL_ONLY", NULL
, 2, 0 }, /* default: false */
268 /* remapped features that affect all opers */
269 { "OPER_NO_CHAN_LIMIT", "chan_limit", 3, 0 },
270 { "OPER_MODE_LCHAN", "mode_lchan", 3, 0 },
271 { "OPER_WALK_THROUGH_LMODES", "walk_lchan", 3, 0 },
272 { "NO_OPER_DEOP_LCHAN", "deop_lchan", 3, 0 },
273 { "SHOW_INVISIBLE_USERS", "show_invis", 3, 0 },
274 { "SHOW_ALL_INVISIBLE_USERS", "show_all_invis", 3, 0 },
275 { "UNLIMIT_OPER_QUERY", "unlimit_query", 3, 0 },
276 /* remapped features affecting only global opers */
277 { "OPER_REHASH", "rehash", 2, 0 },
278 { "OPER_RESTART", "restart", 2, 0 },
279 { "OPER_DIE", "die", 2, 0 },
280 { "OPER_GLINE", "gline", 2, 0 },
281 { "OPER_LGLINE", "local_gline", 2, 0 },
282 { "OPER_JUPE", "jupe", 2, 0 },
283 { "OPER_LJUPE", "local_jupe", 2, 0 },
284 { "OPER_OPMODE", "opmode", 2, 0 },
285 { "OPER_LOPMODE", "local_opmode", 2, 0 },
286 { "OPER_FORCE_OPMODE", "force_opmode", 2, 0 },
287 { "OPER_FORCE_LOPMODE", "force_local_opmode", 2, 0 },
288 { "OPER_BADCHAN", "badchan", 2, 0 },
289 { "OPER_LBADCHAN", "local_badchan", 2, 0 },
290 { "OPER_SET", "set", 2, 0 },
291 { "OPER_WIDE_GLINE", "wide_gline", 2, 0 },
292 /* remapped features affecting only local opers */
293 { "LOCOP_KILL", "kill", 1, 0 },
294 { "LOCOP_REHASH", "rehash", 1, 0 },
295 { "LOCOP_RESTART", "restart", 1, 0 },
296 { "LOCOP_DIE", "die", 1, 0 },
297 { "LOCOP_LGLINE", "local_gline", 1, 0 },
298 { "LOCOP_LJUPE", "local_jupe", 1, 0 },
299 { "LOCOP_LOPMODE", "local_opmode", 1, 0 },
300 { "LOCOP_FORCE_LOPMODE", "force_local_opmode", 1, 0 },
301 { "LOCOP_LBADCHAN", "local_badchan", 1, 0 },
302 { "LOCOP_WIDE_GLINE", "wide_gline", 1, 0 },
306 static void do_feature(void)
308 struct feature
*feat
;
311 ii
= strlen(fields
[0]);
312 feat
= calloc(1, sizeof(*feat
) + ii
);
314 feat
->name
[ii
] = fields
[0][ii
];
315 feat
->next
= features
;
317 string_get(&feat
->origins
, orig_line
);
318 for (ii
= 1; fields
[ii
] && fields
[ii
][0]; ++ii
)
319 string_get(&feat
->values
, fields
[ii
]);
322 static void finish_features(void)
324 struct remapped_feature
*rmf
;
325 struct string_list
*sl
;
326 struct feature
*feat
;
329 fputs("Features {\n", stdout
);
330 fputs("\t\"OPLEVELS\" = \"FALSE\";\n", stdout
);
331 fputs("\t\"ZANNELS\" = \"FALSE\";\n", stdout
);
333 for (feat
= features
; feat
; feat
= feat
->next
) {
334 /* Display the original feature line we are talking about. */
335 for (sl
= feat
->origins
; sl
; sl
= sl
->next
)
336 fprintf(stdout
, "# %s\n", sl
->value
);
338 /* See if the feature was remapped to an oper privilege. */
339 for (rmf
= remapped_features
; rmf
->name
; rmf
++)
340 if (0 == strcmp(feat
->name
, rmf
->name
))
344 fprintf(stdout
, "# Above feature mapped to an oper privilege.\n");
348 /* Was it removed? */
349 for (ii
= 0; removed_features
[ii
]; ++ii
)
350 if (0 == strcmp(feat
->name
, removed_features
[ii
]))
352 if (removed_features
[ii
]) {
353 fprintf(stdout
, "# Above feature no longer exists.\n");
357 /* Wasn't remapped, wasn't removed: print it out. */
358 fprintf(stdout
, "\t\"%s\" =", feat
->name
);
359 for (sl
= feat
->values
; sl
; sl
= sl
->next
)
360 fprintf(stdout
, " \"%s\"", sl
->value
);
361 fprintf(stdout
, ";\n");
363 fputs("};\n\n", stdout
);
367 /*** OPERATOR BLOCKS ***/
376 struct operator *next
;
379 static void do_operator(int is_local
)
381 struct operator *oper
;
383 oper
= calloc(1, sizeof(*oper
));
384 dupstring(oper
->host
, fields
[0]);
385 dupstring(oper
->password
, fields
[1]);
386 dupstring(oper
->name
, fields
[2]);
387 dupstring(oper
->class, fields
[4]);
388 dupstring(oper
->origin
, orig_line
);
389 oper
->is_local
= is_local
;
390 oper
->next
= operators
;
394 static void finish_operators(void)
396 struct remapped_feature
*remap
;
397 struct operator *oper
;
398 struct feature
*feat
;
400 int global_kill
= 0, mask
= 0;
403 if ((feat
= remapped_features
[0].feature
) && feat
->values
404 && 0 == strcmp(feat
->values
->value
, "FALSE"))
407 if ((feat
= remapped_features
[1].feature
) && feat
->values
408 && 0 == strcmp(feat
->values
->value
, "FALSE"))
410 else if ((feat
= remapped_features
[2].feature
) && feat
->values
411 && 0 == strcmp(feat
->values
->value
, "FALSE"))
414 for (oper
= operators
; oper
; oper
= oper
->next
) {
415 fprintf(stdout
, "# %s\nOperator {\n\tname = \"%s\";\n"
416 "\thost = \"%s\";\n\tpassword = \"%s%s\";\n"
417 "\tclass = \"%s\";\n",
418 oper
->origin
, oper
->name
, oper
->host
, pw_salt
,
419 oper
->password
, oper
->class);
420 if (oper
->is_local
) {
421 fputs("\tlocal = yes;\n", stdout
);
424 fputs("\tlocal = no;\n", stdout
);
425 if (global_kill
== 1)
426 fputs("\tkill = no;\n\tlocal_kill = no;\n", stdout
);
427 else if (global_kill
== 2)
428 fputs("\tkill = no;\n\tlocal_kill = yes;\n", stdout
);
431 for (ii
= 0; (remap
= &remapped_features
[ii
++])->name
; ) {
432 if (!remap
->feature
|| !remap
->privilege
433 || !remap
->feature
->values
|| !(remap
->flags
& mask
))
435 fprintf(stdout
, "\t%s = %s;\n", remap
->privilege
,
436 strcmp(remap
->feature
->values
->value
, "TRUE") ? "no" : "yes");
438 fputs("};\n\n", stdout
);
442 /*** OTHER CONFIG TRANSFORMS ***/
444 static void do_kill(void)
446 const char *host
= fields
[0], *reason
= fields
[1], *user
= fields
[2];
448 if (!memcmp(host
, "$R", 3)) {
449 fprintf(stderr
, "Empty realname K: line at line %u.\n", lineno
);
453 /* Print the current line and start the new block. */
454 fprintf(stdout
, "# %s\nKill {\n", orig_line
);
456 /* Translate the user-matching portions. */
457 if (host
[0] == '$' && host
[1] == 'R') {
458 /* Realname kill, possibly with a username */
459 fprintf(stdout
, "\trealname = \"%s\";\n", host
+ 2);
460 if (user
[0] != '\0' && (user
[0] != '*' || user
[1] != '\0'))
461 fprintf(stdout
, "\thost = \"%s@*\";\n", user
);
463 /* Normal host or IP-based kill */
464 if (user
[0] != '\0' && (user
[0] != '*' || user
[1] != '\0'))
465 fprintf(stdout
, "\thost = \"%s@%s\";\n", user
, host
);
467 fprintf(stdout
, "\thost = \"%s\";\n", host
);
470 /* Translate the reason section. */
471 if (reason
[0] == '!')
472 fprintf(stdout
, "\tfile = \"%s\";\n", reason
+ 1);
474 fprintf(stdout
, "\treason = \"%s\";\n", reason
);
476 /* Close the block. */
477 fprintf(stdout
, "};\n");
480 static void do_port(void)
482 const char *ipmask
= fields
[0], *iface
= fields
[1], *flags
= fields
[2], *port
= fields
[3];
484 /* Print the current line and start the new block. */
485 fprintf(stdout
, "# %s\nPort {\n", orig_line
);
487 /* Print the easy fields. */
488 fprintf(stdout
, "\tport = %s;\n", port
);
489 if (iface
&& iface
[0] != '\0')
490 fprintf(stdout
, "\tvhost = \"%s\";\n", iface
);
491 if (ipmask
&& ipmask
[0] != '\0')
492 fprintf(stdout
, "\tmask = \"%s\";\n", ipmask
);
494 /* Translate flag field. */
495 while (*flags
) switch (*flags
++) {
496 case 'C': case 'c': /* client port is default state */; break;
497 case 'S': case 's': fprintf(stdout
, "\tserver = yes;\n"); break;
498 case 'H': case 'h': fprintf(stdout
, "\thidden = yes;\n"); break;
501 /* Close the block. */
502 fprintf(stdout
, "};\n");
505 struct string_list
*quarantines
;
507 static void do_quarantine(void)
509 struct string_list
*q
;
510 q
= string_get(&quarantines
, fields
[0]);
511 dupstring(q
->origin
, orig_line
);
512 dupstring(q
->extra
, fields
[1]);
515 static void finish_quarantines(void)
517 struct string_list
*sl
;
521 fputs("Quarantine {\n", stdout
);
522 for (sl
= quarantines
; sl
; sl
= sl
->next
)
523 fprintf(stdout
, "# %s\n\t\"%s\" = \"%s\";\n", sl
->origin
, sl
->value
, sl
->extra
);
524 fputs("};\n\n", stdout
);
528 static void do_uworld(void)
530 fprintf(stdout
, "# %s\n", orig_line
);
531 if (fields
[0] && fields
[0][0])
532 fprintf(stdout
, "Uworld { name = \"%s\"; };\n", fields
[0]);
533 if (fields
[1] && fields
[1][0])
534 fprintf(stdout
, "Jupe { nick = \"%s\"; };\n", fields
[1]);
537 static void emit_client(const char *mask
, const char *passwd
, const char *class, long maxlinks
, int is_ip
)
542 delim
= strchr(mask
, '@');
546 len
= strspn(delim
, "0123456789.*");
548 fprintf(stderr
, "Invalid IP mask on line %u.\n", lineno
);
551 fprintf(stdout
, "Client {\n\tusername = \"%s\";\n\tip = \"%s\";\n", mask
, delim
);
553 fprintf(stdout
, "Client {\n\tusername =\"%s\";\n\thost = \"%s\";\n", mask
, delim
);
556 len
= strspn(mask
, "0123456789.*");
559 fprintf(stdout
, "Client {\n\tip = \"%s\";\n", mask
);
561 if (!strchr(mask
, '.') && !strchr(mask
, '*'))
563 fprintf(stdout
, "Client {\n\thost = \"%s\";\n", mask
);
567 fprintf(stdout
, "\tpassword = \"%s\";\n", passwd
);
570 fprintf(stdout
, "\tmaxlinks = %ld;\n", maxlinks
);
572 fprintf(stdout
, "\tclass = \"%s\";\n};\n", class);
575 static void do_client(void)
577 char *passwd
= NULL
, *delim
;
580 /* Print the current line. */
581 fprintf(stdout
, "# %s\n", orig_line
);
583 /* See if the password is really a maxlinks count. */
584 maxlinks
= strtol(fields
[1], &delim
, 10);
585 if (fields
[1][0] == '\0')
587 else if (maxlinks
< 0 || maxlinks
> 99 || *delim
!= '\0')
590 /* Translate the IP and host mask fields into blocks. */
591 emit_client(fields
[0], passwd
, fields
[4], maxlinks
, 1);
592 emit_client(fields
[2], passwd
, fields
[4], maxlinks
, 0);
595 int main(int argc
, char *argv
[])
601 else if (!(ifile
= fopen(argv
[1], "rt"))) {
602 fprintf(stderr
, "Unable to open file %s for input.\n", argv
[1]);
606 for (lineno
= 1; fgets(line
, sizeof(line
), ifile
); ++lineno
) {
607 /* Read line and pass comments through. */
608 size_t len
= strlen(line
);
609 if (line
[0] == '#') {
613 /* Strip trailing whitespace. */
614 while (len
> 0 && isspace(line
[len
-1]))
616 /* Pass blank lines through. */
621 /* Skip but report invalid lines. */
622 if (line
[1] != ':') {
623 fprintf(stdout
, "# %s\n", line
);
624 fprintf(stderr
, "Invalid input line %d.\n", lineno
);
627 /* Copy the original line into a reusable variable. */
628 strcpy(orig_line
, line
);
629 /* Split line into fields. */
630 nfields
= split_line(line
+ 2, fields
);
632 /* Process the input line. */
634 case 'A': case 'a': simple_line("Admin", admin_names
, NULL
); break;
635 case 'C': case 'c': do_connect(); break;
636 case 'D': simple_line("CRule", crule_names
, "all = yes;"); break;
637 case 'd': simple_line("CRule", crule_names
, NULL
); break;
638 case 'F': case 'f': do_feature(); break;
639 case 'H': case 'h': do_hub(); break;
640 case 'I': case 'i': do_client(); break;
641 case 'K': case 'k': do_kill(); break;
642 case 'L': case 'l': do_leaf(); break;
643 case 'M': case 'm': simple_line("General", general_names
, NULL
); break;
644 case 'O': do_operator(0); break;
645 case 'o': do_operator(1); break;
646 case 'P': case 'p': do_port(); break;
647 case 'Q': case 'q': do_quarantine(); break;
648 case 'T': case 't': simple_line("Motd", motd_names
, NULL
); break;
649 case 'U': case 'u': do_uworld(); break;
650 case 'Y': case 'y': simple_line("Class", class_names
, NULL
); break;
652 fprintf(stderr
, "Unknown line %u with leading character '%c'.\n", lineno
, line
[0]);
659 fputs("\n# The following lines were intentionally moved and rearranged."
660 "\n# Our apologies for any inconvenience this may cause."
663 finish_quarantines();