ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hybrid-ircservices-1/modules/chanserv/main.c
Revision: 1209
Committed: Thu Aug 25 19:05:49 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 94097 byte(s)
Log Message:
- run everything thru indent
  "-bli0 -di1 -npcs -nut -cdw -bls -nbbo -bap"

File Contents

# Content
1 /* Main ChanServ module.
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 #include "services.h"
11 #include "modules.h"
12 #include "conffile.h"
13 #include "commands.h"
14 #include "databases.h"
15 #include "encrypt.h"
16 #include "language.h"
17 #include "modules/nickserv/nickserv.h"
18 #include "modules/operserv/operserv.h"
19
20 #include "chanserv.h"
21 #include "cs-local.h"
22
23 /*************************************************************************/
24 /************************** Declaration section **************************/
25 /*************************************************************************/
26
27 static Module *module_operserv;
28 static Module *module_nickserv;
29
30 static int cb_clear = -1;
31 static int cb_command = -1;
32 static int cb_help = -1;
33 static int cb_help_cmds = -1;
34 static int cb_invite = -1;
35 static int cb_unban = -1;
36
37 char *s_ChanServ;
38 static char *desc_ChanServ;
39 EXPORT_VAR(char *, s_ChanServ)
40 static int CSEnableRegister;
41 int CSRegisteredOnly;
42 int32 CSMaxReg;
43 int32 CSDefFlags;
44 static int32 CSDefModeLockOn = CMODE_n | CMODE_t;
45 static int32 CSDefModeLockOff = 0;
46 time_t CSExpire;
47 int CSShowPassword;
48 int32 CSAccessMax;
49 int32 CSAutokickMax;
50 time_t CSInhabit;
51 time_t CSRestrictDelay;
52 int CSListOpersOnly;
53 time_t CSSuspendExpire;
54 time_t CSSuspendGrace;
55 int CSForbidShortChannel;
56 int CSSkipModeRCheck;
57 EXPORT_VAR(int32, CSMaxReg)
58 /*************************************************************************/
59 /* Channel option list. */
60 #define CHANOPT(x) \
61 { #x, CF_##x, CHAN_INFO_OPT_##x, \
62 CHAN_SET_##x##_ON, CHAN_SET_##x##_OFF, CHAN_SET_##x##_SYNTAX }
63 ChanOpt chanopts[] = {
64 CHANOPT(KEEPTOPIC),
65 CHANOPT(TOPICLOCK),
66 CHANOPT(PRIVATE),
67 CHANOPT(SECUREOPS),
68 CHANOPT(LEAVEOPS),
69 CHANOPT(RESTRICTED),
70 CHANOPT(SECURE),
71 CHANOPT(OPNOTICE),
72 CHANOPT(ENFORCE),
73 {"MEMO-RESTRICTED", CF_MEMO_RESTRICTED, CHAN_INFO_OPT_MEMO_RESTRICTED,
74 CHAN_SET_MEMO_RESTRICTED_ON, CHAN_SET_MEMO_RESTRICTED_OFF,
75 CHAN_SET_MEMO_RESTRICTED_SYNTAX},
76 {"NOEXPIRE", CF_NOEXPIRE, -1, CHAN_SET_NOEXPIRE_ON,
77 CHAN_SET_NOEXPIRE_OFF, CHAN_SET_NOEXPIRE_SYNTAX},
78 {NULL}
79 };
80
81 #undef CHANOPT
82
83 /*************************************************************************/
84
85 /* Local functions. */
86
87 static void do_help(User * u);
88 static void do_register(User * u);
89 static void do_identify(User * u);
90 static void do_drop(User * u);
91 static void do_dropchan(User * u);
92 static void do_info(User * u);
93 static void do_list(User * u);
94 static void do_akick(User * u);
95 static void do_op(User * u);
96 static void do_deop(User * u);
97 static void do_voice(User * u);
98 static void do_devoice(User * u);
99 static void do_halfop(User * u);
100 static void do_dehalfop(User * u);
101 static void do_protect(User * u);
102 static void do_deprotect(User * u);
103 static void do_invite(User * u);
104 static void do_unban(User * u);
105 static void do_cskick(User * u);
106 static void do_cstopic(User * u);
107 static void do_clear(User * u);
108 static void do_getpass(User * u);
109 static void do_forbid(User * u);
110 static void do_suspend(User * u);
111 static void do_unsuspend(User * u);
112 static void do_status(User * u);
113
114 /*************************************************************************/
115
116 /* Command list. */
117
118 static Command cmds[] = {
119 {"HELP", do_help, NULL, -1, -1, -1},
120 {"REGISTER", do_register, NULL, CHAN_HELP_REGISTER, -1, -1},
121 {"IDENTIFY", do_identify, NULL, CHAN_HELP_IDENTIFY, -1, -1},
122 {"DROP", do_drop, NULL, CHAN_HELP_DROP, -1, -1},
123 {"DROPCHAN", do_dropchan, is_services_admin, -1,
124 -1, CHAN_OPER_HELP_DROPCHAN},
125 {"SET", do_set, NULL, CHAN_HELP_SET, -1, CHAN_OPER_HELP_SET},
126 {"SET FOUNDER", NULL, NULL, CHAN_HELP_SET_FOUNDER, -1, -1},
127 {"SET SUCCESSOR", NULL, NULL, CHAN_HELP_SET_SUCCESSOR, -1, -1},
128 {"SET PASSWORD", NULL, NULL, CHAN_HELP_SET_PASSWORD, -1, -1},
129 {"SET DESC", NULL, NULL, CHAN_HELP_SET_DESC, -1, -1},
130 {"SET URL", NULL, NULL, CHAN_HELP_SET_URL, -1, -1},
131 {"SET EMAIL", NULL, NULL, CHAN_HELP_SET_EMAIL, -1, -1},
132 {"SET ENTRYMSG", NULL, NULL, CHAN_HELP_SET_ENTRYMSG, -1, -1},
133 {"SET KEEPTOPIC", NULL, NULL, CHAN_HELP_SET_KEEPTOPIC, -1, -1},
134 {"SET TOPICLOCK", NULL, NULL, CHAN_HELP_SET_TOPICLOCK, -1, -1},
135 {"SET MLOCK", NULL, NULL, CHAN_HELP_SET_MLOCK, -1, -1},
136 {"SET HIDE", NULL, NULL, CHAN_HELP_SET_HIDE, -1, -1},
137 {"SET PRIVATE", NULL, NULL, CHAN_HELP_SET_PRIVATE, -1, -1},
138 {"SET RESTRICTED", NULL, NULL, CHAN_HELP_SET_RESTRICTED, -1, -1},
139 {"SET SECURE", NULL, NULL, CHAN_HELP_SET_SECURE, -1, -1},
140 {"SET SECUREOPS", NULL, NULL, CHAN_HELP_SET_SECUREOPS, -1, -1},
141 {"SET LEAVEOPS", NULL, NULL, CHAN_HELP_SET_LEAVEOPS, -1, -1},
142 {"SET OPNOTICE", NULL, NULL, CHAN_HELP_SET_OPNOTICE, -1, -1},
143 {"SET ENFORCE", NULL, NULL, CHAN_HELP_SET_ENFORCE, -1, -1},
144 {"SET MEMO-RESTRICTED", NULL, NULL, CHAN_HELP_SET_MEMO_RESTRICTED, -1, -1},
145 {"SET NOEXPIRE", NULL, NULL, -1, -1, CHAN_OPER_HELP_SET_NOEXPIRE},
146 {"UNSET", do_unset, NULL, CHAN_HELP_UNSET,
147 -1, CHAN_OPER_HELP_UNSET},
148 {"INFO", do_info, NULL, CHAN_HELP_INFO,
149 -1, CHAN_OPER_HELP_INFO},
150 {"LIST", do_list, NULL, -1,
151 CHAN_HELP_LIST, CHAN_OPER_HELP_LIST},
152 {"AKICK", do_akick, NULL, CHAN_HELP_AKICK, -1, -1,
153 (void *) ACCLEV_SOP},
154 {"OP", do_op, NULL, CHAN_HELP_OP, -1, -1,
155 (void *) ACCLEV_AOP},
156 {"DEOP", do_deop, NULL, CHAN_HELP_DEOP, -1, -1,
157 (void *) ACCLEV_AOP},
158 {"VOICE", do_voice, NULL, CHAN_HELP_VOICE, -1, -1,
159 (void *) ACCLEV_VOP},
160 {"DEVOICE", do_devoice, NULL, CHAN_HELP_DEVOICE, -1, -1,
161 (void *) ACCLEV_VOP},
162 {"INVITE", do_invite, NULL, CHAN_HELP_INVITE, -1, -1,
163 (void *) ACCLEV_AOP},
164 {"UNBAN", do_unban, NULL, CHAN_HELP_UNBAN, -1, -1,
165 (void *) ACCLEV_AOP},
166 {"KICK", do_cskick, NULL, CHAN_HELP_KICK, -1, -1,
167 (void *) ACCLEV_AOP},
168 {"TOPIC", do_cstopic, NULL, CHAN_HELP_TOPIC, -1, -1,
169 (void *) ACCLEV_AOP},
170 {"CLEAR", do_clear, NULL, CHAN_HELP_CLEAR, -1, -1,
171 (void *) ACCLEV_SOP},
172 {"STATUS", do_status, NULL, CHAN_HELP_STATUS, -1, -1,
173 (void *) ACCLEV_SOP},
174 {"GETPASS", do_getpass, is_services_admin, -1,
175 -1, CHAN_OPER_HELP_GETPASS},
176 {"FORBID", do_forbid, is_services_admin, -1,
177 -1, CHAN_OPER_HELP_FORBID},
178 {"SUSPEND", do_suspend, is_services_admin, -1,
179 -1, CHAN_OPER_HELP_SUSPEND},
180 {"UNSUSPEND", do_unsuspend, is_services_admin, -1,
181 -1, CHAN_OPER_HELP_UNSUSPEND},
182 {NULL}
183 };
184
185 static Command cmds_halfop[] = {
186 {"HALFOP", do_halfop, NULL, CHAN_HELP_HALFOP, -1, -1,
187 (void *) ACCLEV_AOP},
188 {"DEHALFOP", do_dehalfop, NULL, CHAN_HELP_DEHALFOP, -1, -1,
189 (void *) ACCLEV_AOP},
190 {NULL}
191 };
192
193 static Command cmds_chanprot[] = {
194 {"PROTECT", do_protect, NULL, CHAN_HELP_PROTECT, -1, -1,
195 (void *) ACCLEV_AOP},
196 {"DEPROTECT", do_deprotect, NULL, CHAN_HELP_DEPROTECT, -1, -1,
197 (void *) ACCLEV_AOP},
198 {NULL}
199 };
200
201 /* Command alias type and array */
202 typedef struct
203 {
204 char *alias, *command;
205 } Alias;
206 static Alias *aliases;
207 static int aliases_count;
208
209 /*************************************************************************/
210 /**************************** Database stuff *****************************/
211 /*************************************************************************/
212
213 /* Check whether the given channel (or its suspension) should be expired,
214 * and do the expiration if so. Return 1 if the channel was deleted, else 0.
215 */
216
217 static int
218 check_expire_channel(ChannelInfo * ci)
219 {
220 time_t now = time(NULL);
221
222 /* Don't bother updating last used time unless it's been a while */
223 if (ci->c && CSExpire && now >= ci->last_used + CSExpire / 2)
224 {
225 struct c_userlist *cu;
226 LIST_FOREACH(cu, ci->c->users)
227 {
228 if (check_access(cu->user, ci, CA_AUTOOP))
229 {
230 module_log_debug(2, "updating last used time for %s", ci->name);
231 ci->last_used = time(NULL);
232 break;
233 }
234 }
235 }
236
237 if (CSExpire
238 && now >= ci->last_used + CSExpire
239 && !(ci->flags & (CF_VERBOTEN | CF_SUSPENDED | CF_NOEXPIRE)))
240 {
241 module_log("Expiring channel %s", ci->name);
242 delchan(ci);
243 return 1;
244 }
245
246 if ((ci->flags & CF_SUSPENDED)
247 && ci->suspend_expires && now >= ci->suspend_expires)
248 {
249 module_log("Expiring suspension for %s", ci->name);
250 unsuspend_channel(ci, 1);
251 }
252 return 0;
253 }
254
255 /*************************************************************************/
256
257 #define HASH_STATIC static
258 #define HASHFUNC(key) DEFAULT_HASHFUNC(key+1)
259 #define EXPIRE_CHECK(node) check_expire_channel(node)
260 #include "hash.h"
261 DEFINE_HASH(channelinfo_, ChannelInfo, name);
262
263 EXPORT_FUNC(add_channelinfo)
264 ChannelInfo *add_channelinfo(ChannelInfo * ci)
265 {
266 add_channelinfo_(ci);
267 ci->usecount = 1;
268 return ci;
269 }
270
271 EXPORT_FUNC(del_channelinfo)
272 void del_channelinfo(ChannelInfo * ci)
273 {
274 del_channelinfo_(ci);
275 free_channelinfo(ci);
276 }
277
278 EXPORT_FUNC(get_channelinfo)
279 ChannelInfo *get_channelinfo(const char *chan)
280 {
281 ChannelInfo *ci;
282 if ((ci = get_channelinfo_(chan)) != NULL)
283 ci->usecount++;
284 return ci;
285 }
286
287 EXPORT_FUNC(put_channelinfo)
288 ChannelInfo *put_channelinfo(ChannelInfo * ci)
289 {
290 if (ci)
291 {
292 if (ci->usecount > 0)
293 ci->usecount--;
294 else
295 module_log_debug(1, "BUG: put_channelinfo(%s) with usecount==0",
296 ci->name);
297 }
298 return ci;
299 }
300
301 EXPORT_FUNC(first_channelinfo)
302 ChannelInfo *first_channelinfo(void)
303 {
304 return first_channelinfo_();
305 }
306
307 EXPORT_FUNC(next_channelinfo)
308 ChannelInfo *next_channelinfo(void)
309 {
310 return next_channelinfo_();
311 }
312
313 /*************************************************************************/
314
315 /* Free all memory used by database tables. */
316
317 static void
318 clean_dbtables(void)
319 {
320 ChannelInfo *ci;
321 int save_noexpire = noexpire;
322
323 noexpire = 1;
324 for (ci = first_channelinfo(); ci; ci = next_channelinfo())
325 free_channelinfo(ci);
326 noexpire = save_noexpire;
327 }
328
329 /*************************************************************************/
330
331 /* Helper functions for loading and saving database tables. */
332
333 static ChanAccess dbuse_chan_access;
334 static AutoKick dbuse_chan_akick;
335 static ChannelInfo *dbuse_ci_iterator;
336 static int dbuse_ca_iterator, dbuse_ak_iterator;
337
338 /************************************/
339
340 static void
341 insert_channel(void *record)
342 {
343 ChannelInfo *ci = add_channelinfo(record);
344 count_chan(ci);
345 put_channelinfo(ci);
346 }
347
348 /************************************/
349
350 static ChanAccess *
351 new_chan_access(void)
352 {
353 return memset(&dbuse_chan_access, 0, sizeof(dbuse_chan_access));
354 }
355
356 static void
357 free_chan_access(ChanAccess * ca)
358 {
359 /* nothing to do */
360 }
361
362 static void
363 insert_chan_access(ChanAccess * ca)
364 {
365 if (ca->channel)
366 {
367 ChanAccess *ca2;
368 ARRAY_EXTEND(ca->channel->access);
369 ca2 = &ca->channel->access[ca->channel->access_count - 1];
370 memcpy(ca2, ca, sizeof(*ca2));
371 }
372 else
373 {
374 free_chan_access(ca);
375 }
376 }
377
378 static ChanAccess *
379 next_chan_access(void)
380 {
381 while (dbuse_ci_iterator
382 && dbuse_ca_iterator >= dbuse_ci_iterator->access_count)
383 {
384 dbuse_ci_iterator = next_channelinfo();
385 dbuse_ca_iterator = 0;
386 }
387 if (dbuse_ci_iterator)
388 return &dbuse_ci_iterator->access[dbuse_ca_iterator++];
389 else
390 return NULL;
391 }
392
393 static ChanAccess *
394 first_chan_access(void)
395 {
396 dbuse_ci_iterator = first_channelinfo();
397 dbuse_ca_iterator = 0;
398 return next_chan_access();
399 }
400
401 static void
402 chan_access_get_channel(const ChanAccess * record, char **value_ret)
403 {
404 *value_ret = record->channel->name;
405 }
406
407 static void
408 chan_access_put_channel(ChanAccess * record, const char **value)
409 {
410 ChannelInfo *ci = get_channelinfo(*value);
411 if (ci)
412 {
413 record->channel = ci;
414 }
415 else
416 {
417 module_log("Skipping access record for missing channel %s", *value);
418 record->channel = NULL;
419 }
420 free((char *) *value);
421 }
422
423 /************************************/
424
425 static AutoKick *
426 new_chan_akick(void)
427 {
428 return memset(&dbuse_chan_akick, 0, sizeof(dbuse_chan_akick));
429 }
430
431 static void
432 free_chan_akick(AutoKick * ak)
433 {
434 free(ak->mask);
435 free(ak->reason);
436 }
437
438 static void
439 insert_chan_akick(AutoKick * ak)
440 {
441 if (ak->channel)
442 {
443 AutoKick *ak2;
444 ARRAY_EXTEND(ak->channel->akick);
445 ak2 = &ak->channel->akick[ak->channel->akick_count - 1];
446 memcpy(ak2, ak, sizeof(*ak2));
447 }
448 else
449 {
450 free_chan_akick(ak);
451 }
452 }
453
454 static AutoKick *
455 next_chan_akick(void)
456 {
457 while (dbuse_ci_iterator
458 && dbuse_ak_iterator >= dbuse_ci_iterator->akick_count)
459 {
460 dbuse_ci_iterator = next_channelinfo();
461 dbuse_ak_iterator = 0;
462 }
463 if (dbuse_ci_iterator)
464 return &dbuse_ci_iterator->akick[dbuse_ak_iterator++];
465 else
466 return NULL;
467 }
468
469 static AutoKick *
470 first_chan_akick(void)
471 {
472 dbuse_ci_iterator = first_channelinfo();
473 dbuse_ak_iterator = 0;
474 return next_chan_akick();
475 }
476
477 static void
478 chan_akick_get_channel(const AutoKick * record, char **value_ret)
479 {
480 *value_ret = record->channel->name;
481 }
482
483 static void
484 chan_akick_put_channel(AutoKick * record, const char **value)
485 {
486 ChannelInfo *ci = get_channelinfo(*value);
487 if (ci)
488 {
489 record->channel = ci;
490 }
491 else
492 {
493 module_log("Skipping autokick record for missing channel %s", *value);
494 record->channel = NULL;
495 }
496 free((char *) *value);
497 }
498
499 /*************************************************************************/
500
501 /* Channel database table info */
502
503 #define FIELD(name,type,...) \
504 { #name, type, offsetof(ChannelInfo,name) , ##__VA_ARGS__ }
505 #define FIELD_LEVELS(name) \
506 { "levels." #name, DBTYPE_INT16, \
507 offsetof(ChannelInfo,levels) + (sizeof(int16) * CA_##name) }
508 #define FIELD_MLOCK(name,type,...) \
509 { "mlock." #name, type, \
510 offsetof(ChannelInfo,mlock) + offsetof(ModeLock,name) , ##__VA_ARGS__ }
511
512 static DBField chan_dbfields[] = {
513 FIELD(name, DBTYPE_BUFFER, CHANMAX),
514 FIELD(founder, DBTYPE_UINT32),
515 FIELD(successor, DBTYPE_UINT32),
516 FIELD(founderpass, DBTYPE_PASSWORD),
517 FIELD(desc, DBTYPE_STRING),
518 FIELD(url, DBTYPE_STRING),
519 FIELD(email, DBTYPE_STRING),
520 FIELD(entry_message, DBTYPE_STRING),
521 FIELD(time_registered, DBTYPE_TIME),
522 FIELD(last_used, DBTYPE_TIME),
523 FIELD(last_topic, DBTYPE_STRING),
524 FIELD(last_topic_setter, DBTYPE_BUFFER, NICKMAX),
525 FIELD(last_topic_time, DBTYPE_TIME),
526 FIELD(flags, DBTYPE_INT32),
527 FIELD(suspend_who, DBTYPE_BUFFER, NICKMAX),
528 FIELD(suspend_reason, DBTYPE_STRING),
529 FIELD(suspend_time, DBTYPE_TIME),
530 FIELD(suspend_expires, DBTYPE_TIME),
531 FIELD_LEVELS(INVITE),
532 FIELD_LEVELS(AKICK),
533 FIELD_LEVELS(SET),
534 FIELD_LEVELS(UNBAN),
535 FIELD_LEVELS(AUTOOP),
536 FIELD_LEVELS(AUTODEOP),
537 FIELD_LEVELS(AUTOVOICE),
538 FIELD_LEVELS(OPDEOP),
539 FIELD_LEVELS(ACCESS_LIST),
540 FIELD_LEVELS(CLEAR),
541 FIELD_LEVELS(NOJOIN),
542 FIELD_LEVELS(ACCESS_CHANGE),
543 FIELD_LEVELS(MEMO),
544 FIELD_LEVELS(VOICE),
545 FIELD_LEVELS(AUTOHALFOP),
546 FIELD_LEVELS(HALFOP),
547 FIELD_LEVELS(AUTOPROTECT),
548 FIELD_LEVELS(PROTECT),
549 FIELD_LEVELS(KICK),
550 FIELD_LEVELS(STATUS),
551 FIELD_LEVELS(TOPIC),
552 FIELD_MLOCK(on, DBTYPE_INT32),
553 FIELD_MLOCK(off, DBTYPE_INT32),
554 FIELD_MLOCK(limit, DBTYPE_INT32),
555 FIELD_MLOCK(key, DBTYPE_STRING),
556 FIELD_MLOCK(link, DBTYPE_STRING),
557 FIELD_MLOCK(flood, DBTYPE_STRING),
558 FIELD_MLOCK(joindelay, DBTYPE_INT32),
559 FIELD_MLOCK(joinrate1, DBTYPE_INT32),
560 FIELD_MLOCK(joinrate2, DBTYPE_INT32),
561
562 {"suspendinfo.who", DBTYPE_BUFFER,
563 offsetof(ChannelInfo, suspend_who), NICKMAX,.load_only = 1},
564 {"suspendinfo.reason", DBTYPE_STRING,
565 offsetof(ChannelInfo, suspend_reason),.load_only = 1},
566 {"suspendinfo.suspended", DBTYPE_TIME,
567 offsetof(ChannelInfo, suspend_time),.load_only = 1},
568 {"suspendinfo.expires", DBTYPE_TIME,
569 offsetof(ChannelInfo, suspend_expires),.load_only = 1},
570
571 {NULL}
572 };
573
574 static DBTable chan_dbtable = {
575 .name = "chan",
576 .newrec = (void *) new_channelinfo,
577 .freerec = (void *) free_channelinfo,
578 .insert = insert_channel,
579 .first = (void *) first_channelinfo,
580 .next = (void *) next_channelinfo,
581 .fields = chan_dbfields,
582 };
583
584 static DBField chan_access_dbfields[] = {
585 {"channel", DBTYPE_STRING,
586 .get = (void *) chan_access_get_channel,
587 .put = (void *) chan_access_put_channel},
588 {"nickgroup", DBTYPE_UINT32, offsetof(ChanAccess, nickgroup)},
589 {"level", DBTYPE_INT16, offsetof(ChanAccess, level)},
590 {NULL}
591 };
592
593 static DBTable chan_access_dbtable = {
594 .name = "chan-access",
595 .newrec = (void *) new_chan_access,
596 .freerec = (void *) free_chan_access,
597 .insert = (void *) insert_chan_access,
598 .first = (void *) first_chan_access,
599 .next = (void *) next_chan_access,
600 .fields = chan_access_dbfields,
601 };
602
603 static DBField chan_akick_dbfields[] = {
604 {"channel", DBTYPE_STRING,
605 .get = (void *) chan_akick_get_channel,
606 .put = (void *) chan_akick_put_channel},
607 {"mask", DBTYPE_STRING, offsetof(AutoKick, mask)},
608 {"reason", DBTYPE_STRING, offsetof(AutoKick, reason)},
609 {"who", DBTYPE_BUFFER, offsetof(AutoKick, who), NICKMAX},
610 {"set", DBTYPE_TIME, offsetof(AutoKick, set)},
611 {"lastused", DBTYPE_TIME, offsetof(AutoKick, lastused)},
612 {NULL}
613 };
614
615 static DBTable chan_akick_dbtable = {
616 .name = "chan-akick",
617 .newrec = (void *) new_chan_akick,
618 .freerec = (void *) free_chan_akick,
619 .insert = (void *) insert_chan_akick,
620 .first = (void *) first_chan_akick,
621 .next = (void *) next_chan_akick,
622 .fields = chan_akick_dbfields,
623 };
624
625 /*************************************************************************/
626 /************************ Main ChanServ routines *************************/
627 /*************************************************************************/
628
629 /* Introduce the ChanServ pseudoclient. */
630
631 static int
632 introduce_chanserv(const char *nick)
633 {
634 if (!nick || irc_stricmp(nick, s_ChanServ) == 0)
635 {
636 send_pseudo_nick(s_ChanServ, desc_ChanServ, PSEUDO_OPER);
637 return nick ? 1 : 0;
638 }
639 return 0;
640 }
641
642 /*************************************************************************/
643
644 /* Main ChanServ routine. */
645
646 static int
647 chanserv(const char *source, const char *target, char *buf)
648 {
649 char *cmd;
650 User *u = get_user(source);
651
652 if (irc_stricmp(target, s_ChanServ) != 0)
653 return 0;
654
655 if (!u)
656 {
657 module_log("user record for %s not found", source);
658 notice(s_ChanServ, source, getstring(NULL, INTERNAL_ERROR));
659 return 1;
660 }
661
662 cmd = strtok(buf, " ");
663
664 if (!cmd)
665 {
666 return 1;
667 }
668 else if (stricmp(cmd, "\1PING") == 0)
669 {
670 const char *s;
671 if (!(s = strtok_remaining()))
672 s = "\1";
673 notice(s_ChanServ, source, "\1PING %s", s);
674 }
675 else
676 {
677 int i;
678 ARRAY_FOREACH(i, aliases)
679 {
680 if (stricmp(cmd, aliases[i].alias) == 0)
681 {
682 cmd = aliases[i].command;
683 break;
684 }
685 }
686 if (call_callback_2(cb_command, u, cmd) <= 0)
687 run_cmd(s_ChanServ, u, THIS_MODULE, cmd);
688 }
689 return 1;
690 }
691
692 /*************************************************************************/
693
694 /* Return a /WHOIS response for ChanServ. */
695
696 static int
697 chanserv_whois(const char *source, char *who, char *extra)
698 {
699 if (irc_stricmp(who, s_ChanServ) != 0)
700 return 0;
701 send_cmd(ServerName, "311 %s %s %s %s * :%s", source, who,
702 ServiceUser, ServiceHost, desc_ChanServ);
703 send_cmd(ServerName, "312 %s %s %s :%s", source, who,
704 ServerName, ServerDesc);
705 send_cmd(ServerName, "318 %s %s End of /WHOIS response.", source, who);
706 return 1;
707 }
708
709 /*************************************************************************/
710
711 /* Callback for newly-created channels. */
712
713 static int
714 do_channel_create(Channel * c, User * u, int32 modes)
715 {
716 /* Store ChannelInfo pointer in channel record */
717 c->ci = get_channelinfo(c->name);
718 if (c->ci)
719 {
720 /* Store return pointer in ChannelInfo record */
721 c->ci->c = c;
722 }
723
724 /* Restore locked modes and saved topic */
725 /* Note: these should be outside the c->ci test to ensure any spurious
726 * +r modes are cleared */
727 check_modes(c);
728 restore_topic(c);
729
730 return 0;
731 }
732
733 /*************************************************************************/
734
735 /* Callback for users trying to join channels. */
736
737 static int
738 do_channel_join_check(const char *channel, User * user)
739 {
740 return check_kick(user, channel, 1);
741 }
742
743 /*************************************************************************/
744
745 /* Callback for users joining channels. */
746
747 static int
748 do_channel_join(Channel * c, struct c_userlist *u)
749 {
750 User *user = u->user;
751 ChannelInfo *ci = c->ci;
752
753 check_chan_user_modes(NULL, u, c, -1);
754 if (ci && ci->entry_message)
755 notice(s_ChanServ, user->nick, "(%s) %s", ci->name, ci->entry_message);
756 return 0;
757 }
758
759 /*************************************************************************/
760
761 /* Callback for users leaving channels. Update the channel's last used
762 * time if the user was an auto-op user.
763 */
764
765 static int
766 do_channel_part(Channel * c, User * u, const char *reason)
767 {
768 if (c->ci && check_access(u, c->ci, CA_AUTOOP))
769 c->ci->last_used = time(NULL);
770 return 0;
771 }
772
773 /*************************************************************************/
774
775 /* Callback for channels being deleted. */
776
777 static int
778 do_channel_delete(Channel * c)
779 {
780 if (c->ci)
781 {
782 put_channelinfo(c->ci);
783 c->ci->c = NULL;
784 }
785 return 0;
786 }
787
788 /*************************************************************************/
789
790 /* Callback for channel mode changes. */
791
792 static int
793 do_channel_mode_change(const char *source_unused, Channel * c)
794 {
795 check_modes(c);
796 return 0;
797 }
798
799 /*************************************************************************/
800
801 /* Callback for channel user mode changes. */
802
803 static int
804 do_channel_umode_change(const char *source, Channel * c,
805 struct c_userlist *u, int32 oldmodes)
806 {
807 if (!(u->mode & CUMODE_o))
808 u->flags &= ~CUFLAG_DEOPPED;
809 check_chan_user_modes(source, u, c, oldmodes);
810 return 0;
811 }
812
813 /*************************************************************************/
814
815 /* Callback for channel topic changes. */
816
817 static int
818 do_channel_topic(Channel * c, const char *topic, const char *setter,
819 time_t topic_time)
820 {
821 ChannelInfo *ci = c->ci;
822
823 if (check_topiclock(c, topic_time))
824 return 1;
825 record_topic(ci, topic, setter, topic_time);
826 return 0;
827 }
828
829 /*************************************************************************/
830
831 /* Callback for NickServ REGISTER/LINK check; we disallow
832 * registration/linking of the ChanServ pseudoclient nickname.
833 */
834
835 static int
836 do_reglink_check(const User * u, const char *nick,
837 const char *pass, const char *email)
838 {
839 return irc_stricmp(nick, s_ChanServ) == 0;
840 }
841
842 /*************************************************************************/
843
844 /* Callback for users who have identified to their nicks: give them modes
845 * as if they had just joined the channel.
846 */
847
848 static int
849 do_nick_identified(User * u, int old_authstat)
850 {
851 struct u_chanlist *uc; /* Node in list of channels the user is in */
852 struct c_userlist *cu; /* Node in list of users in a channel */
853
854 LIST_FOREACH(uc, u->chans)
855 {
856 LIST_SEARCH_SCALAR(uc->chan->users, user, u, cu);
857 if (!cu)
858 {
859 module_log("do_nick_identified(): BUG: user record not found in"
860 " channel %s for user %s", uc->chan->name, u->nick);
861 continue;
862 }
863 /* Use an empty source to force a mode recheck */
864 check_chan_user_modes("", cu, uc->chan, -1);
865 }
866 return 0;
867 }
868
869 /*************************************************************************/
870
871 /* Remove a (deleted or expired) nickname group from all channel lists. */
872
873 static int
874 do_nickgroup_delete(const NickGroupInfo * ngi, const char *oldnick)
875 {
876 int i;
877 int id = ngi->id;
878 ChannelInfo *ci;
879
880 for (ci = first_channelinfo(); ci; ci = next_channelinfo())
881 {
882 if (ci->founder == id)
883 {
884 int was_suspended = (ci->flags & CF_SUSPENDED);
885 char name_save[CHANMAX];
886 strbcpy(name_save, ci->name);
887 if (ci->successor)
888 {
889 NickGroupInfo *ngi2 = get_ngi_id(ci->successor);
890 if (!ngi2)
891 {
892 module_log("Unable to access successor group %u for"
893 " deleted channel %s, deleting channel",
894 ci->successor, ci->name);
895 goto delete;
896 }
897 else if (check_channel_limit(ngi2, NULL) < 0)
898 {
899 module_log("Transferring foundership of %s from deleted"
900 " nick %s to successor %s", ci->name,
901 oldnick, ngi_mainnick(ngi2));
902 put_nickgroupinfo(ngi2);
903 uncount_chan(ci);
904 ci->founder = ci->successor;
905 ci->successor = 0;
906 count_chan(ci);
907 }
908 else
909 {
910 module_log("Successor (%s) of %s owns too many channels,"
911 " deleting channel", ngi_mainnick(ngi2), ci->name);
912 put_nickgroupinfo(ngi2);
913 goto delete;
914 }
915 }
916 else
917 {
918 module_log("Deleting channel %s owned by deleted nick %s",
919 ci->name, oldnick);
920 delete:
921 delchan(ci);
922 if (was_suspended)
923 {
924 /* Channel was suspended, so make it forbidden */
925 Channel *c;
926 module_log("Channel %s was suspended, forbidding it", name_save);
927 ci = makechan(name_save);
928 ci->flags |= CF_VERBOTEN;
929 if ((c = get_channel(ci->name)) != NULL)
930 {
931 c->ci = ci;
932 ci->c = c;
933 }
934 else
935 {
936 put_channelinfo(ci);
937 }
938 }
939 continue;
940 }
941 }
942 if (ci->successor == id)
943 ci->successor = 0;
944 ARRAY_FOREACH(i, ci->access)
945 {
946 if (ci->access[i].nickgroup == id)
947 ci->access[i].nickgroup = 0;
948 }
949 }
950 return 0;
951 }
952
953 /*************************************************************************/
954
955 static int
956 do_stats_all(User * user, const char *s_OperServ)
957 {
958 int32 count, mem;
959 int i;
960 ChannelInfo *ci;
961
962 count = mem = 0;
963 for (ci = first_channelinfo(); ci; ci = next_channelinfo())
964 {
965 count++;
966 mem += sizeof(*ci);
967 if (ci->desc)
968 mem += strlen(ci->desc) + 1;
969 if (ci->url)
970 mem += strlen(ci->url) + 1;
971 if (ci->email)
972 mem += strlen(ci->email) + 1;
973 if (ci->last_topic)
974 mem += strlen(ci->last_topic) + 1;
975 if (ci->suspend_reason)
976 mem += strlen(ci->suspend_reason) + 1;
977 if (ci->levels)
978 mem += sizeof(*ci->levels) * CA_SIZE;
979 mem += ci->access_count * sizeof(*ci->access);
980 mem += ci->akick_count * sizeof(*ci->akick);
981 ARRAY_FOREACH(i, ci->akick)
982 {
983 if (ci->akick[i].mask)
984 mem += strlen(ci->akick[i].mask) + 1;
985 if (ci->akick[i].reason)
986 mem += strlen(ci->akick[i].reason) + 1;
987 }
988 if (ci->mlock.key)
989 mem += strlen(ci->mlock.key) + 1;
990 if (ci->mlock.link)
991 mem += strlen(ci->mlock.link) + 1;
992 if (ci->mlock.flood)
993 mem += strlen(ci->mlock.flood) + 1;
994 if (ci->entry_message)
995 mem += strlen(ci->entry_message) + 1;
996 }
997 notice_lang(s_OperServ, user, OPER_STATS_ALL_CHANSERV_MEM,
998 count, (mem + 512) / 1024);
999
1000 return 0;
1001 }
1002
1003 /*************************************************************************/
1004 /*********************** ChanServ command routines ***********************/
1005 /*************************************************************************/
1006
1007 /* Short routine for do_help() to return the proper access level string for
1008 * a given level based on which access modules are loaded. Assumes numeric
1009 * levels if no access module is loaded.
1010 */
1011 static const char *
1012 getstring_cmdacc(NickGroupInfo * ngi, int16 level)
1013 {
1014 int str_levxop, str_lev, str_xop;
1015
1016 switch (level)
1017 {
1018 case ACCLEV_SOP:
1019 str_levxop = CHAN_HELP_REQSOP_LEVXOP;
1020 str_lev = CHAN_HELP_REQSOP_LEV;
1021 str_xop = CHAN_HELP_REQSOP_XOP;
1022 break;
1023 case ACCLEV_AOP:
1024 str_levxop = CHAN_HELP_REQAOP_LEVXOP;
1025 str_lev = CHAN_HELP_REQAOP_LEV;
1026 str_xop = CHAN_HELP_REQAOP_XOP;
1027 break;
1028 case ACCLEV_HOP:
1029 str_levxop = CHAN_HELP_REQHOP_LEVXOP;
1030 str_lev = CHAN_HELP_REQHOP_LEV;
1031 str_xop = CHAN_HELP_REQHOP_XOP;
1032 break;
1033 case ACCLEV_VOP:
1034 str_levxop = CHAN_HELP_REQVOP_LEVXOP;
1035 str_lev = CHAN_HELP_REQVOP_LEV;
1036 str_xop = CHAN_HELP_REQVOP_XOP;
1037 break;
1038 default:
1039 module_log("BUG: weird level (%d) in getstring_cmdacc()", level);
1040 return "???";
1041 }
1042 if (find_module("chanserv/access-xop"))
1043 {
1044 if (find_module("chanserv/access-levels"))
1045 return getstring(ngi, str_levxop);
1046 else
1047 return getstring(ngi, str_xop);
1048 }
1049 else
1050 {
1051 return getstring(ngi, str_lev);
1052 }
1053 }
1054
1055
1056 static void
1057 do_help(User * u)
1058 {
1059 char *cmd = strtok_remaining();
1060 Command *cmdrec;
1061
1062 if (!cmd)
1063 {
1064 notice_help(s_ChanServ, u, CHAN_HELP);
1065 if (CSExpire)
1066 notice_help(s_ChanServ, u, CHAN_HELP_EXPIRES,
1067 maketime(u->ngi, CSExpire, 0));
1068 }
1069 else if (call_callback_2(cb_help, u, cmd) > 0)
1070 {
1071 return;
1072 }
1073 else if (stricmp(cmd, "COMMANDS") == 0)
1074 {
1075 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS);
1076 if (find_module("chanserv/access-levels"))
1077 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_LEVELS);
1078 if (find_module("chanserv/access-xop"))
1079 {
1080 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_XOP);
1081 if (protocol_features & PF_HALFOP)
1082 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_HOP);
1083 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_XOP_2);
1084 }
1085 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_OPVOICE);
1086 if (protocol_features & PF_HALFOP)
1087 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_HALFOP);
1088 if (protocol_features & PF_CHANPROT)
1089 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_PROTECT);
1090 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_INVITE);
1091 if (!CSListOpersOnly)
1092 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_LIST);
1093 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_AKICK);
1094 call_callback_2(cb_help_cmds, u, 0);
1095 if (is_oper(u))
1096 {
1097 notice_help(s_ChanServ, u, CHAN_OPER_HELP_COMMANDS);
1098 if (EnableGetpass)
1099 notice_help(s_ChanServ, u, CHAN_OPER_HELP_COMMANDS_GETPASS);
1100 notice_help(s_ChanServ, u, CHAN_OPER_HELP_COMMANDS_FORBID);
1101 if (CSListOpersOnly)
1102 notice_help(s_ChanServ, u, CHAN_HELP_COMMANDS_LIST);
1103 call_callback_2(cb_help_cmds, u, 1);
1104 notice_help(s_ChanServ, u, CHAN_OPER_HELP_COMMANDS_END);
1105 }
1106 }
1107 else if (!CSEnableRegister && is_oper(u) && stricmp(cmd, "REGISTER") == 0)
1108 {
1109 notice_help(s_ChanServ, u, CHAN_HELP_REGISTER, s_NickServ);
1110 notice_help(s_ChanServ, u, CHAN_HELP_REGISTER_ADMINONLY);
1111 }
1112 else if (stricmp(cmd, "LIST") == 0)
1113 {
1114 if (is_oper(u))
1115 notice_help(s_ChanServ, u, CHAN_OPER_HELP_LIST);
1116 else
1117 notice_help(s_ChanServ, u, CHAN_HELP_LIST);
1118 if (CSListOpersOnly)
1119 notice_help(s_ChanServ, u, CHAN_HELP_LIST_OPERSONLY);
1120 }
1121 else if (stricmp(cmd, "KICK") == 0)
1122 {
1123 cmdrec = lookup_cmd(THIS_MODULE, cmd);
1124 notice_help(s_ChanServ, u, CHAN_HELP_KICK,
1125 getstring_cmdacc(u->ngi,
1126 cmdrec ? (int) (long) cmdrec->help_param1 :
1127 -1));
1128 if (protocol_features & PF_CHANPROT)
1129 notice_help(s_ChanServ, u, CHAN_HELP_KICK_PROTECTED);
1130 }
1131 else if (stricmp(cmd, "CLEAR") == 0)
1132 {
1133 notice_help(s_ChanServ, u, CHAN_HELP_CLEAR);
1134 if (protocol_features & PF_BANEXCEPT)
1135 notice_help(s_ChanServ, u, CHAN_HELP_CLEAR_EXCEPTIONS);
1136 if (protocol_features & PF_INVITEMASK)
1137 notice_help(s_ChanServ, u, CHAN_HELP_CLEAR_INVITES);
1138 notice_help(s_ChanServ, u, CHAN_HELP_CLEAR_MID);
1139 if (protocol_features & PF_HALFOP)
1140 notice_help(s_ChanServ, u, CHAN_HELP_CLEAR_HALFOPS);
1141 cmdrec = lookup_cmd(THIS_MODULE, cmd);
1142 notice_help(s_ChanServ, u, CHAN_HELP_CLEAR_END,
1143 getstring_cmdacc(u->ngi,
1144 cmdrec ? (int) (long) cmdrec->help_param1 :
1145 -1));
1146 }
1147 else if ((stricmp(cmd, "AKICK") == 0
1148 || stricmp(cmd, "OP") == 0
1149 || stricmp(cmd, "DEOP") == 0
1150 || stricmp(cmd, "VOICE") == 0
1151 || stricmp(cmd, "DEVOICE") == 0
1152 || stricmp(cmd, "HALFOP") == 0
1153 || stricmp(cmd, "DEHALFOP") == 0
1154 || stricmp(cmd, "PROTECT") == 0
1155 || stricmp(cmd, "DEPROTECT") == 0
1156 || stricmp(cmd, "INVITE") == 0
1157 || stricmp(cmd, "UNBAN") == 0
1158 || stricmp(cmd, "TOPIC") == 0
1159 || stricmp(cmd, "CLEAR") == 0
1160 || stricmp(cmd, "STATUS") == 0)
1161 && (cmdrec = lookup_cmd(THIS_MODULE, cmd)) != NULL)
1162 {
1163 notice_help(s_ChanServ, u, cmdrec->helpmsg_all,
1164 getstring_cmdacc(u->ngi, (int) (long) cmdrec->help_param1));
1165 }
1166 else
1167 {
1168 help_cmd(s_ChanServ, u, THIS_MODULE, cmd);
1169 }
1170 }
1171
1172 /*************************************************************************/
1173
1174 static void
1175 do_register(User * u)
1176 {
1177 char *chan = strtok(NULL, " ");
1178 char *pass = strtok(NULL, " ");
1179 char *desc = strtok_remaining();
1180 NickInfo *ni = u->ni;
1181 NickGroupInfo *ngi = u->ngi;
1182 Channel *c;
1183 ChannelInfo *ci;
1184 struct u_chaninfolist *uc;
1185 int max;
1186
1187 if (readonly)
1188 {
1189 notice_lang(s_ChanServ, u, CHAN_REGISTER_DISABLED);
1190 return;
1191 }
1192
1193 if (!chan || !pass || !desc)
1194 {
1195 syntax_error(s_ChanServ, u, "REGISTER", CHAN_REGISTER_SYNTAX);
1196 }
1197 else if (!is_chanop(u, chan))
1198 {
1199 notice_lang(s_ChanServ, u, CHAN_MUST_BE_CHANOP);
1200 }
1201 else if (strcmp(chan, "#") == 0)
1202 {
1203 notice_lang(s_ChanServ, u, CHAN_REGISTER_SHORT_CHANNEL);
1204 }
1205 else if (*chan == '&')
1206 {
1207 notice_lang(s_ChanServ, u, CHAN_REGISTER_NOT_LOCAL);
1208 }
1209 else if (*chan != '#')
1210 {
1211 notice_lang(s_ChanServ, u, CHAN_REGISTER_INVALID_NAME);
1212 }
1213 else if (!ni)
1214 {
1215 notice_lang(s_ChanServ, u, CHAN_MUST_REGISTER_NICK, s_NickServ);
1216 }
1217 else if (!user_identified(u))
1218 {
1219 notice_lang(s_ChanServ, u, CHAN_MUST_IDENTIFY_NICK,
1220 s_NickServ, s_NickServ);
1221
1222 }
1223 else if ((ci = get_channelinfo(chan)) != NULL)
1224 {
1225 if (ci->flags & CF_VERBOTEN)
1226 {
1227 module_log("Attempt to register forbidden channel %s by %s!%s@%s",
1228 ci->name, u->nick, u->username, u->host);
1229 notice_lang(s_ChanServ, u, CHAN_MAY_NOT_BE_REGISTERED, chan);
1230 }
1231 else if (ci->flags & CF_SUSPENDED)
1232 {
1233 module_log("Attempt to register suspended channel %s by %s!%s@%s",
1234 ci->name, u->nick, u->username, u->host);
1235 notice_lang(s_ChanServ, u, CHAN_ALREADY_REGISTERED, chan);
1236 }
1237 else
1238 {
1239 notice_lang(s_ChanServ, u, CHAN_ALREADY_REGISTERED, chan);
1240 }
1241 put_channelinfo(ci);
1242
1243 }
1244 else if (!(NoAdminPasswordCheck && is_services_admin(u))
1245 && (stricmp(pass, chan) == 0
1246 || stricmp(pass, chan + 1) == 0
1247 || stricmp(pass, u->nick) == 0
1248 || (StrictPasswords && strlen(pass) < 5)))
1249 {
1250 notice_lang(s_ChanServ, u, MORE_OBSCURE_PASSWORD);
1251
1252 }
1253 else if (!is_services_admin(u) && check_channel_limit(ngi, &max) >= 0)
1254 {
1255 notice_lang(s_ChanServ, u, ngi->channels_count > max
1256 ? CHAN_EXCEEDED_CHANNEL_LIMIT
1257 : CHAN_REACHED_CHANNEL_LIMIT, max);
1258
1259 }
1260 else if (!(c = get_channel(chan)))
1261 {
1262 /* Should not fail because we checked is_chanop() above, but just
1263 * in case... */
1264 module_log("Channel %s not found for REGISTER", chan);
1265 notice_lang(s_ChanServ, u, CHAN_REGISTRATION_FAILED);
1266
1267 }
1268 else
1269 {
1270 Password passbuf;
1271
1272 init_password(&passbuf);
1273 if (encrypt_password(pass, strlen(pass), &passbuf) != 0)
1274 {
1275 clear_password(&passbuf);
1276 memset(pass, 0, strlen(pass));
1277 module_log("Failed to encrypt password for %s (register)", chan);
1278 notice_lang(s_ChanServ, u, CHAN_REGISTRATION_FAILED);
1279 return;
1280 }
1281 ci = makechan(chan);
1282 if (!ci)
1283 {
1284 clear_password(&passbuf);
1285 module_log("makechan() failed for REGISTER %s", chan);
1286 notice_lang(s_ChanServ, u, CHAN_REGISTRATION_FAILED);
1287 return;
1288 }
1289 c->ci = ci;
1290 ci->c = c;
1291 ci->flags = CSDefFlags;
1292 ci->mlock.on = CSDefModeLockOn;
1293 ci->mlock.off = CSDefModeLockOff;
1294 ci->last_used = ci->time_registered;
1295 ci->founder = u->ngi->id;
1296 copy_password(&ci->founderpass, &passbuf);
1297 clear_password(&passbuf);
1298 ci->desc = sstrdup(desc);
1299 if (c->topic)
1300 {
1301 ci->last_topic = sstrdup(c->topic);
1302 strbcpy(ci->last_topic_setter, c->topic_setter);
1303 ci->last_topic_time = c->topic_time;
1304 }
1305 count_chan(ci);
1306 module_log("Channel %s registered by %s!%s@%s",
1307 chan, u->nick, u->username, u->host);
1308 notice_lang(s_ChanServ, u, CHAN_REGISTERED, chan, u->nick);
1309 if (CSShowPassword)
1310 notice_lang(s_ChanServ, u, CHAN_PASSWORD_IS, pass);
1311 memset(pass, 0, strlen(pass));
1312 uc = smalloc(sizeof(*uc));
1313 LIST_INSERT(uc, u->id_chans);
1314 strbcpy(uc->chan, ci->name);
1315 /* Implement new mode lock */
1316 check_modes(ci->c);
1317
1318 }
1319 }
1320
1321 /*************************************************************************/
1322
1323 static void
1324 do_identify(User * u)
1325 {
1326 char *chan = strtok(NULL, " ");
1327 char *pass = strtok_remaining();
1328 ChannelInfo *ci = NULL;
1329 struct u_chaninfolist *uc;
1330
1331 if (!chan || !pass)
1332 {
1333 syntax_error(s_ChanServ, u, "IDENTIFY", CHAN_IDENTIFY_SYNTAX);
1334 }
1335 else if (!(ci = get_channelinfo(chan)))
1336 {
1337 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
1338 }
1339 else if (ci->flags & CF_VERBOTEN)
1340 {
1341 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
1342 }
1343 else if (ci->flags & CF_SUSPENDED)
1344 {
1345 notice_lang(s_ChanServ, u, CHAN_X_SUSPENDED, chan);
1346 }
1347 else
1348 {
1349 int res = check_password(pass, &ci->founderpass);
1350 if (res == 1)
1351 {
1352 ci->bad_passwords = 0;
1353 ci->last_used = time(NULL);
1354 if (!is_identified(u, ci))
1355 {
1356 uc = smalloc(sizeof(*uc));
1357 LIST_INSERT(uc, u->id_chans);
1358 strbcpy(uc->chan, ci->name);
1359 module_log("%s!%s@%s identified for %s",
1360 u->nick, u->username, u->host, ci->name);
1361 }
1362 notice_lang(s_ChanServ, u, CHAN_IDENTIFY_SUCCEEDED, chan);
1363 }
1364 else if (res < 0)
1365 {
1366 module_log("check_password failed for %s", ci->name);
1367 notice_lang(s_ChanServ, u, CHAN_IDENTIFY_FAILED);
1368 }
1369 else
1370 {
1371 module_log("Failed IDENTIFY for %s by %s!%s@%s",
1372 ci->name, u->nick, u->username, u->host);
1373 chan_bad_password(u, ci);
1374 }
1375 }
1376 put_channelinfo(ci);
1377 }
1378
1379 /*************************************************************************/
1380
1381 static void
1382 do_drop(User * u)
1383 {
1384 const char *chan = strtok(NULL, " ");
1385 const char *pass = strtok_remaining();
1386 ChannelInfo *ci;
1387 int res;
1388
1389 if (readonly)
1390 {
1391 notice_lang(s_ChanServ, u, CHAN_DROP_DISABLED);
1392 return;
1393 }
1394
1395 if (!chan || !pass)
1396 {
1397 syntax_error(s_ChanServ, u, "DROP", CHAN_DROP_SYNTAX);
1398 }
1399 else if (!(ci = get_channelinfo(chan)))
1400 {
1401 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
1402 }
1403 else if (ci->flags & CF_VERBOTEN)
1404 {
1405 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
1406 put_channelinfo(ci);
1407 }
1408 else if (ci->flags & CF_SUSPENDED)
1409 {
1410 notice_lang(s_ChanServ, u, CHAN_X_SUSPENDED, chan);
1411 put_channelinfo(ci);
1412 }
1413 else if ((res = check_password(pass, &ci->founderpass)) != 1)
1414 {
1415 if (res < 0)
1416 {
1417 module_log("check_password failed for %s", ci->name);
1418 notice_lang(s_ChanServ, u, INTERNAL_ERROR);
1419 }
1420 else
1421 {
1422 module_log("Failed DROP for %s by %s!%s@%s",
1423 ci->name, u->nick, u->username, u->host);
1424 chan_bad_password(u, ci);
1425 }
1426 put_channelinfo(ci);
1427 }
1428 else
1429 {
1430 const char *founder;
1431 char tmpbuf[64];
1432
1433 if (ci->founder)
1434 {
1435 NickGroupInfo *ngi = get_ngi_id(ci->founder);
1436 if (ngi)
1437 {
1438 founder = ngi_mainnick(ngi);
1439 }
1440 else
1441 {
1442 snprintf(tmpbuf, sizeof(tmpbuf), "<unknown: ID %u>", ci->founder);
1443 founder = tmpbuf;
1444 }
1445 }
1446 else
1447 {
1448 founder = "<none>";
1449 }
1450 module_log("Channel %s (founder %s) dropped by %s!%s@%s",
1451 ci->name, founder, u->nick, u->username, u->host);
1452 delchan(ci);
1453 notice_lang(s_ChanServ, u, CHAN_DROPPED, chan);
1454 }
1455 }
1456
1457 /*************************************************************************/
1458
1459 static void
1460 do_dropchan(User * u)
1461 {
1462 const char *chan = strtok(NULL, " ");
1463 ChannelInfo *ci;
1464
1465 if (!chan)
1466 {
1467 syntax_error(s_ChanServ, u, "DROPCHAN", CHAN_DROPCHAN_SYNTAX);
1468 }
1469 else if (!(ci = get_channelinfo(chan)))
1470 {
1471 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
1472 }
1473 else
1474 {
1475 const char *founder;
1476 char tmpbuf[64];
1477
1478 if (readonly)
1479 notice_lang(s_ChanServ, u, READ_ONLY_MODE);
1480 if (ci->founder)
1481 {
1482 NickGroupInfo *ngi = get_ngi_id(ci->founder);
1483 if (ngi)
1484 {
1485 founder = ngi_mainnick(ngi);
1486 }
1487 else
1488 {
1489 snprintf(tmpbuf, sizeof(tmpbuf), "<unknown: ID %u>", ci->founder);
1490 founder = tmpbuf;
1491 }
1492 }
1493 else
1494 {
1495 founder = "<none>";
1496 }
1497 module_log("Channel %s (founder %s) dropped by %s!%s@%s",
1498 ci->name, founder, u->nick, u->username, u->host);
1499 delchan(ci);
1500 notice_lang(s_ChanServ, u, CHAN_DROPPED, chan);
1501 }
1502 }
1503
1504 /*************************************************************************/
1505
1506 /* SADMINS, and users who have identified for a channel, can now cause its
1507 * entry message and successor to be displayed by supplying the ALL
1508 * parameter.
1509 * Syntax: INFO channel [ALL]
1510 * -TheShadow (29 Mar 1999)
1511 */
1512
1513 /* Check the status of show_all and make a note of having done so. See
1514 * comments at nickserv/main.c/do_info() for details. */
1515 #define CHECK_SHOW_ALL (used_all++, show_all)
1516
1517 static void
1518 do_info(User * u)
1519 {
1520 char *chan = strtok(NULL, " ");
1521 char *param = strtok(NULL, " ");
1522 ChannelInfo *ci = NULL;
1523 NickGroupInfo *ngi = NULL, *ngi2 = NULL;
1524 char buf[BUFSIZE], *end, *s;
1525 int is_servadmin = is_services_admin(u);
1526 int can_show_all = 0, show_all = 0, used_all = 0;
1527
1528 if (!chan)
1529 {
1530 syntax_error(s_ChanServ, u, "INFO", CHAN_INFO_SYNTAX);
1531 }
1532 else if (!(ci = get_channelinfo(chan)))
1533 {
1534 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
1535 }
1536 else if (ci->flags & CF_VERBOTEN)
1537 {
1538 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
1539 }
1540 else if (!ci->founder)
1541 {
1542 /* Paranoia... this shouldn't be able to happen */
1543 module_log("INFO: non-forbidden channel %s has no founder, deleting",
1544 ci->name);
1545 delchan(ci);
1546 ci = NULL;
1547 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
1548 }
1549 else if (!(ngi = get_ngi_id(ci->founder))
1550 || (ci->successor && !(ngi2 = get_ngi_id(ci->successor))))
1551 {
1552 notice_lang(s_ChanServ, u, INTERNAL_ERROR);
1553 }
1554 else
1555 {
1556
1557 /* Update last used time if the channel is currently in use. */
1558 if (ci->c)
1559 {
1560 struct c_userlist *cu;
1561 LIST_FOREACH(cu, ci->c->users)
1562 {
1563 if (check_access(cu->user, ci, CA_AUTOOP))
1564 {
1565 module_log_debug(2, "updating last used time for %s"
1566 " (INFO)", ci->name);
1567 ci->last_used = time(NULL);
1568 break;
1569 }
1570 }
1571 }
1572
1573 /* Only show all the channel's settings to sadmins and founders. */
1574 can_show_all = (is_founder(u, ci) || is_servadmin);
1575
1576 if ((param && stricmp(param, "ALL") == 0) && can_show_all)
1577 show_all = 1;
1578
1579 notice_lang(s_ChanServ, u, CHAN_INFO_HEADER, chan);
1580 notice_lang(s_ChanServ, u, CHAN_INFO_FOUNDER, ngi_mainnick(ngi));
1581 if (ngi2 != NULL && CHECK_SHOW_ALL)
1582 {
1583 notice_lang(s_ChanServ, u, CHAN_INFO_SUCCESSOR, ngi_mainnick(ngi2));
1584 }
1585 notice_lang(s_ChanServ, u, CHAN_INFO_DESCRIPTION, ci->desc);
1586 strftime_lang(buf, sizeof(buf), u->ngi, STRFTIME_DATE_TIME_FORMAT,
1587 ci->time_registered);
1588 notice_lang(s_ChanServ, u, CHAN_INFO_TIME_REGGED, buf);
1589 strftime_lang(buf, sizeof(buf), u->ngi, STRFTIME_DATE_TIME_FORMAT,
1590 ci->last_used);
1591 notice_lang(s_ChanServ, u, CHAN_INFO_LAST_USED, buf);
1592
1593 /* Do not show last_topic if channel is mlock'ed +s or +p, or if the
1594 * channel's current modes include +s or +p. -TheShadow */
1595 /* But show it if we're showing all info. --AC */
1596 if (ci->last_topic)
1597 {
1598 int mlock_sp = (ci->mlock.on & (CMODE_s | CMODE_p));
1599 int mode_sp = (ci->c && (ci->c->mode & (CMODE_s | CMODE_p)));
1600 int hide = (ci->flags & CF_HIDE_TOPIC);
1601 if ((!mlock_sp && !mode_sp && !hide) || CHECK_SHOW_ALL)
1602 {
1603 notice_lang(s_ChanServ, u, CHAN_INFO_LAST_TOPIC, ci->last_topic);
1604 notice_lang(s_ChanServ, u, CHAN_INFO_TOPIC_SET_BY,
1605 ci->last_topic_setter);
1606 }
1607 }
1608
1609 if (ci->entry_message && CHECK_SHOW_ALL)
1610 notice_lang(s_ChanServ, u, CHAN_INFO_ENTRYMSG, ci->entry_message);
1611 if (ci->url)
1612 notice_lang(s_ChanServ, u, CHAN_INFO_URL, ci->url);
1613 if (ci->email && (!(ci->flags & CF_HIDE_EMAIL) || CHECK_SHOW_ALL))
1614 notice_lang(s_ChanServ, u, CHAN_INFO_EMAIL, ci->email);
1615 s = chanopts_to_string(ci, u->ngi);
1616 notice_lang(s_ChanServ, u, CHAN_INFO_OPTIONS,
1617 *s ? s : getstring(u->ngi, CHAN_INFO_OPT_NONE));
1618 end = buf;
1619 *end = 0;
1620 if (ci->mlock.on || ci->mlock.key || ci->mlock.limit)
1621 end += snprintf(end, sizeof(buf) - (end - buf), "+%s",
1622 mode_flags_to_string(ci->mlock.on, MODE_CHANNEL));
1623 if (ci->mlock.off)
1624 end += snprintf(end, sizeof(buf) - (end - buf), "-%s",
1625 mode_flags_to_string(ci->mlock.off, MODE_CHANNEL));
1626 if (*buf && (!(ci->flags & CF_HIDE_MLOCK) || CHECK_SHOW_ALL))
1627 notice_lang(s_ChanServ, u, CHAN_INFO_MODE_LOCK, buf);
1628
1629 if ((ci->flags & CF_NOEXPIRE) && CHECK_SHOW_ALL)
1630 notice_lang(s_ChanServ, u, CHAN_INFO_NO_EXPIRE);
1631
1632 if (ci->flags & CF_SUSPENDED)
1633 {
1634 notice_lang(s_ChanServ, u, CHAN_X_SUSPENDED, chan);
1635 if (CHECK_SHOW_ALL)
1636 {
1637 char timebuf[BUFSIZE], expirebuf[BUFSIZE];
1638 strftime_lang(timebuf, sizeof(timebuf), u->ngi,
1639 STRFTIME_DATE_TIME_FORMAT, ci->suspend_time);
1640 expires_in_lang(expirebuf, sizeof(expirebuf), u->ngi,
1641 ci->suspend_expires);
1642 notice_lang(s_ChanServ, u, CHAN_INFO_SUSPEND_DETAILS,
1643 ci->suspend_who, timebuf, expirebuf);
1644 notice_lang(s_ChanServ, u, CHAN_INFO_SUSPEND_REASON,
1645 ci->suspend_reason);
1646 }
1647 }
1648
1649 if (can_show_all && !show_all && used_all)
1650 notice_lang(s_ChanServ, u, CHAN_INFO_SHOW_ALL, s_ChanServ, ci->name);
1651
1652 }
1653
1654 put_channelinfo(ci);
1655 put_nickgroupinfo(ngi);
1656 put_nickgroupinfo(ngi2);
1657 }
1658
1659 /*************************************************************************/
1660
1661 /* SADMINS can search for channels based on their CF_VERBOTEN and
1662 * CF_NOEXPIRE flags and suspension status. This works in the same way as
1663 * NickServ's LIST command.
1664 * Syntax for sadmins: LIST pattern [FORBIDDEN] [NOEXPIRE] [SUSPENDED]
1665 * Also fixed CF_PRIVATE channels being shown to non-sadmins.
1666 * -TheShadow
1667 */
1668
1669 static void
1670 do_list(User * u)
1671 {
1672 char *pattern = strtok(NULL, " ");
1673 char *keyword;
1674 ChannelInfo *ci;
1675 int nchans;
1676 char buf[BUFSIZE];
1677 int is_servadmin = is_services_admin(u);
1678 int32 matchflags = 0; /* CF_ flags a chan must match one of to qualify */
1679 int skip = 0; /* number of records to skip before displaying */
1680
1681
1682 if (CSListOpersOnly && (!u || !is_oper(u)))
1683 {
1684 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
1685 return;
1686 }
1687
1688 if (pattern && *pattern == '+')
1689 {
1690 skip = (int) atolsafe(pattern + 1, 0, INT_MAX);
1691 if (skip < 0)
1692 {
1693 syntax_error(s_ChanServ, u, "LIST",
1694 is_oper(u) ? CHAN_LIST_OPER_SYNTAX : CHAN_LIST_SYNTAX);
1695 return;
1696 }
1697 pattern = strtok(NULL, " ");
1698 }
1699
1700 if (!pattern)
1701 {
1702 syntax_error(s_ChanServ, u, "LIST",
1703 is_oper(u) ? CHAN_LIST_OPER_SYNTAX : CHAN_LIST_SYNTAX);
1704 }
1705 else
1706 {
1707 nchans = 0;
1708
1709 while (is_servadmin && (keyword = strtok(NULL, " ")))
1710 {
1711 if (stricmp(keyword, "FORBIDDEN") == 0)
1712 {
1713 matchflags |= CF_VERBOTEN;
1714 }
1715 else if (stricmp(keyword, "NOEXPIRE") == 0)
1716 {
1717 matchflags |= CF_NOEXPIRE;
1718 }
1719 else if (stricmp(keyword, "SUSPENDED") == 0)
1720 {
1721 matchflags |= CF_SUSPENDED;
1722 }
1723 else
1724 {
1725 syntax_error(s_ChanServ, u, "LIST",
1726 is_oper(u) ? CHAN_LIST_OPER_SYNTAX : CHAN_LIST_SYNTAX);
1727 }
1728 }
1729
1730 for (ci = first_channelinfo(); ci; ci = next_channelinfo())
1731 {
1732 if (!is_servadmin && (ci->flags & (CF_PRIVATE | CF_VERBOTEN)))
1733 continue;
1734 if (matchflags && !(ci->flags & matchflags))
1735 continue;
1736
1737 snprintf(buf, sizeof(buf), "%-20s %s", ci->name,
1738 ci->desc ? ci->desc : "");
1739 if (irc_stricmp(pattern, ci->name) == 0
1740 || match_wild_nocase(pattern, buf))
1741 {
1742 nchans++;
1743 if (nchans > skip && nchans <= skip + ListMax)
1744 {
1745 char noexpire_char = ' ', suspended_char = ' ';
1746 if (is_servadmin)
1747 {
1748 if (ci->flags & CF_NOEXPIRE)
1749 noexpire_char = '!';
1750 if (ci->flags & CF_SUSPENDED)
1751 suspended_char = '*';
1752 }
1753
1754 /* This can only be true for SADMINS - normal users
1755 * will never get this far with a VERBOTEN channel.
1756 * -TheShadow */
1757 if (ci->flags & CF_VERBOTEN)
1758 {
1759 snprintf(buf, sizeof(buf), "%-20s [Forbidden]", ci->name);
1760 }
1761
1762 if (nchans == 1) /* display header before first result */
1763 notice_lang(s_ChanServ, u, CHAN_LIST_HEADER, pattern);
1764 notice(s_ChanServ, u->nick, " %c%c%s",
1765 suspended_char, noexpire_char, buf);
1766 }
1767 }
1768 }
1769 if (nchans)
1770 {
1771 int count = nchans - skip;
1772 if (count < 0)
1773 count = 0;
1774 else if (count > ListMax)
1775 count = ListMax;
1776 notice_lang(s_ChanServ, u, LIST_RESULTS, count, nchans);
1777 }
1778 else
1779 {
1780 notice_lang(s_ChanServ, u, CHAN_LIST_NO_MATCH);
1781 }
1782 }
1783
1784 }
1785
1786 /*************************************************************************/
1787
1788 static int list_akick(User * u, int index, ChannelInfo * ci, int *sent_header,
1789 int is_view);
1790
1791 static void
1792 do_akick(User * u)
1793 {
1794 char *chan = strtok(NULL, " ");
1795 char *cmd = strtok(NULL, " ");
1796 char *mask = strtok(NULL, " ");
1797 char *reason = strtok_remaining();
1798 ChannelInfo *ci = NULL;
1799 int i;
1800 int is_list = (cmd && (stricmp(cmd, "LIST") == 0
1801 || stricmp(cmd, "VIEW") == 0
1802 || stricmp(cmd, "COUNT") == 0));
1803
1804 if (!chan
1805 || !cmd
1806 || (!mask && (stricmp(cmd, "ADD") == 0 || stricmp(cmd, "DEL") == 0)))
1807 {
1808 syntax_error(s_ChanServ, u, "AKICK", CHAN_AKICK_SYNTAX);
1809 }
1810 else if (!(ci = get_channelinfo(chan)))
1811 {
1812 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
1813 }
1814 else if (ci->flags & CF_VERBOTEN)
1815 {
1816 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
1817 }
1818 else if (!check_access_cmd(u, ci, "AKICK", is_list ? "LIST" : cmd)
1819 && !is_services_admin(u))
1820 {
1821 if (ci->founder && valid_ngi(u) && ci->founder == u->ngi->id)
1822 notice_lang(s_ChanServ, u, CHAN_IDENTIFY_REQUIRED, s_ChanServ, chan);
1823 else
1824 notice_lang(s_ChanServ, u, ACCESS_DENIED);
1825
1826 }
1827 else if (stricmp(cmd, "ADD") == 0)
1828 {
1829
1830 char *mask2, *user, *host;
1831 const char *nick;
1832
1833 if (readonly)
1834 {
1835 notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
1836 put_channelinfo(ci);
1837 return;
1838 }
1839
1840 /* Make sure we have a valid nick!user@host mask (fill in missing
1841 * parts with "*"). Error out on @ in nick (also catches a@b!c),
1842 * missing host, or empty nick/user/host. */
1843 mask2 = sstrdup(mask);
1844 nick = mask2;
1845 user = strchr(mask2, '!');
1846 if (user)
1847 {
1848 *user++ = 0;
1849 }
1850 else
1851 {
1852 nick = "*";
1853 user = mask2;
1854 }
1855 host = strchr(user, '@');
1856 if (host)
1857 *host++ = 0;
1858 if (!*nick || !*user || !host || !*host || strchr(nick, '@'))
1859 {
1860 notice_lang(s_ChanServ, u, BAD_NICKUSERHOST_MASK);
1861 free(mask2);
1862 put_channelinfo(ci);
1863 return;
1864 }
1865 mask = smalloc(strlen(nick) + strlen(user) + strlen(host) + 3);
1866 sprintf(mask, "%s!%s@%s", nick, user, host);
1867 free(mask2);
1868
1869 ARRAY_SEARCH(ci->akick, mask, mask, stricmp, i);
1870 if (i < ci->akick_count)
1871 {
1872 notice_lang(s_ChanServ, u, CHAN_AKICK_ALREADY_EXISTS,
1873 ci->akick[i].mask, chan);
1874 free(mask);
1875 }
1876 else if (ci->akick_count >= CSAutokickMax)
1877 {
1878 notice_lang(s_ChanServ, u, CHAN_AKICK_REACHED_LIMIT, CSAutokickMax);
1879 free(mask);
1880 }
1881 else
1882 {
1883 ARRAY_EXTEND(ci->akick);
1884 ci->akick[i].channel = ci; /* i points at new entry, from above */
1885 ci->akick[i].mask = mask;
1886 ci->akick[i].reason = reason ? sstrdup(reason) : NULL;
1887 time(&ci->akick[i].set);
1888 ci->akick[i].lastused = 0;
1889 memset(ci->akick[i].who, 0, NICKMAX); // Avoid leaking random data
1890 strbcpy(ci->akick[i].who, u->nick);
1891 notice_lang(s_ChanServ, u, CHAN_AKICK_ADDED, mask, chan);
1892 }
1893
1894 }
1895 else if (stricmp(cmd, "DEL") == 0)
1896 {
1897
1898 if (readonly)
1899 {
1900 notice_lang(s_ChanServ, u, CHAN_AKICK_DISABLED);
1901 put_channelinfo(ci);
1902 return;
1903 }
1904
1905 ARRAY_SEARCH(ci->akick, mask, mask, stricmp, i);
1906 if (i < ci->akick_count)
1907 {
1908 free(ci->akick[i].mask);
1909 free(ci->akick[i].reason);
1910 ARRAY_REMOVE(ci->akick, i);
1911 notice_lang(s_ChanServ, u, CHAN_AKICK_DELETED, mask, chan);
1912 }
1913 else
1914 {
1915 notice_lang(s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask, chan);
1916 }
1917
1918 }
1919 else if (stricmp(cmd, "LIST") == 0 || stricmp(cmd, "VIEW") == 0)
1920 {
1921 int is_view = stricmp(cmd, "VIEW") == 0;
1922 int count = 0, sent_header = 0, skip = 0;
1923
1924 if (mask && *mask == '+')
1925 {
1926 skip = (int) atolsafe(mask + 1, 0, INT_MAX);
1927 if (skip < 0)
1928 {
1929 syntax_error(s_ChanServ, u, "AKICK",
1930 is_view ? CHAN_AKICK_VIEW_SYNTAX
1931 : CHAN_AKICK_LIST_SYNTAX);
1932 put_channelinfo(ci);
1933 return;
1934 }
1935 mask = reason ? strtok(reason, " ") : NULL;
1936 }
1937 if (ci->akick_count == 0)
1938 {
1939 notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, chan);
1940 put_channelinfo(ci);
1941 return;
1942 }
1943 ARRAY_FOREACH(i, ci->akick)
1944 {
1945 if (mask)
1946 {
1947 if (!match_wild_nocase(mask, ci->akick[i].mask))
1948 continue;
1949 }
1950 count++;
1951 if (count > skip && count <= skip + ListMax)
1952 list_akick(u, i, ci, &sent_header, is_view);
1953 }
1954 if (count)
1955 {
1956 int shown = count - skip;
1957 if (shown < 0)
1958 shown = 0;
1959 else if (shown > ListMax)
1960 shown = ListMax;
1961 notice_lang(s_ChanServ, u, LIST_RESULTS, shown, count);
1962 }
1963 else
1964 {
1965 notice_lang(s_ChanServ, u, CHAN_AKICK_NO_MATCH, chan);
1966 }
1967
1968 }
1969 else if (stricmp(cmd, "ENFORCE") == 0)
1970 {
1971 Channel *c = get_channel(ci->name);
1972 struct c_userlist *cu, *next;
1973 int count = 0;
1974
1975 if (!c)
1976 {
1977 notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, ci->name);
1978 put_channelinfo(ci);
1979 return;
1980 }
1981 LIST_FOREACH_SAFE(cu, c->users, next)
1982 {
1983 if (check_kick(cu->user, c->name, 0))
1984 count++;
1985 }
1986
1987 notice_lang(s_ChanServ, u, CHAN_AKICK_ENFORCE_DONE, chan, count);
1988
1989 }
1990 else if (stricmp(cmd, "COUNT") == 0)
1991 {
1992 notice_lang(s_ChanServ, u, CHAN_AKICK_COUNT, ci->name, ci->akick_count);
1993
1994 }
1995 else
1996 {
1997 syntax_error(s_ChanServ, u, "AKICK", CHAN_AKICK_SYNTAX);
1998 }
1999
2000 put_channelinfo(ci);
2001 }
2002
2003 /************************************/
2004
2005 static int
2006 list_akick(User * u, int index, ChannelInfo * ci, int *sent_header,
2007 int is_view)
2008 {
2009 AutoKick *akick = &ci->akick[index];
2010 char buf[BUFSIZE];
2011
2012 if (!akick->mask)
2013 return 0;
2014 if (!*sent_header)
2015 {
2016 notice_lang(s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name);
2017 *sent_header = 1;
2018 }
2019 if (akick->reason)
2020 snprintf(buf, sizeof(buf), " (%s)", akick->reason);
2021 else
2022 *buf = 0;
2023 if (is_view)
2024 {
2025 char setbuf[BUFSIZE], usedbuf[BUFSIZE];
2026 strftime_lang(setbuf, sizeof(setbuf), u->ngi,
2027 STRFTIME_DATE_TIME_FORMAT, akick->set);
2028 if (akick->lastused)
2029 {
2030 strftime_lang(usedbuf, sizeof(usedbuf), u->ngi,
2031 STRFTIME_DATE_TIME_FORMAT, akick->lastused);
2032 notice_lang(s_ChanServ, u, CHAN_AKICK_VIEW_FORMAT,
2033 akick->mask, akick->who[0] ? akick->who : "<unknown>",
2034 setbuf, usedbuf, buf);
2035 }
2036 else
2037 {
2038 notice_lang(s_ChanServ, u, CHAN_AKICK_VIEW_UNUSED_FORMAT,
2039 akick->mask, akick->who[0] ? akick->who : "<unknown>",
2040 setbuf, buf);
2041 }
2042 }
2043 else
2044 {
2045 notice(s_ChanServ, u->nick, " %s%s", akick->mask, buf);
2046 }
2047 return 1;
2048 }
2049
2050 /*************************************************************************/
2051
2052 /* Internal routine to handle all op/voice-type requests. */
2053
2054 static struct
2055 {
2056 const char *cmd;
2057 int add;
2058 char mode;
2059 int target_acc; /* Target access (CA_*) at which we refuse command */
2060 int success_msg, already_msg, failure_msg;
2061 } opvoice_data[] =
2062 {
2063 {
2064 "VOICE", 1, 'v', -1,
2065 CHAN_VOICE_SUCCEEDED, CHAN_VOICE_ALREADY, CHAN_VOICE_FAILED},
2066 {
2067 "HALFOP", 1, 'h', CA_AUTODEOP,
2068 CHAN_HALFOP_SUCCEEDED, CHAN_HALFOP_ALREADY, CHAN_HALFOP_FAILED},
2069 {
2070 "OP", 1, 'o', CA_AUTODEOP,
2071 CHAN_OP_SUCCEEDED, CHAN_OP_ALREADY, CHAN_OP_FAILED},
2072 {
2073 "PROTECT", 1, 'a', CA_AUTODEOP,
2074 CHAN_PROTECT_SUCCEEDED, CHAN_PROTECT_ALREADY, CHAN_PROTECT_FAILED},
2075 {
2076 "DEVOICE", 0, 'v', CA_AUTOVOICE,
2077 CHAN_DEVOICE_SUCCEEDED, CHAN_DEVOICE_ALREADY, CHAN_DEVOICE_FAILED},
2078 {
2079 "DEHALFOP", 0, 'h', CA_AUTOHALFOP,
2080 CHAN_DEHALFOP_SUCCEEDED, CHAN_DEHALFOP_ALREADY, CHAN_DEHALFOP_FAILED},
2081 {
2082 "DEOP", 0, 'o', CA_AUTOOP,
2083 CHAN_DEOP_SUCCEEDED, CHAN_DEOP_ALREADY, CHAN_DEOP_FAILED},
2084 {
2085 "DEPROTECT", 0, 'a', CA_AUTOPROTECT,
2086 CHAN_DEPROTECT_SUCCEEDED, CHAN_DEPROTECT_ALREADY,
2087 CHAN_DEPROTECT_FAILED},};
2088
2089 static void
2090 do_opvoice(User * u, const char *cmd)
2091 {
2092 const char *cmd2 = (strnicmp(cmd, "DE", 2) == 0 ? cmd + 2 : cmd);
2093 char *chan = strtok(NULL, " ");
2094 char *target = strtok(NULL, " ");
2095 Channel *c;
2096 ChannelInfo *ci;
2097 User *target_user;
2098 int i;
2099 int add, target_acc, success_msg, failure_msg, already_msg;
2100 int target_nextacc; /* Target level upper bound for DEVOICE, DEHALFOP */
2101 int32 mode;
2102
2103 ARRAY2_SEARCH(opvoice_data, lenof(opvoice_data), cmd, cmd, strcmp, i);
2104 if (i >= lenof(opvoice_data))
2105 {
2106 module_log("do_opvoice: BUG: command `%s' not found in table", cmd);
2107 notice_lang(s_ChanServ, u, INTERNAL_ERROR);
2108 return;
2109 }
2110 add = opvoice_data[i].add;
2111 mode = mode_char_to_flag(opvoice_data[i].mode, MODE_CHANUSER);
2112 target_acc = opvoice_data[i].target_acc;
2113 success_msg = opvoice_data[i].success_msg;
2114 already_msg = opvoice_data[i].already_msg;
2115 failure_msg = opvoice_data[i].failure_msg;
2116 if (strcmp(cmd, "DEVOICE") == 0)
2117 target_nextacc = (protocol_features & PF_HALFOP)
2118 ? CA_AUTOHALFOP : CA_AUTOOP;
2119 else if (strcmp(cmd, "DEHALFOP") == 0)
2120 target_nextacc = CA_AUTOOP;
2121 else
2122 target_nextacc = -1;
2123
2124 do
2125 {
2126 if (target)
2127 {
2128 target_user = get_user(target);
2129 }
2130 else
2131 {
2132 target = u->nick;
2133 target_user = u;
2134 }
2135 if (!chan)
2136 {
2137 syntax_error(s_ChanServ, u, cmd, CHAN_OPVOICE_SYNTAX);
2138 }
2139 else if (!(c = get_channel(chan)))
2140 {
2141 notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
2142 }
2143 else if (c->bouncy_modes)
2144 {
2145 notice_lang(s_ChanServ, u, CHAN_BOUNCY_MODES, cmd);
2146 }
2147 else if (!(ci = c->ci))
2148 {
2149 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2150 }
2151 else if (ci->flags & CF_VERBOTEN)
2152 {
2153 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2154 }
2155 else if (!u || !check_access_cmd(u, ci, cmd2, NULL))
2156 {
2157 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
2158 }
2159 else if (!target_user)
2160 {
2161 notice_lang(s_ChanServ, u, NICK_X_NOT_IN_USE, target);
2162 }
2163 else
2164 {
2165 struct c_userlist *cu;
2166 LIST_SEARCH_SCALAR(c->users, user, target_user, cu);
2167 if (!cu)
2168 {
2169 notice_lang(s_ChanServ, u, NICK_X_NOT_ON_CHAN_X, target, chan);
2170 return;
2171 }
2172 else if (
2173 /* Allow changing own mode */
2174 !(target_user == u)
2175 /* Allow deops if !ENFORCE */
2176 && !(!add && !(ci->flags & CF_ENFORCE))
2177 /* Disallow if user is at/above disallow level... */
2178 && target_acc >= 0
2179 && check_access(target_user, ci, target_acc)
2180 /* ... and below level-above-disallow-level (if any) */
2181 && (target_nextacc < 0
2182 || !check_access(target_user, ci, target_nextacc)))
2183 {
2184 notice_lang(s_ChanServ, u, failure_msg, target, chan);
2185 }
2186 else
2187 {
2188 char modebuf[3];
2189 int32 umode, thismode;
2190
2191 /* Check whether they already have / don't have the mode */
2192 umode = cu->mode & mode;
2193 if (add)
2194 umode ^= mode; /* note which ones they DON'T have */
2195 if (!umode)
2196 {
2197 /* Target user already has (or doesn't have, if !add)
2198 * mode(s), so don't do anything */
2199 notice_lang(s_ChanServ, u, already_msg, target, chan);
2200 return;
2201 }
2202
2203 /* Set appropriate mode(s) */
2204 modebuf[0] = add ? '+' : '-';
2205 modebuf[2] = 0;
2206 thismode = 1;
2207 while (umode)
2208 {
2209 while (!(umode & thismode))
2210 thismode <<= 1;
2211 if (!thismode)
2212 { /* impossible */
2213 module_log("BUG: thismode==0 in opvoice!");
2214 break;
2215 }
2216 modebuf[1] = mode_flag_to_char(thismode, MODE_CHANUSER);
2217 set_cmode(s_ChanServ, c, modebuf, target);
2218 umode &= ~thismode;
2219 }
2220 set_cmode(NULL, c); /* Flush mode change(s) out */
2221 if (ci->flags & CF_OPNOTICE)
2222 {
2223 notice(s_ChanServ, chan, "%s command used for %s by %s",
2224 cmd, target, u->nick);
2225 }
2226 notice_lang(s_ChanServ, u, success_msg, target, chan);
2227 /* If it was an OP command, update the last-used time */
2228 if (strcmp(cmd, "OP") == 0)
2229 ci->last_used = time(NULL);
2230 } /* if allowed */
2231 }
2232 }
2233 while ((target = strtok(NULL, " ")) != NULL);
2234 }
2235
2236 static void
2237 do_op(User * u)
2238 {
2239 do_opvoice(u, "OP");
2240 }
2241
2242 static void
2243 do_deop(User * u)
2244 {
2245 do_opvoice(u, "DEOP");
2246 }
2247
2248 static void
2249 do_voice(User * u)
2250 {
2251 do_opvoice(u, "VOICE");
2252 }
2253
2254 static void
2255 do_devoice(User * u)
2256 {
2257 do_opvoice(u, "DEVOICE");
2258 }
2259
2260 static void
2261 do_halfop(User * u)
2262 {
2263 do_opvoice(u, "HALFOP");
2264 }
2265
2266 static void
2267 do_dehalfop(User * u)
2268 {
2269 do_opvoice(u, "DEHALFOP");
2270 }
2271
2272 static void
2273 do_protect(User * u)
2274 {
2275 do_opvoice(u, "PROTECT");
2276 }
2277
2278 static void
2279 do_deprotect(User * u)
2280 {
2281 do_opvoice(u, "DEPROTECT");
2282 }
2283
2284 /*************************************************************************/
2285
2286 static void
2287 do_invite(User * u)
2288 {
2289 char *chan = strtok(NULL, " ");
2290 Channel *c;
2291 ChannelInfo *ci;
2292
2293 if (!chan)
2294 {
2295 syntax_error(s_ChanServ, u, "INVITE", CHAN_INVITE_SYNTAX);
2296 }
2297 else if (!(c = get_channel(chan)))
2298 {
2299 notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
2300 }
2301 else if (c->bouncy_modes)
2302 {
2303 notice_lang(s_ChanServ, u, CHAN_BOUNCY_MODES, "INVITE");
2304 }
2305 else if (!(ci = c->ci))
2306 {
2307 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2308 }
2309 else if (ci->flags & CF_VERBOTEN)
2310 {
2311 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2312 }
2313 else if (!u || !check_access_cmd(u, ci, "INVITE", NULL))
2314 {
2315 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
2316 }
2317 else if (call_callback_3(cb_invite, u, c, ci) <= 0)
2318 {
2319 send_cmd(s_ChanServ, "INVITE %s %s", u->nick, chan);
2320 notice_lang(s_ChanServ, u, CHAN_INVITE_OK, u->nick, chan);
2321 }
2322 }
2323
2324 /*************************************************************************/
2325
2326 static void
2327 do_unban(User * u)
2328 {
2329 char *chan = strtok(NULL, " ");
2330 Channel *c;
2331 ChannelInfo *ci;
2332
2333 if (!chan)
2334 {
2335 syntax_error(s_ChanServ, u, "UNBAN", CHAN_UNBAN_SYNTAX);
2336 }
2337 else if (!(c = get_channel(chan)))
2338 {
2339 notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
2340 }
2341 else if (c->bouncy_modes)
2342 {
2343 notice_lang(s_ChanServ, u, CHAN_BOUNCY_MODES, "UNBAN");
2344 }
2345 else if (!(ci = c->ci))
2346 {
2347 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2348 }
2349 else if (ci->flags & CF_VERBOTEN)
2350 {
2351 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2352 }
2353 else if (!u || !check_access_cmd(u, ci, "UNBAN", NULL))
2354 {
2355 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
2356 }
2357 else if (call_callback_3(cb_unban, u, c, ci) <= 0)
2358 {
2359 clear_channel(c, CLEAR_BANS, u);
2360 notice_lang(s_ChanServ, u, CHAN_UNBANNED, chan);
2361 }
2362 }
2363
2364 /*************************************************************************/
2365
2366 /* do_kick() is used by users.c, so we use a different function name */
2367
2368 static void
2369 do_cskick(User * u)
2370 {
2371 char *chan = strtok(NULL, " ");
2372 char *target = strtok(NULL, " ");
2373 char *reason = strtok_remaining();
2374 Channel *c;
2375 ChannelInfo *ci;
2376 User *target_user;
2377
2378 if (!chan || !target)
2379 {
2380 syntax_error(s_ChanServ, u, "KICK", CHAN_KICK_SYNTAX);
2381 }
2382 else if (!(c = get_channel(chan)))
2383 {
2384 notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
2385 }
2386 else if (c->bouncy_modes)
2387 {
2388 notice_lang(s_ChanServ, u, CHAN_BOUNCY_MODES, "KICK");
2389 }
2390 else if (!(ci = c->ci))
2391 {
2392 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2393 }
2394 else if (ci->flags & CF_VERBOTEN)
2395 {
2396 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2397 }
2398 else if (!u || !check_access_cmd(u, ci, "KICK", NULL))
2399 {
2400 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
2401 }
2402 else if (!(target_user = get_user(target)))
2403 {
2404 notice_lang(s_ChanServ, u, NICK_X_NOT_IN_USE, target);
2405 }
2406 else
2407 {
2408 struct c_userlist *cu;
2409 char reasonbuf[BUFSIZE];
2410 char *kick_av[3];
2411
2412 /* Retrieve c_userlist entry and see (1) if they're even on the
2413 * channel and (2) if they're protected (if the ircd supports that) */
2414 LIST_SEARCH_SCALAR(c->users, user, target_user, cu);
2415 if (!cu)
2416 {
2417 notice_lang(s_ChanServ, u, NICK_X_NOT_ON_CHAN_X, target, chan);
2418 return;
2419 }
2420 if (protocol_features & PF_CHANPROT)
2421 {
2422 if (cu->mode & mode_char_to_flag('a', MODE_CHANUSER))
2423 {
2424 notice_lang(s_ChanServ, u, CHAN_KICK_PROTECTED, target, chan);
2425 return;
2426 }
2427 }
2428 /* Also prevent Services opers and above from being kicked */
2429 if (is_services_oper(target_user))
2430 {
2431 notice_lang(s_ChanServ, u, CHAN_KICK_PROTECTED, target, chan);
2432 return;
2433 }
2434
2435 /* Construct reason string: "KICK by Nick" / "KICK by Nick (reason)" */
2436 if (reason && !*reason)
2437 reason = NULL;
2438 snprintf(reasonbuf, sizeof(reasonbuf), "KICK by %s%s%s%s", u->nick,
2439 reason ? " (" : "", reason ? reason : "", reason ? ")" : "");
2440
2441 /* Actually kick user */
2442 send_cmd(s_ChanServ, "KICK %s %s :%s", chan, target, reasonbuf);
2443 kick_av[0] = chan;
2444 kick_av[1] = target;
2445 kick_av[2] = reasonbuf;
2446 do_kick(s_ChanServ, 3, kick_av);
2447 notice_lang(s_ChanServ, u, CHAN_KICKED, target, chan);
2448 }
2449 }
2450
2451 /*************************************************************************/
2452
2453 static void
2454 do_cstopic(User * u)
2455 {
2456 char *chan = strtok(NULL, " ");
2457 char *topic = strtok_remaining();
2458 Channel *c;
2459 ChannelInfo *ci;
2460
2461 if (!chan || !topic)
2462 {
2463 syntax_error(s_ChanServ, u, "TOPIC", CHAN_TOPIC_SYNTAX);
2464 }
2465 else if (!(c = get_channel(chan)))
2466 {
2467 notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
2468 }
2469 else if (c->bouncy_modes)
2470 {
2471 notice_lang(s_ChanServ, u, CHAN_BOUNCY_MODES, "TOPIC");
2472 }
2473 else if (!(ci = c->ci))
2474 {
2475 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2476 }
2477 else if (ci->flags & CF_VERBOTEN)
2478 {
2479 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2480 }
2481 else if (!u || !check_access_cmd(u, ci, "TOPIC", NULL))
2482 {
2483 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
2484 }
2485 else
2486 {
2487 time_t now = time(NULL);
2488 set_topic(s_ChanServ, c, topic, u->nick, now);
2489 record_topic(ci, topic, u->nick, now);
2490 }
2491 }
2492
2493 /*************************************************************************/
2494
2495 static void
2496 do_clear(User * u)
2497 {
2498 char *chan = strtok(NULL, " ");
2499 char *what = strtok(NULL, " ");
2500 Channel *c;
2501 ChannelInfo *ci;
2502
2503 if (!chan || !what)
2504 {
2505 syntax_error(s_ChanServ, u, "CLEAR", CHAN_CLEAR_SYNTAX);
2506 }
2507 else if (!(c = get_channel(chan)))
2508 {
2509 notice_lang(s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
2510 }
2511 else if (c->bouncy_modes)
2512 {
2513 notice_lang(s_ChanServ, u, CHAN_BOUNCY_MODES, "CLEAR");
2514 }
2515 else if (!(ci = c->ci))
2516 {
2517 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2518 }
2519 else if (ci->flags & CF_VERBOTEN)
2520 {
2521 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2522 }
2523 else if (!u || !check_access_cmd(u, ci, "CLEAR", what))
2524 {
2525 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
2526 }
2527 else if (call_callback_3(cb_clear, u, c, what) > 0)
2528 {
2529 return;
2530 }
2531 else if (stricmp(what, "BANS") == 0)
2532 {
2533 clear_channel(c, CLEAR_BANS, NULL);
2534 notice_lang(s_ChanServ, u, CHAN_CLEARED_BANS, chan);
2535 }
2536 else if (stricmp(what, "MODES") == 0)
2537 {
2538 clear_channel(c, CLEAR_MODES, NULL);
2539 notice_lang(s_ChanServ, u, CHAN_CLEARED_MODES, chan);
2540 }
2541 else if (stricmp(what, "OPS") == 0)
2542 {
2543 clear_channel(c, CLEAR_UMODES, (void *) CUMODE_o);
2544 notice_lang(s_ChanServ, u, CHAN_CLEARED_OPS, chan);
2545 }
2546 else if (stricmp(what, "VOICES") == 0)
2547 {
2548 clear_channel(c, CLEAR_UMODES, (void *) CUMODE_v);
2549 notice_lang(s_ChanServ, u, CHAN_CLEARED_VOICES, chan);
2550 }
2551 else if (stricmp(what, "USERS") == 0)
2552 {
2553 char buf[BUFSIZE];
2554 snprintf(buf, sizeof(buf), "CLEAR USERS command from %s", u->nick);
2555 clear_channel(c, CLEAR_USERS, buf);
2556 notice_lang(s_ChanServ, u, CHAN_CLEARED_USERS, chan);
2557 }
2558 else
2559 {
2560 syntax_error(s_ChanServ, u, "CLEAR", CHAN_CLEAR_SYNTAX);
2561 }
2562 }
2563
2564 /*************************************************************************/
2565
2566 static void
2567 do_status(User * u)
2568 {
2569 ChannelInfo *ci;
2570 User *u2;
2571 char *nick, *chan;
2572
2573 chan = strtok(NULL, " ");
2574 nick = strtok(NULL, " ");
2575 if (!chan || !nick || strtok(NULL, " "))
2576 {
2577 notice(s_ChanServ, u->nick, "STATUS ? ? ERROR Syntax error");
2578 return;
2579 }
2580 if (!(ci = get_channelinfo(chan)))
2581 {
2582 char *temp = chan;
2583 chan = nick;
2584 nick = temp;
2585 ci = get_channelinfo(chan);
2586 }
2587 if (!ci)
2588 {
2589 notice(s_ChanServ, u->nick, "STATUS %s %s ERROR Channel not"
2590 " registered", chan, nick);
2591 }
2592 else if (ci->flags & CF_VERBOTEN)
2593 {
2594 notice(s_ChanServ, u->nick, "STATUS %s %s ERROR Channel forbidden",
2595 chan, nick);
2596 }
2597 else if (!is_services_admin(u) && !check_access_cmd(u, ci, "STATUS", NULL))
2598 {
2599 notice(s_ChanServ, u->nick, "STATUS %s %s ERROR Permission denied",
2600 chan, nick);
2601 }
2602 else if ((u2 = get_user(nick)) != NULL)
2603 {
2604 int acc = get_access(u2, ci);
2605 int have_acclev = (find_module("chanserv/access-levels") != NULL);
2606 int have_accxop = (find_module("chanserv/access-xop") != NULL);
2607 char accbuf[BUFSIZE];
2608
2609 if (have_accxop)
2610 {
2611 const char *xop;
2612 if (acc == ACCLEV_FOUNDER)
2613 xop = "Founder";
2614 else if (acc >= ACCLEV_SOP)
2615 xop = "SOP";
2616 else if (acc >= ACCLEV_AOP)
2617 xop = "AOP";
2618 else if (acc >= ACCLEV_HOP && (protocol_features & PF_HALFOP))
2619 xop = "HOP";
2620 else if (acc >= ACCLEV_VOP)
2621 xop = "VOP";
2622 else
2623 xop = "---";
2624 if (have_acclev)
2625 snprintf(accbuf, sizeof(accbuf), "%d (%s)", acc, xop);
2626 else
2627 snprintf(accbuf, sizeof(accbuf), "%s", xop);
2628 }
2629 else
2630 { /* access-levels only, or none */
2631 snprintf(accbuf, sizeof(accbuf), "%d", acc);
2632 }
2633 notice(s_ChanServ, u->nick, "STATUS %s %s %s", chan, nick, accbuf);
2634 }
2635 else
2636 { /* !u2 */
2637 notice(s_ChanServ, u->nick, "STATUS %s %s ERROR Nick not online",
2638 chan, nick);
2639 }
2640 put_channelinfo(ci);
2641 }
2642
2643 /*************************************************************************/
2644
2645 /* Assumes that permission checking has already been done. */
2646
2647 static void
2648 do_getpass(User * u)
2649 {
2650 char *chan = strtok(NULL, " ");
2651 char pass[PASSMAX];
2652 ChannelInfo *ci = NULL;
2653 int i;
2654
2655 if (!chan)
2656 {
2657 syntax_error(s_ChanServ, u, "GETPASS", CHAN_GETPASS_SYNTAX);
2658 }
2659 else if (!(ci = get_channelinfo(chan)))
2660 {
2661 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2662 }
2663 else if (ci->flags & CF_VERBOTEN)
2664 {
2665 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2666 }
2667 else if ((i = decrypt_password(&ci->founderpass, pass, PASSMAX)) == -2)
2668 {
2669 notice_lang(s_ChanServ, u, CHAN_GETPASS_UNAVAILABLE, ci->name);
2670 }
2671 else if (i != 0)
2672 {
2673 module_log("decrypt_password() failed for GETPASS on %s", ci->name);
2674 notice_lang(s_ChanServ, u, INTERNAL_ERROR);
2675 }
2676 else
2677 {
2678 module_log("%s!%s@%s used GETPASS on %s",
2679 u->nick, u->username, u->host, ci->name);
2680 if (WallAdminPrivs)
2681 {
2682 wallops(s_ChanServ, "\2%s\2 used GETPASS on \2%s\2", u->nick, ci->name);
2683 }
2684 notice_lang(s_ChanServ, u, CHAN_GETPASS_PASSWORD_IS, ci->name, pass);
2685 }
2686 put_channelinfo(ci);
2687 }
2688
2689 /*************************************************************************/
2690
2691 static void
2692 do_forbid(User * u)
2693 {
2694 ChannelInfo *ci;
2695 char *chan = strtok(NULL, " ");
2696
2697 /* Assumes that permission checking has already been done. */
2698 if (!chan || *chan != '#')
2699 {
2700 syntax_error(s_ChanServ, u, "FORBID", CHAN_FORBID_SYNTAX);
2701 return;
2702 }
2703 else if (strcmp(chan, "#") == 0)
2704 {
2705 notice_lang(s_ChanServ, u, CHAN_FORBID_SHORT_CHANNEL);
2706 return;
2707 }
2708 if (readonly)
2709 notice_lang(s_ChanServ, u, READ_ONLY_MODE);
2710 if ((ci = get_channelinfo(chan)) != NULL)
2711 delchan(ci);
2712 ci = makechan(chan);
2713 if (ci)
2714 {
2715 Channel *c;
2716 module_log("%s!%s@%s set FORBID for channel %s",
2717 u->nick, u->username, u->host, ci->name);
2718 ci->flags |= CF_VERBOTEN;
2719 ci->time_registered = time(NULL);
2720 notice_lang(s_ChanServ, u, CHAN_FORBID_SUCCEEDED, chan);
2721 if (WallAdminPrivs)
2722 {
2723 wallops(s_ChanServ, "\2%s\2 used FORBID on \2%s\2", u->nick, ci->name);
2724 }
2725 c = get_channel(chan);
2726 if (c)
2727 {
2728 c->ci = ci;
2729 ci->c = c;
2730 clear_channel(c, CLEAR_USERS, "Use of this channel has been forbidden");
2731 }
2732 else
2733 {
2734 put_channelinfo(ci);
2735 }
2736 }
2737 else
2738 {
2739 module_log("Valid FORBID for %s by %s!%s@%s failed",
2740 ci->name, u->nick, u->username, u->host);
2741 notice_lang(s_ChanServ, u, CHAN_FORBID_FAILED, chan);
2742 }
2743 }
2744
2745 /*************************************************************************/
2746
2747 static void
2748 do_suspend(User * u)
2749 {
2750 ChannelInfo *ci = NULL;
2751 char *expiry, *chan, *reason;
2752 time_t expires;
2753
2754 chan = strtok(NULL, " ");
2755 if (chan && *chan == '+')
2756 {
2757 expiry = chan + 1;
2758 chan = strtok(NULL, " ");
2759 }
2760 else
2761 {
2762 expiry = NULL;
2763 }
2764 reason = strtok_remaining();
2765
2766 if (!chan || !reason)
2767 {
2768 syntax_error(s_ChanServ, u, "SUSPEND", CHAN_SUSPEND_SYNTAX);
2769 }
2770 else if (!(ci = get_channelinfo(chan)))
2771 {
2772 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2773 }
2774 else if (ci->flags & CF_VERBOTEN)
2775 {
2776 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2777 }
2778 else if (ci->flags & CF_SUSPENDED)
2779 {
2780 notice_lang(s_ChanServ, u, CHAN_SUSPEND_ALREADY_SUSPENDED, chan);
2781 }
2782 else
2783 {
2784 Channel *c;
2785 if (expiry)
2786 expires = dotime(expiry);
2787 else
2788 expires = CSSuspendExpire;
2789 if (expires < 0)
2790 {
2791 notice_lang(s_ChanServ, u, BAD_EXPIRY_TIME);
2792 return;
2793 }
2794 else if (expires > 0)
2795 {
2796 expires += time(NULL); /* Set an absolute time */
2797 }
2798 module_log("%s!%s@%s suspended %s",
2799 u->nick, u->username, u->host, ci->name);
2800 suspend_channel(ci, reason, u->nick, expires);
2801 notice_lang(s_ChanServ, u, CHAN_SUSPEND_SUCCEEDED, chan);
2802 c = get_channel(chan);
2803 if (c)
2804 clear_channel(c, CLEAR_USERS, "Use of this channel has been forbidden");
2805 if (readonly)
2806 notice_lang(s_ChanServ, u, READ_ONLY_MODE);
2807 if (WallAdminPrivs)
2808 {
2809 wallops(s_ChanServ, "\2%s\2 used SUSPEND on \2%s\2", u->nick, ci->name);
2810 }
2811 }
2812 put_channelinfo(ci);
2813 }
2814
2815 /*************************************************************************/
2816
2817 static void
2818 do_unsuspend(User * u)
2819 {
2820 ChannelInfo *ci = NULL;
2821 char *chan = strtok(NULL, " ");
2822
2823 if (!chan)
2824 {
2825 syntax_error(s_ChanServ, u, "UNSUSPEND", CHAN_UNSUSPEND_SYNTAX);
2826 }
2827 else if (!(ci = get_channelinfo(chan)))
2828 {
2829 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
2830 }
2831 else if (ci->flags & CF_VERBOTEN)
2832 {
2833 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
2834 }
2835 else if (!(ci->flags & CF_SUSPENDED))
2836 {
2837 notice_lang(s_ChanServ, u, CHAN_UNSUSPEND_NOT_SUSPENDED, chan);
2838 }
2839 else
2840 {
2841 if (readonly)
2842 notice_lang(s_ChanServ, u, READ_ONLY_MODE);
2843 module_log("%s!%s@%s unsuspended %s",
2844 u->nick, u->username, u->host, ci->name);
2845 unsuspend_channel(ci, 1);
2846 notice_lang(s_ChanServ, u, CHAN_UNSUSPEND_SUCCEEDED, chan);
2847 if (WallAdminPrivs)
2848 {
2849 wallops(s_ChanServ, "\2%s\2 used UNSUSPEND on \2%s\2",
2850 u->nick, ci->name);
2851 }
2852 }
2853 put_channelinfo(ci);
2854 }
2855
2856 /*************************************************************************/
2857 /***************************** Module stuff ******************************/
2858 /*************************************************************************/
2859
2860 static int CSDefKeepTopic;
2861 static int CSDefSecureOps;
2862 static int CSDefPrivate;
2863 static int CSDefTopicLock;
2864 static int CSDefLeaveOps;
2865 static int CSDefSecure;
2866 static int CSDefOpNotice;
2867 static int CSDefEnforce;
2868 static int CSDefMemoRestricted;
2869 static int CSDefHideEmail;
2870 static int CSDefHideTopic;
2871 static int CSDefHideMlock;
2872
2873 static int do_CSAlias(const char *filename, int linenum, char *param);
2874 static int do_CSDefModeLock(const char *filename, int linenum, char *param);
2875
2876 ConfigDirective module_config[] = {
2877 {"ChanServName", {{CD_STRING, CF_DIRREQ, &s_ChanServ},
2878 {CD_STRING, 0, &desc_ChanServ}}},
2879 {"CSAccessMax", {{CD_POSINT, CF_DIRREQ, &CSAccessMax}}},
2880 {"CSAlias", {{CD_FUNC, 0, do_CSAlias}}},
2881 {"CSAutokickMax", {{CD_POSINT, CF_DIRREQ, &CSAutokickMax}}},
2882 {"CSDefEnforce", {{CD_SET, 0, &CSDefEnforce}}},
2883 {"CSDefHideEmail", {{CD_SET, 0, &CSDefHideEmail}}},
2884 {"CSDefHideMlock", {{CD_SET, 0, &CSDefHideMlock}}},
2885 {"CSDefHideTopic", {{CD_SET, 0, &CSDefHideTopic}}},
2886 {"CSDefKeepTopic", {{CD_SET, 0, &CSDefKeepTopic}}},
2887 {"CSDefLeaveOps", {{CD_SET, 0, &CSDefLeaveOps}}},
2888 {"CSDefMemoRestricted", {{CD_SET, 0, &CSDefMemoRestricted}}},
2889 {"CSDefModeLock", {{CD_FUNC, 0, do_CSDefModeLock}}},
2890 {"CSDefOpNotice", {{CD_SET, 0, &CSDefOpNotice}}},
2891 {"CSDefPrivate", {{CD_SET, 0, &CSDefPrivate}}},
2892 {"CSDefSecure", {{CD_SET, 0, &CSDefSecure}}},
2893 {"CSDefSecureOps", {{CD_SET, 0, &CSDefSecureOps}}},
2894 {"CSDefTopicLock", {{CD_SET, 0, &CSDefTopicLock}}},
2895 {"CSEnableRegister", {{CD_SET, 0, &CSEnableRegister}}},
2896 {"CSExpire", {{CD_TIME, 0, &CSExpire}}},
2897 {"CSForbidShortChannel", {{CD_SET, 0, &CSForbidShortChannel}}},
2898 {"CSInhabit", {{CD_TIME, CF_DIRREQ, &CSInhabit}}},
2899 {"CSListOpersOnly", {{CD_SET, 0, &CSListOpersOnly}}},
2900 {"CSMaxReg", {{CD_POSINT, 0, &CSMaxReg}}},
2901 {"CSRegisteredOnly", {{CD_SET, 0, &CSRegisteredOnly}}},
2902 {"CSRestrictDelay", {{CD_TIME, 0, &CSRestrictDelay}}},
2903 {"CSShowPassword", {{CD_SET, 0, &CSShowPassword}}},
2904 {"CSSkipModeRCheck", {{CD_SET, 0, &CSSkipModeRCheck}}},
2905 {"CSSuspendExpire", {{CD_TIME, 0, &CSSuspendExpire},
2906 {CD_TIME, 0, &CSSuspendGrace}}},
2907 {NULL}
2908 };
2909
2910 /* Pointers to command records (for !CSEnableCommand) */
2911 static Command *cmd_REGISTER;
2912 static Command *cmd_GETPASS;
2913
2914 /* Previous value of clear_channel() sender */
2915 static char old_clearchan_sender[NICKMAX];
2916 static int old_clearchan_sender_set = 0;
2917
2918 /*************************************************************************/
2919
2920 /* CSAlias handling */
2921
2922 static int
2923 do_CSAlias(const char *filename, int linenum, char *param)
2924 {
2925 static Alias *new_aliases = NULL;
2926 static int new_aliases_count = 0;
2927 int i;
2928 char *s;
2929
2930 if (!filename)
2931 {
2932 switch (linenum)
2933 {
2934 case CDFUNC_INIT:
2935 /* Prepare for reading config file: clear out "new" array */
2936 ARRAY_FOREACH(i, new_aliases)
2937 {
2938 free(new_aliases[i].alias);
2939 free(new_aliases[i].command);
2940 }
2941 free(new_aliases);
2942 new_aliases = NULL;
2943 new_aliases_count = 0;
2944 break;
2945 case CDFUNC_SET:
2946 /* Copy data to config variables */
2947 ARRAY_FOREACH(i, aliases)
2948 {
2949 free(aliases[i].alias);
2950 free(aliases[i].command);
2951 }
2952 free(aliases);
2953 aliases = new_aliases;
2954 aliases_count = new_aliases_count;
2955 new_aliases = NULL;
2956 new_aliases_count = 0;
2957 break;
2958 case CDFUNC_DECONFIG:
2959 /* Clear out config variables */
2960 ARRAY_FOREACH(i, aliases)
2961 {
2962 free(aliases[i].alias);
2963 free(aliases[i].command);
2964 }
2965 free(aliases);
2966 aliases = NULL;
2967 aliases_count = 0;
2968 break;
2969 }
2970 return 1;
2971 } /* if (!filename) */
2972
2973 s = strchr(param, '=');
2974 if (!s)
2975 {
2976 config_error(filename, linenum, "Missing = in NSAlias parameter");
2977 return 0;
2978 }
2979 *s++ = 0;
2980 ARRAY_EXTEND(new_aliases);
2981 new_aliases[new_aliases_count - 1].alias = sstrdup(param);
2982 new_aliases[new_aliases_count - 1].command = sstrdup(s);
2983 return 1;
2984 }
2985
2986 /*************************************************************************/
2987
2988 static int
2989 do_CSDefModeLock(const char *filename, int linenum, char *param)
2990 {
2991 static int32 new_on = 0, new_off = 0;
2992
2993 if (!filename)
2994 {
2995 switch (linenum)
2996 {
2997 case CDFUNC_INIT:
2998 /* Prepare for reading config file */
2999 new_on = new_off = 0;
3000 break;
3001 case CDFUNC_SET:
3002 /* Copy data to config variables */
3003 CSDefModeLockOn = new_on;
3004 CSDefModeLockOff = new_off;
3005 break;
3006 case CDFUNC_DECONFIG:
3007 /* Reset to initial values */
3008 CSDefModeLockOn = CMODE_n | CMODE_t;
3009 CSDefModeLockOff = 0;
3010 break;
3011 } /* switch (linenum) */
3012 }
3013 else
3014 { /* filename != NULL, process parameter */
3015 int add = -1;
3016 while (*param)
3017 {
3018 if (*param == '+')
3019 {
3020 add = 1;
3021 }
3022 else if (*param == '-')
3023 {
3024 add = 0;
3025 }
3026 else if (add < 0)
3027 {
3028 config_error(filename, linenum, "Mode characters must be"
3029 " preceded by + or -");
3030 return 0;
3031 }
3032 else
3033 {
3034 int32 flag = mode_char_to_flag(*param, MODE_CHANNEL);
3035 int nparams = mode_char_to_params(*param, MODE_CHANNEL);
3036 if (!flag)
3037 {
3038 config_error(filename, linenum, "Invalid mode"
3039 " character `%c'", *param);
3040 return 0;
3041 }
3042 else if (nparams)
3043 {
3044 config_error(filename, linenum, "Modes with parameters"
3045 " cannot be used with CSDefModeLock");
3046 return 0;
3047 }
3048 else
3049 {
3050 if (add)
3051 new_on |= flag;
3052 else
3053 new_off |= flag;
3054 }
3055 }
3056 param++;
3057 }
3058 }
3059 return 1;
3060 }
3061
3062 /*************************************************************************/
3063
3064 static void
3065 handle_config(void)
3066 {
3067 CSDefFlags = 0;
3068 if (CSDefKeepTopic)
3069 CSDefFlags |= CF_KEEPTOPIC;
3070 if (CSDefSecureOps)
3071 CSDefFlags |= CF_SECUREOPS;
3072 if (CSDefPrivate)
3073 CSDefFlags |= CF_PRIVATE;
3074 if (CSDefTopicLock)
3075 CSDefFlags |= CF_TOPICLOCK;
3076 if (CSDefLeaveOps)
3077 CSDefFlags |= CF_LEAVEOPS;
3078 if (CSDefSecure)
3079 CSDefFlags |= CF_SECURE;
3080 if (CSDefOpNotice)
3081 CSDefFlags |= CF_OPNOTICE;
3082 if (CSDefEnforce)
3083 CSDefFlags |= CF_ENFORCE;
3084 if (CSDefMemoRestricted)
3085 CSDefFlags |= CF_MEMO_RESTRICTED;
3086 if (CSDefHideEmail)
3087 CSDefFlags |= CF_HIDE_EMAIL;
3088 if (CSDefHideTopic)
3089 CSDefFlags |= CF_HIDE_TOPIC;
3090 if (CSDefHideMlock)
3091 CSDefFlags |= CF_HIDE_MLOCK;
3092
3093 if (CSMaxReg > MAX_CHANNELCOUNT)
3094 {
3095 module_log("CSMaxReg upper-bounded at MAX_CHANNELCOUNT (%d)",
3096 MAX_CHANNELCOUNT);
3097 CSMaxReg = MAX_CHANNELCOUNT;
3098 }
3099 }
3100
3101 /*************************************************************************/
3102
3103 static int
3104 do_reconfigure(int after_configure)
3105 {
3106 static char old_s_ChanServ[NICKMAX];
3107 static char *old_desc_ChanServ = NULL;
3108 static int old_CSEnableRegister;
3109
3110 if (!after_configure)
3111 {
3112 /* Before reconfiguration: save old values. */
3113 strbcpy(old_s_ChanServ, s_ChanServ);
3114 old_desc_ChanServ = strdup(desc_ChanServ);
3115 old_CSEnableRegister = CSEnableRegister;
3116 }
3117 else
3118 {
3119 Command *cmd;
3120 /* After reconfiguration: handle value changes. */
3121 handle_config();
3122 if (strcmp(old_s_ChanServ, s_ChanServ) != 0)
3123 {
3124 if (strcmp(set_clear_channel_sender(PTR_INVALID), old_s_ChanServ) == 0)
3125 set_clear_channel_sender(s_ChanServ);
3126 send_nickchange(old_s_ChanServ, s_ChanServ);
3127 }
3128 if (!old_desc_ChanServ || strcmp(old_desc_ChanServ, desc_ChanServ) != 0)
3129 send_namechange(s_ChanServ, desc_ChanServ);
3130 free(old_desc_ChanServ);
3131 if (CSEnableRegister && !old_CSEnableRegister)
3132 {
3133 cmd_REGISTER->helpmsg_all = cmd_REGISTER->helpmsg_oper;
3134 cmd_REGISTER->helpmsg_oper = -1;
3135 cmd_REGISTER->has_priv = NULL;
3136 }
3137 else if (!CSEnableRegister && old_CSEnableRegister)
3138 {
3139 cmd_REGISTER->has_priv = is_services_admin;
3140 cmd_REGISTER->helpmsg_oper = cmd_REGISTER->helpmsg_all;
3141 cmd_REGISTER->helpmsg_all = -1;
3142 }
3143 if (EnableGetpass)
3144 cmd_GETPASS->name = "GETPASS";
3145 else
3146 cmd_GETPASS->name = "";
3147 /* Update command help parameters */
3148 cmd_REGISTER->help_param1 = s_NickServ;
3149 if ((cmd = lookup_cmd(THIS_MODULE, "SET SECURE")) != NULL)
3150 {
3151 cmd->help_param1 = s_NickServ;
3152 cmd->help_param2 = s_NickServ;
3153 }
3154 } /* if (!after_configure) */
3155 return 0;
3156 }
3157
3158 /*************************************************************************/
3159
3160 int
3161 init_module(void)
3162 {
3163 Command *cmd;
3164
3165
3166 handle_config();
3167
3168 module_operserv = find_module("operserv/main");
3169 if (!module_operserv)
3170 {
3171 module_log("OperServ main module not loaded");
3172 exit_module(0);
3173 return 0;
3174 }
3175 use_module(module_operserv);
3176
3177 module_nickserv = find_module("nickserv/main");
3178 if (!module_nickserv)
3179 {
3180 module_log("NickServ main module not loaded");
3181 exit_module(0);
3182 return 0;
3183 }
3184 use_module(module_nickserv);
3185
3186 if (!new_commandlist(THIS_MODULE) || !register_commands(THIS_MODULE, cmds)
3187 || ((protocol_features & PF_HALFOP)
3188 && !register_commands(THIS_MODULE, cmds_halfop))
3189 || ((protocol_features & PF_CHANPROT)
3190 && !register_commands(THIS_MODULE, cmds_chanprot)))
3191 {
3192 module_log("Unable to register commands");
3193 exit_module(0);
3194 return 0;
3195 }
3196
3197 cb_clear = register_callback("CLEAR");
3198 cb_command = register_callback("command");
3199 cb_help = register_callback("HELP");
3200 cb_help_cmds = register_callback("HELP COMMANDS");
3201 cb_invite = register_callback("INVITE");
3202 cb_unban = register_callback("UNBAN");
3203 if (cb_command < 0 || cb_clear < 0 || cb_help < 0 || cb_help_cmds < 0
3204 || cb_invite < 0 || cb_unban < 0)
3205 {
3206 module_log("Unable to register callbacks");
3207 exit_module(0);
3208 return 0;
3209 }
3210
3211 cmd_REGISTER = lookup_cmd(THIS_MODULE, "REGISTER");
3212 if (!cmd_REGISTER)
3213 {
3214 module_log("BUG: unable to find REGISTER command entry");
3215 exit_module(0);
3216 return 0;
3217 }
3218 cmd_REGISTER->help_param1 = s_NickServ;
3219 if (!CSEnableRegister)
3220 {
3221 cmd_REGISTER->has_priv = is_services_admin;
3222 cmd_REGISTER->helpmsg_oper = cmd_REGISTER->helpmsg_all;
3223 cmd_REGISTER->helpmsg_all = -1;
3224 }
3225 cmd_GETPASS = lookup_cmd(THIS_MODULE, "GETPASS");
3226 if (!cmd_GETPASS)
3227 {
3228 module_log("BUG: unable to find GETPASS command entry");
3229 exit_module(0);
3230 return 0;
3231 }
3232 if (!EnableGetpass)
3233 cmd_GETPASS->name = "";
3234 cmd = lookup_cmd(THIS_MODULE, "SET SECURE");
3235 if (cmd)
3236 {
3237 cmd->help_param1 = s_NickServ;
3238 cmd->help_param2 = s_NickServ;
3239 }
3240 cmd = lookup_cmd(THIS_MODULE, "SET SUCCESSOR");
3241 if (cmd)
3242 cmd->help_param1 = (char *) (long) CSMaxReg;
3243 cmd = lookup_cmd(THIS_MODULE, "SUSPEND");
3244 if (cmd)
3245 cmd->help_param1 = s_OperServ;
3246
3247 if (!add_callback(NULL, "reconfigure", do_reconfigure)
3248 || !add_callback(NULL, "introduce_user", introduce_chanserv)
3249 || !add_callback(NULL, "m_privmsg", chanserv)
3250 || !add_callback(NULL, "m_whois", chanserv_whois)
3251 || !add_callback(NULL, "channel create", do_channel_create)
3252 || !add_callback(NULL, "channel JOIN check", do_channel_join_check)
3253 || !add_callback(NULL, "channel JOIN", do_channel_join)
3254 || !add_callback(NULL, "channel PART", do_channel_part)
3255 || !add_callback(NULL, "channel delete", do_channel_delete)
3256 || !add_callback(NULL, "channel mode change", do_channel_mode_change)
3257 || !add_callback(NULL, "channel umode change", do_channel_umode_change)
3258 || !add_callback(NULL, "channel TOPIC", do_channel_topic)
3259 || !add_callback(module_operserv, "STATS ALL", do_stats_all)
3260 || !add_callback(module_nickserv, "REGISTER/LINK check",
3261 do_reglink_check)
3262 || !add_callback(module_nickserv, "identified", do_nick_identified)
3263 || !add_callback(module_nickserv, "nickgroup delete",
3264 do_nickgroup_delete))
3265 {
3266 module_log("Unable to add callbacks");
3267 exit_module(0);
3268 return 0;
3269 }
3270
3271 if (!register_dbtable(&chan_dbtable)
3272 || !register_dbtable(&chan_access_dbtable)
3273 || !register_dbtable(&chan_akick_dbtable))
3274 {
3275 module_log("Unable to register database tables");
3276 exit_module(0);
3277 return 0;
3278 }
3279
3280 if (!init_access() || !init_check() || !init_set())
3281 {
3282 exit_module(0);
3283 return 0;
3284 }
3285
3286 if (encrypt_all)
3287 {
3288 ChannelInfo *ci;
3289 int done = 0, already = 0, failed = 0;
3290 module_log("Re-encrypting passwords...");
3291 for (ci = first_channelinfo(); ci; ci = next_channelinfo())
3292 {
3293 if ((EncryptionType && ci->founderpass.cipher
3294 && strcmp(ci->founderpass.cipher, EncryptionType) == 0)
3295 || (!EncryptionType && !ci->founderpass.cipher))
3296 {
3297 already++;
3298 }
3299 else
3300 {
3301 char plainbuf[PASSMAX];
3302 Password newpass;
3303 int res;
3304
3305 init_password(&newpass);
3306 res = decrypt_password(&ci->founderpass, plainbuf, sizeof(plainbuf));
3307 if (res != 0)
3308 {
3309 failed++;
3310 }
3311 else
3312 {
3313 res = encrypt_password(plainbuf, strlen(plainbuf), &newpass);
3314 memset(plainbuf, 0, sizeof(plainbuf));
3315 if (res != 0)
3316 {
3317 failed++;
3318 }
3319 else
3320 {
3321 copy_password(&ci->founderpass, &newpass);
3322 clear_password(&newpass);
3323 done++;
3324 }
3325 }
3326 }
3327 }
3328 module_log("%d passwords re-encrypted, %d already encrypted, %d"
3329 " failed", done, already, failed);
3330 } /* if (encrypt_all) */
3331
3332 if (linked)
3333 introduce_chanserv(NULL);
3334
3335 strbcpy(old_clearchan_sender, set_clear_channel_sender(s_ChanServ));
3336 old_clearchan_sender_set = 1;
3337
3338 return 1;
3339 }
3340
3341 /*************************************************************************/
3342
3343 int
3344 exit_module(int shutdown_unused)
3345 {
3346 if (old_clearchan_sender_set)
3347 {
3348 set_clear_channel_sender(old_clearchan_sender);
3349 old_clearchan_sender_set = 0;
3350 }
3351
3352 if (linked)
3353 send_cmd(s_ChanServ, "QUIT :");
3354
3355 exit_set();
3356 exit_check();
3357 exit_access();
3358
3359 unregister_dbtable(&chan_akick_dbtable);
3360 unregister_dbtable(&chan_access_dbtable);
3361 unregister_dbtable(&chan_dbtable);
3362 clean_dbtables();
3363
3364 remove_callback(NULL, "channel TOPIC", do_channel_topic);
3365 remove_callback(NULL, "channel umode change", do_channel_umode_change);
3366 remove_callback(NULL, "channel mode change", do_channel_mode_change);
3367 remove_callback(NULL, "channel delete", do_channel_delete);
3368 remove_callback(NULL, "channel PART", do_channel_part);
3369 remove_callback(NULL, "channel JOIN", do_channel_join);
3370 remove_callback(NULL, "channel JOIN check", do_channel_join_check);
3371 remove_callback(NULL, "channel create", do_channel_create);
3372 remove_callback(NULL, "m_whois", chanserv_whois);
3373 remove_callback(NULL, "m_privmsg", chanserv);
3374 remove_callback(NULL, "introduce_user", introduce_chanserv);
3375 remove_callback(NULL, "reconfigure", do_reconfigure);
3376
3377 cmd_GETPASS->name = "GETPASS";
3378 if (!CSEnableRegister)
3379 {
3380 cmd_REGISTER->helpmsg_all = cmd_REGISTER->helpmsg_oper;
3381 cmd_REGISTER->helpmsg_oper = -1;
3382 cmd_REGISTER->has_priv = NULL;
3383 }
3384
3385 unregister_callback(cb_unban);
3386 unregister_callback(cb_invite);
3387 unregister_callback(cb_help_cmds);
3388 unregister_callback(cb_help);
3389 unregister_callback(cb_command);
3390 unregister_callback(cb_clear);
3391
3392 if (protocol_features & PF_CHANPROT)
3393 unregister_commands(THIS_MODULE, cmds_chanprot);
3394 if (protocol_features & PF_HALFOP)
3395 unregister_commands(THIS_MODULE, cmds_halfop);
3396 unregister_commands(THIS_MODULE, cmds);
3397 del_commandlist(THIS_MODULE);
3398
3399 if (module_nickserv)
3400 {
3401 remove_callback(module_nickserv, "nickgroup delete", do_nickgroup_delete);
3402 remove_callback(module_nickserv, "identified", do_nick_identified);
3403 remove_callback(module_nickserv, "REGISTER/LINK check", do_reglink_check);
3404 unuse_module(module_nickserv);
3405 module_nickserv = NULL;
3406 }
3407
3408 if (module_operserv)
3409 {
3410 remove_callback(module_operserv, "STATS ALL", do_stats_all);
3411 unuse_module(module_operserv);
3412 module_operserv = NULL;
3413 }
3414
3415 return 1;
3416 }
3417
3418 /*************************************************************************/
3419
3420 /*
3421 * Local variables:
3422 * c-file-style: "stroustrup"
3423 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
3424 * indent-tabs-mode: nil
3425 * End:
3426 *
3427 * vim: expandtab shiftwidth=4:
3428 */