]>
Commit | Line | Data |
---|---|---|
189935b1 | 1 | /* convert-conf.c - Convert ircu2.10.11 ircd.conf to ircu2.10.12 format. |
2 | * Copyright 2005 Michael Poole | |
3 | * | |
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. | |
8 | * | |
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. | |
13 | * | |
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 | |
17 | * USA. | |
18 | */ | |
19 | ||
37d25209 | 20 | #include <ctype.h> /* tolower() */ |
189935b1 | 21 | #include <stdio.h> /* *printf(), fgets() */ |
22 | #include <stdlib.h> /* free(), strtol() */ | |
23 | #include <string.h> /* strlen(), memcpy(), strchr(), strspn() */ | |
24 | ||
25 | #define MAX_FIELDS 5 | |
26 | ||
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 }, | |
37d25209 | 33 | *removed_features[] = { "VIRTUAL_HOST", "TIMESEC", "OPERS_SEE_IN_SECRET_CHANNELS", "LOCOP_SEE_IN_SECRET_CHANNELS", "HIS_STATS_h", "HIS_DESYNCS", "AUTOHIDE", 0 }; |
189935b1 | 34 | char orig_line[512], line[512], dbuf[512]; |
35 | char *fields[MAX_FIELDS + 1]; | |
36 | unsigned int nfields; | |
37 | unsigned int lineno; | |
38 | ||
39 | /*** GENERIC SUPPORT CODE ***/ | |
40 | ||
41 | static int split_line(char *input, char **output) | |
42 | { | |
43 | size_t quoted = 0, jj; | |
44 | char *dest = dbuf, ch; | |
45 | ||
46 | nfields = 1; | |
47 | output[0] = dest; | |
48 | while (*input != '\0' && *input != '#') switch (ch = *input++) { | |
49 | case ':': | |
50 | if (quoted) | |
51 | *dest++ = ch; | |
52 | else { | |
53 | *dest++ = '\0'; | |
54 | if (nfields >= MAX_FIELDS) | |
55 | return nfields; | |
56 | output[nfields++] = dest; | |
57 | } | |
58 | break; | |
59 | case '\\': | |
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; | |
68 | } | |
69 | break; | |
70 | case '"': quoted = !quoted; break; | |
71 | default: *dest++ = ch; break; | |
72 | } | |
73 | ||
74 | *dest = '\0'; | |
75 | for (jj = nfields; jj < MAX_FIELDS; ++jj) | |
76 | output[jj] = dest; | |
77 | return nfields; | |
78 | } | |
79 | ||
80 | static void simple_line(const char *block, const char **names, const char *extra) | |
81 | { | |
82 | size_t ii; | |
83 | ||
84 | /* Print the current line and start the new block. */ | |
85 | fprintf(stdout, "# %s\n%s {\n", orig_line, block); | |
86 | ||
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]) | |
90 | continue; | |
91 | else if (names[ii][0] == '#') | |
92 | fprintf(stdout, "\t%s = %s;\n", names[ii] + 1, fields[ii]); | |
93 | else | |
94 | fprintf(stdout, "\t%s = \"%s\";\n", names[ii], fields[ii]); | |
95 | } | |
96 | ||
97 | /* Close the new block (including any fixed-form text). */ | |
98 | if (extra) | |
99 | fprintf(stdout, "\t%s\n", extra); | |
100 | fputs("};\n", stdout); | |
101 | } | |
102 | ||
37d25209 | 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) |
189935b1 | 104 | |
105 | /*** MANAGING LISTS OF STRINGS ***/ | |
106 | ||
107 | struct string_list { | |
108 | struct string_list *next; | |
109 | char *origin; | |
110 | char *extra; | |
111 | char value[1]; | |
112 | }; | |
113 | ||
37d25209 | 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 | |
117 | * return it. | |
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. | |
121 | */ | |
189935b1 | 122 | static struct string_list *string_get(struct string_list **list, const char *value) |
123 | { | |
124 | struct string_list *curr; | |
125 | size_t len = strlen(value), ii; | |
126 | ||
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') | |
130 | return curr; | |
131 | list = &curr->next; | |
132 | } | |
133 | ||
134 | *list = calloc(1, sizeof(**list) + len); | |
135 | memcpy((*list)->value, value, len); | |
136 | return *list; | |
137 | } | |
138 | ||
139 | /*** SERVER CONNECTION RELATED CODE ***/ | |
140 | ||
141 | struct connect { | |
142 | char *host; | |
143 | char *password; | |
144 | char *port; | |
145 | char *class; | |
146 | char *hub; | |
147 | char *maximum; | |
148 | struct connect *next; | |
149 | struct string_list *origins; | |
150 | char name[1]; | |
151 | } *connects; | |
152 | ||
153 | static struct connect *get_connect(const char *name) | |
154 | { | |
155 | struct connect *conn; | |
156 | size_t ii, nlen; | |
157 | ||
158 | /* Look for a pre-existing connection with the same name. */ | |
159 | nlen = strlen(name); | |
160 | for (conn = connects; conn; conn = conn->next) | |
161 | { | |
37d25209 | 162 | for (ii = 0; tolower(name[ii]) == tolower(conn->name[ii]) && ii < nlen; ++ii) ; |
189935b1 | 163 | if (conn->name[ii] == '\0' && name[ii] == '\0') |
164 | break; | |
165 | } | |
166 | ||
167 | /* If none was found, create a new one. */ | |
168 | if (!conn) | |
169 | { | |
170 | conn = calloc(1, sizeof(*conn) + nlen); | |
171 | for (ii = 0; ii < nlen; ++ii) | |
37d25209 | 172 | conn->name[ii] = name[ii]; |
189935b1 | 173 | conn->next = connects; |
174 | connects = conn; | |
175 | } | |
176 | ||
177 | /* Return the connection. */ | |
178 | return conn; | |
179 | } | |
180 | ||
181 | static void do_connect(void) | |
182 | { | |
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); | |
189 | } | |
190 | ||
191 | static void do_hub(void) | |
192 | { | |
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); | |
197 | } | |
198 | ||
199 | static void do_leaf(void) | |
200 | { | |
201 | struct connect *conn = get_connect(fields[2]); | |
202 | free(conn->hub); | |
203 | conn->hub = 0; | |
204 | string_get(&conn->origins, orig_line); | |
205 | } | |
206 | ||
207 | static void finish_connects(void) | |
208 | { | |
209 | struct connect *conn; | |
210 | struct string_list *sl; | |
211 | ||
212 | for (conn = connects; conn; conn = conn->next) | |
213 | { | |
214 | for (sl = conn->origins; sl; sl = sl->next) | |
215 | fprintf(stdout, "# %s\n", sl->value); | |
37d25209 | 216 | if (conn->name == NULL |
217 | || conn->host == NULL | |
218 | || conn->password == NULL | |
219 | || conn->class == NULL) | |
220 | { | |
221 | fprintf(stderr, "H:line missing C:line for %s\n",sl->value); | |
222 | continue; | |
223 | } | |
224 | ||
189935b1 | 225 | fprintf(stdout, |
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); | |
231 | else | |
232 | fprintf(stdout, | |
233 | "# Every Connect block should have a port number.\n" | |
234 | "# To prevent autoconnects, set autoconnect = no.\n" | |
235 | "#\tport = 4400;\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"); | |
242 | ||
243 | } | |
244 | } | |
245 | ||
246 | /*** FEATURE MANAGEMENT CODE ***/ | |
247 | ||
248 | struct feature { | |
249 | struct string_list *values; | |
250 | struct string_list *origins; | |
251 | struct feature *next; | |
252 | char name[1]; | |
253 | } *features; | |
254 | ||
255 | struct remapped_feature { | |
256 | const char *name; | |
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! | |
264 | */ | |
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 }, | |
303 | { 0, 0, 0, 0 } | |
304 | }; | |
305 | ||
306 | static void do_feature(void) | |
307 | { | |
308 | struct feature *feat; | |
309 | size_t ii; | |
310 | ||
311 | ii = strlen(fields[0]); | |
312 | feat = calloc(1, sizeof(*feat) + ii); | |
313 | while (ii-- > 0) | |
37d25209 | 314 | feat->name[ii] = fields[0][ii]; |
189935b1 | 315 | feat->next = features; |
316 | features = feat; | |
317 | string_get(&feat->origins, orig_line); | |
318 | for (ii = 1; fields[ii] && fields[ii][0]; ++ii) | |
319 | string_get(&feat->values, fields[ii]); | |
320 | } | |
321 | ||
322 | static void finish_features(void) | |
323 | { | |
324 | struct remapped_feature *rmf; | |
325 | struct string_list *sl; | |
326 | struct feature *feat; | |
327 | size_t ii; | |
328 | ||
37d25209 | 329 | fputs("Features {\n", stdout); |
330 | fputs("\t\"OPLEVELS\" = \"FALSE\";\n", stdout); | |
331 | fputs("\t\"ZANNELS\" = \"FALSE\";\n", stdout); | |
189935b1 | 332 | |
333 | for (feat = features; feat; feat = feat->next) { | |
37d25209 | 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); | |
337 | ||
189935b1 | 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)) | |
341 | break; | |
342 | if (rmf->name) { | |
343 | rmf->feature = feat; | |
344 | fprintf(stdout, "# Above feature mapped to an oper privilege.\n"); | |
345 | continue; | |
346 | } | |
347 | ||
348 | /* Was it removed? */ | |
349 | for (ii = 0; removed_features[ii]; ++ii) | |
350 | if (0 == strcmp(feat->name, removed_features[ii])) | |
351 | break; | |
352 | if (removed_features[ii]) { | |
353 | fprintf(stdout, "# Above feature no longer exists.\n"); | |
354 | continue; | |
355 | } | |
356 | ||
9f8856e9 | 357 | /* If it had no value before, drop it now since the lexer does |
358 | * not accept empty strings and the grammar does not accept | |
359 | * empty stringlists.*/ | |
360 | if (!feat->values) { | |
361 | fprintf(stdout, "# Above feature had no value.\n"); | |
362 | continue; | |
363 | } | |
364 | ||
189935b1 | 365 | /* Wasn't remapped, wasn't removed: print it out. */ |
366 | fprintf(stdout, "\t\"%s\" =", feat->name); | |
367 | for (sl = feat->values; sl; sl = sl->next) | |
368 | fprintf(stdout, " \"%s\"", sl->value); | |
369 | fprintf(stdout, ";\n"); | |
370 | } | |
371 | fputs("};\n\n", stdout); | |
372 | ||
373 | } | |
374 | ||
375 | /*** OPERATOR BLOCKS ***/ | |
376 | ||
377 | struct operator { | |
378 | char *name; | |
379 | char *host; | |
380 | char *password; | |
381 | char *class; | |
382 | char *origin; | |
383 | int is_local; | |
384 | struct operator *next; | |
385 | } *operators; | |
386 | ||
387 | static void do_operator(int is_local) | |
388 | { | |
389 | struct operator *oper; | |
390 | ||
391 | oper = calloc(1, sizeof(*oper)); | |
392 | dupstring(oper->host, fields[0]); | |
393 | dupstring(oper->password, fields[1]); | |
394 | dupstring(oper->name, fields[2]); | |
395 | dupstring(oper->class, fields[4]); | |
396 | dupstring(oper->origin, orig_line); | |
397 | oper->is_local = is_local; | |
398 | oper->next = operators; | |
399 | operators = oper; | |
400 | } | |
401 | ||
402 | static void finish_operators(void) | |
403 | { | |
404 | struct remapped_feature *remap; | |
405 | struct operator *oper; | |
406 | struct feature *feat; | |
407 | char *pw_salt = ""; | |
408 | int global_kill = 0, mask = 0; | |
409 | size_t ii; | |
410 | ||
411 | if ((feat = remapped_features[0].feature) && feat->values | |
412 | && 0 == strcmp(feat->values->value, "FALSE")) | |
413 | pw_salt = "$PLAIN$"; | |
414 | ||
415 | if ((feat = remapped_features[1].feature) && feat->values | |
416 | && 0 == strcmp(feat->values->value, "FALSE")) | |
417 | global_kill = 1; | |
418 | else if ((feat = remapped_features[2].feature) && feat->values | |
419 | && 0 == strcmp(feat->values->value, "FALSE")) | |
420 | global_kill = 2; | |
421 | ||
422 | for (oper = operators; oper; oper = oper->next) { | |
423 | fprintf(stdout, "# %s\nOperator {\n\tname = \"%s\";\n" | |
424 | "\thost = \"%s\";\n\tpassword = \"%s%s\";\n" | |
425 | "\tclass = \"%s\";\n", | |
426 | oper->origin, oper->name, oper->host, pw_salt, | |
427 | oper->password, oper->class); | |
428 | if (oper->is_local) { | |
429 | fputs("\tlocal = yes;\n", stdout); | |
430 | mask = 1; | |
431 | } else { | |
432 | fputs("\tlocal = no;\n", stdout); | |
433 | if (global_kill == 1) | |
434 | fputs("\tkill = no;\n\tlocal_kill = no;\n", stdout); | |
435 | else if (global_kill == 2) | |
436 | fputs("\tkill = no;\n\tlocal_kill = yes;\n", stdout); | |
437 | mask = 2; | |
438 | } | |
439 | for (ii = 0; (remap = &remapped_features[ii++])->name; ) { | |
440 | if (!remap->feature || !remap->privilege | |
37d25209 | 441 | || !remap->feature->values || !(remap->flags & mask)) |
189935b1 | 442 | continue; |
443 | fprintf(stdout, "\t%s = %s;\n", remap->privilege, | |
444 | strcmp(remap->feature->values->value, "TRUE") ? "no" : "yes"); | |
445 | } | |
446 | fputs("};\n\n", stdout); | |
447 | } | |
448 | } | |
449 | ||
450 | /*** OTHER CONFIG TRANSFORMS ***/ | |
451 | ||
452 | static void do_kill(void) | |
453 | { | |
454 | const char *host = fields[0], *reason = fields[1], *user = fields[2]; | |
455 | ||
456 | if (!memcmp(host, "$R", 3)) { | |
457 | fprintf(stderr, "Empty realname K: line at line %u.\n", lineno); | |
458 | return; | |
459 | } | |
460 | ||
461 | /* Print the current line and start the new block. */ | |
462 | fprintf(stdout, "# %s\nKill {\n", orig_line); | |
463 | ||
464 | /* Translate the user-matching portions. */ | |
465 | if (host[0] == '$' && host[1] == 'R') { | |
466 | /* Realname kill, possibly with a username */ | |
467 | fprintf(stdout, "\trealname = \"%s\";\n", host + 2); | |
468 | if (user[0] != '\0' && (user[0] != '*' || user[1] != '\0')) | |
469 | fprintf(stdout, "\thost = \"%s@*\";\n", user); | |
470 | } else { | |
471 | /* Normal host or IP-based kill */ | |
472 | if (user[0] != '\0' && (user[0] != '*' || user[1] != '\0')) | |
473 | fprintf(stdout, "\thost = \"%s@%s\";\n", user, host); | |
474 | else | |
475 | fprintf(stdout, "\thost = \"%s\";\n", host); | |
476 | } | |
477 | ||
478 | /* Translate the reason section. */ | |
479 | if (reason[0] == '!') | |
480 | fprintf(stdout, "\tfile = \"%s\";\n", reason + 1); | |
481 | else | |
482 | fprintf(stdout, "\treason = \"%s\";\n", reason); | |
483 | ||
484 | /* Close the block. */ | |
485 | fprintf(stdout, "};\n"); | |
486 | } | |
487 | ||
488 | static void do_port(void) | |
489 | { | |
490 | const char *ipmask = fields[0], *iface = fields[1], *flags = fields[2], *port = fields[3]; | |
491 | ||
492 | /* Print the current line and start the new block. */ | |
493 | fprintf(stdout, "# %s\nPort {\n", orig_line); | |
494 | ||
495 | /* Print the easy fields. */ | |
496 | fprintf(stdout, "\tport = %s;\n", port); | |
497 | if (iface && iface[0] != '\0') | |
498 | fprintf(stdout, "\tvhost = \"%s\";\n", iface); | |
499 | if (ipmask && ipmask[0] != '\0') | |
500 | fprintf(stdout, "\tmask = \"%s\";\n", ipmask); | |
501 | ||
502 | /* Translate flag field. */ | |
503 | while (*flags) switch (*flags++) { | |
504 | case 'C': case 'c': /* client port is default state */; break; | |
505 | case 'S': case 's': fprintf(stdout, "\tserver = yes;\n"); break; | |
506 | case 'H': case 'h': fprintf(stdout, "\thidden = yes;\n"); break; | |
507 | } | |
508 | ||
509 | /* Close the block. */ | |
510 | fprintf(stdout, "};\n"); | |
511 | } | |
512 | ||
513 | struct string_list *quarantines; | |
514 | ||
515 | static void do_quarantine(void) | |
516 | { | |
517 | struct string_list *q; | |
518 | q = string_get(&quarantines, fields[0]); | |
519 | dupstring(q->origin, orig_line); | |
520 | dupstring(q->extra, fields[1]); | |
521 | } | |
522 | ||
523 | static void finish_quarantines(void) | |
524 | { | |
525 | struct string_list *sl; | |
526 | ||
527 | if (quarantines) | |
528 | { | |
529 | fputs("Quarantine {\n", stdout); | |
530 | for (sl = quarantines; sl; sl = sl->next) | |
531 | fprintf(stdout, "# %s\n\t\"%s\" = \"%s\";\n", sl->origin, sl->value, sl->extra); | |
532 | fputs("};\n\n", stdout); | |
533 | } | |
534 | } | |
535 | ||
536 | static void do_uworld(void) | |
537 | { | |
538 | fprintf(stdout, "# %s\n", orig_line); | |
539 | if (fields[0] && fields[0][0]) | |
540 | fprintf(stdout, "Uworld { name = \"%s\"; };\n", fields[0]); | |
541 | if (fields[1] && fields[1][0]) | |
542 | fprintf(stdout, "Jupe { nick = \"%s\"; };\n", fields[1]); | |
543 | } | |
544 | ||
545 | static void emit_client(const char *mask, const char *passwd, const char *class, long maxlinks, int is_ip) | |
546 | { | |
547 | char *delim; | |
548 | size_t len; | |
549 | ||
550 | delim = strchr(mask, '@'); | |
551 | if (delim) { | |
552 | *delim++ = '\0'; | |
553 | if (is_ip) { | |
554 | len = strspn(delim, "0123456789.*"); | |
555 | if (delim[len]) { | |
556 | fprintf(stderr, "Invalid IP mask on line %u.\n", lineno); | |
557 | return; | |
558 | } | |
559 | fprintf(stdout, "Client {\n\tusername = \"%s\";\n\tip = \"%s\";\n", mask, delim); | |
560 | } else { | |
561 | fprintf(stdout, "Client {\n\tusername =\"%s\";\n\thost = \"%s\";\n", mask, delim); | |
562 | } | |
563 | } else if (is_ip) { | |
564 | len = strspn(mask, "0123456789.*"); | |
565 | if (mask[len]) | |
566 | return; | |
567 | fprintf(stdout, "Client {\n\tip = \"%s\";\n", mask); | |
568 | } else { | |
569 | if (!strchr(mask, '.') && !strchr(mask, '*')) | |
570 | return; | |
571 | fprintf(stdout, "Client {\n\thost = \"%s\";\n", mask); | |
572 | } | |
573 | ||
574 | if (passwd) | |
575 | fprintf(stdout, "\tpassword = \"%s\";\n", passwd); | |
576 | ||
577 | if (maxlinks >= 0) | |
578 | fprintf(stdout, "\tmaxlinks = %ld;\n", maxlinks); | |
579 | ||
580 | fprintf(stdout, "\tclass = \"%s\";\n};\n", class); | |
581 | } | |
582 | ||
583 | static void do_client(void) | |
584 | { | |
585 | char *passwd = NULL, *delim; | |
586 | long maxlinks; | |
587 | ||
588 | /* Print the current line. */ | |
589 | fprintf(stdout, "# %s\n", orig_line); | |
590 | ||
591 | /* See if the password is really a maxlinks count. */ | |
592 | maxlinks = strtol(fields[1], &delim, 10); | |
593 | if (fields[1][0] == '\0') | |
594 | maxlinks = -1; | |
595 | else if (maxlinks < 0 || maxlinks > 99 || *delim != '\0') | |
596 | passwd = fields[1]; | |
597 | ||
598 | /* Translate the IP and host mask fields into blocks. */ | |
599 | emit_client(fields[0], passwd, fields[4], maxlinks, 1); | |
600 | emit_client(fields[2], passwd, fields[4], maxlinks, 0); | |
601 | } | |
602 | ||
603 | int main(int argc, char *argv[]) | |
604 | { | |
605 | FILE *ifile; | |
606 | ||
607 | if (argc < 2) | |
608 | ifile = stdin; | |
609 | else if (!(ifile = fopen(argv[1], "rt"))) { | |
610 | fprintf(stderr, "Unable to open file %s for input.\n", argv[1]); | |
611 | return 1; | |
612 | } | |
613 | ||
614 | for (lineno = 1; fgets(line, sizeof(line), ifile); ++lineno) { | |
615 | /* Read line and pass comments through. */ | |
616 | size_t len = strlen(line); | |
617 | if (line[0] == '#') { | |
618 | fputs(line, stdout); | |
619 | continue; | |
620 | } | |
37d25209 | 621 | /* Strip trailing whitespace. */ |
622 | while (len > 0 && isspace(line[len-1])) | |
189935b1 | 623 | line[--len] = '\0'; |
37d25209 | 624 | /* Pass blank lines through. */ |
189935b1 | 625 | if (len == 0) { |
626 | fputc('\n', stdout); | |
627 | continue; | |
628 | } | |
629 | /* Skip but report invalid lines. */ | |
630 | if (line[1] != ':') { | |
631 | fprintf(stdout, "# %s\n", line); | |
632 | fprintf(stderr, "Invalid input line %d.\n", lineno); | |
633 | continue; | |
634 | } | |
635 | /* Copy the original line into a reusable variable. */ | |
636 | strcpy(orig_line, line); | |
637 | /* Split line into fields. */ | |
638 | nfields = split_line(line + 2, fields); | |
639 | ||
640 | /* Process the input line. */ | |
641 | switch (line[0]) { | |
642 | case 'A': case 'a': simple_line("Admin", admin_names, NULL); break; | |
643 | case 'C': case 'c': do_connect(); break; | |
644 | case 'D': simple_line("CRule", crule_names, "all = yes;"); break; | |
645 | case 'd': simple_line("CRule", crule_names, NULL); break; | |
646 | case 'F': case 'f': do_feature(); break; | |
647 | case 'H': case 'h': do_hub(); break; | |
648 | case 'I': case 'i': do_client(); break; | |
649 | case 'K': case 'k': do_kill(); break; | |
650 | case 'L': case 'l': do_leaf(); break; | |
651 | case 'M': case 'm': simple_line("General", general_names, NULL); break; | |
652 | case 'O': do_operator(0); break; | |
653 | case 'o': do_operator(1); break; | |
654 | case 'P': case 'p': do_port(); break; | |
655 | case 'Q': case 'q': do_quarantine(); break; | |
656 | case 'T': case 't': simple_line("Motd", motd_names, NULL); break; | |
657 | case 'U': case 'u': do_uworld(); break; | |
658 | case 'Y': case 'y': simple_line("Class", class_names, NULL); break; | |
659 | default: | |
660 | fprintf(stderr, "Unknown line %u with leading character '%c'.\n", lineno, line[0]); | |
661 | break; | |
662 | } | |
663 | } | |
664 | ||
665 | fclose(ifile); | |
666 | ||
667 | fputs("\n# The following lines were intentionally moved and rearranged." | |
668 | "\n# Our apologies for any inconvenience this may cause." | |
669 | "\n\n", stdout); | |
670 | finish_connects(); | |
671 | finish_quarantines(); | |
672 | finish_features(); | |
673 | finish_operators(); | |
674 | ||
675 | return 0; | |
676 | } |