1 # This file is part of SurrealServices.
3 # SurrealServices is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # SurrealServices is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with SurrealServices; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 use SrSv
::Timer
qw(add_timer);
22 use SrSv
::Message
qw(current_message);
23 use SrSv
::IRCd
::State
qw($ircline synced initial_synced %IRCd_capabilities);
24 use SrSv
::Message
qw(message current_message);
25 use SrSv
::HostMask
qw(normalize_hostmask make_hostmask parse_mask);
27 #FIXME: This needs to be abstracted into a proper SrSv::IRCd module
28 use SrSv
::Unreal
::Modes
qw(@opmodes %opmodes $scm $ocm $acm sanitize_mlockable);
29 use SrSv
::IRCd
::Validate
qw( valid_nick validate_chmodes validate_ban );
32 use SrSv
::Shared
qw(%enforcers $chanuser_table);
34 #use SrSv::Conf qw(services);
35 use SrSv
::Conf2Consts
qw( services sql main );
38 use SrSv
::Text
::Format
qw( columnar enum );
44 get_user_nick get_user_agent get_user_id
45 is_online :user_flags get_host get_vhost
48 use SrSv
::User
::Notice
;
49 use SrSv
::Help
qw( sendhelp );
51 use SrSv
::ChanReg
::Flags
;
53 use SrSv
::NickReg
::Flags
;
54 use SrSv
::NickReg
::NickText
;
55 use SrSv
::NickReg
::User
qw(is_identified get_nick_users get_nick_user_nicks);
57 use SrSv
::MySQL
qw( $dbh :sql_types );
58 use SrSv
::MySQL
::Glob
;
60 use SrSv
::Util
qw( makeSeqList );
71 # Maybe this should be a config option
72 DEFAULT_BANTYPE
=> 10,
78 *get_root_nick
= \
&nickserv
::get_root_nick
;
80 our @levels = ("no", "UOp", "VOp", "HOp", "AOp", "SOp", "co-founder", "founder");
82 if(!ircd
::PREFIXAQ_DISABLE
()) {
83 @ops = (0, 0, 1, 2, 4, 8, 16, 16); # PREFIX_AQ
84 } else { # lame IRC scripts and admins who don't enable PREFIX_AQ
85 @ops = (0, 0, 1, 2, 4, 12, 20, 20); # normal
87 our @plevels = ('AKICK', 'anyone', 'UOp', 'VOp', 'HOp', 'AOp', 'SOp', 'co-founder', 'founder', 'disabled');
143 our $csnick_default = 'ChanServ';
144 our $csnick = $csnick_default;
145 our $csUser = { NICK
=> $csnick, ID
=> ircd
::getAgentUuid
($csnick) };
147 our ($cur_lock, $cnt_lock);
150 $get_joinpart_lock, $get_modelock_lock, $get_update_modes_lock,
152 $chanjoin, $chanpart, $chop, $chdeop, $get_op, $get_user_chans, $get_user_chans_recent,
153 $get_all_closed_chans, $get_user_count,
157 #$lock_chanuser, $get_all_chan_users,
159 $get_chan_users, $get_chan_users_noacc, $get_chan_users_mask, $get_chan_users_mask_noacc,
161 $get_users_nochans, $get_users_nochans_noid,
163 $get_using_nick_chans,
165 $get_lock, $release_lock, $is_free_lock,
167 $chan_create, $chan_delete, $get_chanmodes, $set_chanmodes,
169 $is_registered, $get_modelock, $set_modelock, $set_descrip,
171 $get_topic, $set_topic1, $set_topic2,
173 $get_acc, $set_acc1, $set_acc2, $del_acc, $get_acc_list, $get_acc_list2, $get_acc_list_mask, $get_acc_list2_mask,
175 $get_best_acc, $get_all_acc, $get_highrank, $get_acc_count,
176 $copy_acc, $copy_acc_rank,
178 $get_eos_lock, $get_status_all, $get_status_all_server, $get_modelock_all,
180 $get_akick, $get_akick_allchan, $get_akick_alluser, $get_akick_all, $add_akick, $del_akick,
181 $get_akick_list, $get_akick_by_num,
183 $add_nick_akick, $del_nick_akick, $get_nick_akick, $drop_nick_akick,
186 $is_level, $get_level, $get_levels, $add_level, $set_level, $reset_level, $clear_levels, $get_level_max,
189 $get_founder, $get_successor,
190 $set_founder, $set_successor, $del_successor,
192 $get_nick_own_chans, $delete_successors,
196 $register, $drop_acc, $drop_lvl, $drop_akick, $drop,
201 $get_close, $set_close, $del_close,
203 $add_welcome, $del_welcome, $list_welcome, $get_welcomes, $drop_welcome,
204 $count_welcome, $consolidate_welcome,
206 $add_ban, $delete_bans, $delete_ban,
207 $get_all_bans, $get_ban_num,
208 $find_bans, $list_bans, $wipe_bans,
209 $find_bans_chan_user, $delete_bans_chan_user,
211 $add_auth, $list_auth_chan, $check_auth_chan, $get_auth_nick, $get_auth_num, $find_auth,
213 $set_bantype, $get_bantype,
215 $drop_chantext, $drop_nicktext,
219 $csUser = { NICK
=> $csnick, ID
=> ircd
::getAgentUuid
($csnick) };
220 #$chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET id=(RAND()*294967293)+1, chan=?");
221 $get_joinpart_lock = $dbh->prepare("LOCK TABLES chan WRITE, chanuser WRITE");
222 $get_modelock_lock = $dbh->prepare("LOCK TABLES chanreg READ LOCAL, chan WRITE");
223 $get_update_modes_lock = $dbh->prepare("LOCK TABLES chan WRITE");
225 $chanjoin = $dbh->prepare("REPLACE INTO chanuser (seq,nickid,chan,op,joined) VALUES (?, ?, ?, ?, 1)");
226 $chanpart = $dbh->prepare("UPDATE chanuser SET joined=0, seq=?
227 WHERE nickid=? AND chan=? AND (seq <= ? OR seq > ?)");
228 #$chop = $dbh->prepare("UPDATE chanuser SET op=op+? WHERE nickid=? AND chan=?");
229 $chop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op, op ^ ?) WHERE nickid=? AND chan=?");
230 $chdeop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op ^ ?, op) WHERE nickid=? AND chan=?");
231 $get_op = $dbh->prepare("SELECT op FROM chanuser WHERE nickid=? AND chan=?");
232 $get_user_chans = $dbh->prepare("SELECT chan, op FROM chanuser WHERE nickid=? AND joined=1 AND (seq <= ? OR seq > ?)");
233 $get_user_chans_recent = $dbh->prepare("SELECT chan, joined, op FROM chanuser WHERE nickid=?");
235 $get_all_closed_chans = $dbh->prepare("SELECT chanclose.chan, chanclose.type, chanclose.reason, chanclose.nick, chanclose.time FROM chanreg, chanuser, chanclose WHERE chanreg.chan=chanuser.chan AND chanreg.chan=chanclose.chan AND chanreg.flags & ? GROUP BY chanclose.chan ORDER BY NULL");
236 $get_user_count = $dbh->prepare("SELECT COUNT(*) FROM chanuser WHERE chan=? AND joined=1");
238 $is_in_chan = $dbh->prepare("SELECT 1 FROM chanuser WHERE nickid=? AND chan=? AND joined=1");
240 #$lock_chanuser = $dbh->prepare("LOCK TABLES chanuser READ, user READ");
241 #$get_all_chan_users = $dbh->prepare("SELECT user.nick, chanuser.nickid, chanuser.chan FROM chanuser, user WHERE user.id=chanuser.nickid AND chanuser.joined=1");
242 $unlock_tables = $dbh->prepare("UNLOCK TABLES");
244 $get_chan_users = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
245 WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1");
246 my $chan_users_noacc_tables = 'user '.
247 'JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1 AND user.online=1) '.
248 'LEFT JOIN nickid ON (chanuser.nickid=nickid.id) '.
249 'LEFT JOIN chanacc ON (nickid.nrid=chanacc.nrid AND chanuser.chan=chanacc.chan)';
250 $get_chan_users_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
251 WHERE chanuser.chan=?
252 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
254 my $check_mask = "((user.nick LIKE ?) AND (user.ident LIKE ?)
255 AND ((user.vhost LIKE ?) OR (user.host LIKE ?) OR (user.cloakhost LIKE ?)))";
256 $get_chan_users_mask = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
257 WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1 AND $check_mask");
258 $get_chan_users_mask_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
259 WHERE chanuser.chan=? AND $check_mask
260 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
263 $get_users_nochans = $dbh->prepare("SELECT user.nick, user.id
264 FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
265 WHERE chanuser.chan IS NULL AND user.online=1");
266 $get_users_nochans_noid = $dbh->prepare("SELECT user.nick, user.id
267 FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
268 LEFT JOIN nickid ON (nickid.id=user.id)
269 WHERE chanuser.chan IS NULL AND nickid.id IS NULL
272 $get_using_nick_chans = $dbh->prepare("SELECT user.nick FROM user, nickid, nickreg, chanuser
273 WHERE user.id=nickid.id AND user.id=chanuser.nickid AND nickid.nrid=nickreg.id AND chanuser.joined=1
274 AND nickreg.nick=? AND chanuser.chan=?");
276 $get_lock = $dbh->prepare("SELECT GET_LOCK(?, 3)");
277 $release_lock = $dbh->prepare("DO RELEASE_LOCK(?)");
278 $is_free_lock = $dbh->prepare("SELECT IS_FREE_LOCK(?)");
280 $chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET seq=?, chan=?");
281 $chan_delete = $dbh->prepare("DELETE FROM chan WHERE chan=?");
282 $get_chanmodes = $dbh->prepare("SELECT modes FROM chan WHERE chan=?");
283 $set_chanmodes = $dbh->prepare("REPLACE INTO chan SET modes=?, chan=?");
285 $is_registered = $dbh->prepare("SELECT 1 FROM chanreg WHERE chan=?");
286 $get_modelock = $dbh->prepare("SELECT modelock FROM chanreg WHERE chan=?");
287 $set_modelock = $dbh->prepare("UPDATE chanreg SET modelock=? WHERE chan=?");
289 $set_descrip = $dbh->prepare("UPDATE chanreg SET descrip=? WHERE chan=?");
291 $get_topic = $dbh->prepare("SELECT chantext.data, topicer, topicd FROM chanreg, chantext
292 WHERE chanreg.chan=chantext.chan AND chantext.chan=?");
293 $set_topic1 = $dbh->prepare("UPDATE chanreg SET chanreg.topicer=?, chanreg.topicd=?
294 WHERE chanreg.chan=?");
295 $set_topic2 = $dbh->prepare("REPLACE INTO chantext SET chan=?, type=".CRT_TOPIC
().", data=?");
297 $get_acc = $dbh->prepare("SELECT chanacc.level FROM chanacc, nickalias
298 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
299 $set_acc1 = $dbh->prepare("INSERT IGNORE INTO chanacc SELECT ?, nrid, ?, NULL, UNIX_TIMESTAMP(), 0
300 FROM nickalias WHERE alias=?");
301 $set_acc2 = $dbh->prepare("UPDATE chanacc, nickalias
302 SET chanacc.level=?, chanacc.adder=?, chanacc.time=UNIX_TIMESTAMP()
303 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
304 $del_acc = $dbh->prepare("DELETE FROM chanacc USING chanacc, nickalias
305 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
306 $wipe_acc_list = $dbh->prepare("DELETE FROM chanacc WHERE chan=? AND level=?");
307 $get_acc_list = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.time,
308 chanacc.last, nickreg.ident, nickreg.vhost
309 FROM chanacc, nickreg
310 WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
311 $get_acc_list2 = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.level, chanacc.time,
312 chanacc.last, nickreg.ident, nickreg.vhost
313 FROM chanacc, nickreg
314 WHERE chanacc.chan=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
315 $get_acc_list_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias), chanacc.adder, chanacc.time,
316 chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
317 FROM chanacc, nickalias, nickreg
318 WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
319 AND chanacc.level > 0
320 AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
322 ORDER BY nickalias.alias");
323 $get_acc_list2_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias),
324 chanacc.adder, chanacc.level, chanacc.time,
325 chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
326 FROM chanacc, nickalias, nickreg
327 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
328 AND chanacc.level > 0
329 AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
331 ORDER BY nickalias.alias");
333 $get_best_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
334 FROM nickid, nickalias, nickreg, chanacc
335 WHERE nickid.nrid=nickreg.id AND nickalias.nrid=nickreg.id AND nickid.id=?
336 AND chanacc.nrid=nickreg.id AND chanacc.chan=? ORDER BY chanacc.level DESC LIMIT 1");
337 $get_all_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
338 FROM nickid, nickreg, chanacc
339 WHERE nickid.nrid=nickreg.id AND nickid.id=? AND chanacc.nrid=nickreg.id
340 AND chanacc.chan=? ORDER BY chanacc.level");
341 $get_highrank = $dbh->prepare("SELECT user.nick, chanacc.level FROM chanuser, nickid, chanacc, user WHERE chanuser.chan=? AND chanuser.joined=1 AND chanuser.chan=chanacc.chan AND chanuser.nickid=nickid.id AND user.id=nickid.id AND nickid.nrid=chanacc.nrid ORDER BY chanacc.level DESC LIMIT 1");
342 $get_acc_count = $dbh->prepare("SELECT COUNT(*) FROM chanacc WHERE chan=? AND level=?");
343 $copy_acc = $dbh->prepare("REPLACE INTO chanacc
344 ( chan, nrid, level, adder, time)
345 SELECT ?, nrid, level, adder, time FROM chanacc JOIN nickreg ON (chanacc.nrid=nickreg.id)
346 WHERE chan=? AND nickreg.nick!=? AND chanacc.level!=7");
347 $copy_acc_rank = $dbh->prepare("REPLACE INTO chanacc
348 ( chan, nrid, level, adder, time)
349 SELECT ?, nrid, level, adder, time FROM chanacc
350 WHERE chan=? AND chanacc.level=?");
352 $get_eos_lock = $dbh->prepare("LOCK TABLES akick READ LOCAL, welcome READ LOCAL, chanuser WRITE, user WRITE,
353 user AS u1 READ, user AS u2 READ, chan WRITE, chanreg WRITE, nickid READ LOCAL, nickreg READ LOCAL,
354 nickalias READ LOCAL, chanacc READ LOCAL, chanban WRITE, svsop READ");
355 my $get_status_all_1 = "SELECT chanuser.chan, chanreg.flags, chanreg.bot, user.nick, user.id, user.flags, MAX(chanacc.level), chanuser.op, MAX(nickreg.flags & ".NRF_NEVEROP
().")
356 FROM user, chanreg, chanuser
357 LEFT JOIN nickid ON(nickid.id=chanuser.nickid)
358 LEFT JOIN nickreg ON(nickid.nrid=nickreg.id)
359 LEFT JOIN chanacc ON(chanacc.chan=chanuser.chan AND chanacc.nrid=nickid.nrid AND (nickreg.flags & ".NRF_NEVEROP
().")=0)
361 my $get_status_all_2 = "(user.flags & ".UF_FINISHED
().")=0 AND chanuser.joined=1 AND (chanreg.flags & ".(CRF_CLOSE
|CRF_DRONE
).") = 0 AND chanreg.chan=chanuser.chan AND user.id=chanuser.nickid AND (nickid.nrid IS NULL OR nickreg.id IS NOT NULL)
362 GROUP BY chanuser.chan, chanuser.nickid ORDER BY NULL";
363 $get_status_all = $dbh->prepare("$get_status_all_1 $get_status_all_2");
364 $get_status_all_server = $dbh->prepare("$get_status_all_1 user.server=? AND $get_status_all_2");
366 $get_modelock_all = $dbh->prepare("SELECT chanuser.chan, chan.modes, chanreg.modelock FROM chanreg, chan, chanuser WHERE chanuser.joined=1 AND chanreg.chan=chan.chan AND chanreg.chan=chanuser.chan GROUP BY chanreg.chan ORDER BY NULL");
368 my $akick_rows = "user.nick, akick.nick, akick.ident, akick.host, akick.reason";
369 my $akick_no_zerolen = "(akick.ident != '' AND akick.host != '')";
370 my $akick_single_cond = "$akick_no_zerolen AND user.nick LIKE akick.nick AND user.ident LIKE akick.ident ".
371 "AND ( (user.host LIKE akick.host) OR (user.vhost LIKE akick.host) OR ".
372 "(IF((user.ip IS NOT NULL) AND (user.ip != 0), INET_NTOA(user.ip) LIKE akick.host, 0)) OR ".
373 "(IF(user.cloakhost IS NOT NULL, user.cloakhost LIKE akick.host, 0)) )";
374 my $akick_multi_cond = "chanuser.chan=akick.chan AND $akick_single_cond";
376 $get_akick = $dbh->prepare("SELECT $akick_rows FROM akick, user ".
377 "WHERE user.id=? AND akick.chan=? AND $akick_single_cond LIMIT 1");
378 $get_akick_allchan = $dbh->prepare("SELECT $akick_rows FROM $chan_users_noacc_tables
379 JOIN akick ON($akick_multi_cond)
381 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
383 $get_akick_alluser = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
384 JOIN akick ON($akick_multi_cond)
385 WHERE chanuser.nickid=?
386 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
388 $get_akick_all = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
389 JOIN akick ON($akick_multi_cond)
390 GROUP BY akick.chan, user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
393 $add_akick = $dbh->prepare("INSERT INTO akick SET chan=?, nick=?, ident=?, host=?, adder=?, reason=?, time=UNIX_TIMESTAMP()");
394 $add_akick->{PrintError
} = 0;
395 $del_akick = $dbh->prepare("DELETE FROM akick WHERE chan=? AND nick=? AND ident=? AND host=?");
396 $get_akick_list = $dbh->prepare("SELECT nick, ident, host, adder, reason, time FROM akick WHERE chan=? ORDER BY time");
398 $add_nick_akick = $dbh->prepare("INSERT INTO akick SELECT ?, nickalias.nrid, '', '', ?, ?, UNIX_TIMESTAMP()
399 FROM nickalias WHERE alias=?");
400 $del_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickalias
401 WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
402 $get_nick_akick = $dbh->prepare("SELECT reason FROM akick, nickalias
403 WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
404 $drop_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickreg
405 WHERE akick.nick=nickreg.id AND akick.ident='' AND akick.host='' AND nickreg.nick=?");
406 $copy_akick = $dbh->prepare("REPLACE INTO akick
407 ( chan, nick, ident, host, adder, reason, time)
408 SELECT ?, nick, ident, host, adder, reason, time FROM akick WHERE chan=?");
409 $get_akick_by_num = $dbh->prepare("SELECT akick.nick, akick.ident, akick.host FROM akick WHERE chan=?
410 ORDER BY time LIMIT 1 OFFSET ?");
411 $get_akick_by_num->bind_param(2, 0, SQL_INTEGER
);
413 $is_level = $dbh->prepare("SELECT 1 FROM chanperm WHERE chanperm.name=?");
414 $get_level = $dbh->prepare("SELECT IF(chanlvl.level IS NULL, chanperm.level, chanlvl.level), chanlvl.level
415 FROM chanperm LEFT JOIN chanlvl ON chanlvl.perm=chanperm.id AND chanlvl.chan=?
416 WHERE chanperm.name=?");
417 $get_levels = $dbh->prepare("SELECT chanperm.name, chanperm.level, chanlvl.level FROM chanperm LEFT JOIN chanlvl ON chanlvl.perm=chanperm.id AND chanlvl.chan=? ORDER BY chanperm.name");
418 $add_level = $dbh->prepare("INSERT IGNORE INTO chanlvl SELECT ?, chanperm.id, chanperm.level FROM chanperm WHERE chanperm.name=?");
419 $set_level = $dbh->prepare("UPDATE chanlvl, chanperm SET chanlvl.level=? WHERE chanlvl.chan=? AND chanperm.id=chanlvl.perm AND chanperm.name=?");
420 $reset_level = $dbh->prepare("DELETE FROM chanlvl USING chanlvl, chanperm WHERE chanperm.name=? AND chanlvl.perm=chanperm.id AND chanlvl.chan=?");
421 $clear_levels = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
422 $get_level_max = $dbh->prepare("SELECT max FROM chanperm WHERE name=?");
423 $copy_levels = $dbh->prepare("REPLACE INTO chanlvl
425 SELECT ?, perm, level FROM chanlvl WHERE chan=?");
427 $get_founder = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.founderid=nickreg.id");
428 $get_successor = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.successorid=nickreg.id");
429 $set_founder = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.founderid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
430 $set_successor = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
431 $del_successor = $dbh->prepare("UPDATE chanreg SET chanreg.successorid=NULL WHERE chanreg.chan=?");
433 $get_nick_own_chans = $dbh->prepare("SELECT chanreg.chan FROM chanreg, nickreg WHERE nickreg.nick=? AND chanreg.founderid=nickreg.id");
434 $delete_successors = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=NULL WHERE nickreg.nick=? AND chanreg.successorid=nickreg.id");
437 $get_info = $dbh->prepare("SELECT chanreg.descrip, chanreg.regd, chanreg.last, chantext.data,
438 chanreg.topicer, chanreg.modelock, foundernick.nick, successornick.nick, chanreg.bot, chanreg.bantype
439 FROM nickreg AS foundernick, chanreg
440 LEFT JOIN nickreg AS successornick ON(successornick.id=chanreg.successorid)
441 LEFT JOIN chantext ON (chanreg.chan=chantext.chan AND chantext.type=".CRT_TOPIC
().")
442 WHERE chanreg.chan=? AND foundernick.id=chanreg.founderid");
444 $register = $dbh->prepare("INSERT INTO chanreg
445 SELECT ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), NULL, NULL,
446 NULL, id, NULL, NULL, NULL, ".DEFAULT_BANTYPE
()." FROM nickreg WHERE nick=?");
447 $register->{PrintError
} = 0;
448 $copy_chanreg = $dbh->prepare("INSERT INTO chanreg
449 ( chan, descrip, regd, last, modelock, founderid, successorid, bot, flags, bantype)
450 SELECT ?, descrip, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), modelock, founderid, successorid, bot, flags, bantype
451 FROM chanreg WHERE chan=?");
453 $drop_acc = $dbh->prepare("DELETE FROM chanacc WHERE chan=?");
454 $drop_lvl = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
455 $drop_akick = $dbh->prepare("DELETE FROM akick WHERE chan=?");
456 $drop = $dbh->prepare("DELETE FROM chanreg WHERE chan=?");
458 $get_expired = $dbh->prepare("SELECT chanreg.chan, nickreg.nick FROM nickreg, chanreg
459 LEFT JOIN chanuser ON(chanreg.chan=chanuser.chan AND chanuser.op!=0)
460 WHERE chanreg.founderid=nickreg.id AND chanuser.chan IS NULL AND chanreg.last<? AND
461 !(chanreg.flags & " . CRF_HOLD
. ")");
463 $get_close = $dbh->prepare("SELECT reason, nick, time FROM chanclose WHERE chan=?");
464 $set_close = $dbh->prepare("REPLACE INTO chanclose SET chan=?, reason=?, nick=?, time=UNIX_TIMESTAMP(), type=?");
465 $del_close = $dbh->prepare("DELETE FROM chanclose WHERE chan=?");
467 $add_welcome = $dbh->prepare("REPLACE INTO welcome SET chan=?, id=?, adder=?, time=UNIX_TIMESTAMP(), msg=?");
468 $del_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=? AND id=?");
469 $list_welcome = $dbh->prepare("SELECT id, time, adder, msg FROM welcome WHERE chan=? ORDER BY id");
470 $get_welcomes = $dbh->prepare("SELECT msg FROM welcome WHERE chan=? ORDER BY id");
471 $drop_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=?");
472 $count_welcome = $dbh->prepare("SELECT COUNT(*) FROM welcome WHERE chan=?");
473 $consolidate_welcome = $dbh->prepare("UPDATE welcome SET id=id-1 WHERE chan=? AND id>?");
475 $add_ban = $dbh->prepare("INSERT IGNORE INTO chanban SET chan=?, mask=?, setter=?, type=?, time=UNIX_TIMESTAMP()");
476 $delete_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
477 # likely need a better name for this or for the above.
478 $delete_ban = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND mask=? AND type=?");
479 $find_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
480 $get_all_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=?");
481 $get_ban_num = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=? ORDER BY time, mask LIMIT 1 OFFSET ?");
482 $get_ban_num->bind_param(3, 0, SQL_INTEGER
);
483 $list_bans = $dbh->prepare("SELECT mask, setter, time FROM chanban WHERE chan=? AND type=? ORDER BY time, mask");
484 $wipe_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=?");
486 my $chanban_mask = "((CONCAT(user.nick, '!', user.ident, '\@', user.host) LIKE chanban.mask) ".
487 "OR (CONCAT(user.nick , '!' , user.ident , '\@' , user.vhost) LIKE chanban.mask) ".
488 "OR IF(user.cloakhost IS NOT NULL, ".
489 "(CONCAT(user.nick , '!' , user.ident , '\@' , user.cloakhost) LIKE chanban.mask), 0))";
490 $find_bans_chan_user = $dbh->prepare("SELECT mask FROM chanban,user
491 WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
492 $delete_bans_chan_user = $dbh->prepare("DELETE FROM chanban USING chanban,user
493 WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
495 $add_auth = $dbh->prepare("REPLACE INTO nicktext
496 SELECT nickalias.nrid, (".NTF_AUTH
()."), 1, ?, ? FROM nickalias WHERE nickalias.alias=?");
497 $list_auth_chan = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nicktext
498 WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".NTF_AUTH
().") AND nicktext.chan=?");
499 $check_auth_chan = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nicktext
500 WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".nickserv
::NTF_AUTH
().") AND nicktext.chan=? AND nickreg.nick=?");
501 $get_auth_nick = $dbh->prepare("SELECT nicktext.data FROM nickreg, nickalias, nicktext
502 WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH
().")
503 AND nicktext.chan=? AND nickalias.alias=?");
504 $get_auth_num = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nickalias, nicktext
505 WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH
().")
506 AND nicktext.chan=? LIMIT 1 OFFSET ?");
507 $get_auth_num->bind_param(2, 0, SQL_INTEGER
);
508 $find_auth = $dbh->prepare("SELECT 1 FROM nickalias, nicktext
509 WHERE nickalias.nrid=nicktext.nrid AND nicktext.type=(".NTF_AUTH
().")
510 AND nicktext.chan=? AND nickalias.alias=?");
512 $set_bantype = $dbh->prepare("UPDATE chanreg SET bantype=? WHERE chan=?");
513 $get_bantype = $dbh->prepare("SELECT bantype FROM chanreg WHERE chan=?");
515 $drop_chantext = $dbh->prepare("DELETE FROM chantext WHERE chan=?");
516 $drop_nicktext = $dbh->prepare("DELETE nicktext.* FROM nicktext WHERE nicktext.chan=?");
519 use SrSv
::MySQL
::Stub
{
520 set_lastop
=> ['NULL', "UPDATE chanreg SET last=UNIX_TIMESTAMP() WHERE chan=?"],
521 set_lastused
=> ['NULL', "UPDATE chanacc, nickid SET chanacc.last=UNIX_TIMESTAMP() WHERE
522 chanacc.chan=? AND nickid.id=? AND chanacc.nrid=nickid.nrid AND chanacc.level > 0"],
523 get_recent_private_chans
=> ['COLUMN', "SELECT DISTINCT chanuser.chan FROM chanuser
524 JOIN chanacc ON (chanuser.chan=chanacc.chan AND chanuser.joined=0)
525 JOIN chanlvl ON (chanlvl.level <= chanacc.level AND chanlvl.level > 0 AND chanuser.chan=chanlvl.chan)
526 JOIN chanperm ON (chanlvl.perm=chanperm.id)
527 JOIN nickid ON (chanuser.nickid=nickid.id AND chanacc.nrid=nickid.nrid)
528 WHERE chanperm.name='Join'
532 ### CHANSERV COMMANDS ###
534 our %high_priority_cmds = (
545 our $csUser = { NICK
=> $csnick, ID
=> ircd
::getAgentUuid
($csnick) };
546 my ($user, $dstUser, $msg) = @_;
547 my $src = $user->{NICK
};
549 my @args = split(/\s+/, $msg);
550 my $cmd = shift @args;
551 $user->{AGENT
} = $csUser;
553 return if flood_check
($user);
554 return unless (lc $dstUser->{NICK
} eq lc $csnick);
555 if(!defined($high_priority_cmds{lc $cmd}) &&
556 !adminserv
::is_svsop
($user) &&
557 $SrSv::IRCd
::State
::queue_depth
> main_conf_highqueue
)
559 notice
($user, get_user_agent
($user)." is too busy right now. Please try your command again later.");
563 if($cmd =~ /^register$/i) {
565 my @args = split(/\s+/, $msg, 4);
566 cs_register
($user, { CHAN
=> $args[1] }, $args[2], $args[3]);
568 notice
($user, 'Syntax: REGISTER <#channel> [password] [description]');
571 elsif($cmd =~ /^(?:[uvhas]op|co?f(ounder)?)$/i) {
572 my ($cn, $cmd2) = splice(@args, 0, 2);
573 my $chan = { CHAN
=> $cn };
575 if($cmd2 =~ /^add$/i) {
577 cs_xop_add
($user, $chan, $cmd, $args[0]);
579 notice
($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
582 elsif($cmd2 =~ /^del(ete)?$/i) {
584 cs_xop_del
($user, $chan, $cmd, $args[0]);
586 notice
($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
589 elsif($cmd2 =~ /^list$/i) {
591 cs_xop_list
($user, $chan, $cmd, $args[0]);
593 notice
($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
596 elsif($cmd2 =~ /^(wipe|clear)$/i) {
598 cs_xop_wipe
($user, $chan, $cmd);
600 notice
($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
604 notice
($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
607 elsif($cmd =~ /^levels$/i) {
609 notice
($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
613 my $cmd2 = lc(splice(@args, 1, 1));
617 cs_levels_set
($user, { CHAN
=> $args[0] }, $args[1], $args[2]);
619 notice
($user, 'Syntax: LEVELS <#channel> SET <permission> <level>');
622 elsif($cmd2 eq 'reset') {
624 cs_levels_set
($user, { CHAN
=> $args[0] }, $args[1]);
626 notice
($user, 'Syntax: LEVELS <#channel> RESET <permission>');
629 elsif($cmd2 eq 'list') {
631 cs_levels_list
($user, { CHAN
=> $args[0] });
633 notice
($user, 'Syntax: LEVELS <#channel> LIST');
636 elsif($cmd2 eq 'clear') {
638 cs_levels_clear
($user, { CHAN
=> $args[0] });
640 notice
($user, 'Syntax: LEVELS <#channel> CLEAR');
644 notice
($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
647 elsif($cmd =~ /^akick$/i) {
649 notice
($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
653 #my $cmd2 = lc($args[1]);
654 my $cmd2 = lc(splice(@args, 1, 1));
658 my @args = split(/\s+/, $msg, 5);
659 cs_akick_add
($user, { CHAN
=> $args[1] }, $args[3], $args[4]);
661 notice
($user, 'Syntax: AKICK <#channel> ADD <nick|mask> <reason>');
664 elsif($cmd2 eq 'del') {
666 cs_akick_del
($user, { CHAN
=> $args[0] }, $args[1]);
668 notice
($user, 'Syntax: AKICK <#channel> DEL <nick|mask|num|seq>');
671 elsif($cmd2 eq 'list') {
673 cs_akick_list
($user, { CHAN
=> $args[0] });
675 notice
($user, 'Syntax: AKICK <#channel> LIST');
678 elsif($cmd2 =~ /^(wipe|clear)$/i) {
680 cs_akick_wipe
($user, { CHAN
=> $args[0] });
682 notice
($user, 'Syntax: AKICK <#channel> WIPE');
685 elsif($cmd2 =~ /^enforce$/i) {
687 cs_akick_enforce
($user, { CHAN
=> $args[0] });
689 notice
($user, 'Syntax: AKICK <#channel> ENFORCE');
693 notice
($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
696 elsif($cmd =~ /^info$/i) {
698 cs_info
($user, { CHAN
=> $args[0] });
700 notice
($user, 'Syntax: INFO <channel>');
703 elsif($cmd =~ /^set$/i) {
704 if(@args == 2 and lc($args[1]) eq 'unsuccessor') {
705 cs_set
($user, { CHAN
=> $args[0] }, $args[1]);
707 elsif(@args >= 3 and (
708 $args[1] =~ /m(?:ode)?lock/i or
709 lc($args[1]) eq 'desc'
711 my @args = split(/\s+/, $msg, 4);
712 cs_set
($user, { CHAN
=> $args[1] }, $args[2], $args[3]);
715 cs_set
($user, { CHAN
=> $args[0] }, $args[1], $args[2]);
718 notice
($user, 'Syntax: SET <channel> <option> <value>');
721 elsif($cmd =~ /^why$/i) {
723 cs_why
($user, { CHAN
=> shift @args }, $src);
726 cs_why
($user, { CHAN
=> shift @args }, @args);
728 notice
($user, 'Syntax: WHY <channel> <nick> [nick [nick ...]]');
732 elsif($cmd =~ /^(de)?(voice|h(alf)?op|op|protect|admin|owner)$/i) {
734 cs_setmodes
($user, $cmd, { CHAN
=> shift(@args) }, @args);
736 notice
($user, 'Syntax: '.uc($cmd).' <channel> [nick [nick ...]]');
739 elsif($cmd =~ /^(up|down)$/i) {
740 cs_updown
($user, $cmd, @args);
742 elsif($cmd =~ /^drop$/i) {
744 cs_drop
($user, { CHAN
=> $args[0] });
746 notice
($user, 'Syntax: DROP <channel>');
749 elsif($cmd =~ /^help$/i) {
750 sendhelp
($user, 'chanserv', @args)
752 elsif($cmd =~ /^count$/i) {
754 cs_count
($user, { CHAN
=> $args[0] });
756 notice
($user, 'Syntax: COUNT <channel>');
759 elsif($cmd =~ /^k(?:ick)?$/i) {
760 my @args = split(/\s+/, $msg, 4); shift @args;
762 cs_kick
($user, { CHAN
=> $args[0] }, $args[1], 0, $args[2])
765 notice
($user, 'Syntax: KICK <channel> <nick> [reason]');
768 elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)$/i) {
769 my @args = split(/\s+/, $msg, 4); shift @args;
771 cs_kick
($user, { CHAN
=> $args[0] }, $args[1], 1, $args[2]);
773 notice
($user, 'Syntax: KICKBAN <channel> <nick> [reason]');
776 elsif($cmd =~ /^k(ick)?m(ask)?$/i) {
777 my @args = split(/\s+/, $msg, 4); shift @args;
779 cs_kickmask
($user, { CHAN
=> $args[0] }, $args[1], 0, $args[2])
782 notice
($user, 'Syntax: KICKMASK <channel> <mask> [reason]');
785 elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)m(ask)?$/i) {
786 my @args = split(/\s+/, $msg, 4); shift @args;
788 cs_kickmask
($user, { CHAN
=> $args[0] }, $args[1], 1, $args[2]);
790 notice
($user, 'Syntax: KICKBANMASK <channel> <mask> [reason]');
793 elsif($cmd =~ /^invite$/i) {
794 my $chan = shift @args;
796 cs_invite
($user, { CHAN
=> $chan }, $src)
799 cs_invite
($user, { CHAN
=> $chan }, @args)
802 notice
($user, 'Syntax: INVITE <channel> <nick>');
805 elsif($cmd =~ /^(close|forbid)$/i) {
807 my @args = split(/\s+/, $msg, 3);
808 cs_close
($user, { CHAN
=> $args[1] }, $args[2], CRF_CLOSE
);
811 notice
($user, 'Syntax: CLOSE <chan> <reason>');
814 elsif($cmd =~ /^drone$/i) {
816 my @args = split(/\s+/, $msg, 3);
817 cs_close
($user, { CHAN
=> $args[1] }, $args[2], CRF_DRONE
);
820 notice
($user, 'Syntax: DRONE <chan> <reason>');
823 elsif($cmd =~ /^clear$/i) {
824 my ($cmd, $chan, $clearcmd, $reason) = split(/\s+/, $msg, 4);
825 unless ($chan and $clearcmd) {
826 notice
($user, 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
829 if($clearcmd =~ /^modes$/i) {
830 cs_clear_modes
($user, { CHAN
=> $chan }, $reason);
832 elsif($clearcmd =~ /^ops$/i) {
833 cs_clear_ops
($user, { CHAN
=> $chan }, $reason);
835 elsif($clearcmd =~ /^users$/i) {
836 cs_clear_users
($user, { CHAN
=> $chan }, $reason);
838 elsif($clearcmd =~ /^bans?$/i) {
839 cs_clear_bans
($user, { CHAN
=> $chan }, 0, $reason);
841 elsif($clearcmd =~ /^excepts?$/i) {
842 cs_clear_bans
($user, { CHAN
=> $chan }, 128, $reason);
845 notice
($user, "Unknown CLEAR command \002$clearcmd\002",
846 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
849 elsif($cmd =~ /^mkick$/i) {
850 my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
852 cs_clear_users
($user, { CHAN
=> $chan }, $reason);
855 notice
($user, 'Syntax: MKICK <chan> [reason]');
858 elsif($cmd =~ /^mdeop$/i) {
859 my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
861 cs_clear_ops
($user, { CHAN
=> $chan }, $reason);
864 notice
($user, 'Syntax: MDEOP <chan> [reason]');
867 elsif($cmd =~ /^welcome$/i) {
868 my $wcmd = splice(@args, 1, 1);
869 if(lc($wcmd) eq 'add') {
870 my ($chan, $wmsg) = (splice(@args, 0, 1), join(' ', @args));
871 unless ($chan and $wmsg) {
872 notice
($user, 'Syntax: WELCOME <channel> ADD <message>');
875 cs_welcome_add
($user, { CHAN
=> $chan }, $wmsg);
877 elsif(lc($wcmd) eq 'del') {
878 if (@args != 2 or !misc
::isint
($args[1])) {
879 notice
($user, 'Syntax: WELCOME <channnel> DEL <number>');
882 cs_welcome_del
($user, { CHAN
=> $args[0] }, $args[1]);
884 elsif(lc($wcmd) eq 'list') {
886 notice
($user, 'Syntax: WELCOME <channel> LIST');
889 cs_welcome_list
($user, { CHAN
=> $args[0] });
892 notice
($user, 'Syntax: WELCOME <channel> <ADD|DEL|LIST>');
895 elsif($cmd =~ /^alist$/i) {
897 cs_alist
($user, { CHAN
=> shift @args }, shift @args);
899 notice
($user, 'Syntax: ALIST <channel> [mask]');
902 elsif($cmd =~ /^unban$/i) {
904 cs_unban
($user, { CHAN
=> shift @args }, $src);
907 cs_unban
($user, { CHAN
=> shift @args }, @args);
909 notice
($user, 'Syntax: UNBAN <channel> [nick]');
912 elsif($cmd =~ /^getkey$/i) {
914 cs_getkey
($user, { CHAN
=> $args[0] });
916 notice
($user, 'Syntax: GETKEY <channel>');
919 elsif($cmd =~ /^auth$/i) {
921 notice
($user, 'Syntax: AUTH <channel> <LIST|DELETE> [param]');
923 cs_auth
($user, { CHAN
=> shift @args }, shift @args, @args);
926 elsif($cmd =~ /^dice$/i) {
927 notice
($user, botserv
::get_dice
($args[0]));
929 elsif($cmd =~ /^(q|n)?ban$/i) {
931 my $chan = shift @args;
933 cs_ban
($user, { CHAN
=> $chan }, $type, @args)
936 notice
($user, 'Syntax: BAN <channel> <nick|mask>');
939 elsif($cmd =~ /^banlist$/i) {
940 my $chan = shift @args;
942 cs_banlist
($user, { CHAN
=> $chan });
945 notice
($user, 'Syntax: BANLIST <channel>');
948 elsif($cmd =~ /^assign$/i) {
949 my $chan = shift @args;
950 notice
($user, "$csnick ASSIGN is deprecated. Please use $botserv::bsnick ASSIGN");
952 botserv
::bs_assign
($user, { CHAN
=> shift @args }, shift @args);
955 notice
($user, 'Syntax: ASSIGN <#channel> <bot>');
958 elsif($cmd =~ /^mode$/i) {
959 my $chan = shift @args;
961 cs_mode
($user, { CHAN
=> $chan }, @args)
964 notice
($user, 'Syntax: MODE <channel> <modes> [parms]');
967 elsif($cmd =~ /^copy$/i) {
968 my $chan = shift @args;
970 cs_copy
($user, { CHAN
=> $chan }, @args)
973 notice
($user, 'Syntax: COPY #chan1 [type] #chan2');
976 elsif($cmd =~ /^m(?:ode)?lock$/i) {
977 my $chan = shift @args;
979 cs_mlock
($user, { CHAN
=> $chan }, @args)
982 notice
($user, 'Syntax: MLOCK <channel> <ADD|DEL|SET|RESET> <modes> [parms]');
985 elsif($cmd =~ /^resync$/i) {
987 notice
($user, 'Syntax: RESYNC <chan1> [chan2 [chan3 [..]]]');
989 cs_resync
($user, @args);
992 elsif($cmd =~ /^JOIN$/i) {
994 notice
($user, 'Syntax: JOIN <chan1> [chan2 [chan3 [..]]]');
996 cs_join
($user, @args);
999 elsif($cmd =~ /^topic$/i) {
1000 my $chan = shift @args;
1002 notice
($user, 'Syntax: TOPIC <#channel> <message|NONE>');
1004 $msg =~ s/^topic #(?:\S+)? //i;
1005 cs_topic
($user, { CHAN
=> $chan }, $msg);
1009 notice
($user, "Unrecognized command \002$cmd\002.", "For help, type: \002/msg chanserv help\002");
1010 wlog
($csnick, LOG_DEBUG
(), "$src tried to use $csnick $msg");
1014 sub cs_register
($$;$$) {
1015 my ($user, $chan, $pass, $desc) = @_;
1016 # $pass is still passed in, but never used!
1017 my $src = get_user_nick
($user);
1018 my $cn = $chan->{CHAN
};
1020 unless(is_identified
($user, $src)) {
1021 notice
($user, 'You must register your nickname first.', "Type \002/msg NickServ HELP\002 for information on registering nicknames.");
1025 unless(is_in_chan
($user, $chan)) {
1026 notice
($user, "You are not in \002$cn\002.");
1030 if(services_conf_chanreg_needs_oper
&& !adminserv
::is_svsop
($user)) {
1031 notice
($user, "You must be network staff to register a channel\n");
1034 unless(get_op
($user, $chan) & ($opmodes{o
} | $opmodes{a
} | $opmodes{q
})) {
1035 # This would be preferred to be a 'opmode_mask' or something
1036 # However that might be misleading due to hop not being enough to register
1037 notice
($user, "You must have channel operator status to register \002$cn\002.");
1041 my $root = get_root_nick
($src);
1044 my $dlength = length($desc);
1045 if($dlength >= 350) {
1046 notice
($user, 'Channel description is too long by '. $dlength-350 .' character(s). Maximum length is 350 characters.');
1051 if($register->execute($cn, $desc, $root)) {
1052 notice
($user, "\002Your channel is now registered. Thank you.\002");
1053 notice
($user, ' ', "\002NOTICE:\002 Channel passwords are not used, as a security precaution.")
1055 set_acc
($root, $user, $chan, FOUNDER
);
1056 $set_modelock->execute(services_conf_default_channel_mlock
, $cn);
1058 services
::ulog
($csnick, LOG_INFO
(), "registered $cn", $user, $chan);
1059 botserv
::bs_assign
($user, $chan, services_conf_default_chanbot
) if services_conf_default_chanbot
;
1061 notice
($user, 'That channel has already been registered.');
1066 cs_command new SrSv
::AgentUI
::Simple
{
1067 COMMAND
=> [qw(uop vop hop aop sop cf cofounder cof cfounder)],
1068 SYNTAX
=> '#chan add/del/list/wipe/clear [nick/mask]',
1069 CALL
=> \
&cs_xop_dispatch
,
1073 sub cs_xop_dispatch
{
1074 my ($user, $cmd, $chan, $cmd2, @args) = @_;
1077 if($cmd2 =~ /^add$/i) {
1079 cs_xop_add
($user, $chan, $cmd, $args[0]);
1081 notice
($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
1084 elsif($cmd2 =~ /^del(ete)?$/i) {
1086 cs_xop_del
($user, $chan, $cmd, $args[0]);
1088 notice
($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
1091 elsif($cmd2 =~ /^list$/i) {
1093 cs_xop_list
($user, $chan, $cmd, $args[0]);
1095 notice
($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
1098 elsif($cmd2 =~ /^(wipe|clear)$/i) {
1100 cs_xop_wipe
($user, $chan, $cmd);
1102 notice
($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
1106 notice
($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
1110 sub cs_xop_ad_pre
($$$$$) {
1111 my ($user, $chan, $nick, $level, $del) = @_;
1113 my $old = get_acc
($nick, $chan); $old = 0 unless $old;
1114 my $slevel = get_best_acc
($user, $chan);
1116 unless(($del and is_identified
($user, $nick)) or adminserv
::can_do
($user, 'SERVOP')) {
1117 unless($level < $slevel and $old < $slevel) {
1118 notice
($user, $err_deny);
1121 my $cn = $chan->{CHAN
};
1122 my $overrideMsg = "$levels[$level] $cn ".($del ? 'DEL' : 'ADD')." $nick";
1123 can_do
($chan, 'ACCCHANGE', $user, { OVERRIDE_MSG
=> $overrideMsg }) or return undef;
1126 nickserv
::chk_registered
($user, $nick) or return undef;
1127 if (nr_chk_flag
($nick, NRF_NOACC
()) and !adminserv
::can_do
($user, 'SERVOP') and !$del) {
1128 notice
($user, "\002$nick\002 is not able to be added to access lists.");
1135 sub cs_xop_list
($$$;$) {
1136 my ($user, $chan, $cmd, $mask) = @_;
1137 chk_registered
($user, $chan) or return;
1138 my $cn = $chan->{CHAN
};
1139 my $level = xop_byname
($cmd);
1141 my $overrideMsg = "$cmd $cn LIST";
1142 can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> $overrideMsg }) or return;
1146 my ($mnick, $mident, $mhost) = glob2sql
(parse_mask
($mask));
1147 $mnick = '%' if($mnick eq '');
1148 $mident = '%' if($mident eq '');
1149 $mhost = '%' if($mhost eq '');
1151 $get_acc_list_mask->execute($mnick, $cn, $level, $mnick, $mident, $mhost);
1152 while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list_mask->fetchrow_array) {
1153 push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
1154 push @reply, ' '.($t ? 'Date/time added: '. gmtime2
($t).' ' : '').
1155 ($lu ? 'Last used '.time_ago
($lu).' ago' : '') if ($t or $lu);
1157 $get_acc_list_mask->finish();
1159 $get_acc_list->execute($cn, $level);
1160 while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list->fetchrow_array) {
1161 push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
1162 push @reply, ' '.($t ? 'Date/time added: '. gmtime2
($t).' ' : '').
1163 ($lu ? 'Last used '.time_ago
($lu).' ago' : '') if ($t or $lu);
1165 $get_acc_list->finish();
1168 notice
($user, "$levels[$level] list for \002$cn\002:", @reply);
1173 sub cs_xop_wipe
($$$) {
1174 my ($user, $chan, $cmd, $nick) = @_;
1175 chk_registered
($user, $chan) or return;
1177 my $slevel = get_best_acc
($user, $chan);
1178 my $level = xop_byname
($cmd);
1180 unless($level < $slevel) {
1181 notice
($user, $err_deny);
1184 my $cn = $chan->{CHAN
};
1185 my $overrideMsg = "$cmd $cn WIPE";
1186 my $srcnick = can_do
($chan, 'ACCCHANGE', $user, { ACC
=> $slevel, OVERRIDE_MSG
=> $overrideMsg }) or return;
1188 $wipe_acc_list->execute($cn, $level);
1190 my $log_str = "wiped the $cmd list of \002$cn\002.";
1191 my $src = get_user_nick
($user);
1192 notice
($user, "You have $log_str");
1193 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 has $log_str")
1194 if cr_chk_flag
($chan, CRF_VERBOSE
);
1195 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1197 memolog
($chan, "\002$srcnick\002 $log_str");
1200 sub cs_xop_add
($$$$) {
1201 my ($user, $chan, $cmd, $nick) = @_;
1203 chk_registered
($user, $chan) or return;
1204 my $level = xop_byname
($cmd);
1205 my $old = cs_xop_ad_pre
($user, $chan, $nick, $level, 0);
1206 return unless defined($old);
1208 my $cn = $chan->{CHAN
};
1209 if ($level == 3 && $IRCd_capabilities{"HALFOP"} eq "") {
1210 notice
($user, "m_halfop.so is required to add half ops.");
1211 notice
($user, "Please notify your friendly network administrators to enable it.");
1214 if ($level == 5 && $IRCd_capabilities{"ADMIN"} eq "") {
1215 notice
($user, "m_chanprotect.so is required to add SOPs.");
1216 notice
($user, "Please notify your friendly network administrators to enable it.");
1219 if($old == $level) {
1220 notice
($user, "\002$nick\002 already has $levels[$level] access to \002$cn\002.");
1224 if($old == FOUNDER
) {
1225 notice
($user, "\002$nick\002 is the founder of \002$cn\002 and cannot be added to access lists.",
1226 "For more information, type: \002/msg chanserv help set founder\002");
1230 my $root = get_root_nick
($nick);
1231 my $auth = nr_chk_flag
($root, NRF_AUTH
());
1232 my $src = get_user_nick
($user);
1235 $add_auth->execute($cn, "$src:".($old ? $old : 0 ).":$level:".time(), $root);
1236 del_acc
($root, $chan) if $level < $old;
1239 set_acc
($root, $user, $chan, $level);
1243 $del_nick_akick->execute($cn, $root);
1244 my $log_str = "moved $root from the AKICK list to the ${levels[$level]} list of \002$cn\002".
1245 ($auth ? ' (requires authorization)' : '');
1247 my $src = get_user_nick
($user);
1248 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1249 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1250 if cr_chk_flag
($chan, CRF_VERBOSE
);
1251 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1252 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1253 memolog
($chan, "\002$srcnick\002 $log_str");
1255 my $log_str = ($old?'moved':'added')." \002$root\002"
1256 . ($old ? " from the ${levels[$old]}" : '') .
1257 " to the ${levels[$level]} list of \002$cn\002" .
1258 ($auth ? ' (requires authorization)' : '');
1259 my $src = get_user_nick
($user);
1260 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1261 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1262 if cr_chk_flag
($chan, CRF_VERBOSE
);
1263 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1264 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1265 memolog
($chan, "\002$srcnick\002 $log_str");
1269 sub cs_xop_del
($$$) {
1270 my ($user, $chan, $cmd, $nick) = @_;
1272 chk_registered
($user, $chan) or return;
1273 my $level = xop_byname
($cmd);
1274 my $old = cs_xop_ad_pre
($user, $chan, $nick, $level, 1);
1275 return unless defined($old);
1277 my $cn = $chan->{CHAN
};
1279 unless($old == $level) {
1280 notice
($user, "\002$nick\002 is not on the ${levels[$level]} list of \002$cn\002.");
1284 my $root = get_root_nick
($nick);
1285 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1287 del_acc
($root, $chan);
1289 my $src = get_user_nick
($user);
1290 my $log_str = "removed \002$root\002 ($nick) from the ${levels[$level]} list of \002$cn\002";
1291 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1292 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1293 if cr_chk_flag
($chan, CRF_VERBOSE
);
1294 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1295 memolog
($chan, "\002$srcnick\002 $log_str");
1299 my ($user, $chan) = @_;
1301 chk_registered
($user, $chan) or return;
1303 my $cn = $chan->{CHAN
};
1304 my $overrideMsg = "COUNT $cn";
1305 if(can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> $overrideMsg })) {
1311 for (my $level = $plzero + 1; $level < COFOUNDER
+ 2; $level++) {
1312 $get_acc_count->execute($cn, $level - 1);
1313 my ($num_recs) = $get_acc_count->fetchrow_array;
1314 $reply = $reply." $plevels[$level]: ".$num_recs;
1316 notice
($user, "\002$cn Count:\002 ".$reply);
1319 sub cs_levels_pre
($$$;$) {
1320 my($user, $chan, $cmd, $listonly) = @_;
1322 chk_registered
($user, $chan) or return 0;
1323 my $cn = $chan->{CHAN
};
1324 my $overrideMsg = "LEVELS $cn $cmd";
1325 return can_do
($chan, ($listonly ? 'LEVELSLIST' : 'LEVELS'), $user, { OVERRIDE_MSG
=> $overrideMsg });
1328 sub cs_levels_set
($$$;$) {
1329 my ($user, $chan, $perm, $level) = @_;
1331 cs_levels_pre
($user, $chan, "$perm $level") or return;
1332 my $cn = $chan->{CHAN
};
1334 unless(is_level
($perm)) {
1335 notice
($user, "$perm is not a valid permission.");
1339 if(defined($level)) {
1340 $level = xop_byname
($level);
1341 unless(defined($level) and $level >= 0) {
1342 notice
($user, 'You must specify one of the following levels: '.
1343 'any, uop, vop, hop, aop, sop, cofounder, founder, nobody');
1347 $get_level_max->execute($perm);
1348 my ($max) = $get_level_max->fetchrow_array;
1349 $get_level_max->finish();
1351 if($max and $level > $max) {
1352 notice
($user, "\002$perm\002 cannot be set to " . $plevels[$level+$plzero] . '.');
1356 $add_level->execute($cn, $perm);
1357 $set_level->execute($level, $cn, $perm);
1360 notice
($user, "\002$perm\002 is now disabled in \002$cn\002.");
1362 notice
($user, "\002$perm\002 now requires " . $levels[$level] . " access in \002$cn\002.");
1365 $reset_level->execute($perm, $cn);
1367 notice
($user, "\002$perm\002 has been reset to default.");
1371 sub cs_levels_list
($$) {
1372 my ($user, $chan) = @_;
1374 cs_levels_pre
($user, $chan, 'LIST', 1) or return;
1375 my $cn = $chan->{CHAN
};
1377 $get_levels->execute($cn);
1379 while(my ($name, $def, $lvl) = $get_levels->fetchrow_array) {
1381 (defined($lvl) ? $plevels[$lvl+$plzero] : $plevels[$def+$plzero]),
1382 (defined($lvl) ? '' : '(default)')];
1385 notice
($user, columnar
{ TITLE
=> "Permission levels for \002$cn\002:",
1386 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
) }, @data);
1389 sub cs_levels_clear
($$) {
1390 my ($user, $chan) = @_;
1392 cs_levels_pre
($user, $chan, 'CLEAR') or return;
1393 my $cn = $chan->{CHAN
};
1395 $clear_levels->execute($cn);
1397 notice
($user, "All permissions have been reset to default.");
1400 sub cs_akick_pre
($$$;$) {
1401 my ($user, $chan, $overrideMsg, $list) = @_;
1403 chk_registered
($user, $chan) or return 0;
1405 return can_do
($chan, ($list ? 'AKICKLIST' : 'AKICK'), $user, { OVERRIDE_MSG
=> $overrideMsg });
1408 sub cs_akick_add
($$$$) {
1409 my ($user, $chan, $mask, $reason) = @_;
1410 my $cn = $chan->{CHAN
};
1412 my $adder = cs_akick_pre
($user, $chan, "ADD $mask $reason") or return;
1414 my ($nick, $ident, $host) = parse_mask
($mask);
1416 if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
1417 notice
($user, 'Invalid hostmask.');
1424 unless(valid_nick
($nick)) {
1425 $mask = normalize_hostmask
($mask);
1426 ($nick, $ident, $host) = parse_mask
($mask);
1430 if ($ident eq '' and $host eq '' and !nickserv
::is_registered
($nick)) {
1431 notice
($user, "\002$nick\002 is not registered");
1435 my $rlength = length($reason);
1436 if($rlength >= 350) {
1437 notice
($user, 'AKick reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
1442 my $src = get_user_nick
($user);
1443 if($ident eq '' and $host eq '' and my $old = get_acc
($nick, $chan)) {
1445 notice
($user, "\002$nick\002 is already on the AKick list in \002$cn\002");
1448 if($old < get_best_acc
($user, $chan) or adminserv
::can_do
($user, 'SERVOP')) {
1449 if ($old == FOUNDER
()) {
1450 # This is a fallthrough for the override case.
1451 # It shouldn't happen otherwise.
1452 # I didn't make it part of the previous conditional
1453 # b/c just $err_deny is a bit undescriptive in the override case.
1454 notice
($user, "You can't akick the founder!", $err_deny);
1458 my $root = get_root_nick
($nick);
1459 $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
1460 set_acc
($nick, $user, $chan, -1);
1461 $log_str = "moved \002$nick\002 (root: \002$root\002) from the $levels[$old] list".
1462 " to the AKick list of \002$cn\002";
1463 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1465 notice
($user, $err_deny);
1469 if($ident eq '' and $host eq '') {
1470 $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
1471 if (find_auth
($cn, $nick)) {
1472 # Don't allow a pending AUTH entry to potentially override an AKick entry
1473 # Believe it or not, it almost happened with #animechat on SCnet.
1474 # This would also end up leaving an orphan entry in the akick table.
1475 $nickserv::del_auth-
>execute($nick, $cn);
1476 $nickserv::del_auth-
>finish();
1478 set_acc
($nick, $user, $chan, -1);
1479 my $root = get_root_nick
($nick);
1480 $log_str = "added \002$nick\002 (root: \002$root\002) to the AKick list of \002$cn\002.";
1482 ($nick, $ident, $host) = glob2sql
($nick, $ident, $host);
1483 unless($add_akick->execute($cn, $nick, $ident, $host, $adder, $reason)) {
1484 notice
($user, "\002$mask\002 is already on the AKick list of \002$cn\002.");
1487 $log_str = "added \002$mask\002 to the AKick list of \002$cn\002.";
1491 notice
($user, "You have $log_str");
1492 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1493 if cr_chk_flag
($chan, CRF_VERBOSE
);
1494 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1495 memolog
($chan, "\002$adder\002 $log_str");
1497 akick_allchan
($chan);
1500 sub get_akick_by_num
($$) {
1501 my ($chan, $num) = @_;
1502 my $cn = $chan->{CHAN
};
1504 $get_akick_by_num->execute($cn, $num);
1505 my ($nick, $ident, $host) = $get_akick_by_num->fetchrow_array();
1506 ($nick, $ident, $host) = sql2glob
($nick, $ident, $host);
1507 $get_akick_by_num->finish();
1510 } elsif($ident eq '' and $host eq '') {
1511 # nick based akicks don't use nicks but nickreg.id
1512 # so we have to get the nickreg.nick back
1513 $nick = nickserv
::get_id_nick
($nick);
1515 return ($nick, $ident, $host);
1518 sub cs_akick_del
($$$) {
1519 my ($user, $chan, $mask) = @_;
1520 my $cn = $chan->{CHAN
};
1522 my $adder = cs_akick_pre
($user, $chan, "DEL $mask") or return;
1525 if ($mask =~ /^[0-9\.,-]+$/) {
1526 foreach my $num (makeSeqList
($mask)) {
1527 my ($nick, $ident, $host) = get_akick_by_num
($chan, $num - 1) or next;
1528 if($ident eq '' and $host eq '') {
1531 push @masks, "$nick!$ident\@$host";
1537 foreach my $mask (@masks) {
1538 my ($nick, $ident, $host) = parse_mask
($mask);
1540 if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
1541 notice
($user, 'Invalid hostmask.');
1548 unless(valid_nick
($nick)) {
1549 $mask = normalize_hostmask
($mask);
1550 ($nick, $ident, $host) = parse_mask
($mask);
1554 if ($ident eq '' and $host eq '' and !nickserv
::is_registered
($nick)) {
1555 notice
($user, "\002$nick\002 is not registered");
1559 my ($success, $log_str) = do_akick_del
($chan, $mask, $nick, $ident, $host);
1560 my $src = get_user_nick
($user);
1562 notice
($user, "\002$src\002 $log_str");
1563 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1564 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag
($chan, CRF_VERBOSE
);
1565 memolog
($chan, "\002$adder\002 $log_str");
1567 notice
($user, $log_str);
1572 sub do_akick_del
($$$$$) {
1573 my ($chan, $mask, $nick, $ident, $host) = @_;
1574 my $cn = $chan->{CHAN
};
1577 if($ident eq '' and $host eq '') {
1578 if(get_acc
($nick, $chan) == -1) {
1579 del_acc
($nick, $chan);
1580 $del_nick_akick->execute($cn, $nick); $del_nick_akick->finish();
1581 my $root = get_root_nick
($nick);
1582 return (1, "deleted \002$nick\002 (root: \002$root\002) from the AKick list of \002$cn\002.")
1584 return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
1587 ($nick, $ident, $host) = glob2sql
($nick, $ident, $host);
1588 if($del_akick->execute($cn, $nick, $ident, $host) != 0) {
1589 return (1, "deleted \002$mask\002 from the AKick list of \002$cn\002.");
1591 return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
1596 sub cs_akick_list
($$) {
1597 my ($user, $chan) = @_;
1598 my $cn = $chan->{CHAN
};
1600 cs_akick_pre
($user, $chan, 'LIST', 1) or return;
1604 $get_akick_list->execute($cn);
1606 while(my ($nick, $ident, $host, $adder, $reason, $time) = $get_akick_list->fetchrow_array) {
1608 ($nick, $ident, $host) = sql2glob
($nick, $ident, $host);
1611 if($ident eq '' and $host eq '') {
1612 $nick = nickserv
::get_id_nick
($nick);
1614 $nick = "$nick!$ident\@$host";
1617 push @data, ["\002".++$i."\002", $nick, $adder, ($time ? gmtime2
($time) : ''), $reason];
1620 notice
($user, columnar
{TITLE
=> "AKICK list of \002$cn\002:", DOUBLE
=>1,
1621 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
1624 sub cs_akick_wipe
($$$) {
1625 my ($user, $chan) = @_;
1626 my $cn = $chan->{CHAN
};
1628 my $adder = cs_akick_pre
($user, $chan, 'WIPE') or return;
1630 $drop_akick->execute($cn);
1631 $wipe_acc_list->execute($cn, -1);
1632 my $log_str = "wiped the AKICK list of \002$cn\002.";
1633 my $src = get_user_nick
($user);
1634 notice
($user, "You have $log_str");
1635 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag
($chan, CRF_VERBOSE
);
1636 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1637 memolog
($chan, "\002$adder\002 $log_str");
1640 sub cs_akick_enforce
($$) {
1641 my ($user, $chan) = @_;
1642 my $cn = $chan->{CHAN
};
1644 chk_registered
($user, $chan) or return;
1646 can_do
($chan, 'AKickEnforce', $user, { OVERRIDE_MSG
=> "AKICK $cn ENFORCE" }) or return;
1648 akick_allchan
($chan);
1652 cs_command new SrSv
::AgentUI
::Simple
{
1653 COMMAND
=> [qw(info)],
1654 SYNTAX
=> 'LIST:#chan',
1660 my ($user, @chanList) = @_;
1663 foreach my $cn (@chanList) {
1664 if(ref($cn) eq 'HASH') {
1668 push @chanList, split(',', $cn);
1671 my $chan = { CHAN
=> $cn };
1672 unless(__can_do
($chan, 'INFO', undef, 0)) {
1673 can_do
($chan, 'INFO', $user, { OVERRIDE_MSG
=> "INFO $cn" })
1677 $get_info->execute($cn);
1678 my @result = $get_info->fetchrow_array;
1680 push @reply, "The channel \002$cn\002 is not registered.";
1684 my ($descrip, $regd, $last, $topic, $topicer, $modelock, $founder, $successor, $bot, $bantype) = @result;
1686 $modelock = modes
::sanitize
($modelock) unless can_do
($chan, 'GETKEY', $user, { NOREPLY
=> 1 });
1690 my $topiclock = get_level
($chan, 'SETTOPIC');
1691 push @opts, "Topic Lock ($levels[$topiclock])" if $topiclock;
1693 if(cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
1694 push @reply, "\002$cn\002 is closed and cannot be used: ". get_close
($chan);
1699 push @extra, 'Will not expire' if cr_chk_flag
($chan, CRF_HOLD
);
1700 push @extra, 'Channel is frozen and access suspended' if cr_chk_flag
($chan, CRF_FREEZE
);
1702 push @opts, 'OpGuard' if cr_chk_flag
($chan, CRF_OPGUARD
);
1703 push @opts, 'BotStay' if cr_chk_flag
($chan, CRF_BOTSTAY
);
1704 push @opts, 'SplitOps' if cr_chk_flag
($chan, CRF_SPLITOPS
);
1705 push @opts, 'Verbose' if cr_chk_flag
($chan, CRF_VERBOSE
);
1706 push @opts, 'NeverOp' if cr_chk_flag
($chan, CRF_NEVEROP
);
1707 push @opts, 'Ban type '.$bantype if $bantype;
1708 my $opts = join(', ', @opts);
1712 push @data, ['Founder:', $founder];
1713 push @data, ['Successor:', $successor] if $successor;
1714 push @data, ['Description:', $descrip] if $descrip;
1715 push @data, ['Mode lock:', $modelock];
1716 push @data, ['Settings:', $opts] if $opts;
1717 push @data, ['ChanBot:', $bot] if $bot and $bot ne '';
1719 push @data, ['Registered:', gmtime2
($regd)],
1720 ['Last opping:', gmtime2
($last)],
1721 ['Time now:', gmtime2
(time)];
1723 push @reply, columnar
{TITLE
=> "ChanServ info for \002$cn\002:", NOHIGHLIGHT
=> 1}, @data,
1724 {COLLAPSE
=> \
@extra, BULLET
=> 1};
1726 notice
($user, @reply);
1729 sub cs_set_pre
($$$$) {
1730 my ($user, $chan, $set, $parm) = @_;
1731 my $cn = $chan->{CHAN
};
1735 'founder' => 1, 'successor' => 1, 'unsuccessor' => 1,
1736 #'mlock' => 1, 'modelock' => 1,
1738 'topiclock' => 1, 'greet' => 1, 'opguard' => 1,
1739 'freeze' => 1, 'botstay' => 1, 'verbose' => 1,
1740 'splitops' => 1, 'bantype' => 1, 'dice' => 1,
1741 'welcomeinchan' => 1, 'log' => 1,
1743 'hold' => 1, 'noexpire' => 1, 'no-expire' => 1,
1745 'autovoice' => 1, 'avoice' => 1,
1746 'neverop' => 1, 'noop' => 1,
1748 my %override_set = (
1749 'hold' => 'SERVOP', 'noexpire' => 'SERVOP', 'no-expire' => 'SERVOP',
1750 'freeze' => 'FREEZE', 'botstay' => 'BOT', 'log' => 'LOG',
1753 chk_registered
($user, $chan) or return 0;
1754 if($set =~ /m(?:ode)?lock/) {
1755 notice
($user, "CS SET MLOCK is deprecated and replaced with CS MLOCK",
1756 "For more information, please /CS HELP MLOCK");
1759 unless($valid_set{lc $set}) {
1760 notice
($user, "$set is not a valid ChanServ setting.");
1764 if($override_set{lc($set)}) {
1765 if(adminserv
::can_do
($user, $override_set{lc($set)}) ) {
1766 if(services_conf_log_overrides
) {
1767 my $src = get_user_nick
($user);
1768 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override CS SET $cn $set $parm");
1772 notice
($user, $err_deny);
1777 can_do
($chan, 'SET', $user) or return 0;
1784 my ($user, $chan, $set, $parm) = @_;
1785 my $cn = $chan->{CHAN
};
1788 cs_set_pre
($user, $chan, $set, $parm) or return;
1790 if($set =~ /^founder$/i) {
1792 unless(get_best_acc
($user, $chan) == FOUNDER
) {
1793 if(adminserv
::can_do
($user, 'SERVOP')) {
1796 notice
($user, $err_deny);
1802 unless($root = get_root_nick
($parm)) {
1803 notice
($user, "The nick \002$parm\002 is not registered.");
1807 $get_founder->execute($cn);
1808 my ($prev) = $get_founder->fetchrow_array;
1809 $get_founder->finish();
1811 if(lc($root) eq lc($prev)) {
1812 notice
($user, "\002$parm\002 is already the founder of \002$cn\002.");
1816 set_acc
($prev, $user, $chan, COFOUNDER
);
1818 $set_founder->execute($root, $cn); $set_founder->finish();
1819 set_acc
($root, $user, $chan, FOUNDER
);
1820 notice
($user, ($override ? "The previous founder, \002$prev\002, has" : "You have") . " been moved to the co-founder list of \002$cn\002.");
1821 notice_all_nicks
($user, $root, "\002$root\002 has been set as the founder of \002$cn\002.");
1822 services
::ulog
($csnick, LOG_INFO
(), "set founder of \002$cn\002 to \002$root\002", $user, $chan);
1823 $del_nick_akick->execute($cn, $root); $del_nick_akick->finish(); #just in case
1824 $get_successor->execute($cn);
1825 my $suc = $get_successor->fetchrow_array; $get_successor->finish();
1826 if(lc($suc) eq lc($root)) {
1827 $del_successor->execute($cn); $del_successor->finish();
1828 notice
($user, "Successor has been removed from \002$cn\002.");
1834 if($set eq 'successor') {
1835 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
1836 notice
($user, $err_deny);
1840 if(get_acc
($parm, $chan) == 7) {
1841 notice
($user, "The channel founder may not be the successor.");
1846 unless($root = get_root_nick
($parm)) {
1847 notice
($user, "The nick \002$parm\002 is not registered.");
1851 $set_successor->execute($root, $cn); $set_successor->finish();
1853 notice
($user, "\002$parm\002 is now the successor of \002$cn\002");
1854 services
::ulog
($csnick, LOG_INFO
(), "set successor of \002$cn\002 to \002$root\002", $user, $chan);
1858 if($set eq 'unsuccessor') {
1859 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
1860 notice
($user, $err_deny);
1864 $del_successor->execute($cn); $del_successor->finish();
1866 notice
($user, "Successor has been removed from \002$cn\002.");
1867 services
::ulog
($csnick, LOG_INFO
(), "removed successor from \002$cn\002", $user, $chan);
1871 if($set =~ /m(?:ode)?lock/) {
1872 my $modes = modes
::merge
($parm, '+r', 1);
1873 $modes = sanitize_mlockable
($modes);
1874 $set_modelock->execute($modes, $cn);
1876 notice
($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
1881 if($set eq 'desc') {
1882 $set_descrip->execute($parm, $cn);
1884 notice
($user, "Description of \002$cn\002 has been changed.");
1888 if($set eq 'topiclock') {
1889 my $perm = xop_byname
($parm);
1890 if($parm =~ /^(?:no|off|false|0)$/i) {
1891 cs_levels_set
($user, $chan, 'SETTOPIC');
1892 cs_levels_set
($user, $chan, 'TOPIC');
1893 } elsif($perm >= 0 and defined($perm)) {
1894 cs_levels_set
($user, $chan, 'SETTOPIC', $parm);
1895 cs_levels_set
($user, $chan, 'TOPIC', $parm);
1897 notice
($user, 'Syntax: SET <#chan> TOPICLOCK <off|any|uop|vop|hop|aop|sop|cf|founder>');
1902 if($set =~ /^bantype$/i) {
1903 unless (misc
::isint
($parm) and ($parm >= 0 and $parm <= 10)) {
1904 notice
($user, 'Invalid bantype');
1908 $set_bantype->execute($parm, $cn);
1910 notice
($user, "Ban-Type for \002$cn\002 now set to \002$parm\002.");
1916 if($parm =~ /^(?:no|off|false|0)$/i) { $val = 0; }
1917 elsif($parm =~ /^(?:yes|on|true|1)$/i) { $val = 1; }
1919 notice
($user, "Please say \002on\002 or \002off\002.");
1923 if($set =~ /^(?:opguard|secureops)$/i) {
1924 cr_set_flag
($chan, CRF_OPGUARD
, $val);
1928 "OpGuard is now \002ON\002.",
1929 "Channel status may not be granted by unauthorized users in \002$cn\002."#,
1930 #"Note that you must change the $csnick LEVELS settings for VOICE, HALFOP, OP, and/or ADMIN for this setting to have any effect."
1934 "OpGuard is now \002OFF\002.",
1935 "Channel status may be given freely in \002$cn\002."
1942 if($set =~ /^(?:splitops)$/i) {
1943 cr_set_flag
($chan, CRF_SPLITOPS
, $val);
1946 notice
($user, "SplitOps is now \002ON\002.");
1948 notice
($user, "SplitOps is now \002OFF\002.");
1954 if($set =~ /^(hold|no-?expire)$/i) {
1955 cr_set_flag
($chan, CRF_HOLD
, $val);
1958 notice
($user, "\002$cn\002 will not expire");
1959 services
::ulog
($csnick, LOG_INFO
(), "has held \002$cn\002", $user, $chan);
1961 notice
($user, "\002$cn\002 is no longer held from expiration");
1962 services
::ulog
($csnick, LOG_INFO
(), "has removed \002$cn\002 from hold", $user, $chan);
1968 if($set =~ /^freeze$/i) {
1969 cr_set_flag
($chan, CRF_FREEZE
, $val);
1972 notice
($user, "\002$cn\002 is now frozen and access suspended");
1973 services
::ulog
($csnick, LOG_INFO
(), "has frozen \002$cn\002", $user, $chan);
1975 notice
($user, "\002$cn\002 is now unfrozen and access restored");
1976 services
::ulog
($csnick, LOG_INFO
(), "has unfrozen \002$cn\002", $user, $chan);
1982 if($set =~ /^botstay$/i) {
1983 cr_set_flag
($chan, CRF_BOTSTAY
, $val);
1986 notice
($user, "Bot will now always stay in \002$cn");
1987 botserv
::bot_join
($chan, undef);
1989 notice
($user, "Bot will now part if less than one user is in \002$cn");
1990 botserv
::bot_part_if_needed
(undef, $chan, "Botstay turned off");
1995 if($set =~ /^verbose$/i) {
1996 cr_set_flag
($chan, CRF_VERBOSE
, $val);
1999 notice
($user, "Verbose mode enabled on \002$cn");
2002 notice
($user, "Verbose mode disabled on \002$cn");
2007 if($set =~ /^greet$/i) {
2009 notice
($user, "$csnick SET $cn GREET ON is deprecated.",
2010 "Please use $csnick LEVELS $cn SET GREET <rank>");
2012 cs_levels_set
($user, $chan, 'GREET', 'nobody');
2018 if($set =~ /^dice$/i) {
2020 notice
($user, "$csnick SET $cn DICE ON is deprecated.",
2021 "Please use $csnick LEVELS $cn SET DICE <rank>");
2023 cs_levels_set
($user, $chan, 'DICE', 'nobody');
2029 if($set =~ /^welcomeinchan$/i) {
2030 cr_set_flag
($chan, CRF_WELCOMEINCHAN
(), $val);
2033 notice
($user, "WELCOME messages will be put in the channel.");
2035 notice
($user, "WELCOME messages will be sent privately.");
2041 if($set =~ /^log$/i) {
2042 unless(module
::is_loaded
('logserv')) {
2043 notice
($user, "module logserv is not loaded, logging is not available.");
2048 logserv
::addchan
($user, $cn) and cr_set_flag
($chan, CRF_LOG
, $val);
2051 logserv
::delchan
($user, $cn) and cr_set_flag
($chan, CRF_LOG
, $val);
2056 if($set =~ /^a(?:uto)?voice$/i) {
2057 cr_set_flag
($chan, CRF_AUTOVOICE
(), $val);
2060 notice
($user, "All users w/o access will be autovoiced on join.");
2062 notice
($user, "AUTOVOICE disabled.");
2068 if($set =~ /^(?:never|no)op$/i) {
2069 cr_set_flag
($chan, CRF_NEVEROP
(), $val);
2072 notice
($user, "Users will not be automatically opped on join.");
2074 notice
($user, "Users with access will now be automatically opped on join.");
2082 my ($user, $chan, @tnicks) = @_;
2084 chk_registered
($user, $chan) or return;
2086 my $cn = $chan->{CHAN
};
2088 my ($candoNick, $override) = can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> "WHY $cn @tnicks" });
2089 return unless $candoNick;
2092 foreach my $tnick (@tnicks) {
2093 my $tuser = { NICK
=> $tnick };
2094 unless(get_user_id
($tuser)) {
2095 push @reply, "\002$tnick\002: No such user.";
2100 if(is_online
($tnick)) {
2107 $get_all_acc->execute(get_user_id
($tuser), $cn);
2108 while(my ($rnick, $acc) = $get_all_acc->fetchrow_array) {
2110 push @reply, "\002$tnick\002 $has $plevels[$acc+$plzero] access to \002$cn\002 due to identification to the nick \002$rnick\002.";
2112 $get_all_acc->finish();
2115 $check_auth_chan -> execute
($cn, $tnick);
2116 if (my ($nick, $data) = $check_auth_chan->fetchrow_array()) {
2117 my ($adder, $old, $level, $time) = split(/:/, $data);
2118 push @reply, "\002$tnick\002 is awaiting authorization to be added to the $cn \002$levels[$level]\002 list.\n";
2121 push @reply, "\002$tnick\002 has no access to \002$cn\002.";
2125 notice
($user, @reply);
2128 sub cs_setmodes
($$$@) {
2129 my ($user, $cmd, $chan, @args) = @_;
2131 my $agent = $user->{AGENT
} or $csUser;
2132 my $src = get_user_nick
($user);
2133 my $cn = $chan->{CHAN
};
2136 if (cr_chk_flag
($chan, CRF_FREEZE
())) {
2137 notice
($user, "\002$cn\002 is frozen and access suspended.");
2141 if(scalar(@args) == 0) {
2144 } elsif($args[0] =~ /^#/) {
2145 foreach my $chn ($cn, @args) {
2146 next unless $chn =~ /^#/;
2147 no warnings
'prototype'; # we call ourselves
2148 cs_setmodes
($user, $cmd, { CHAN
=> $chn });
2151 } elsif((scalar(@args) == 1) and (lc($args[0]) eq lc($src))) {
2155 # PROTECT is deprecated. remove it in a couple versions.
2156 # It should be called ADMIN under PREFIX_AQ
2157 my @mperms = ('VOICE', 'HALFOP', 'OP', 'ADMIN', 'OWNER');
2158 my @l = ('v', 'h', 'o', 'a', 'q');
2159 my ($level, @modes, $count);
2161 if($cmd =~ /voice$/i) { $level = 0 }
2162 elsif($cmd =~ /h(alf)?op$/i) { $level = 1 }
2163 elsif($cmd =~ /op$/i) { $level = 2 }
2164 elsif($cmd =~ /(protect|admin)$/i) { $level = 3 }
2165 elsif($cmd =~ /owner$/i) { $level = 4 }
2166 my $de = 1 if($cmd =~ s/^de//i);
2169 my $acc = get_best_acc
($user, $chan);
2171 # XXX I'm not sure this is the best way to do it.
2173 ($de and $self) or ($self and ($level + 2) <= $acc) or
2174 can_do
($chan, $mperms[$level], $user, { ACC
=> $acc, NOREPLY
=> 1, OVERRIDE_MSG
=> "$cmd $cn @args" }) )
2176 notice
($user, "$cn: $err_deny");
2180 my ($override, $check_override);
2182 foreach my $target (@args) {
2185 $tuser = ($self ? $user : { NICK
=> $target } );
2187 unless(is_in_chan
($tuser, $chan)) {
2188 notice
($user, "\002$target\002 is not in \002$cn\002.");
2192 my $top = get_op
($tuser, $chan);
2195 unless($top & (2**$level)) {
2196 notice
($user, "\002$target\002 has no $cmd in \002$cn\002.");
2200 if(!$override and get_best_acc
($tuser, $chan) > $acc) {
2201 unless($check_override) {
2202 $override = adminserv
::can_do
($user, 'SUPER');
2203 $check_override = 1;
2205 if($check_override and !$override) {
2206 notice
($user, "\002$target\002 outranks you in \002$cn\002.");
2211 if($top & (2**$level)) {
2213 notice
($user, "You already have $cmd in \002$cn\002.");
2215 notice
($user, "\002$target\002 already has $cmd in \002$cn\002.");
2219 if (cr_chk_flag
($chan, CRF_OPGUARD
()) and
2220 !can_keep_op
($user, $chan, $tuser, $l[$level]))
2222 notice
($user, "$target may not hold ops in $cn because OpGuard is enabled. ".
2223 "Please respect the founders wishes.");
2227 get_user_id
($tuser);
2228 push @modes, [($de ? '-' : '+').$l[$level], $tuser];
2233 ircd
::setmode2
(agent
($chan), $cn, @modes) if scalar @modes;
2234 ircd
::notice
(agent
($chan), '%'.$cn, "$src used ".($de ? "de$cmd" : $cmd).' '.join(' ', @args))
2235 if !$self and (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
2239 my ($user, $chan) = @_;
2240 my $cn = $chan->{CHAN
};
2242 chk_registered
($user, $chan) or return;
2244 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
2245 notice
($user, $err_deny);
2250 notice
($user, $cn.' has been dropped.');
2251 services
::ulog
($csnick, LOG_INFO
(), "dropped $cn", $user, $chan);
2253 undef($enforcers{lc $cn});
2254 botserv
::bot_part_if_needed
(undef(), $chan, "Channel dropped.");
2257 sub cs_kick
($$$;$$) {
2258 my ($user, $chan, $target, $ban, $reason) = @_;
2260 my $cmd = ($ban ? 'KICKBAN' : 'KICK');
2261 my $perm = ($ban ? 'BAN' : 'KICK');
2262 if(ref($chan) ne 'HASH' || !defined($chan->{CHAN
})) {
2263 notice
($user, "Invalid $cmd command, no channel specified");
2267 my $srclevel = get_best_acc
($user, $chan);
2269 my ($nick, $override) = can_do
($chan, ($ban ? 'BAN' : 'KICK'), $user, { ACC
=> $srclevel });
2271 print "NO NICK???\n";
2275 my $src = get_user_nick
($user);
2276 my $cn = $chan->{CHAN
};
2278 $reason = "Requested by $src".($reason?": $reason":'');
2281 ["I'm sorry, $src, I'm afraid I can't do that."],
2282 ["They are not in \002$cn\002."],
2287 my $peace = ({modes
::splitmodes
(get_modelock
($chan))}->{Q
}->[0] eq '+');
2289 my @targets = split(/\,/, $target);
2290 foreach $target (@targets) {
2291 my $tuser = { NICK
=> $target };
2292 get_user_id
($tuser);
2293 my $targetlevel = get_best_acc
($tuser, $chan);
2295 if(lc $target eq lc agent
($chan) or adminserv
::is_service
($tuser)) {
2296 push @{$errors[0]}, $target;
2300 if(get_user_id
($tuser)) {
2301 unless(is_in_chan
($tuser, $chan)) {
2303 push @notinchan, $tuser;
2305 push @{$errors[1]}, $target;
2310 push @{$errors[3]}, $target;
2314 if( ( ($peace and $targetlevel > 0) or ($srclevel <= $targetlevel) )
2315 and not ($override && check_override
($user, ($ban ? 'BAN' : 'KICK'), "$cmd $cn $target")) )
2317 push @{$errors[2]}, $target;
2322 kickban
($chan, $tuser, undef, $reason, 1);
2324 print "is service? " , adminserv
::is_service
($user);
2325 ircd
::kick
(agent
($chan), $cn, $tuser, $reason) unless adminserv
::is_service
($user);
2328 ircd
::flushmodes
() if($ban);
2330 foreach my $errlist (@errors) {
2332 my $msg = shift @$errlist;
2334 foreach my $e (@$errlist) { $e = "\002$e\002" }
2338 enum
("or", @$errlist).
2343 cs_ban
($user, $chan, '', @notinchan) if ($ban and scalar (@notinchan));
2346 sub cs_kickmask
($$$;$$) {
2347 my ($user, $chan, $mask, $ban, $reason) = @_;
2349 my $srclevel = get_best_acc
($user, $chan);
2350 my $src = get_user_nick
($user);
2351 my $cn = $chan->{CHAN
};
2353 my $candoOpts = { ACC
=> $srclevel, OVERRIDE_MSG
=> 'KICK'.($ban ? 'BAN' : '')."MASK $cn $mask $reason" };
2354 my ($nick, $override) = can_do
($chan, ($ban ? 'BAN' : 'KICK'), $user, $candoOpts);
2355 return unless $nick;
2358 $reason = "Requested by $src".($reason?": $reason":'');
2360 my $count = kickmask_noacc
($chan, $mask, $reason, $ban);
2361 notice
($user, ($count ? "Users kicked from \002$cn\002: $count." : "No users in \002$cn\002 matched $mask."))
2365 my ($user, $chan, $type, @targets) = @_;
2366 my $cn = $chan->{CHAN
};
2367 my $src = get_user_nick
($user);
2369 my $srclevel = get_best_acc
($user, $chan);
2370 my ($nick, $override) = can_do
($chan, 'BAN', $user, { ACC
=> $srclevel });
2371 return unless $nick;
2374 ["I'm sorry, $src, I'm afraid I can't do that."],
2379 my (@bans, @unbans);
2380 foreach my $target (@targets) {
2386 elsif($target =~ /\,/) {
2387 push @targets, split(',', $target);
2390 elsif($target eq '') {
2391 # Should never happen
2392 # but it could, given the split above
2395 elsif($target =~ /^-/) {
2397 push @unbans, $target;
2401 elsif($target =~ /[!@]+/) {
2402 ircd
::debug
("normalizing hostmask $target");
2403 #$target = normalize_hostmask($target);
2405 my ($nick, $ident, $host) = parse_mask
($target);
2406 $nick = '*' unless length($nick);
2407 $ident = '*' unless length($ident);
2408 $host = '*' unless length($host);
2409 $target = "$nick\!$ident\@$host";
2411 ircd
::debug
("normalized hostmask: $target");
2413 push @bans, $target;
2417 elsif(valid_nick
($target)) {
2418 $tuser = { NICK
=> $target };
2420 elsif($target = validate_ban
($target)) {
2421 push @bans, $target;
2424 notice
($user, "Not a valid ban target: $target");
2427 my $targetlevel = get_best_acc
($tuser, $chan);
2429 if(lc $target eq lc agent
($chan) or adminserv
::is_service
($tuser)) {
2430 push @{$errors[0]}, get_user_nick
($tuser);
2434 unless(get_user_id
($tuser)) {
2435 push @{$errors[1]}, get_user_nick
($tuser);
2438 if( $srclevel <= $targetlevel and not ($override && check_override
($user, 'BAN', "BAN $cn $target")) ) {
2439 push @{$errors[2]}, $target;
2443 push @bans, make_banmask
($chan, $tuser, $type);
2446 foreach my $errlist (@errors) {
2448 my $msg = shift @$errlist;
2450 foreach my $e (@$errlist) { $e = "\002$e\002" }
2454 enum
("or", @$errlist).
2460 ircd
::ban_list
(agent
($chan), $cn, +1, 'b', @bans) if (scalar(@bans));
2461 ircd
::notice
(agent
($chan), $cn, "$src used BAN ".join(' ', @bans))
2462 if (lc $user->{AGENT
} eq lc $csnick) and (cr_chk_flag
($chan, CRF_VERBOSE
) and scalar(@bans));
2463 cs_unban
($user, $chan, @unbans) if scalar(@unbans);
2466 sub cs_invite
($$@) {
2467 my ($user, $chan, @targets) = @_;
2468 my $src = get_user_nick
($user);
2469 my $cn = $chan->{CHAN
};
2470 my $srclevel = get_best_acc
($user, $chan);
2473 ["They are not online."],
2474 ["They are already in \002$cn\002."],
2479 foreach my $target (@targets) {
2484 $tnick = get_user_nick
($tuser);
2485 } elsif(lc($src) eq lc($target)) {
2488 } elsif($target =~ /\,/) {
2489 push @targets, split(',', $target);
2491 } elsif($target eq '') {
2492 # Should never happen
2493 # but it could, given the split above
2496 $tuser = { NICK
=> $target };
2500 my $candoOpts = { ACC
=> $srclevel, NOREPLY
=> 1, OVERRIDE_MSG
=> "INVITE $cn $target" };
2501 if(lc($src) eq lc($tnick)) {
2502 unless(can_do
($chan, 'InviteSelf', $user, $candoOpts)) {
2503 push @{$errors[2]}, $tnick;
2508 unless(can_do
($chan, 'INVITE', $user, $candoOpts)) {
2509 push @{$errors[2]}, $tnick;
2513 unless(nickserv
::is_online
($tnick)) {
2514 push @{$errors[0]}, $tnick;
2518 # invite is annoying, so punish them mercilessly
2519 return if flood_check
($user, 2);
2522 if(is_in_chan
($tuser, $chan)) {
2523 push @{$errors[1]}, $tnick;
2527 ircd
::invite
(agent
($chan), $cn, $tuser); push @invited, $tnick;
2528 ircd
::notice
(agent
($chan), $tuser, "\002$src\002 has invited you to \002$cn\002.")
2529 unless(lc($src) eq lc($tnick));
2532 foreach my $errlist (@errors) {
2534 my $msg = shift @$errlist;
2536 foreach my $e (@$errlist) { $e = "\002$e\002" }
2540 enum
("or", @$errlist).
2546 ircd
::notice
(agent
($chan), $cn, "$src used INVITE ".join(' ', @invited))
2547 if (lc $user->{AGENT
} eq lc $csnick)and cr_chk_flag
($chan, CRF_VERBOSE
) and scalar(@invited);
2551 my ($user, $chan, $reason, $type) = @_;
2552 # $type is a flag, either CRF_CLOSE or CRF_DRONE
2553 my $cn = $chan->{CHAN
};
2556 unless($oper = adminserv
::can_do
($user, 'SERVOP')) {
2557 notice
($user, $err_deny);
2561 my $rlength = length($reason);
2562 if($rlength >= 350) {
2563 notice
($user, 'Close reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2567 if(is_registered
($chan)) {
2568 $drop_acc->execute($cn);
2569 $drop_lvl->execute($cn);
2570 $del_close->execute($cn);
2571 $drop_akick->execute($cn);
2572 $drop_welcome->execute($cn);
2573 $drop_chantext->execute($cn);
2574 $drop_nicktext->execute($cn); # Leftover channel auths
2576 $set_founder->execute($oper, $cn);
2579 $register->execute($cn, $reason, $oper);
2581 $set_modelock->execute('+rsnt', $cn);
2583 set_acc
($oper, undef, $chan, FOUNDER
);
2585 $set_close->execute($cn, $reason, $oper, $type);
2586 cr_set_flag
($chan, (CRF_FREEZE
| CRF_CLOSE
| CRF_DRONE
), 0); #unset flags
2587 cr_set_flag
($chan, CRF_HOLD
, 1); #set flags
2588 cr_set_flag
($chan, $type, 1); #set flags
2589 my $src = get_user_nick
($user);
2590 my $time = gmtime2
(time);
2591 my $cmsg = "is closed [$src $time]: $reason";
2593 if ($type == CRF_CLOSE
) {
2594 cr_set_flag
($chan, CRF_CLOSE
, 1); #set flags
2595 clear_users
($chan, "Channel $cmsg");
2596 ircd
::settopic
(agent
($chan), $cn, $src, time(), "Channel $cmsg")
2598 elsif ($type == CRF_DRONE
) {
2599 cr_set_flag
($chan, CRF_DRONE
, 1); #set flags
2600 chan_kill
($chan, "$cn $cmsg");
2603 notice
($user, "The channel \002$cn\002 is now closed.");
2604 services
::ulog
($csnick, LOG_INFO
(), "closed $cn with reason: $reason", $user, $chan);
2607 sub cs_clear_pre
($$) {
2608 my ($user, $chan) = @_;
2609 my $cn = $chan->{CHAN
};
2611 my $srclevel = get_best_acc
($user, $chan);
2613 my ($cando, $override) = can_do
($chan, 'CLEAR', $user, { ACC
=> $srclevel });
2614 return 0 unless($cando);
2616 $get_highrank->execute($cn);
2617 my ($highrank_nick, $highrank_level) = $get_highrank->fetchrow_array();
2618 $get_highrank->finish();
2620 if($highrank_level > $srclevel && !$override) {
2621 notice
($user, "$highrank_nick outranks you in $cn (level: $levels[$highrank_level])");
2628 sub cs_clear_users
($$;$) {
2629 my ($user, $chan, $reason) = @_;
2630 my $src = get_user_nick
($user);
2632 cs_clear_pre
($user, $chan) or return;
2634 my $rlength = length($reason);
2635 if($rlength >= 350) {
2636 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2640 clear_users
($chan, "CLEAR USERS by \002$src\002".($reason?" reason: $reason":''));
2643 sub cs_clear_modes
($$;$) {
2644 my ($user, $chan, $reason) = @_;
2645 my $cn = $chan->{CHAN
};
2646 my $src = get_user_nick
($user);
2648 cs_clear_pre
($user, $chan) or return;
2650 my $rlength = length($reason);
2651 if($rlength >= 350) {
2652 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2656 my $agent = agent
($chan);
2657 ircd
::notice
($agent, $cn, "CLEAR MODES by \002$src\002".($reason?" reason: $reason":''));
2659 $get_chanmodes->execute($cn);
2660 my ($curmodes) = $get_chanmodes->fetchrow_array;
2661 my $ml = get_modelock
($chan);
2663 # This method may exceed the 12-mode limit
2664 # But it seems to succeed anyway, even with more than 12.
2665 my ($modes, $parms) = split(/ /, modes
::merge
(modes
::invert
($curmodes), $ml, 1). ' * *', 2);
2666 # we split this separately,
2667 # as otherwise it insists on taking the result of the split as a scalar quantity
2668 ircd
::setmode
($agent, $cn, $modes, $parms);
2672 sub cs_clear_ops
($$;$) {
2673 my ($user, $chan, $reason) = @_;
2674 my $cn = $chan->{CHAN
};
2675 my $src = get_user_nick
($user);
2677 cs_clear_pre
($user, $chan) or return;
2679 my $rlength = length($reason);
2680 if($rlength >= 350) {
2681 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2687 ircd
::notice
(agent
($chan), $cn, "CLEAR OPS by \002$src\002".($reason?" reason: $reason":''));
2691 sub cs_clear_bans
($$;$$) {
2692 my ($user, $chan, $type, $reason) = @_;
2693 my $cn = $chan->{CHAN
};
2694 my $src = get_user_nick
($user);
2695 $type = 0 unless defined $type;
2697 cs_clear_pre
($user, $chan) or return;
2699 my $rlength = length($reason);
2700 if($rlength >= 350) {
2701 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2705 clear_bans
($chan, $type);
2707 ircd
::notice
(agent
($chan), $cn, "CLEAR BANS by \002$src\002".($reason?" reason: $reason":''));
2710 sub cs_welcome_pre
($$) {
2711 my ($user, $chan) = @_;
2713 return can_do
($chan, 'WELCOME', $user);
2716 sub cs_welcome_add
($$$) {
2717 my ($user, $chan, $msg) = @_;
2718 my $src = get_best_acc
($user, $chan, 1);
2719 my $cn = $chan->{CHAN
};
2721 cs_welcome_pre
($user, $chan) or return;
2723 my $mlength = length($msg);
2724 if($mlength >= 350) {
2725 notice
($user, 'Welcome Message is too long by '. $mlength-350 .' character(s). Maximum length is 350 characters.');
2729 $count_welcome->execute($cn);
2730 my $count = $count_welcome->fetchrow_array;
2732 notice
($user, 'There is a maximum of five (5) Channel Welcome Messages.');
2736 $add_welcome->execute($cn, ++$count, $src, $msg);
2738 notice
($user, "Welcome message number $count for \002$cn\002 set to:", " $msg");
2741 sub cs_welcome_list
($$) {
2742 my ($user, $chan) = @_;
2743 my $cn = $chan->{CHAN
};
2745 cs_welcome_pre
($user, $chan) or return;
2747 $list_welcome->execute($cn);
2751 while(my ($id, $time, $adder, $msg) = $list_welcome->fetchrow_array) {
2752 push @data, ["$id.", $adder, gmtime2
($time), $msg];
2754 $list_welcome->finish();
2756 notice
($user, columnar
{TITLE
=> "Welcome message list for \002$cn\002:", DOUBLE
=>1,
2757 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2760 sub cs_welcome_del
($$$) {
2761 my ($user, $chan, $id) = @_;
2762 my $cn = $chan->{CHAN
};
2764 cs_welcome_pre
($user, $chan) or return;
2766 if ($del_welcome->execute($cn, $id) == 1) {
2767 notice
($user, "Welcome Message \002$id\002 deleted from \002$cn\002");
2768 $consolidate_welcome->execute($cn, $id);
2772 "Welcome Message number $id for \002$cn\002 does not exist.");
2776 sub cs_alist
($$;$) {
2777 my ($user, $chan, $mask) = @_;
2778 my $cn = $chan->{CHAN
};
2780 chk_registered
($user, $chan) or return;
2782 my $slevel = get_best_acc
($user, $chan);
2784 can_do
($chan, 'ACCLIST', $user, { ACC
=> $slevel }) or return;
2789 my ($mnick, $mident, $mhost) = glob2sql
(parse_mask
($mask));
2790 $mnick = '%' if($mnick eq '');
2791 $mident = '%' if($mident eq '');
2792 $mhost = '%' if($mhost eq '');
2794 $get_acc_list2_mask->execute($mnick, $cn, $mnick, $mident, $mhost);
2795 while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2_mask->fetchrow_array) {
2796 push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
2797 push @reply, ' '.($time ? 'Date/time added: '. gmtime2
($time).' ' : '').
2798 ($last_used ? 'Last used '.time_ago
($last_used).' ago' : '') if ($time or $last_used);
2800 $get_acc_list2_mask->finish();
2802 $get_acc_list2->execute($cn);
2803 while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2->fetchrow_array) {
2804 push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
2805 push @reply, ' '.($time ? 'Date/time added: '. gmtime2
($time).' ' : '').
2806 ($last_used ? 'Last used '.time_ago
($last_used).' ago' : '') if ($time or $last_used);
2808 $get_acc_list2->finish();
2811 notice
($user, "Access list for \002$cn\002:", @reply);
2816 sub cs_banlist
($$) {
2817 my ($user, $chan) = @_;
2818 my $cn = $chan->{CHAN
};
2819 can_do
($chan, 'UnbanSelf', $user, { NOREPLY
=> 1 }) or can_do
($chan, 'BAN', $user) or return;
2821 my $i = 0; my @data;
2822 $list_bans->execute($cn, 0);
2823 while(my ($mask, $setter, $time) = $list_bans->fetchrow_array()) {
2824 push @data, ["\002".++$i."\002", sql2glob
($mask), $setter, ($time ? gmtime2
($time) : '')];
2827 notice
($user, columnar
{TITLE
=> "Ban list of \002$cn\002:", DOUBLE
=>1,
2828 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2832 my ($user, $chan, @parms) = @_;
2833 my $cn = $chan->{CHAN
};
2836 $self = 1 if ( (scalar(@parms) == 1) and ( lc($parms[0]) eq lc(get_user_nick
($user)) ) );
2837 if ($parms[0] eq '*') {
2838 cs_clear_bans
($user, $chan);
2842 can_do
($chan, ($self ? 'UnbanSelf' : 'UNBAN'), $user) or return;
2845 my (@userlist, @masklist);
2846 foreach my $parm (@parms) {
2847 if(valid_nick
($parm)) {
2848 my $tuser = ($self ? $user : { NICK
=> $parm });
2849 unless(get_user_id
($tuser)) {
2850 notice
($user, "No such user: \002$parm\002");
2853 push @userlist, $tuser;
2854 } elsif($parm =~ /^[0-9\.,-]+$/) {
2855 foreach my $num (makeSeqList
($parm)) {
2856 push @masklist, get_ban_num
($chan, $num);
2859 push @masklist, $parm;
2863 if(scalar(@userlist)) {
2864 unban_user
($chan, @userlist);
2865 notice
($user, "All bans affecting " .
2866 ( $self ? 'you' : enum
( 'and', map(get_user_nick
($_), @userlist) ) ) .
2867 " on \002$cn\002 have been removed.");
2869 if(scalar(@masklist)) {
2870 ircd
::ban_list
(agent
($chan), $cn, -1, 'b', @masklist);
2871 notice
($user, "The following bans have been removed: ".join(' ', @masklist))
2872 if scalar(@masklist);
2876 sub cs_updown
($$@) {
2877 my ($user, $cmd, @chans) = @_;
2878 return cs_updown2
($user, $cmd, { CHAN
=> shift @chans }, @chans)
2879 if (defined($chans[1]) and $chans[1] !~ "^\#" and $chans[0] =~ "^\#");
2881 @chans = get_user_chans
($user)
2884 if (uc($cmd) eq 'UP') {
2885 foreach my $cn (@chans) {
2886 next unless ($cn =~ /^\#/);
2887 my $chan = { CHAN
=> $cn };
2888 next if cr_chk_flag
($chan, (CRF_DRONE
| CRF_CLOSE
| CRF_FREEZE
), 1);
2889 chanserv
::set_modes
($user, $chan, chanserv
::get_best_acc
($user, $chan));
2892 elsif (uc($cmd) eq 'DOWN') {
2893 foreach my $cn (@chans) {
2894 next unless ($cn =~ /^\#/);
2895 chanserv
::unset_modes
($user, { CHAN
=> $cn });
2900 sub cs_updown2
($$$@) {
2901 my ($user, $cmd, $chan, @targets) = @_;
2903 my $agent = $user->{AGENT
} or $csnick;
2904 my $cn = $chan->{CHAN
};
2906 return unless chk_registered
($user, $chan);
2907 if (cr_chk_flag
($chan, CRF_FREEZE
())) {
2908 notice
($user, "\002$cn\002 is frozen and access suspended.");
2912 my $acc = get_best_acc
($user, $chan);
2913 return unless(can_do
($chan, 'UPDOWN', $user, { ACC
=> $acc }));
2915 my $updown = ((uc($cmd) eq 'UP') ? 1 : 0);
2917 my ($override, $check_override);
2919 foreach my $target (@targets) {
2921 my $tuser = { NICK
=> $target };
2923 unless(is_in_chan
($tuser, $chan)) {
2924 notice
($user, "\002$target\002 is not in \002$cn\002.");
2929 push @list, $target;
2930 chanserv
::set_modes
($tuser, $chan, chanserv
::get_best_acc
($tuser, $chan));
2933 my $top = get_op
($tuser, $chan);
2935 notice
($user, "\002$target\002 is already deopped in \002$cn\002.");
2939 if(!$override and get_best_acc
($tuser, $chan) > $acc) {
2940 unless($check_override) {
2941 $override = adminserv
::can_do
($user, 'SUPER');
2942 $check_override = 1;
2944 if($check_override and !$override) {
2945 notice
($user, "\002$target\002 outranks you in \002$cn\002.");
2949 push @list, $target;
2950 chanserv
::unset_modes
($tuser, { CHAN
=> $cn });
2955 my $src = get_user_nick
($user);
2956 ircd
::notice
(agent
($chan), '%'.$cn, "$src used $cmd ".join(' ', @list))
2957 if (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
2961 my ($user, $chan) = @_;
2962 my $cn = $chan->{CHAN
};
2964 can_do
($chan, 'GETKEY', $user) or return;
2966 $get_chanmodes->execute($cn);
2967 my $modes = $get_chanmodes->fetchrow_array; $get_chanmodes->finish();
2969 if(my $key = modes
::get_key
($modes)) {
2970 notice
($user, "Channel key for \002$cn\002: $key");
2973 notice
($user, "\002$cn\002 has no channel key.");
2978 my ($user, $chan, $cmd, @args) = @_;
2979 my $cn = $chan->{CHAN
};
2982 return unless chk_registered
($user, $chan);
2983 return unless can_do
($chan, 'AccChange', $user);
2984 my $userlevel = get_best_acc
($user, $chan);
2985 if($cmd eq 'list') {
2987 $list_auth_chan->execute($cn);
2988 while(my ($nick, $data) = $list_auth_chan->fetchrow_array()) {
2989 my ($adder, $old, $level, $time) = split(/:/, $data);
2990 push @data, ["\002$nick\002", $levels[$level], $adder, gmtime2
($time)];
2992 if ($list_auth_chan->rows()) {
2993 notice
($user, columnar
{TITLE
=> "Pending authorizations for \002$cn\002:",
2994 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2997 notice
($user, "There are no pending authorizations for \002$cn\002");
2999 $list_auth_chan->finish();
3001 elsif($cmd eq 'remove' or $cmd eq 'delete' or $cmd eq 'del') {
3002 my ($nick, $adder, $old, $level, $time);
3003 my $parm = shift @args;
3004 if(misc
::isint
($parm) and ($nick, $adder, $old, $level, $time) = get_auth_num
($cn, $parm))
3007 elsif (($adder, $old, $level, $time) = get_auth_nick
($cn, $parm))
3012 # This should normally be an 'else' as the elsif above should prove false
3013 # For some reason, it doesn't work. the unless ($nick) fixes it.
3014 # It only doesn't work for numbered entries
3015 notice
($user, "There is no entry for \002$parm\002 in \002$cn\002's AUTH list");
3018 $nickserv::del_auth-
>execute($nick, $cn); $nickserv::del_auth-
>finish();
3019 my $log_str = "deleted AUTH entry $cn $nick $levels[$level]";
3020 my $src = get_user_nick
($user);
3021 notice
($user, "You have $log_str");
3022 ircd
::notice
(agent
($chan), '%'.$cn, "has \002$src\002 has $log_str")
3023 if cr_chk_flag
($chan, CRF_VERBOSE
);
3024 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "has $log_str", $user, $chan);
3027 notice
($user, "Unknown AUTH command \002$cmd\002");
3032 my ($user, $chan, $modes_in, @parms_in) = @_;
3033 can_do
($chan, 'MODE', $user) or return undef;
3034 ($modes_in, @parms_in) = validate_chmodes
($modes_in, @parms_in);
3043 my $sign = '+'; my $cn = $chan->{CHAN
};
3044 my ($modes_out, @parms_out, @bans);
3045 foreach my $mode (split(//, $modes_in)) {
3046 $sign = $mode if $mode =~ /[+-]/;
3047 if ($permhash{$mode}) {
3048 my $parm = shift @parms_in;
3049 cs_setmodes
($user, ($sign eq '-' ? 'de' : '').$permhash{$mode}, $chan, $parm);
3051 elsif ($mode eq 'b') {
3052 my $parm = shift @parms_in;
3058 elsif($mode =~ /[eIlLkjf]/) {
3059 $modes_out .= $mode;
3060 push @parms_out, shift @parms_in;
3062 $modes_out .= $mode;
3067 cs_ban
($user, $chan, undef, @bans);
3069 return if $modes_out =~ /^[+-]*$/;
3070 ircd
::setmode
(agent
($chan), $chan->{CHAN
}, $modes_out, join(' ', @parms_out));
3071 do_modelock
($chan, $modes_out.' '.join(' ', @parms_out));
3073 $modes_out =~ s/^[+-]*([+-].*)$/$1/;
3074 ircd
::notice
(agent
($chan), '%'.$cn, get_user_nick
($user).' used MODE '.join(' ', $modes_out, @parms_out))
3075 if (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
3079 my ($user, $chan1, @args) = @_;
3080 my $cn1 = $chan1->{CHAN
};
3083 if($args[0] =~ /^#/) {
3087 if($args[0] =~ /(?:acc(?:ess)?|akick|levels|all)/i) {
3088 $type = shift @args;
3089 $cn2 = shift @args unless $cn2;
3092 if($type =~ /^acc(?:ess)?/i) {
3094 $rank = shift @args;
3100 unless(defined $cn2 and defined $type) {
3101 notice
($user, 'Unknown COPY command', 'Syntax: COPY #chan1 [type] #chan2');
3103 my $chan2 = { CHAN
=> $cn2 };
3104 if(lc($cn1) eq lc($cn2)) {
3105 notice
($user, "You cannot copy a channel onto itself.");
3107 unless(is_registered
($chan1)) {
3108 notice
($user, "Source channel \002$cn1\002 must be registered.");
3111 can_do
($chan1, 'COPY', $user) or return undef;
3112 if(lc $type eq 'all') {
3113 if(is_registered
($chan2)) {
3114 notice
($user, "When copying all channel details, destination channel cannot be registered.");
3116 } elsif(!(get_op
($user, $chan2) & ($opmodes{o
} | $opmodes{a
} | $opmodes{q
}))) {
3117 # This would be preferred to be a 'opmode_mask' or something
3118 # However that might be misleading due to hop not being enough to register
3119 notice
($user, "You must have channel operator status to register \002$cn2\002.");
3122 cs_copy_chan_all
($user, $chan1, $chan2);
3126 unless(is_registered
($chan2)) {
3127 notice
($user, "When copying channel lists, destination channel must be registered.");
3130 can_do
($chan2, 'COPY', $user) or return undef;
3132 if(lc $type eq 'akick') {
3133 cs_copy_chan_akick
($user, $chan1, $chan2);
3134 } elsif(lc $type eq 'levels') {
3135 cs_copy_chan_levels
($user, $chan1, $chan2);
3136 } elsif($type =~ /^acc(?:ess)?/i) {
3137 cs_copy_chan_acc
($user, $chan1, $chan2, xop_byname
($rank));
3141 sub cs_copy_chan_all
($$$) {
3142 my ($user, $chan1, $chan2) = @_;
3143 cs_copy_chan_chanreg
($user, $chan1, $chan2);
3144 cs_copy_chan_levels
($user, $chan1, $chan2);
3145 cs_copy_chan_acc
($user, $chan1, $chan2);
3146 cs_copy_chan_akick
($user, $chan1, $chan2);
3150 sub cs_copy_chan_chanreg
($$$) {
3151 my ($user, $chan1, $chan2) = @_;
3152 my $cn1 = $chan1->{CHAN
};
3153 my $cn2 = $chan2->{CHAN
};
3155 copy_chan_chanreg
($cn1, $cn2);
3156 botserv
::bot_join
($chan2) unless (lc(agent
($chan2)) eq lc($csnick) );
3157 do_modelock
($chan2);
3158 notice
($user, "Registration for \002$cn1\002 copied to \002$cn2\002");
3160 my $log_str = "copied the channel registration for \002$cn1\002 to \002$cn2\002";
3161 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3163 my $src = get_user_nick
($user);
3164 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3165 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3166 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3167 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3170 sub cs_copy_chan_acc
($$$;$) {
3171 my ($user, $chan1, $chan2, $level) = @_;
3172 my $cn1 = $chan1->{CHAN
};
3173 my $cn2 = $chan2->{CHAN
};
3175 copy_chan_acc
($cn1, $cn2, $level);
3177 unless(cr_chk_flag
($chan2, CRF_NEVEROP
)) {
3178 $get_chan_users->execute($cn2); my @targets;
3179 while (my ($nick, $uid) = $get_chan_users->fetchrow_array()) {
3180 push @targets, $nick unless nr_chk_flag_user
({ NICK
=> $nick, ID
=> $uid }, NRF_NEVEROP
);
3182 cs_updown2
($user, 'UP', $chan2, @targets);
3185 notice
($user, "Access list for \002$cn1\002 ".
3186 ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
3187 "copied to \002$cn2\002");
3189 my $log_str = "copied the channel access list for \002$cn1\002 ".
3190 ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
3192 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3194 my $src = get_user_nick
($user);
3195 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3196 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3197 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3198 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3201 sub cs_copy_chan_levels
($$$) {
3202 my ($user, $chan1, $chan2) = @_;
3203 my $cn1 = $chan1->{CHAN
};
3204 my $cn2 = $chan2->{CHAN
};
3206 copy_chan_levels
($cn1, $cn2);
3207 notice
($user, "LEVELS for \002$cn1\002 copied to \002$cn2\002");
3209 my $log_str = "copied the LEVELS list for \002$cn1\002 to \002$cn2\002";
3210 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3212 my $src = get_user_nick
($user);
3213 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3214 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3215 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3216 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3219 sub cs_copy_chan_akick
($$$) {
3220 my ($user, $chan1, $chan2) = @_;
3221 my $cn1 = $chan1->{CHAN
};
3222 my $cn2 = $chan2->{CHAN
};
3224 copy_chan_akick
($cn1, $cn2);
3225 notice
($user, "Channel AKick list for \002$cn1\002 copied to \002$cn2\002");
3227 my $log_str = "copied the AKick list for \002$cn1\002 to \002$cn2\002";
3228 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3230 my $src = get_user_nick
($user);
3231 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3232 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3233 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3234 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3237 sub cs_mlock
($$$@) {
3238 my ($user, $chan, $cmd, @args) = @_;
3239 my $cn = $chan->{CHAN
};
3240 # does this need its own privilege now?
3241 can_do
($chan, 'SET', $user) or return;
3244 my ($modes_in, @parms_in) = validate_chmodes
(shift @args, @args);
3245 $modes = $modes_in.' '.join(' ', @parms_in);
3249 my $cur_modelock = get_modelock
($chan);
3250 if(lc $cmd eq 'add') {
3251 $modes = modes
::merge
($cur_modelock, $modes, 1);
3252 $modes = sanitize_mlockable
($modes);
3253 $set_modelock->execute($modes, $cn);
3255 elsif(lc $cmd eq 'del') {
3256 $modes =~ s/[+-]//g;
3257 $modes = modes
::add
($cur_modelock, "-$modes", 1);
3258 $set_modelock->execute($modes, $cn);
3260 elsif(lc $cmd eq 'set') {
3261 $modes = modes
::merge
($modes, "+r", 1);
3262 $set_modelock->execute($modes, $cn);
3264 elsif(lc $cmd eq 'reset') {
3265 $set_modelock->execute(services_conf_default_channel_mlock
, $cn);
3267 notice
($user, "Unknown MLOCK command \"$cmd\"");
3271 notice
($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
3275 notice
($user, columnar
{TITLE
=> "Ban list of \002$cn\002:", DOUBLE
=>1,
3276 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
3280 use SrSv
::MySQL
::Stub
{
3281 getChanUsers
=> ['COLUMN', "SELECT user.nick FROM chanuser JOIN user ON (user.id=chanuser.nickid)
3282 WHERE chanuser.chan=? AND chanuser.joined=1"]
3286 my ($user, @cns) = @_;
3287 foreach my $cn (@cns) {
3288 my $chan = { CHAN
=> $cn };
3289 next unless cs_clear_ops
($user, $chan, 'Resync');
3290 cs_updown2
($user, 'up', $chan, getChanUsers
($cn));
3291 if(can_do
($chan, 'AKickEnforce', $user, { OVERRIDE_MSG
=> "AKICK $cn ENFORCE", NOREPLY
=> 1 })) {
3292 cs_akick_enforce
($user, $chan);
3298 my ($user, @cns) = @_;
3301 foreach my $cn (@cns) {
3303 push @cns, split(',', $cn);
3308 my $chan = { CHAN
=> $cn };
3309 my $cando_opts = { NOREPLY
=> 1 };
3310 if(check_akick
($user, $chan, 1)) {
3311 push @reply, "You are banned from $cn";
3313 } elsif(!can_do
($chan, 'JOIN', $user, $cando_opts)) {
3314 push @reply, "$cn is a private channel.";
3317 if(is_in_chan
($user, $chan)) {
3320 if(can_do
($chan, 'InviteSelf', $user, $cando_opts)) {
3321 cs_invite
($user, $chan, $user);
3326 ircd
::svsjoin
(get_user_agent
($user), $user, @out_cns) if scalar @out_cns;
3327 notice
($user, @reply) if scalar @reply;
3331 my ($user, $cn, @args) = @_;
3332 my ($chan, $msg) = ($cn->{CHAN
}, join(" ", @args));
3333 can_do
($cn, 'SETTOPIC', $user) or return undef;
3334 ircd
::settopic
(agent
($cn), $chan, get_user_nick
($user), time, ($msg =~ /^none/i ? "" : $msg));
3339 # these are helpers and do NOT check if $cn1 or $cn2 is reg'd
3340 sub copy_chan_acc
($$;$) {
3341 my ($cn1, $cn2, $level) = @_;
3343 $copy_acc_rank->execute($cn2, $cn1, $level);
3344 $copy_acc_rank->finish();
3346 $get_founder->execute($cn2);
3347 my ($founder) = $get_founder->fetchrow_array;
3348 $get_founder->finish();
3350 $copy_acc->execute($cn2, $cn1, $founder);
3351 $copy_acc->finish();
3355 sub copy_chan_akick
($$;$) {
3356 my ($cn1, $cn2) = @_;
3357 $copy_akick->execute($cn2, $cn1);
3358 $copy_akick->finish();
3359 copy_chan_acc
($cn1, $cn2, -1);
3362 sub copy_chan_levels
($$) {
3363 my ($cn1, $cn2) = @_;
3364 $copy_levels->execute($cn2, $cn1);
3365 $copy_levels->finish();
3368 sub copy_chan_chanreg
($$) {
3369 my ($cn1, $cn2) = @_;
3370 $get_founder->execute($cn1);
3371 my ($founder) = $get_founder->fetchrow_array;
3372 $get_founder->finish();
3373 set_acc
($founder, undef, { CHAN
=> $cn2 }, FOUNDER
);
3374 $copy_chanreg->execute($cn2, $cn1);
3375 $copy_chanreg->finish();
3378 sub do_welcome
($$) {
3379 my ($user, $chan) = @_;
3380 my $cn = $chan->{CHAN
};
3382 $get_welcomes->execute($cn);
3383 if($get_welcomes->rows) {
3385 while(my ($msg) = $get_welcomes->fetchrow_array) {
3386 push @welcomes, (cr_chk_flag
($chan, CRF_WELCOMEINCHAN
) ? '' : "[$cn] " ).$msg;
3388 if(cr_chk_flag
($chan, CRF_WELCOMEINCHAN
)) {
3389 ircd
::privmsg
(agent
($chan), $cn, @welcomes);
3391 notice
($user, @welcomes);
3394 $get_welcomes->finish();
3398 my ($user, $chan) = @_;
3399 my $cn = $chan->{CHAN
};
3401 if(can_do
($chan, 'GREET', $user)) {
3402 my $src = get_user_nick
($user);
3403 $nickserv::get_greet-
>execute(get_user_id
($user));
3404 my ($greet) = $nickserv::get_greet-
>fetchrow_array();
3405 $nickserv::get_greet-
>finish();
3406 ircd
::privmsg
(agent
($chan), $cn, "[\002$src\002] $greet") if $greet;
3410 sub chk_registered
($$) {
3411 my ($user, $chan) = @_;
3413 unless(is_registered
($chan)) {
3414 my $cn = $chan->{CHAN
};
3416 notice
($user, "The channel \002$cn\002 is not registered.");
3423 sub make_banmask
($$;$) {
3424 my ($chan, $tuser, $type) = @_;
3425 my $nick = get_user_nick
($tuser);
3427 my ($ident, $vhost) = get_vhost
($tuser);
3429 my ($nick, $ident, $vhost) = make_hostmask
(get_bantype
($chan), $nick, $ident, $vhost);
3432 } elsif($type eq 'n') {
3437 return $type."$nick!$ident\@$vhost";
3440 sub kickban
($$$$;$) {
3441 my ($chan, $user, $mask, $reason, $noflush) = @_;
3442 my $cn = $chan->{CHAN
};
3443 my $nick = get_user_nick
($user);
3445 return 0 if adminserv
::is_service
($user);
3447 my $agent = agent
($chan);
3450 $mask = make_banmask
($chan, $user);
3453 enforcer_join
($chan) if (get_user_count
($chan) <= 1);
3454 ircd
::setmode
($agent, $cn, '+b', $mask);
3455 ircd
::flushmodes
() unless $noflush;
3456 ircd
::kick
($agent, $cn, $user, $reason);
3460 sub kickban_multi
($$$) {
3461 my ($chan, $users, $reason) = @_;
3462 my $cn = $chan->{CHAN
};
3463 my $agent = agent
($chan);
3465 enforcer_join
($chan);
3466 ircd
::setmode
($agent, $cn, '+b', '*!*@*');
3469 foreach my $user (@$users) {
3470 next if adminserv
::is_ircop
($user) or adminserv
::is_svsop
($user, adminserv
::S_HELP
());
3471 ircd
::kick
($agent, $cn, $user, $reason);
3475 sub clear_users
($$) {
3476 my ($chan, $reason) = @_;
3477 my $cn = $chan->{CHAN
};
3478 my $agent = agent
($chan);
3481 enforcer_join
($chan);
3482 ircd
::setmode
($agent, $cn, '+b', '*!*@*');
3484 $get_chan_users->execute($cn);
3485 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3486 my $user = { NICK
=> $nick, ID
=> $uid };
3487 ircd
::kick
($agent, $cn, $user, $reason)
3488 unless adminserv
::is_ircop
($user) or adminserv
::is_svsop
($user, adminserv
::S_HELP
());
3495 sub kickmask
($$$$) {
3496 my ($chan, $mask, $reason, $ban) = @_;
3497 my $cn = $chan->{CHAN
};
3498 my $agent = agent
($chan);
3500 my ($nick, $ident, $host) = glob2sql
(parse_mask
($mask));
3501 $nick = '%' if ($nick eq '');
3502 $ident = '%' if ($ident eq '');
3503 $host = '%' if ($host eq '');
3506 my $banmask = $nick.'!'.$ident.'@'.$host;
3507 $banmask =~ tr/%_/*?/;
3508 ircd
::setmode
($agent, $cn, '+b', $banmask);
3513 $get_chan_users_mask->execute($cn, $nick, $ident, $host, $host, $host);
3514 while(my ($nick, $uid) = $get_chan_users_mask->fetchrow_array) {
3515 my $user = { NICK
=> $nick, ID
=> $uid };
3516 ircd
::kick
($agent, $cn, $user, $reason)
3517 unless adminserv
::is_service
($user);
3520 $get_chan_users_mask->finish();
3525 sub kickmask_noacc
($$$$) {
3526 my ($chan, $mask, $reason, $ban) = @_;
3527 my $cn = $chan->{CHAN
};
3528 my $agent = agent
($chan);
3530 my ($nick, $ident, $host) = glob2sql
(parse_mask
($mask));
3531 $nick = '%' if ($nick eq '');
3532 $ident = '%' if ($ident eq '');
3533 $host = '%' if ($host eq '');
3536 my $banmask = $nick.'!'.$ident.'@'.$host;
3537 $banmask =~ tr/%_/*?/;
3538 ircd
::setmode
($agent, $cn, '+b', $banmask);
3543 $get_chan_users_mask_noacc->execute($cn, $nick, $ident, $host, $host, $host);
3544 while(my ($nick, $uid) = $get_chan_users_mask_noacc->fetchrow_array) {
3545 my $user = { NICK
=> $nick, ID
=> $uid };
3546 ircd
::kick
($agent, $cn, $user, $reason)
3547 unless adminserv
::is_service
($user);
3550 $get_chan_users_mask_noacc->finish();
3557 my $cn = $chan->{CHAN
};
3559 my $agent = agent
($chan);
3561 $get_chan_users->execute($cn);
3562 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3563 my $user = { NICK
=> $nick };
3564 get_user_id
($user);
3565 my $opmodes = get_op
($user, $chan);
3566 print "OPMODES $opmodes\n";
3567 for(my $i; $i < 5; $i++) {
3568 if($opmodes & 2**$i) {
3569 push @modelist, ['-'.$opmodes[$i], $user];
3574 ircd
::setmode2
($agent, $cn, @modelist);
3577 sub clear_bans
($;$) {
3578 my ($chan, $type) = @_;
3579 my $cn = $chan->{CHAN
};
3581 my $agent = agent
($chan);
3582 $type = 0 unless defined $type;
3583 my $mode = ($type == 128 ? 'e' : 'b');
3586 $get_all_bans->execute($cn, $type);
3587 while(my ($mask) = $get_all_bans->fetchrow_array) {
3588 $mask =~ tr/\%\_/\*\?/;
3589 push @banlist, $mask;
3592 ircd
::ban_list
($agent, $cn, -1, $mode, @banlist);
3596 sub unban_user
($@) {
3597 my ($chan, @userlist) = @_;
3598 my $cn = $chan->{CHAN
};
3600 if (defined(&ircd
::unban_users
)) {
3601 ircd
::unban_users
(@userlist);
3604 foreach my $tuser (@userlist) {
3606 unless($tuid = get_user_id
($tuser)) {
3610 # We don't handle extended bans. Yet.
3611 $find_bans_chan_user->execute($cn, $tuid, 0);
3612 while (my ($mask) = $find_bans_chan_user->fetchrow_array) {
3613 $mask =~ tr/\%\_/\*\?/;
3616 $find_bans_chan_user->finish();
3618 ircd
::ban_list
(agent
($chan), $cn, -1, 'b', @bans) if scalar(@bans);
3619 $delete_bans_chan_user->execute($cn, $tuid, 0); $delete_bans_chan_user->finish();
3625 sub chan_kill
($$;$) {
3626 my ($chan, $reason, $tusers) = @_;
3627 my $cn = $chan->{CHAN
};
3628 my $agent = agent
($chan);
3631 enforcer_join
($chan);
3633 foreach my $tuser (@$tusers) {
3634 $tuser->{ID
} = $tuser->{__ID
} if defined($tuser->{__ID
}); # user_join_multi does this.
3635 nickserv
::kline_user
($tuser, services_conf_chankilltime
, $reason)
3636 unless adminserv
::is_ircop
($tuser) or adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3641 $get_chan_users->execute($cn);
3642 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3643 my $tuser = { NICK
=> $nick, ID
=> $uid, AGENT
=> $agent };
3644 nickserv
::kline_user
($tuser, services_conf_chankilltime
, $reason)
3645 unless adminserv
::is_ircop
($tuser) or adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3653 sub do_nick_akick
($$;$) {
3654 my ($tuser, $chan, $root) = @_;
3655 my $cn = $chan->{CHAN
};
3656 unless(defined($root)) {
3657 (undef, $root) = get_best_acc
($tuser, $chan, 2);
3659 $get_nick_akick->execute($cn, $root);
3660 my ($reason) = $get_nick_akick->fetchrow_array(); $get_nick_akick->finish();
3661 return 0 if adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3662 if(defined($reason) && $reason =~ /\|/) {
3663 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3665 kickban
($chan, $tuser, undef, "User has been banned from ".$cn.($reason?": $reason":''));
3668 sub check_akick
($$;$) {
3669 my ($user, $chan, $check_only) = @_;
3670 if(adminserv
::is_svsop
($user, adminserv
::S_HELP
())) {
3673 my ($acc, $root) = get_best_acc
($user, $chan, 2);
3675 do_nick_akick
($user, $chan, $root) unless $check_only;
3678 my $cn = $chan->{CHAN
};
3679 my $uid = get_user_id
($user);
3681 $get_akick->execute($uid, $cn);
3682 if(my @akick = $get_akick->fetchrow_array) {
3683 akickban
($cn, @akick) unless $check_only;
3690 sub do_status
($$;$) {
3691 my ($user, $chan, $check_only) = @_;
3693 return 0 if cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
));
3695 my $nick = get_user_nick
($user);
3697 if(check_akick
($user, $chan, $check_only)) {
3700 my ($acc, $root) = get_best_acc
($user, $chan, 2);
3701 if(!can_do
($chan, 'JOIN', $user, { ACC
=> $acc, NOREPLY
=> 1 })) {
3702 if (!is_agent
($user->{NICK
})) {
3703 kickban
($chan, $user, undef, 'This is a private channel.')
3709 if( !$check_only && is_registered
($chan) &&
3710 !cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
)) )
3712 my $neverop = (is_neverop_user
($user) || cr_chk_flag
($chan, CRF_NEVEROP
, 1));
3713 my $no_deop = cr_chk_flag
($chan, CRF_SPLITOPS
, 0);
3715 if($neverop && cr_chk_flag
($chan, CRF_AUTOVOICE
, 1) && $acc > 2) {
3720 set_modes
($user, $chan, $acc,
3722 # this probably needs to be configurable for ports
3723 # also Unreal may [optionally] set +q on join.
3725 !$neverop || $op_anyway,
3732 sub akick_alluser
($) {
3734 my $uid = get_user_id
($user);
3736 $get_akick_alluser->execute($uid);
3737 while(my @akick = $get_akick_alluser->fetchrow_array) {
3742 sub akick_allchan
($) {
3744 my $cn = $chan->{CHAN
};
3746 $get_akick_allchan->execute($cn);
3747 while(my @akick = $get_akick_allchan->fetchrow_array) {
3748 akickban
($cn, @akick);
3753 my ($cn, $knick, $bnick, $ident, $host, $reason, $bident) = @_;
3754 my $target = { NICK
=> $knick };
3755 my $chan = { CHAN
=> $cn };
3756 return 0 if adminserv
::is_svsop
($target, adminserv
::S_HELP
());
3759 ($bnick, $ident, $host) = make_hostmask
(get_bantype
($chan), $knick, $bident, $host);
3760 } elsif($host =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
3761 ($bnick, $ident, $host) = make_hostmask
(4, $knick, $bident, $host);
3763 $bnick =~ tr/\%\_/\*\?/;
3764 $ident =~ tr/\%\_/\*\?/;
3765 $host =~ tr/\%\_/\*\?/;
3767 if(defined($reason) && $reason =~ /\|/) {
3768 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3771 if(defined($reason) && $reason =~ /\|/) {
3772 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3775 return kickban
($chan, $target, "$bnick!$ident\@$host", "User has been banned from ".$cn.($reason?": $reason":''));
3778 sub notice_all_nicks
($$$) {
3779 my ($user, $nick, $msg) = @_;
3780 my $src = get_user_nick
($user);
3782 notice
($user, $msg);
3783 foreach my $u (get_nick_user_nicks
$nick) {
3784 notice
({ NICK
=> $u, AGENT
=> $csUser }, $msg) unless lc $src eq lc $u;
3792 if($name =~ /^uop$/i) { $level=1; }
3793 elsif($name =~ /^vop$/i) { $level=2; }
3794 elsif($name =~ /^hop$/i) { $level=3; }
3795 elsif($name =~ /^aop$/i) { $level=4; }
3796 elsif($name =~ /^sop$/i) { $level=5; }
3797 elsif($name =~ /^co?f(ounder)?$/i) { $level=6; }
3798 elsif($name =~ /^founder$/i) { $level=7; }
3799 elsif($name =~ /^(any|all|user)/i) { $level=0; }
3800 elsif($name =~ /^akick$/i) { $level=-1; }
3801 elsif($name =~ /^(none|disabled?|nobody)$/i) { $level=8; }
3807 return if services_conf_noexpire
;
3809 $get_expired->execute(time() - (86400 * services_conf_chanexpire
));
3810 while(my ($cn, $founder) = $get_expired->fetchrow_array) {
3811 drop
({ CHAN
=> $cn });
3812 wlog
($csnick, LOG_INFO
(), "\002$cn\002 has expired. Founder: $founder");
3816 sub enforcer_join
($) {
3818 my $cn = $chan->{CHAN
};
3819 my $bot = agent
($chan);
3821 return if $enforcers{lc $cn};
3822 $enforcers{lc $cn} = lc $bot;
3824 botserv
::bot_join
($chan);
3826 add_timer
("CSEnforce $bot $cn", 60, __PACKAGE__
, 'chanserv::enforcer_part');
3829 sub enforcer_part
($) {
3831 my ($junk, $bot, $cn) = split(/ /, $cookie);
3833 return unless $enforcers{lc $cn};
3834 undef($enforcers{lc $cn});
3836 botserv
::bot_part_if_needed
($bot, {CHAN
=> $cn}, 'Enforcer Leaving');
3839 sub fix_private_join_before_id
($) {
3842 my @cns = get_recent_private_chans
(get_user_id
($user));
3843 foreach my $cn (@cns) {
3844 my $chan = { CHAN
=> $cn };
3845 unban_user
($chan, $user);
3848 ircd
::svsjoin
($csUser, $user, @cns) if @cns;
3851 ### DATABASE UTILITY FUNCTIONS ###
3853 sub get_user_count
($) {
3855 my $cn = $chan->{CHAN
};
3857 $get_user_count->execute($cn);
3859 return $get_user_count->fetchrow_array;
3870 if($cur_lock ne $chan) {
3871 really_release_lock
($chan);
3873 die("Tried to get two locks at the same time: $cur_lock, $chan")
3878 $get_lock->execute(sql_conf_mysql_db
.".chan.$chan");
3883 sub release_lock
($) {
3890 if($cur_lock and $cur_lock ne $chan) {
3891 really_release_lock
($cur_lock);
3893 die("Tried to release the wrong lock");
3899 really_release_lock
($chan);
3903 sub really_release_lock
($) {
3907 $release_lock->execute(sql_conf_mysql_db
.".chan.$chan");
3908 $release_lock->finish;
3912 #sub is_free_lock($) {
3913 # $is_free_lock->execute($_[0]);
3914 # return $is_free_lock->fetchrow_array;
3917 sub get_modelock
($) {
3926 $get_modelock->execute($cn);
3927 my ($ml) = $get_modelock->fetchrow_array;
3928 $get_modelock->finish();
3932 sub do_modelock
($;$) {
3933 my ($chan, $modes) = @_;
3934 my $cn = $chan->{CHAN
};
3938 $get_modelock_lock->execute; $get_modelock_lock->finish;
3940 $get_chanmodes->execute($cn);
3941 my ($omodes) = $get_chanmodes->fetchrow_array;
3942 my $ml = get_modelock
($chan);
3944 $ml = do_modelock_fast
($cn, $modes, $omodes, $ml);
3946 $unlock_tables->execute; $unlock_tables->finish;
3948 ircd
::setmode
(agent
($chan), $cn, $ml) if($ml);
3951 sub do_modelock_fast
($$$$) {
3952 my ($cn, $modes, $omodes, $ml) = @_;
3953 my $nmodes = modes
::add
($omodes, $modes, 1);
3954 $ml = modes
::diff
($nmodes, $ml, 1);
3955 $set_chanmodes->execute(modes
::add
($nmodes, $ml, 1), $cn);
3960 sub update_modes
($$) {
3961 my ($cn, $modes) = @_;
3963 $get_update_modes_lock->execute; $get_update_modes_lock->finish;
3964 $get_chanmodes->execute($cn);
3965 my ($omodes) = $get_chanmodes->fetchrow_array;
3967 $set_chanmodes->execute(modes
::add
($omodes, $modes, 1), $cn);
3968 $unlock_tables->execute; $unlock_tables->finish;
3974 $is_level->execute($perm);
3976 return $is_level->fetchrow_array;
3980 return nr_chk_flag
($_[0], NRF_NEVEROP
(), 1);
3983 sub is_neverop_user
($) {
3984 return nr_chk_flag_user
($_[0], NRF_NEVEROP
(), 1);
3987 sub is_in_chan
($$) {
3988 my ($user, $chan) = @_;
3989 my $cn = $chan->{CHAN
};
3990 my $uid = get_user_id
($user);
3992 $is_in_chan->execute($uid, $cn);
3993 if($is_in_chan->fetchrow_array) {
4000 sub is_registered
($) {
4002 my $cn = $chan->{CHAN
};
4004 $is_registered->execute($cn);
4005 if($is_registered->fetchrow_array) {
4012 sub get_user_chans
($) {
4014 my $uid = get_user_id
($user);
4017 $get_user_chans->execute($uid, $ircline, $ircline+1000);
4018 while(my ($chan) = $get_user_chans->fetchrow_array) {
4025 sub get_user_chans_recent
($) {
4027 my $uid = get_user_id
($user);
4028 my (@curchans, @oldchans);
4030 $get_user_chans_recent->execute($uid);
4031 while(my ($cn, $joined, $op) = $get_user_chans_recent->fetchrow_array) {
4033 push @curchans, make_op_prefix
($op).$cn;
4036 push @oldchans, $cn;
4040 return (\
@curchans, \
@oldchans);
4043 my ($prefixes, $modes);
4044 sub make_op_prefix
($) {
4048 unless(defined($prefixes) and defined($modes)) {
4049 $IRCd_capabilities{PREFIX
} =~ /^\((\S+)\)(\S+)$/;
4050 ($modes, $prefixes) = ($1, $2);
4051 $modes = reverse $modes;
4052 $prefixes = reverse $prefixes;
4056 for(my $i = 0; $i < length($prefixes); $i++) {
4057 $op_prefix = substr($prefixes, $i, 1).$op_prefix if ($op & (2**$i));
4063 my ($user, $chan) = @_;
4064 my $cn = $chan->{CHAN
};
4065 my $uid = get_user_id
($user);
4067 $get_op->execute($uid, $cn);
4068 my ($op) = $get_op->fetchrow_array;
4073 sub get_best_acc
($$;$) {
4074 my ($user, $chan, $retnick) = @_;
4075 my $uid = get_user_id
($user);
4076 my $cn = $chan->{CHAN
};
4078 $get_best_acc->execute($uid, $cn);
4079 my ($bnick, $best) = $get_best_acc->fetchrow_array;
4080 $get_best_acc->finish();
4083 return ($best, $bnick);
4084 } elsif($retnick == 1) {
4092 my ($nick, $chan) = @_;
4093 my $cn = $chan->{CHAN
};
4096 if cr_chk_flag
($chan, (CRF_DRONE
| CRF_CLOSE
| CRF_FREEZE
), 1);
4098 $get_acc->execute($cn, $nick);
4099 my ($acc) = $get_acc->fetchrow_array;
4105 my ($nick, $user, $chan, $level) = @_;
4106 my $cn = $chan->{CHAN
};
4108 $adder = get_best_acc
($user, $chan, 1) if $user;
4110 $set_acc1->execute($cn, $level, $nick);
4111 $set_acc2->execute($level, $adder, $cn, $nick);
4113 if ( ( $level > 0 and !is_neverop
($nick) and !cr_chk_flag
($chan, CRF_NEVEROP
) )
4116 set_modes_allnick
($nick, $chan, $level);
4121 my ($nick, $chan) = @_;
4122 my $cn = $chan->{CHAN
};
4124 $del_acc->execute($cn, $nick);
4126 foreach my $user (get_nick_users
$nick) {
4127 set_modes
($user, $chan, 0, 1) if is_in_chan
($user, $chan);
4131 sub get_auth_nick
($$) {
4132 my ($cn, $nick) = @_;
4134 $get_auth_nick->execute($cn, $nick);
4135 my ($data) = $get_auth_nick->fetchrow_array();
4136 $get_auth_nick->finish();
4138 return split(/:/, $data);
4140 sub get_auth_num
($$) {
4141 my ($cn, $num) = @_;
4143 $get_auth_num->execute($cn, $num - 1);
4144 my ($nick, $data) = $get_auth_num->fetchrow_array();
4145 $get_auth_num->finish();
4147 return ($nick, split(/:/, $data));
4150 my ($cn, $nick) = @_;
4152 $find_auth->execute($cn, $nick);
4153 my ($ret) = $find_auth->fetchrow_array();
4154 $find_auth->finish();
4159 # Only call this if you've checked the user for NEVEROP already.
4160 sub set_modes_allchan
($;$) {
4161 my ($user, $neverop) = @_;
4162 my $uid = get_user_id
($user);
4164 $get_user_chans->execute($uid, $ircline, $ircline+1000);
4165 while(my ($cn) = $get_user_chans->fetchrow_array) {
4166 my $chan = { CHAN
=> $cn };
4167 my $acc = get_best_acc
($user, $chan);
4169 set_modes
($user, $chan, $acc) unless ($neverop or cr_chk_flag
($chan, CRF_NEVEROP
));
4171 do_nick_akick
($user, $chan);
4176 # Only call this if you've checked for NEVEROP already.
4177 sub set_modes_allnick
($$$) {
4178 my ($nick, $chan, $level) = @_;
4179 my $cn = $chan->{CHAN
};
4181 $get_using_nick_chans->execute($nick, $cn);
4182 while(my ($n) = $get_using_nick_chans->fetchrow_array) {
4183 my $user = { NICK
=> $n };
4184 my $l = get_best_acc
($user, $chan);
4186 set_modes
($user, $chan, $level, 1) if($level == $l);
4188 do_nick_akick
($user, $chan);
4193 # If channel has OPGUARD, $doneg is true.
4194 sub set_modes
($$$;$$) {
4195 my ($user, $chan, $acc, $doneg, $dopos) = @_;
4197 $dopos = 1 unless defined($dopos);
4198 $doneg = 0 unless defined($doneg);
4199 my $cn = $chan->{CHAN
};
4202 # Do akick stuff here.
4205 my $dst = ( $acc > 0 ? $ops[$acc] : 0 );
4206 my $cur = get_op
($user, $chan);
4209 if (cr_chk_flag
($chan, CRF_FREEZE
)) {
4210 set_mode_mask
($user, $chan, $cur, undef);
4213 if (($acc == 0) and cr_chk_flag
($chan, CRF_AUTOVOICE
)) {
4214 set_mode_mask
($user, $chan, $cur, 1);
4218 $pos = $dst ^ ($dst & $cur);
4219 $neg = ($dst ^ $cur) & $cur if $doneg;
4222 set_mode_mask
($user, $chan, ($doneg ? $neg : '-'), ($dopos ? $pos : '+'));
4227 set_lastused
($cn, get_user_id
($user));
4231 sub unset_modes
($$) {
4232 my ($user, $chan) = @_;
4234 my $mask = get_op
($user, $chan);
4236 set_mode_mask
($user, $chan, $mask, 0);
4239 sub set_mode_mask
($$$$) {
4240 my ($user, $chan, @masks) = @_;
4241 my $nick = get_user_nick
($user);
4242 my $cn = $chan->{CHAN
};
4245 for(my $sign; $sign < 2; $sign++) {
4246 next if($masks[$sign] == 0);
4248 $out .= '-' if $sign == 0;
4249 $out .= '+' if $sign == 1;
4251 for(my $i; $i < 5; $i++) {
4252 my @l = ('v', 'h', 'o', 'a', 'q');
4253 if ($IRCd_capabilities{"FOUNDER"} eq "") {
4256 if ($IRCd_capabilities{"ADMIN"} eq "") {
4259 if ($IRCd_capabilities{"HALFOP"} eq "") {
4262 if($masks[$sign] & 2**$i) {
4264 my $user_ = { NICK
=> $nick, AGENT
=> $csnick};
4265 get_user_id
($user_);
4272 ircd
::setmode_many
(agent
($chan), $cn, $out, @args);
4277 my ($chan, $perm) = @_;
4278 my $cn = $chan->{CHAN
};
4280 $get_level->execute($cn, $perm);
4281 my ($level, $isnotnull) = $get_level->fetchrow_array;
4282 $get_level->finish();
4285 return ($level, $isnotnull);
4292 sub check_override
($$;$) {
4293 my ($user, $perm, $logMsg) = @_;
4296 #{OVERRIDE::$perm} produces funny package problems, so wrap it in double-quotes.
4297 if(exists($user->{"OVERRIDE::$perm"}) && (my $nick = $user->{"OVERRIDE::$perm"})) {
4298 if(defined($nick)) {
4299 if(services_conf_log_overrides
&& $logMsg) {
4300 my $src = get_user_nick
($user);
4301 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override $logMsg");
4303 return (wantarray ? ($nick, 1) : $nick);
4308 foreach my $o (@override) {
4309 my ($operRank, $permHashRef) = @$o;
4310 if($permHashRef->{$perm} and my $nick = adminserv
::can_do
($user, $operRank)) {
4311 $user->{"OVERRIDE::$perm"} = $nick;
4312 if(services_conf_log_overrides
&& $logMsg) {
4313 my $src = get_user_nick
($user);
4314 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override $logMsg");
4316 return (wantarray ? ($nick, 1) : $nick);
4319 $user->{"OVERRIDE::$perm"} = undef;
4323 my ($chan, $perm, $user, $data) = @_;
4324 $data = {} unless defined $data;
4325 # $data is a hashref/struct
4326 my $noreply = $data->{NOREPLY
};
4327 my $acc = $data->{ACC
};
4328 my $overrideMsg = $data->{OVERRIDE_MSG
};
4330 if(my $nick = __can_do
($chan, $perm, $user, $acc)) {
4331 # This is becoming increasingly complicated
4332 # and checking if an override was used is becoming tricky.
4333 # We had a case in cs_kick where an oper should be able to override +Q/$peace
4334 # but cannot b/c they have regular access in that channel.
4336 if(defined($user)) {
4337 (undef, $override) = check_override
($user, $perm);
4339 return (wantarray ? ($nick, $override) : $nick);
4340 } elsif ( $user and adminserv
::is_svsop
($user, adminserv
::S_HELP
()) ) {
4341 #set_lastused($cn, get_user_id($user));
4342 my ($nick, $override) = check_override
($user, $perm, $overrideMsg);
4343 return (wantarray ? ($nick, $override) : $nick) if $override;
4345 if($user and !$noreply) {
4346 my $cn = $chan->{CHAN
};
4347 if (cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
4348 notice
($user, "\002$cn\002 is closed and cannot be used".
4349 ((uc $perm eq 'INFO') ? ': '.get_close
($chan) : '.'));
4351 elsif(cr_chk_flag
($chan, CRF_FREEZE
)) {
4352 notice
($user, "\002$cn\002 is frozen and access suspended.");
4355 notice
($user, "$cn: $err_deny");
4361 sub __can_do
($$$;$) {
4362 my ($chan, $perm, $user, $acc) = @_;
4364 my $cn = $chan->{CHAN
};
4368 unless(exists($chan->{"PERM::$perm"})) {
4369 $level = $chan->{"PERM::$perm"} = get_level
($chan, $perm);
4371 $level = $chan->{"PERM::$perm"};
4374 unless(defined($acc)) {
4375 unless (defined $user && ref($user) eq 'HASH') {
4376 die "invalid __can_do call";
4378 my $chanuser = $user->{lc $cn};
4379 unless (defined($chanuser) && exists($chanuser->{ACC
})) {
4380 ($acc, $nick) = get_best_acc
($user, $chan, 2);
4381 ($chanuser->{ACC
}, $chanuser->{ACCNICK
}) = ($acc, $nick);
4383 ($acc, $nick) = ($chanuser->{ACC
}, $chanuser->{ACCNICK
});
4386 $nick = 1 unless $nick;
4388 if($acc >= $level and !cr_chk_flag
($chan, (CRF_CLOSE
| CRF_FREEZE
| CRF_DRONE
))) {
4389 set_lastused
($cn, get_user_id
($user)) if $user;
4390 return (wantarray ? ($nick, 0) : $nick);
4393 if(cr_chk_flag
($chan, CRF_FREEZE
) and ($perm eq 'JOIN')) {
4394 return (wantarray ? ($nick, 0) : $nick);
4400 sub can_keep_op
($$$$) {
4401 # This is a naïve implemenation using a loop.
4402 # If we ever do a more flexible version that further restricts how
4403 # LEVELS affect opguard, the loop will have to be unrolled.
4405 # Only call this if you've already checked opguard, as we do not check it here.
4407 # Remember, this isn't a permission check if someone is allowed to op someone [else],
4408 # rather this checks if the person being opped is allowed to keep/have it.
4409 my ($user, $chan, $tuser, $opmode) = @_;
4410 return 1 if $opmode eq 'v'; # why remove a voice?
4412 'q' => ['OWNER', 4],
4413 'a' => ['ADMIN', 3],
4415 'h' => ['HALFOP', 1],
4419 my $self = (lc(get_user_nick
($user)) eq lc(get_user_nick
($tuser)));
4421 #my ($level, $isnotnull) = get_level($chan, $permhash{$opmode}[1]);
4422 my $level = get_level
($chan, $permhash{$opmode}[0]);
4424 foreach my $luser ($tuser, $user) {
4425 # We check target first, as there seems no reason that
4426 # someone who has access can't be opped by someone
4427 # who technically doesn't.
4428 return 1 if (adminserv
::is_svsop
($luser, adminserv
::S_HELP
()) and
4429 check_override
($luser, $permhash{$opmode}[0]));
4431 my $acc = get_best_acc
($luser, $chan);
4432 return 1 if ($self and ($permhash{opmode
}[2] + 2) <= $acc);
4445 if($chan->{AGENT
}) {
4446 my $a = $chan->{AGENT
};
4447 $a->{ID
} = ircd
::getAgentUuid
($a->{NICK
});
4451 unless(initial_synced
()) {
4454 $botserv::get_chan_bot-
>execute($chan->{CHAN
});
4455 my $agentnick = $botserv::get_chan_bot-
>fetchrow_array;
4456 my ($agent) = { NICK
=> $agentnick, ID
=> ircd
::getAgentUuid
($agentnick)};
4457 $agent = $csUser unless $agentnick;
4458 return $chan->{AGENT
} = $agent;
4463 my $cn = $chan->{CHAN
};
4465 undef($enforcers{lc $cn});
4466 my $agent = agent
($chan);
4467 agent_part
($agent, $cn, 'Channel dropped') unless (lc($agent) eq lc($csnick));
4468 if (module
::is_loaded
('logserv')) {
4469 eval { logserv
::delchan
(undef, $cn); }
4472 $drop_acc->execute($cn);
4473 $drop_lvl->execute($cn);
4474 $del_close->execute($cn);
4475 $drop_akick->execute($cn);
4476 $drop_welcome->execute($cn);
4477 $drop_chantext->execute($cn);
4478 $drop_nicktext->execute($cn); # Leftover channel auths
4479 $drop->execute($cn);
4480 ircd
::setmode
($csUser, $cn, '-r');
4483 sub drop_nick_chans
($) {
4486 $delete_successors->execute($nick);
4488 $get_nick_own_chans->execute($nick);
4489 while(my ($cn) = $get_nick_own_chans->fetchrow_array) {
4490 succeed_chan
($cn, $nick);
4494 sub succeed_chan
($$) {
4495 my ($cn, $nick) = @_;
4497 $get_successor->execute($cn);
4498 my ($suc) = $get_successor->fetchrow_array;
4501 $set_founder->execute($suc, $cn);
4502 set_acc
($suc, undef, {CHAN
=> $cn}, FOUNDER
);
4503 $del_successor->execute($cn);
4505 drop
({CHAN
=> $cn});
4506 wlog
($csnick, LOG_INFO
(), "\002$cn\002 has been dropped due to expiry/drop of \002$nick\002");
4512 my $cn = $chan->{CHAN
};
4513 return undef unless cr_chk_flag
($chan, CRF_CLOSE
| CRF_DRONE
);
4515 $get_close->execute($cn);
4516 my ($reason, $opnick, $time) = $get_close->fetchrow_array();
4517 $get_close->finish();
4519 $reason = "[$opnick ".gmtime2
($time)."] - $reason";
4521 return (wantarray ? ($reason, $opnick, $time) : $reason);
4524 sub get_users_nochans
(;$) {
4529 $get_users_nochans_noid->execute();
4530 while (my ($usernick, $userid) = $get_users_nochans_noid->fetchrow_array()) {
4531 push @users, { NICK
=> $usernick, ID
=> $userid };
4533 $get_users_nochans_noid->finish();
4536 $get_users_nochans->execute();
4537 while (my ($usernick, $userid) = $get_users_nochans->fetchrow_array()) {
4538 push @users, { NICK
=> $usernick, ID
=> $userid };
4540 $get_users_nochans->finish();
4546 sub get_bantype
($) {
4548 my $cn = $chan->{CHAN
};
4550 unless (exists($chan->{BANTYPE
})) {
4551 $get_bantype->execute($cn);
4552 ($chan->{BANTYPE
}) = $get_bantype->fetchrow_array();
4553 $get_bantype->finish();
4556 return $chan->{BANTYPE
};
4560 my ($chan, $log) = @_;
4562 my $level = get_level
($chan, "MemoAccChange");
4563 return if $level == 8; # 8 is 'disable'
4564 $level = 1 if $level == 0;
4565 memoserv
::send_chan_memo
($csnick, $chan, $log, $level);
4568 sub get_ban_num
($$) {
4569 my ($chan, $num) = @_;
4570 $get_ban_num->execute($chan->{CHAN
}, 0, $num-1);
4571 my ($mask) = $get_ban_num->fetchrow_array();
4572 $get_ban_num->finish();
4573 return sql2glob
($mask);
4579 # Due to special casing of '0' this wrapper should be used
4580 # by anyone handling a JOIN (not SJOIN, it's a JOIN) event.
4581 # This is an RFC1459 requirement.
4582 my ($user, $cn) = @_;
4583 my $chan = { CHAN
=> $cn };
4586 # This should be treated as a number
4587 # Just in case we ever got passed '000', not that Unreal does.
4588 # In C, you could check that chan[0] != '#' && chan[0] == '0'
4589 user_part_multi
($user, [ get_user_chans
($user) ], 'Left all channels');
4592 user_join_multi
($chan, [$user]);
4596 sub handle_sjoin
($$$$$$$) {
4597 my ($server, $cn, $ts, $chmodes, $chmodeparms, $userarray, $banarray, $exceptarray) = @_;
4598 my $chan = { CHAN
=> $cn };
4599 print "handle_sjoin\n";
4600 print "USERS: " . Dumper
(@$userarray);
4602 chan_mode
($server, $cn, $chmodes, $chmodeparms) if $chmodes;
4604 update_modes
($cn, "$chmodes $chmodeparms") if $chmodes;
4607 user_join_multi
($chan, $userarray) if scalar @$userarray;
4609 foreach my $ban (@$banarray) {
4610 process_ban
($cn, $ban, $server, 0, 1);
4612 foreach my $except (@$exceptarray) {
4613 process_ban
($cn, $except, $server, 128, 1);
4617 sub user_join_multi
($$) {
4618 print "user_join_multi\n";
4619 my ($chan, $users) = @_;
4620 my $cn = $chan->{CHAN
};
4622 my $multi_tradeoff = 2; # could use some synthetic-benchmark tuning
4623 foreach my $user (@$users) {
4624 if ($user->{ID
} && !$user->{NICK
}) {
4625 get_user_nick
($user); # INSP
4627 $user->{__ID
} = get_user_id
($user);
4628 unless (defined($user->{__ID
})) {
4629 # This does happen occasionally. it's a BUG.
4630 # At least we have a diagnostic for it now.
4631 # Normally we'd just get a [useless] warning from the SQL server
4632 ircd
::debug
($user->{NICK
}.' has a NULL user->{__ID} in user_join_multi('.$cn.', ...');
4636 $get_joinpart_lock->execute; $get_joinpart_lock->finish;
4638 $chan_create->execute($seq, $cn);
4640 $get_user_count->execute($cn);
4641 my ($count) = $get_user_count->fetchrow_array;
4643 if(scalar(@$users) < $multi_tradeoff) {
4644 foreach my $user (@$users) {
4645 # see note above in get_user_id loop
4646 if (defined($user->{__ID
})) {
4647 $chanjoin->execute($seq, $user->{__ID
}, $cn, $user->{__OP
});
4652 my $query = "REPLACE INTO chanuser (seq, nickid, chan, op, joined) VALUES ";
4653 foreach my $user (@$users) {
4654 # a join(',', list) would be nice but would involve preparing the list first.
4655 # I think this will be faster.
4656 if (defined($user->{__ID
})) {
4657 # see note above in get_user_id loop
4658 $query .= '('.$dbh->quote($seq).','.
4659 $dbh->quote($user->{__ID
}).','.
4660 $dbh->quote($cn).','.
4661 $dbh->quote($user->{__OP
}).', 1),';
4668 $unlock_tables->execute; $unlock_tables->finish;
4670 my $bot = agent
($chan);
4671 foreach my $user (@$users) {
4672 $user->{AGENT
} = $bot;
4675 if(initial_synced
() and cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
4676 my ($reason, $opnick, $time) = get_close
($chan);
4677 my $cmsg = "$cn is closed: $reason";
4678 my $preenforce = $enforcers{lc $chan};
4679 if (cr_chk_flag
($chan, CRF_CLOSE
)) {
4680 kickban_multi
($chan, $users, $cmsg);
4682 elsif (cr_chk_flag
($chan, CRF_DRONE
)) {
4683 chan_kill
($chan, $cmsg, $users);
4686 unless($preenforce) {
4687 ircd
::settopic
($bot, $cn, $opnick, $time, $cmsg);
4689 my $ml = get_modelock
($chan);
4690 ircd
::setmode
($bot, $cn, $ml) if($ml);
4694 if(($count == 0 or !is_agent_in_chan
($bot, $cn)) and initial_synced
()) {
4695 unless ($bot eq $csUser) {
4696 unless(is_agent_in_chan
($bot, $cn)) {
4697 botserv
::bot_join
($chan);
4702 #return unless synced() and not cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
4703 return unless not cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
));
4704 #commands aren't sent before synced() anyway
4706 foreach my $user (@$users) {
4707 if(do_status
($user, $chan)) {
4709 $user->{__DO_WELCOME
} = 1;
4713 if($count == 0 and $n) {
4714 my ($ml) = get_modelock
($chan);
4715 ircd
::setmode
($bot, $cn, $ml) if($ml);
4717 $get_topic->execute($cn);
4718 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4719 ircd
::settopic
($bot, $cn, $nsetter, $ntime, $ntopic) if $ntopic;
4725 foreach my $user (@$users) {
4726 if ($user->{__DO_WELCOME
} and chk_user_flag
($user, UF_FINISHED
())) {
4727 do_welcome
($user, $chan);
4728 do_greet
($user, $chan)
4729 if can_do
($chan, 'GREET', $user, { NOREPLY
=> 1 });
4735 sub user_part
($$$) {
4736 my ($nick, $cn, $reason) = @_;
4737 my $user = ( ref $nick eq 'HASH' ? $nick : { NICK
=> $nick });
4738 user_part_multi
($user, [ $cn ], $reason);
4741 sub user_part_multi
($$$) {
4742 # user_join_multi takes a channel and multiple users
4743 # user_part_multi takes a user and multiple channels
4744 # There should probably be a user_join_* that takes one user, multiple channels
4745 # However, it seems that so far, Unreal splits both PART and JOIN (non-SJOIN)
4746 # into multiple events/cmds. The reason is unclear.
4747 # Other ircds may not do so.
4748 # There is also KICK. some IRCds allow KICK #chan user1,user2,...
4749 # Unreal it's _supposed_ to work, but it does not.
4750 my ($user, $chanlist, $reason) = @_;
4752 foreach my $cn (@$chanlist) {
4753 push @chans, { CHAN
=> $cn };
4756 my $uid = get_user_id
($user);
4757 print "part uid $uid\n";
4760 $get_joinpart_lock->execute; $get_joinpart_lock->finish;
4762 foreach my $chan (@chans) {
4763 my $cn = $chan->{CHAN
};
4764 $chanpart->execute($seq, $uid, $cn, $seq, $seq+1000);
4765 $get_user_count->execute($cn);
4766 $chan->{COUNT
} = $get_user_count->fetchrow_array;
4769 $unlock_tables->execute; $unlock_tables->finish;
4771 foreach my $chan (@chans) {
4772 channel_emptied
($chan) if $chan->{COUNT
} == 0;
4776 sub channel_emptied
($) {
4779 botserv
::bot_part_if_needed
(undef, $chan, 'Nobody\'s here', 1);
4780 $chan_delete->execute($chan->{CHAN
});
4781 $wipe_bans->execute($chan->{CHAN
});
4784 sub process_kick
($$$$) {
4785 my ($src, $cn, $target, $reason) = @_;
4786 my $tuser = { NICK
=> $target };
4787 get_user_id
($tuser);
4788 user_part
($tuser, $cn, 'Kicked by '.$src.' ('.$reason.')');
4790 my $chan = { CHAN
=> $cn };
4791 if ( !(is_agent
($src) or $src =~ /\./ or adminserv
::is_ircop
({ NICK
=> $src })) and
4792 ({modes
::splitmodes
(get_modelock
($chan))}->{Q
}->[0] eq '+') )
4794 my $srcUser = { NICK
=> $src };
4795 get_user_id
($srcUser);
4796 #ircd::irckill(agent($chan), $src, "War script detected (kicked $target past +Q in $cn)");
4797 nickserv
::kline_user
($srcUser, 300, "War script detected (kicked $target past +Q in $cn)");
4798 # SVSJOIN won't work while they're banned, unless you invite.
4799 ircd
::invite
(agent
($chan), $cn, $tuser);
4800 ircd
::svsjoin
(undef, $tuser, $cn);
4801 unban_user
($chan, $tuser);
4805 sub chan_mode
($$$$@) {
4806 my ($user, $cn, $modes, $args, @userargs) = @_;
4807 print "CHAN_MODE $user $cn $modes $args\n";
4809 if (ref($user) eq "HASH") {
4810 $src = $user->{NICK
};
4815 my $chan = { CHAN
=> $cn };
4818 # XXX This is not quite right, but maybe it's good enough.
4819 my $mysync = ($src =~ /\./ ? 0 : 1);
4821 if($modes !~ /^[beIvhoaq+-]+$/ and (!synced
() or $mysync)) {
4822 do_modelock
($chan, "$modes $args");
4825 my $opguard = (!current_message-
>{SYNC
} and cr_chk_flag
($chan, CRF_OPGUARD
, 1));
4827 my @perms = ('VOICE', 'HALFOP', 'OP', 'PROTECT');
4831 my @modes = split(//, $modes);
4832 my @args = split(/ /, $args);
4834 foreach my $mode (@modes) {
4835 if($mode eq '+') { $sign = 1; next; }
4836 if($mode eq '-') { $sign = 0; next; }
4838 my $arg = shift(@args) if($mode =~ $scm or $mode =~ $ocm);
4839 if($mode =~ /^[vhoaq]$/) {
4841 next if is_agent
($arg);
4842 my $auser = shift (@userargs);
4843 print "Auser " , Dumper
($auser);
4844 $num = 0 if $mode eq 'v';
4845 $num = 1 if $mode eq 'h';
4846 $num = 2 if $mode eq 'o';
4847 $num = 3 if $mode eq 'a';
4848 $num = 4 if $mode eq 'q';
4849 if($opguard and $sign == 1 and!can_keep_op
($user, $chan, $auser, $mode)) {
4850 push @unargs, ["-" . $mode, $auser];
4853 $nid = get_user_id
($auser) or next;
4857 $r = $chop->execute((2**$num), (2**$num), $nid, $cn);
4859 $r = $chdeop->execute((2**$num), (2**$num), $nid, $cn);
4862 } while($r==0 and $i<10);
4867 process_ban
($cn, $arg, $src, 0, $sign);
4871 process_ban
($cn, $arg, $src, 128, $sign);
4874 next;# if $arg eq '';
4875 #process_ban($cn, $arg, $src, 128, $sign);
4878 ircd
::setmode2
(agent
($chan), $cn, @unargs) if($opguard and @unargs);
4881 sub process_ban
($$$$) {
4882 my ($cn, $arg, $src, $type, $sign) = @_;
4884 $arg =~ tr/\*\?/\%\_/;
4887 $add_ban->execute($cn, $arg, $src, $type);
4889 $delete_ban->execute($cn, $arg, $type);
4894 my ($src, $cn, $suser, $time, $topic) = @_;
4895 my $chan = { CHAN
=> $cn };
4896 $suser -> {AGENT
} = agent
($chan);
4897 my $setter = $suser -> {NICK
};
4898 return if cr_chk_flag
($chan, CRF_CLOSE
, 1);
4900 if(current_message-
>{SYNC
}) { # We don't need to undo our own topic changes.
4901 print "Line @{[__LINE__]}\n";
4902 $set_topic1->execute($setter, $time, $cn);
4903 $set_topic2->execute($cn, $topic);
4907 print "Line @{[__LINE__]}\n";
4908 $get_topic->execute($cn);
4909 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4910 if($topic ne '' and $time == $ntime or can_do
($chan, 'SETTOPIC', undef, { ACC
=> 0 })) {
4911 print "Line @{[__LINE__]}\n";
4912 $set_topic1->execute($setter, $time, $cn);
4913 $set_topic2->execute($cn, $topic);
4915 print "Line @{[__LINE__]}\n";
4916 ircd
::settopic
(agent
($chan), $cn, $nsetter, $ntime, $ntopic);
4919 #lc($src) ne lc($setter) or - removed from previous line because
4920 #i think it was breaking it for insp & idk what it did
4923 # as explained on IRC, the intent is to determine whether it's being set due to a
4924 # net.unsplit, or b/c a user is doing it. You can probably do something more useful
4925 # by paying attn to timestamp
4927 elsif(can_do
($chan, 'SETTOPIC', $suser)) {
4928 print "Line @{[__LINE__]}\n",
4929 "condition : ", can_do
($chan, 'SETTOPIC', $suser),$/;
4930 $set_topic1->execute($setter, $time, $cn);
4931 $set_topic2->execute($cn, $topic);
4933 print "Line @{[__LINE__]}\n";
4934 $get_topic->execute($cn);
4935 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4936 ircd
::settopic
(agent
($chan), $cn, $nsetter, $ntime, $ntopic);
4944 $get_all_closed_chans->execute(CRF_DRONE
|CRF_CLOSE
);
4945 while(my ($cn, $type, $reason, $opnick, $time) = $get_all_closed_chans->fetchrow_array) {
4946 my $chan = { CHAN
=> $cn };
4948 my $cmsg = " is closed [$opnick ".gmtime2
($time)."]: $reason";
4949 if($type == CRF_DRONE
) {
4950 chan_kill
($chan, $cn.$cmsg);
4952 my $user = { NICK
=> $opnick};
4953 get_user_id
($user);
4954 ircd
::settopic
(agent
($chan), $cn, $opnick, $time, "Channel".$cmsg);
4955 clear_users
($chan, "Channel".$cmsg);
4959 while($chanuser_table > 0) { }
4961 $get_eos_lock->execute(); $get_eos_lock->finish;
4962 $get_akick_all->execute();
4964 $get_status_all_server->execute($server);
4965 $gsa = $get_status_all_server;
4967 $get_status_all->execute();
4968 $gsa = $get_status_all;
4970 #$unlock_tables->execute(); $unlock_tables->finish;
4972 while(my @akick = $get_akick_all->fetchrow_array) {
4976 $get_modelock_all->execute();
4977 while(my ($cn, $modes, $ml) = $get_modelock_all->fetchrow_array) {
4978 $ml = do_modelock_fast
($cn, '', $modes, $ml);
4979 ircd
::setmode
(agent
({CHAN
=>$cn}), $cn, $ml) if $ml;
4982 while(my ($cn, $cflags, $agent, $nick, $uid, $uflags, $level, $op, $neverop) = $gsa->fetchrow_array) {
4983 my $user = { NICK
=> $nick, ID
=> $uid };
4984 #next if chk_user_flag($user, UF_FINISHED);
4985 $agent = $csnick unless $agent;
4986 my $chan = { CHAN
=> $cn, FLAGS
=> $cflags, AGENT
=> $agent };
4988 set_modes
($user, $chan, $level, ($cflags & CRF_OPGUARD
)) if not $neverop and $ops[$level] != $op and not $cflags & (CRF_FREEZE
| CRF_CLOSE
| CRF_DRONE
);
4989 do_welcome
($user, $chan);
4992 set_user_flag_all
(UF_FINISHED
());
4993 $unlock_tables->execute(); $unlock_tables->finish;