1 |
/* Convert other programs' databases to XML. |
2 |
* |
3 |
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
4 |
* E-mail: <achurch@achurch.org> |
5 |
* Parts written by Andrew Kempe and others. |
6 |
* This program is free but copyrighted software; see the file GPL.txt for |
7 |
* details. |
8 |
*/ |
9 |
|
10 |
#define CONVERT_DB_MAIN |
11 |
#define STANDALONE_NICKSERV |
12 |
#define STANDALONE_CHANSERV |
13 |
#include "convert-db.h" |
14 |
#include "language.h" |
15 |
#include "modules/misc/xml.h" |
16 |
|
17 |
/*************************************************************************/ |
18 |
|
19 |
/* Data read in. */ |
20 |
|
21 |
NickGroupInfo *ngi_list; |
22 |
NickInfo *ni_list; |
23 |
ChannelInfo *ci_list; |
24 |
NewsItem *news_list; |
25 |
MaskData *md_list[256]; |
26 |
ServerStats *ss_list; |
27 |
int32 maxusercnt; |
28 |
time_t maxusertime; |
29 |
Password supass; |
30 |
int no_supass = 1; |
31 |
|
32 |
/* Symbols needed for core's misc.c. */ |
33 |
char **RejectEmail = NULL; |
34 |
int RejectEmail_count = 0; |
35 |
|
36 |
/*************************************************************************/ |
37 |
|
38 |
/* NULL-terminated list of supported database types. */ |
39 |
|
40 |
extern DBTypeInfo ALL_DB_TYPES(); |
41 |
static DBTypeInfo *dbtypes[] = { |
42 |
ALL_DB_TYPES(&), |
43 |
NULL |
44 |
}; |
45 |
|
46 |
/*************************************************************************/ |
47 |
/*************************** Exported routines ***************************/ |
48 |
/*************************************************************************/ |
49 |
|
50 |
/* Safe memory allocation and string duplication (from memory.c). */ |
51 |
|
52 |
void *smalloc(long size) |
53 |
{ |
54 |
void *ptr; |
55 |
if (!size) |
56 |
size = 1; |
57 |
ptr = malloc(size); |
58 |
if (!ptr) { |
59 |
fprintf(stderr, "Out of memory\n"); |
60 |
exit(1); |
61 |
} |
62 |
return ptr; |
63 |
} |
64 |
|
65 |
void *scalloc(long els, long elsize) |
66 |
{ |
67 |
void *ptr; |
68 |
if (!(els*elsize)) |
69 |
els = elsize = 1; |
70 |
ptr = calloc(els, elsize); |
71 |
if (!ptr) { |
72 |
fprintf(stderr, "Out of memory\n"); |
73 |
exit(1); |
74 |
} |
75 |
return ptr; |
76 |
} |
77 |
|
78 |
void *srealloc(void *ptr, long size) |
79 |
{ |
80 |
if (!size) |
81 |
size = 1; |
82 |
ptr = realloc(ptr, size); |
83 |
if (!ptr) { |
84 |
fprintf(stderr, "Out of memory\n"); |
85 |
exit(1); |
86 |
} |
87 |
return ptr; |
88 |
} |
89 |
|
90 |
char *sstrdup(const char *s) |
91 |
{ |
92 |
int len = strlen(s)+1; |
93 |
char *d = smalloc(len); |
94 |
memcpy(d, s, len); |
95 |
return d; |
96 |
} |
97 |
|
98 |
/*************************************************************************/ |
99 |
|
100 |
/* Initialize or clear a Password structure (from encrypt.c). */ |
101 |
|
102 |
void init_password(Password *password) |
103 |
{ |
104 |
memset(password, 0, sizeof(*password)); |
105 |
password->cipher = NULL; |
106 |
} |
107 |
|
108 |
void clear_password(Password *password) |
109 |
{ |
110 |
memset(password, 0, sizeof(*password)); |
111 |
free((char *)password->cipher); |
112 |
password->cipher = NULL; |
113 |
} |
114 |
|
115 |
/*************************************************************************/ |
116 |
|
117 |
/* NickInfo/NickGroupInfo allocation (from modules/nickserv/util.c). The |
118 |
* NickInfo and NickGroupInfo (if appropriate) are added into their |
119 |
* respective hash tables. Always succeeds (if the memory allocations |
120 |
* fail, aborts the program). |
121 |
*/ |
122 |
|
123 |
#include "modules/nickserv/util.c" |
124 |
|
125 |
NickInfo *makenick(const char *nick, NickGroupInfo **nickgroup_ret) |
126 |
{ |
127 |
NickInfo *ni = new_nickinfo(); |
128 |
strbcpy(ni->nick, nick); |
129 |
if (nickgroup_ret) { |
130 |
NickGroupInfo *ngi = new_nickgroupinfo(ni->nick); |
131 |
while (get_nickgroupinfo(ngi->id)) { |
132 |
/* We assume that eventually we'll find one that's not in use */ |
133 |
ngi->id = rand() + rand(); |
134 |
if (ngi->id == 0) |
135 |
ngi->id = 1; |
136 |
} |
137 |
ni->nickgroup = ngi->id; |
138 |
ARRAY_EXTEND(ngi->nicks); |
139 |
strbcpy(ngi->nicks[0], nick); |
140 |
add_nickgroupinfo(ngi); |
141 |
*nickgroup_ret = ngi; |
142 |
} |
143 |
add_nickinfo(ni); |
144 |
return ni; |
145 |
} |
146 |
|
147 |
/*************************************************************************/ |
148 |
|
149 |
/* ChannelInfo allocation. The channel is added to the hash table. */ |
150 |
|
151 |
#include "modules/chanserv/util.c" |
152 |
|
153 |
ChannelInfo *makechan(const char *name) |
154 |
{ |
155 |
ChannelInfo *ci = new_channelinfo(); |
156 |
strbcpy(ci->name, name); |
157 |
add_channelinfo(ci); |
158 |
return ci; |
159 |
} |
160 |
|
161 |
/*************************************************************************/ |
162 |
|
163 |
/* Open a (Services pre-5.0 style) data file and check the version number. |
164 |
* Prints an error message and exits with 1 if either the file cannot be |
165 |
* opened or the version number is wrong. If `version_ret' is non-NULL, |
166 |
* the version number is stored there. |
167 |
*/ |
168 |
|
169 |
dbFILE *open_db_ver(const char *dir, const char *name, int32 min_version, |
170 |
int32 max_version, int32 *version_ret) |
171 |
{ |
172 |
char filename[PATH_MAX+1]; |
173 |
dbFILE *f; |
174 |
int32 ver; |
175 |
|
176 |
snprintf(filename, sizeof(filename), "%s/%s", dir, name); |
177 |
f = open_db(filename, "r", 0); |
178 |
if (!f) { |
179 |
fprintf(stderr, "Can't open %s for reading: %s\n", filename, |
180 |
strerror(errno)); |
181 |
exit(1); |
182 |
} |
183 |
if (read_int32(&ver, f) < 0) { |
184 |
fprintf(stderr, "Error reading version number on %s\n", filename); |
185 |
exit(1); |
186 |
} |
187 |
if (ver < min_version || ver > max_version) { |
188 |
fprintf(stderr, "Wrong version number on %s\n", filename); |
189 |
exit(1); |
190 |
} |
191 |
if (version_ret) |
192 |
*version_ret = ver; |
193 |
return f; |
194 |
} |
195 |
|
196 |
/*************************************************************************/ |
197 |
|
198 |
/* Retrieve the NickGroupInfo structure for the given nick. Returns NULL |
199 |
* if the nick does not exist or is forbidden (i.e. has no NickGroupInfo). |
200 |
*/ |
201 |
|
202 |
NickGroupInfo *get_nickgroupinfo_by_nick(const char *nick) |
203 |
{ |
204 |
NickInfo *ni = get_nickinfo(nick); |
205 |
return ni ? get_nickgroupinfo(ni->nickgroup) : NULL; |
206 |
} |
207 |
|
208 |
/*************************************************************************/ |
209 |
|
210 |
/* Set the OperServ privilege level (os_priv) for the nickgroup associated |
211 |
* with `nick' to `level', if it is not already greater than `level'. Does |
212 |
* nothing if `nick' is NULL or does not have an associated nickgroup. |
213 |
*/ |
214 |
|
215 |
void set_os_priv(const char *nick, int16 level) |
216 |
{ |
217 |
NickGroupInfo *ngi; |
218 |
|
219 |
if (!nick) |
220 |
return; |
221 |
ngi = get_nickgroupinfo_by_nick(nick); |
222 |
if (ngi && ngi->os_priv < level) |
223 |
ngi->os_priv = level; |
224 |
} |
225 |
|
226 |
/*************************************************************************/ |
227 |
|
228 |
/* Return the Services 5.0 channel access level that corresponds to the |
229 |
* given pre-5.0 level. Code taken from modules/database/version4.c |
230 |
* (convert_old_level()). |
231 |
*/ |
232 |
|
233 |
int16 convert_acclev(int16 old) |
234 |
{ |
235 |
if (old < 0) |
236 |
return -convert_acclev(-old); /* avoid negative division */ |
237 |
else if (old <= 25) |
238 |
return old*10; /* 0.. 25 -> 0..250 (10x) */ |
239 |
else if (old <= 50) |
240 |
return 200 + old*2; /* 25.. 50 -> 250..300 ( 2x) */ |
241 |
else if (old <= 100) |
242 |
return 280 + old*2/5; /* 50.. 100 -> 300..320 ( 0.4x) */ |
243 |
else if (old <= 1000) |
244 |
return 300 + old/5; /* 100..1000 -> 320..500 ( 0.2x) */ |
245 |
else if (old <= 2000) |
246 |
return 400 + old/10; /* 1000..2000 -> 500..600 ( 0.1x) */ |
247 |
else |
248 |
return 500 + old/20; /* 2000..9999 -> 600..999 ( 0.05x) */ |
249 |
} |
250 |
|
251 |
/*************************************************************************/ |
252 |
/*************************************************************************/ |
253 |
|
254 |
/* Replacements for database functions. add_*() functions are defined as |
255 |
* macros in convert-db.h. */ |
256 |
|
257 |
/*************************************************************************/ |
258 |
|
259 |
static NickGroupInfo *ngi_iter; |
260 |
|
261 |
NickGroupInfo *get_nickgroupinfo(uint32 id) |
262 |
{ |
263 |
NickGroupInfo *ngi; |
264 |
if (!id) |
265 |
return NULL; |
266 |
LIST_SEARCH_SCALAR(ngi_list, id, id, ngi); |
267 |
return ngi; |
268 |
} |
269 |
|
270 |
NickGroupInfo *first_nickgroupinfo(void) |
271 |
{ |
272 |
ngi_iter = ngi_list; |
273 |
return next_nickgroupinfo(); |
274 |
} |
275 |
|
276 |
NickGroupInfo *next_nickgroupinfo(void) |
277 |
{ |
278 |
NickGroupInfo *retval = ngi_iter; |
279 |
if (ngi_iter) |
280 |
ngi_iter = ngi_iter->next; |
281 |
return retval; |
282 |
} |
283 |
|
284 |
/*************************************************************************/ |
285 |
|
286 |
static NickInfo *ni_iter; |
287 |
|
288 |
NickInfo *get_nickinfo(const char *nick) |
289 |
{ |
290 |
NickInfo *ni; |
291 |
LIST_SEARCH(ni_list, nick, nick, stricmp, ni); |
292 |
return ni; |
293 |
} |
294 |
|
295 |
NickInfo *first_nickinfo(void) |
296 |
{ |
297 |
ni_iter = ni_list; |
298 |
return next_nickinfo(); |
299 |
} |
300 |
|
301 |
NickInfo *next_nickinfo(void) |
302 |
{ |
303 |
NickInfo *retval = ni_iter; |
304 |
if (ni_iter) |
305 |
ni_iter = ni_iter->next; |
306 |
return retval; |
307 |
} |
308 |
|
309 |
/*************************************************************************/ |
310 |
|
311 |
static ChannelInfo *ci_iter; |
312 |
|
313 |
ChannelInfo *get_channelinfo(const char *channel) |
314 |
{ |
315 |
ChannelInfo *ci; |
316 |
LIST_SEARCH(ci_list, name, channel, stricmp, ci); |
317 |
return ci; |
318 |
} |
319 |
|
320 |
ChannelInfo *first_channelinfo(void) |
321 |
{ |
322 |
ci_iter = ci_list; |
323 |
return next_channelinfo(); |
324 |
} |
325 |
|
326 |
ChannelInfo *next_channelinfo(void) |
327 |
{ |
328 |
ChannelInfo *retval = ci_iter; |
329 |
if (ci_iter) |
330 |
ci_iter = ci_iter->next; |
331 |
return retval; |
332 |
} |
333 |
|
334 |
/*************************************************************************/ |
335 |
|
336 |
static NewsItem *news_iter; |
337 |
|
338 |
NewsItem *first_news(void) |
339 |
{ |
340 |
news_iter = news_list; |
341 |
return next_news(); |
342 |
} |
343 |
|
344 |
NewsItem *next_news(void) |
345 |
{ |
346 |
NewsItem *retval = news_iter; |
347 |
if (news_iter) |
348 |
news_iter = news_iter->next; |
349 |
return retval; |
350 |
} |
351 |
|
352 |
/*************************************************************************/ |
353 |
|
354 |
static MaskData *md_iter[256]; |
355 |
|
356 |
MaskData *first_maskdata(uint8 type) |
357 |
{ |
358 |
md_iter[type] = md_list[type]; |
359 |
return next_maskdata(type); |
360 |
} |
361 |
|
362 |
MaskData *next_maskdata(uint8 type) |
363 |
{ |
364 |
MaskData *retval = md_iter[type]; |
365 |
if (md_iter[type]) |
366 |
md_iter[type] = md_iter[type]->next; |
367 |
return retval; |
368 |
} |
369 |
|
370 |
/*************************************************************************/ |
371 |
|
372 |
static ServerStats *ss_iter; |
373 |
|
374 |
ServerStats *first_serverstats(void) |
375 |
{ |
376 |
ss_iter = ss_list; |
377 |
return next_serverstats(); |
378 |
} |
379 |
|
380 |
ServerStats *next_serverstats(void) |
381 |
{ |
382 |
ServerStats *retval = ss_iter; |
383 |
if (ss_iter) |
384 |
ss_iter = ss_iter->next; |
385 |
return retval; |
386 |
} |
387 |
|
388 |
/*************************************************************************/ |
389 |
|
390 |
int get_operserv_data(int what, void *ptr) |
391 |
{ |
392 |
switch (what) { |
393 |
case OSDATA_MAXUSERCNT: |
394 |
*(int32 *)ptr = maxusercnt; |
395 |
break; |
396 |
case OSDATA_MAXUSERTIME: |
397 |
*(time_t *)ptr = maxusertime; |
398 |
break; |
399 |
case OSDATA_SUPASS: |
400 |
*(Password **)ptr = no_supass ? NULL : &supass; |
401 |
break; |
402 |
default: |
403 |
return 0; |
404 |
} |
405 |
return 1; |
406 |
} |
407 |
|
408 |
/*************************************************************************/ |
409 |
/*************************************************************************/ |
410 |
|
411 |
/* Perform sanity checks on the data after it's been read in. */ |
412 |
|
413 |
static void sanity_check_nicks(void); |
414 |
static void sanity_check_nickgroups(void); |
415 |
static void sanity_check_channels(void); |
416 |
static void sanity_check_maskdata(void); |
417 |
|
418 |
static void sanity_checks(void) |
419 |
{ |
420 |
sanity_check_nicks(); |
421 |
sanity_check_nickgroups(); |
422 |
sanity_check_channels(); |
423 |
sanity_check_maskdata(); |
424 |
} |
425 |
|
426 |
/*************************************************************************/ |
427 |
|
428 |
static void sanity_check_nicks(void) |
429 |
{ |
430 |
NickInfo *ni; |
431 |
NickGroupInfo *ngi; |
432 |
char *s; |
433 |
int i; |
434 |
|
435 |
for (ni = first_nickinfo(); ni; ni = next_nickinfo()) { |
436 |
|
437 |
/* Forbidden nicks should have no other data associated with them. */ |
438 |
if (ni->status & NS_VERBOTEN) { |
439 |
ni->status &= ~(NS_VERBOTEN); |
440 |
ni->last_usermask = NULL; |
441 |
ni->last_realmask = NULL; |
442 |
ni->last_realname = NULL; |
443 |
ni->last_quit = NULL; |
444 |
ni->last_seen = 0; |
445 |
if (ni->nickgroup) { |
446 |
fprintf(stderr, "BUG: Forbidden nickname %s associated with" |
447 |
" nickgroup %u! Clearing nickgroup field.\n", |
448 |
ni->nick, ni->nickgroup); |
449 |
ni->nickgroup = 0; |
450 |
} |
451 |
ni->id_stamp = 0; |
452 |
continue; |
453 |
} |
454 |
|
455 |
/* The nick isn't a forbidden nick. First make sure it has the |
456 |
* right nickgroup. */ |
457 |
if (!ni->nickgroup) { |
458 |
fprintf(stderr, "BUG: Nickname %s has no nickgroup! Deleting.\n", |
459 |
ni->nick); |
460 |
del_nickinfo(ni); |
461 |
continue; |
462 |
} |
463 |
ngi = get_nickgroupinfo(ni->nickgroup); |
464 |
if (!ngi) { |
465 |
fprintf(stderr, "BUG: Nickname %s points to nonexistent nickgroup" |
466 |
" %u! Deleting.\n", ni->nick, ni->nickgroup); |
467 |
del_nickinfo(ni); |
468 |
} else { |
469 |
/* Nicknames in the nicks[] array should match those in the |
470 |
* NickInfo structure, so use strcmp() (this keeps us from |
471 |
* having to worry about irc_stricmp() idiosyncrasies). */ |
472 |
ARRAY_SEARCH_PLAIN(ngi->nicks, ni->nick, strcmp, i); |
473 |
if (i >= ngi->nicks_count) { |
474 |
fprintf(stderr, "BUG: Nickname %s points to nickgroup %u," |
475 |
" but the nickgroup doesn't contain the nickname!" |
476 |
" Deleting.\n", ni->nick, ni->nickgroup); |
477 |
del_nickinfo(ni); |
478 |
} |
479 |
} |
480 |
|
481 |
/* Clear unknown flags. */ |
482 |
ni->status &= NS_PERMANENT; |
483 |
|
484 |
/* Make sure usermasks actually have non-empty user and host parts. */ |
485 |
if (ni->last_usermask) { |
486 |
s = strchr(ni->last_usermask, '@'); |
487 |
if (!s || s==ni->last_usermask || !s[1]) { |
488 |
fprintf(stderr, "Last usermask for nickname %s isn't a valid" |
489 |
" user@host mask, clearing.\n", ni->nick); |
490 |
ni->last_usermask = NULL; |
491 |
} |
492 |
} |
493 |
if (ni->last_realmask) { |
494 |
s = strchr(ni->last_realmask, '@'); |
495 |
if (!s || s==ni->last_realmask || !s[1]) { |
496 |
fprintf(stderr, "Last real usermask for nickname %s isn't" |
497 |
" a valid user@host mask, clearing.\n", ni->nick); |
498 |
ni->last_realmask = NULL; |
499 |
} |
500 |
} |
501 |
|
502 |
/* Make sure last-seen time is not earlier than registered time. |
503 |
* Allow zero, to accommodate cases where the nickname was |
504 |
* registered by an outside entity without the user actually |
505 |
* logging on. */ |
506 |
if (ni->last_seen && ni->last_seen < ni->time_registered) { |
507 |
fprintf(stderr, "Last-seen time for nickname %s is earlier than" |
508 |
" registration time! Setting last-seen time to" |
509 |
" registration time.\n", ni->nick); |
510 |
ni->last_seen = ni->time_registered; |
511 |
} |
512 |
|
513 |
} /* for all nicks */ |
514 |
} |
515 |
|
516 |
/*************************************************************************/ |
517 |
|
518 |
static void sanity_check_nickgroups(void) |
519 |
{ |
520 |
NickInfo *ni; |
521 |
NickGroupInfo *ngi; |
522 |
char *s; |
523 |
int i; |
524 |
|
525 |
for (ngi = first_nickgroupinfo(); ngi; ngi = next_nickgroupinfo()) { |
526 |
|
527 |
/* Make sure nickgroups don't contain extra nicks. */ |
528 |
ARRAY_FOREACH(i, ngi->nicks) { |
529 |
ni = get_nickinfo(ngi->nicks[i]); |
530 |
if (!ni) { |
531 |
fprintf(stderr, "BUG: Nickgroup %u contains nonexistent" |
532 |
" nickname %s! Removing nickname from group.\n", |
533 |
ngi->id, ngi->nicks[i]); |
534 |
ARRAY_REMOVE(ngi->nicks, i); |
535 |
if (ngi->mainnick >= i) |
536 |
ngi->mainnick--; |
537 |
i--; /* to make sure we don't skip any */ |
538 |
} |
539 |
} |
540 |
|
541 |
/* Check for empty nickgroups (possibly from extraneous nickname |
542 |
* removal above). */ |
543 |
if (ngi->nicks_count == 0) { |
544 |
fprintf(stderr, "Removing empty nickgroup %u.\n", ngi->id); |
545 |
del_nickgroupinfo(ngi); |
546 |
continue; |
547 |
} |
548 |
|
549 |
/* Make sure main nick is a valid index. */ |
550 |
if (ngi->mainnick >= ngi->nicks_count) { |
551 |
fprintf(stderr, "Invalid main nick index in nickgroup %u," |
552 |
" resetting.\n", ngi->id); |
553 |
ngi->mainnick = 0; |
554 |
} |
555 |
|
556 |
/* If an authcode is set, make sure the reason is valid. If not, |
557 |
* clear the auth-related fields. */ |
558 |
if (ngi->authcode && (ngi->authreason < NICKAUTH_MIN |
559 |
|| ngi->authreason > NICKAUTH_MAX)) { |
560 |
fprintf(stderr, "Authentication code set for nickgroup %u but" |
561 |
" reason code invalid, setting to SETAUTH.\n", ngi->id); |
562 |
ngi->authreason = NICKAUTH_SETAUTH; |
563 |
} else { |
564 |
ngi->authset = 0; |
565 |
ngi->authreason = 0; |
566 |
} |
567 |
|
568 |
/* Clear all unknown flags. */ |
569 |
ngi->flags &= NF_ALLFLAGS; |
570 |
|
571 |
/* Make sure language setting is in range. */ |
572 |
if ((ngi->language < 0 || ngi->language >= NUM_LANGS) |
573 |
&& ngi->language != LANG_DEFAULT |
574 |
) { |
575 |
fprintf(stderr, "Invalid language set for nickgroup %u," |
576 |
" resetting to default.\n", ngi->id); |
577 |
ngi->language = LANG_DEFAULT; |
578 |
} |
579 |
|
580 |
/* Make sure access, autojoin, and ignore counts are non-negative. */ |
581 |
if (ngi->access_count < 0) { |
582 |
fprintf(stderr, "BUG: Access entry count for nickgroup %u is" |
583 |
" negative! Clearing.\n", ngi->id); |
584 |
ngi->access = NULL; |
585 |
ngi->access_count = 0; |
586 |
} |
587 |
if (ngi->ajoin_count < 0) { |
588 |
fprintf(stderr, "BUG: Autojoin entry count for nickgroup %u is" |
589 |
" negative! Clearing.\n", ngi->id); |
590 |
ngi->ajoin = NULL; |
591 |
ngi->ajoin_count = 0; |
592 |
} |
593 |
if (ngi->ignore_count < 0) { |
594 |
fprintf(stderr, "BUG: Ignore entry count for nickgroup %u is" |
595 |
" negative! Clearing.\n", ngi->id); |
596 |
ngi->ignore = NULL; |
597 |
ngi->ignore_count = 0; |
598 |
} |
599 |
|
600 |
/* Make sure all access entries have non-empty user and host parts. */ |
601 |
ARRAY_FOREACH (i, ngi->access) { |
602 |
if (!ngi->access[i] |
603 |
|| !(s = strchr(ngi->access[i], '@')) |
604 |
|| s == ngi->access[i] |
605 |
|| !s[1] |
606 |
) { |
607 |
fprintf(stderr, "Access entry %d for nickgroup %u %s," |
608 |
" deleting.\n", i, ngi->id, |
609 |
!ngi->access[i] ? "is empty" |
610 |
: "isn't a valid user@host mask"); |
611 |
ARRAY_REMOVE(ngi->access, i); |
612 |
i--; |
613 |
} |
614 |
} |
615 |
|
616 |
/* Make sure memo count is non-negative. */ |
617 |
if (ngi->memos.memos_count < 0) { |
618 |
fprintf(stderr, "BUG: Memo count for nickgroup %u is negative!" |
619 |
" Clearing.\n", ngi->id); |
620 |
ngi->memos.memos = NULL; |
621 |
ngi->memos.memos_count = 0; |
622 |
} |
623 |
|
624 |
/* Clear unknown flags from memos. */ |
625 |
ARRAY_FOREACH (i, ngi->memos.memos) |
626 |
ngi->memos.memos[i].flags &= MF_ALLFLAGS; |
627 |
|
628 |
} /* for all nickgroups */ |
629 |
} |
630 |
|
631 |
/*************************************************************************/ |
632 |
|
633 |
static void sanity_check_channels(void) |
634 |
{ |
635 |
ChannelInfo *ci; |
636 |
char *s; |
637 |
int i; |
638 |
|
639 |
for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { |
640 |
|
641 |
/* Forbidden channels should have no other data associated with |
642 |
* them. */ |
643 |
if (ci->flags & CF_VERBOTEN) { |
644 |
ci->founder = 0; |
645 |
ci->successor = 0; |
646 |
init_password(&ci->founderpass); |
647 |
ci->desc = NULL; |
648 |
ci->url = NULL; |
649 |
ci->email = NULL; |
650 |
ci->last_used = 0; |
651 |
ci->last_topic = NULL; |
652 |
memset(ci->last_topic_setter, 0, sizeof(ci->last_topic_setter)); |
653 |
ci->last_topic_time = 0; |
654 |
ci->flags &= ~(CF_VERBOTEN); |
655 |
reset_levels(ci); |
656 |
ci->access = NULL; |
657 |
ci->access_count = 0; |
658 |
ci->akick = NULL; |
659 |
ci->akick_count = 0; |
660 |
ci->mlock.on = NULL; |
661 |
ci->mlock.off = NULL; |
662 |
ci->mlock.limit = 0; |
663 |
ci->mlock.key = NULL; |
664 |
ci->mlock.link = NULL; |
665 |
ci->mlock.flood = NULL; |
666 |
ci->mlock.joindelay = 0; |
667 |
ci->mlock.joinrate1 = 0; |
668 |
ci->mlock.joinrate2 = 0; |
669 |
ci->entry_message = NULL; |
670 |
continue; |
671 |
} |
672 |
|
673 |
/* Channel is not forbidden. First make sure it has a (valid) |
674 |
* founder. */ |
675 |
if (!ci->founder) { |
676 |
fprintf(stderr, "BUG: Channel %s has no founder! Deleting.\n", |
677 |
ci->name); |
678 |
del_channelinfo(ci); |
679 |
} else if (!get_nickgroupinfo(ci->founder)) { |
680 |
fprintf(stderr, "BUG: Channel %s founder nickgroup %u is" |
681 |
" missing! Deleting channel.\n", ci->name, ci->founder); |
682 |
del_channelinfo(ci); |
683 |
} |
684 |
|
685 |
/* Make sure that the successor is valid if set. */ |
686 |
if (ci->successor && !get_nickgroupinfo(ci->successor)) { |
687 |
fprintf(stderr, "BUG: Channel %s successor nickgroup %u is" |
688 |
" missing! Clearing.", ci->name, ci->successor); |
689 |
ci->successor = 0; |
690 |
} |
691 |
|
692 |
/* Make sure last-used time is not earlier than registered time. |
693 |
* Allow zero, to accommodate cases where the channel was |
694 |
* registered by an outside entity. */ |
695 |
if (ci->last_used && ci->last_used < ci->time_registered) { |
696 |
fprintf(stderr, "Last-used time for channel %s is earlier than" |
697 |
" registration time! Setting last-used time to" |
698 |
" registration time.\n", ci->name); |
699 |
ci->last_used = ci->time_registered; |
700 |
} |
701 |
|
702 |
/* If there is no saved topic, clear the topic setter and time. */ |
703 |
if (!ci->last_topic) { |
704 |
memset(ci->last_topic_setter, 0, sizeof(ci->last_topic_setter)); |
705 |
ci->last_topic_time = 0; |
706 |
} |
707 |
|
708 |
/* Clear unknown flags. */ |
709 |
ci->flags &= CF_ALLFLAGS; |
710 |
|
711 |
/* Check privilege level settings for vaility. */ |
712 |
for (i = 0; i < CA_SIZE; i++) { |
713 |
if (ci->levels[i] != ACCLEV_DEFAULT |
714 |
&& ci->levels[i] != ACCLEV_INVALID |
715 |
&& ci->levels[i] != ACCLEV_FOUNDER |
716 |
&& (ci->levels[i] < ACCLEV_MIN |
717 |
|| ci->levels[i] > ACCLEV_MAX) |
718 |
) { |
719 |
fprintf(stderr, "Privilege level %d on channel %s is" |
720 |
"invalid, resetting to default.\n", i, ci->name); |
721 |
ci->levels[i] = ACCLEV_DEFAULT; |
722 |
break; |
723 |
} |
724 |
} |
725 |
|
726 |
/* Check access level nickgroups and levels for validity. */ |
727 |
ARRAY_FOREACH (i, ci->access) { |
728 |
if (!ci->access[i].nickgroup) |
729 |
continue; |
730 |
if (!get_nickgroupinfo(ci->access[i].nickgroup)) { |
731 |
fprintf(stderr, "BUG: Channel %s access entry %d has an" |
732 |
" invalid nickgroup! Clearing.\n", ci->name, i); |
733 |
ci->access[i].nickgroup = 0; |
734 |
ci->access[i].level = 0; |
735 |
} else if (ci->access[i].level < ACCLEV_MIN |
736 |
|| ci->access[i].level > ACCLEV_MAX) { |
737 |
fprintf(stderr, "BUG: Channel %s access entry %d has an" |
738 |
"out-of-range level (%d)! Clearing.\n", |
739 |
ci->name, i, ci->access[i].level); |
740 |
ci->access[i].nickgroup = 0; |
741 |
ci->access[i].level = 0; |
742 |
} |
743 |
} |
744 |
|
745 |
/* Make sure all autokick entries have non-empty user and host |
746 |
* parts. */ |
747 |
ARRAY_FOREACH (i, ci->akick) { |
748 |
if (!ci->akick[i].mask) |
749 |
continue; |
750 |
s = strchr(ci->akick[i].mask, '@'); |
751 |
if (!s || s==ci->akick[i].mask || !s[1]) { |
752 |
fprintf(stderr, "Autokick entry %d for channel %s isn't a" |
753 |
" valid user@host mask, deleting.\n", i, ci->name); |
754 |
ci->akick[i].mask = NULL; |
755 |
} |
756 |
} |
757 |
|
758 |
} /* for all channels */ |
759 |
|
760 |
/* Check that channel successors and access list entries have valid |
761 |
* nickgroup IDs, and that access levels are in range */ |
762 |
for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { |
763 |
if (ci->flags & CF_VERBOTEN) |
764 |
continue; |
765 |
} |
766 |
} |
767 |
|
768 |
/*************************************************************************/ |
769 |
|
770 |
static void sanity_check_maskdata(void) |
771 |
{ |
772 |
MaskData *md; |
773 |
char *s; |
774 |
int i; |
775 |
|
776 |
/* Make sure all MaskData entries actually have masks allocated. */ |
777 |
for (i = 0; i < 256; i++) { |
778 |
for (md = first_maskdata(i); md; md = next_maskdata(i)) { |
779 |
if (!md->mask) |
780 |
del_maskdata(i, md); |
781 |
} |
782 |
} |
783 |
|
784 |
/* Make sure every autokill has a valid user@host mask and a reason. */ |
785 |
for (md = first_maskdata(MD_AKILL); md; md = next_maskdata(MD_AKILL)) { |
786 |
s = strchr(md->mask, '@'); |
787 |
if (!s || s==md->mask || !s[1]) { |
788 |
fprintf(stderr, "Autokill %s isn't a valid user@host mask," |
789 |
" deleting.\n", md->mask); |
790 |
del_maskdata(MD_AKILL, md); |
791 |
continue; |
792 |
} |
793 |
if (!md->reason) |
794 |
md->reason = "Reason unknown"; |
795 |
} |
796 |
} |
797 |
|
798 |
/*************************************************************************/ |
799 |
/****************************** Main program *****************************/ |
800 |
/*************************************************************************/ |
801 |
|
802 |
void usage(const char *progname) |
803 |
{ |
804 |
int i; |
805 |
|
806 |
fprintf(stderr, "Usage: %s [-v] [+program-name] [options...] dir\n" |
807 |
"The following program names are known:\n", progname); |
808 |
for (i = 0; dbtypes[i]; i++) |
809 |
fprintf(stderr, " %s\n", dbtypes[i]->id); |
810 |
fprintf(stderr, |
811 |
"See the manual for options available for each database type.\n"); |
812 |
exit(1); |
813 |
} |
814 |
|
815 |
/*************************************************************************/ |
816 |
|
817 |
int main(int ac, char **av) |
818 |
{ |
819 |
int newac = 0; /* For passing to processing function */ |
820 |
char **newav; |
821 |
char *dir = NULL; /* Source data file directory */ |
822 |
int verbose = 0; /* Verbose output? */ |
823 |
void (*load)(const char *, int, int, char **) = NULL; |
824 |
int i; |
825 |
char oldpath[PATH_MAX+1], newpath[PATH_MAX+1]; |
826 |
|
827 |
#if CLEAN_COMPILE |
828 |
free_nickinfo(NULL); |
829 |
free_nickgroupinfo(NULL); |
830 |
free_channelinfo(NULL); |
831 |
#endif |
832 |
|
833 |
init_password(&supass); |
834 |
|
835 |
/* Parse command-line parameters */ |
836 |
newac = 1; |
837 |
newav = malloc(sizeof(*newav) * ac); |
838 |
newav[0] = av[0]; |
839 |
for (i = 1; i < ac; i++) { |
840 |
if (strcmp(av[i],"-v") == 0) { |
841 |
verbose++; |
842 |
} else if (strcmp(av[i],"-h") == 0 |
843 |
|| strcmp(av[i],"-?") == 0 |
844 |
|| strcmp(av[i],"-help") == 0 |
845 |
|| strcmp(av[i],"--help") == 0) { |
846 |
usage(av[0]); |
847 |
} else if (av[i][0] == '+') { |
848 |
int j; |
849 |
for (j = 0; dbtypes[j]; j++) { |
850 |
if (stricmp(av[i]+1, dbtypes[j]->id) == 0) { |
851 |
load = dbtypes[j]->load; |
852 |
break; |
853 |
} |
854 |
} |
855 |
if (!load) { |
856 |
fprintf(stderr, "Unknown database type `%s'\n", av[i]+1); |
857 |
usage(av[0]); |
858 |
} |
859 |
} else if (av[i][0] == '-') { |
860 |
newav[newac++] = av[i]; |
861 |
} else { |
862 |
if (dir) { |
863 |
fprintf(stderr, "Only one source directory may be specified\n"); |
864 |
usage(av[0]); |
865 |
} |
866 |
dir = av[i]; |
867 |
} |
868 |
} |
869 |
|
870 |
/* Make sure that we have a source directory and that it's valid */ |
871 |
if (!dir) { |
872 |
fprintf(stderr, "Directory name must be specified\n"); |
873 |
usage(av[0]); |
874 |
} |
875 |
if (access(dir, R_OK) < 0) { |
876 |
perror(dir); |
877 |
exit(1); |
878 |
} |
879 |
|
880 |
/* If the source directory name is relative, make it absolute */ |
881 |
if (*dir != '/') { |
882 |
if (!getcwd(oldpath, sizeof(oldpath))) { |
883 |
perror("Unable to read current directory name"); |
884 |
fprintf(stderr, "Try using an absolute pathname for the source directory.\n"); |
885 |
return 1; |
886 |
} |
887 |
if (strlen(oldpath) + 1 + strlen(dir) + 1 > sizeof(newpath)) { |
888 |
fprintf(stderr, "Source directory pathname too long\n"); |
889 |
return 1; |
890 |
} |
891 |
sprintf(newpath, "%s/%s", oldpath, dir); |
892 |
dir = newpath; |
893 |
} |
894 |
|
895 |
/* If we weren't given a database type, try to figure one out */ |
896 |
if (!load) { |
897 |
for (i = 0; dbtypes[i]; i++) { |
898 |
const char *s; |
899 |
if ((s = dbtypes[i]->check(dir)) != NULL) { |
900 |
fprintf(stderr, "Found %s databases\n", s); |
901 |
load = dbtypes[i]->load; |
902 |
break; |
903 |
} |
904 |
} |
905 |
if (!load) { |
906 |
fprintf(stderr, "Can't determine database type; use +name option\n"); |
907 |
usage(av[0]); |
908 |
} |
909 |
} |
910 |
|
911 |
/* Actually load the databases. If an error occurs, load() will abort the |
912 |
* program for us */ |
913 |
load(dir, verbose, newac, newav); |
914 |
if (verbose) |
915 |
fprintf(stderr, "Data files successfully loaded.\n"); |
916 |
|
917 |
/* Do sanity checks. */ |
918 |
if (verbose) |
919 |
fprintf(stderr, "Checking data integrity...\n"); |
920 |
sanity_checks(); |
921 |
|
922 |
/* Write the database to standard output in XML format */ |
923 |
if (verbose) |
924 |
fprintf(stderr, "Writing converted data...\n"); |
925 |
xml_export((xml_writefunc_t)fprintf, stdout); |
926 |
|
927 |
/* Success */ |
928 |
if (verbose) |
929 |
fprintf(stderr, "Done.\n"); |
930 |
return 0; |
931 |
} |
932 |
|
933 |
/*************************************************************************/ |
934 |
|
935 |
/* |
936 |
* Local variables: |
937 |
* c-file-style: "stroustrup" |
938 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
939 |
* indent-tabs-mode: nil |
940 |
* End: |
941 |
* |
942 |
* vim: expandtab shiftwidth=4: |
943 |
*/ |