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 = 'ChanServ1';
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]');
572 elsif($cmd =~ /^(?:[uvhas]op|co?f(ounder)?)$/i) {
573 my ($cn, $cmd2) = splice(@args, 0, 2);
574 my $chan = { CHAN
=> $cn };
576 if($cmd2 =~ /^add$/i) {
578 cs_xop_add
($user, $chan, $cmd, $args[0]);
580 notice
($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
583 elsif($cmd2 =~ /^del(ete)?$/i) {
585 cs_xop_del
($user, $chan, $cmd, $args[0]);
587 notice
($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
590 elsif($cmd2 =~ /^list$/i) {
592 cs_xop_list
($user, $chan, $cmd, $args[0]);
594 notice
($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
597 elsif($cmd2 =~ /^(wipe|clear)$/i) {
599 cs_xop_wipe
($user, $chan, $cmd);
601 notice
($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
605 notice
($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
608 elsif($cmd =~ /^levels$/i) {
610 notice
($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
614 my $cmd2 = lc(splice(@args, 1, 1));
618 cs_levels_set
($user, { CHAN
=> $args[0] }, $args[1], $args[2]);
620 notice
($user, 'Syntax: LEVELS <#channel> SET <permission> <level>');
623 elsif($cmd2 eq 'reset') {
625 cs_levels_set
($user, { CHAN
=> $args[0] }, $args[1]);
627 notice
($user, 'Syntax: LEVELS <#channel> RESET <permission>');
630 elsif($cmd2 eq 'list') {
632 cs_levels_list
($user, { CHAN
=> $args[0] });
634 notice
($user, 'Syntax: LEVELS <#channel> LIST');
637 elsif($cmd2 eq 'clear') {
639 cs_levels_clear
($user, { CHAN
=> $args[0] });
641 notice
($user, 'Syntax: LEVELS <#channel> CLEAR');
645 notice
($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
648 elsif($cmd =~ /^akick$/i) {
650 notice
($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
654 #my $cmd2 = lc($args[1]);
655 my $cmd2 = lc(splice(@args, 1, 1));
659 my @args = split(/\s+/, $msg, 5);
660 cs_akick_add
($user, { CHAN
=> $args[1] }, $args[3], $args[4]);
662 notice
($user, 'Syntax: AKICK <#channel> ADD <nick|mask> <reason>');
665 elsif($cmd2 eq 'del') {
667 cs_akick_del
($user, { CHAN
=> $args[0] }, $args[1]);
669 notice
($user, 'Syntax: AKICK <#channel> DEL <nick|mask|num|seq>');
672 elsif($cmd2 eq 'list') {
674 cs_akick_list
($user, { CHAN
=> $args[0] });
676 notice
($user, 'Syntax: AKICK <#channel> LIST');
679 elsif($cmd2 =~ /^(wipe|clear)$/i) {
681 cs_akick_wipe
($user, { CHAN
=> $args[0] });
683 notice
($user, 'Syntax: AKICK <#channel> WIPE');
686 elsif($cmd2 =~ /^enforce$/i) {
688 cs_akick_enforce
($user, { CHAN
=> $args[0] });
690 notice
($user, 'Syntax: AKICK <#channel> ENFORCE');
694 notice
($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
697 elsif($cmd =~ /^info$/i) {
699 cs_info
($user, { CHAN
=> $args[0] });
701 notice
($user, 'Syntax: INFO <channel>');
704 elsif($cmd =~ /^set$/i) {
705 if(@args == 2 and lc($args[1]) eq 'unsuccessor') {
706 cs_set
($user, { CHAN
=> $args[0] }, $args[1]);
708 elsif(@args >= 3 and (
709 $args[1] =~ /m(?:ode)?lock/i or
710 lc($args[1]) eq 'desc'
712 my @args = split(/\s+/, $msg, 4);
713 cs_set
($user, { CHAN
=> $args[1] }, $args[2], $args[3]);
716 cs_set
($user, { CHAN
=> $args[0] }, $args[1], $args[2]);
719 notice
($user, 'Syntax: SET <channel> <option> <value>');
722 elsif($cmd =~ /^why$/i) {
724 cs_why
($user, { CHAN
=> shift @args }, $src);
727 cs_why
($user, { CHAN
=> shift @args }, @args);
729 notice
($user, 'Syntax: WHY <channel> <nick> [nick [nick ...]]');
733 elsif($cmd =~ /^(de)?(voice|h(alf)?op|op|protect|admin|owner)$/i) {
735 cs_setmodes
($user, $cmd, { CHAN
=> shift(@args) }, @args);
737 notice
($user, 'Syntax: '.uc($cmd).' <channel> [nick [nick ...]]');
740 elsif($cmd =~ /^(up|down)$/i) {
741 cs_updown
($user, $cmd, @args);
743 elsif($cmd =~ /^drop$/i) {
745 cs_drop
($user, { CHAN
=> $args[0] });
747 notice
($user, 'Syntax: DROP <channel>');
750 elsif($cmd =~ /^help$/i) {
751 sendhelp
($user, 'chanserv', @args)
753 elsif($cmd =~ /^count$/i) {
755 cs_count
($user, { CHAN
=> $args[0] });
757 notice
($user, 'Syntax: COUNT <channel>');
760 elsif($cmd =~ /^k(?:ick)?$/i) {
761 my @args = split(/\s+/, $msg, 4); shift @args;
763 cs_kick
($user, { CHAN
=> $args[0] }, $args[1], 0, $args[2])
766 notice
($user, 'Syntax: KICK <channel> <nick> [reason]');
769 elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)$/i) {
770 my @args = split(/\s+/, $msg, 4); shift @args;
772 cs_kick
($user, { CHAN
=> $args[0] }, $args[1], 1, $args[2]);
774 notice
($user, 'Syntax: KICKBAN <channel> <nick> [reason]');
777 elsif($cmd =~ /^k(ick)?m(ask)?$/i) {
778 my @args = split(/\s+/, $msg, 4); shift @args;
780 cs_kickmask
($user, { CHAN
=> $args[0] }, $args[1], 0, $args[2])
783 notice
($user, 'Syntax: KICKMASK <channel> <mask> [reason]');
786 elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)m(ask)?$/i) {
787 my @args = split(/\s+/, $msg, 4); shift @args;
789 cs_kickmask
($user, { CHAN
=> $args[0] }, $args[1], 1, $args[2]);
791 notice
($user, 'Syntax: KICKBANMASK <channel> <mask> [reason]');
794 elsif($cmd =~ /^invite$/i) {
795 my $chan = shift @args;
797 cs_invite
($user, { CHAN
=> $chan }, $src)
800 cs_invite
($user, { CHAN
=> $chan }, @args)
803 notice
($user, 'Syntax: INVITE <channel> <nick>');
806 elsif($cmd =~ /^(close|forbid)$/i) {
808 my @args = split(/\s+/, $msg, 3);
809 cs_close
($user, { CHAN
=> $args[1] }, $args[2], CRF_CLOSE
);
812 notice
($user, 'Syntax: CLOSE <chan> <reason>');
815 elsif($cmd =~ /^drone$/i) {
817 my @args = split(/\s+/, $msg, 3);
818 cs_close
($user, { CHAN
=> $args[1] }, $args[2], CRF_DRONE
);
821 notice
($user, 'Syntax: DRONE <chan> <reason>');
824 elsif($cmd =~ /^clear$/i) {
825 my ($cmd, $chan, $clearcmd, $reason) = split(/\s+/, $msg, 4);
826 unless ($chan and $clearcmd) {
827 notice
($user, 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
830 if($clearcmd =~ /^modes$/i) {
831 cs_clear_modes
($user, { CHAN
=> $chan }, $reason);
833 elsif($clearcmd =~ /^ops$/i) {
834 cs_clear_ops
($user, { CHAN
=> $chan }, $reason);
836 elsif($clearcmd =~ /^users$/i) {
837 cs_clear_users
($user, { CHAN
=> $chan }, $reason);
839 elsif($clearcmd =~ /^bans?$/i) {
840 cs_clear_bans
($user, { CHAN
=> $chan }, 0, $reason);
842 elsif($clearcmd =~ /^excepts?$/i) {
843 cs_clear_bans
($user, { CHAN
=> $chan }, 128, $reason);
846 notice
($user, "Unknown CLEAR command \002$clearcmd\002",
847 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
850 elsif($cmd =~ /^mkick$/i) {
851 my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
853 cs_clear_users
($user, { CHAN
=> $chan }, $reason);
856 notice
($user, 'Syntax: MKICK <chan> [reason]');
859 elsif($cmd =~ /^mdeop$/i) {
860 my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
862 cs_clear_ops
($user, { CHAN
=> $chan }, $reason);
865 notice
($user, 'Syntax: MDEOP <chan> [reason]');
868 elsif($cmd =~ /^welcome$/i) {
869 my $wcmd = splice(@args, 1, 1);
870 if(lc($wcmd) eq 'add') {
871 my ($chan, $wmsg) = (splice(@args, 0, 1), join(' ', @args));
872 unless ($chan and $wmsg) {
873 notice
($user, 'Syntax: WELCOME <channel> ADD <message>');
876 cs_welcome_add
($user, { CHAN
=> $chan }, $wmsg);
878 elsif(lc($wcmd) eq 'del') {
879 if (@args != 2 or !misc
::isint
($args[1])) {
880 notice
($user, 'Syntax: WELCOME <channnel> DEL <number>');
883 cs_welcome_del
($user, { CHAN
=> $args[0] }, $args[1]);
885 elsif(lc($wcmd) eq 'list') {
887 notice
($user, 'Syntax: WELCOME <channel> LIST');
890 cs_welcome_list
($user, { CHAN
=> $args[0] });
893 notice
($user, 'Syntax: WELCOME <channel> <ADD|DEL|LIST>');
896 elsif($cmd =~ /^alist$/i) {
898 cs_alist
($user, { CHAN
=> shift @args }, shift @args);
900 notice
($user, 'Syntax: ALIST <channel> [mask]');
903 elsif($cmd =~ /^unban$/i) {
905 cs_unban
($user, { CHAN
=> shift @args }, $src);
908 cs_unban
($user, { CHAN
=> shift @args }, @args);
910 notice
($user, 'Syntax: UNBAN <channel> [nick]');
913 elsif($cmd =~ /^getkey$/i) {
915 cs_getkey
($user, { CHAN
=> $args[0] });
917 notice
($user, 'Syntax: GETKEY <channel>');
920 elsif($cmd =~ /^auth$/i) {
922 notice
($user, 'Syntax: AUTH <channel> <LIST|DELETE> [param]');
924 cs_auth
($user, { CHAN
=> shift @args }, shift @args, @args);
927 elsif($cmd =~ /^dice$/i) {
928 notice
($user, botserv
::get_dice
($args[0]));
930 elsif($cmd =~ /^(q|n)?ban$/i) {
932 my $chan = shift @args;
934 cs_ban
($user, { CHAN
=> $chan }, $type, @args)
937 notice
($user, 'Syntax: BAN <channel> <nick|mask>');
940 elsif($cmd =~ /^banlist$/i) {
941 my $chan = shift @args;
943 cs_banlist
($user, { CHAN
=> $chan });
946 notice
($user, 'Syntax: BANLIST <channel>');
949 elsif($cmd =~ /^assign$/i) {
950 my $chan = shift @args;
951 notice
($user, "$csnick ASSIGN is deprecated. Please use $botserv::bsnick ASSIGN");
953 botserv
::bs_assign
($user, { CHAN
=> shift @args }, shift @args);
956 notice
($user, 'Syntax: ASSIGN <#channel> <bot>');
959 elsif($cmd =~ /^mode$/i) {
960 my $chan = shift @args;
962 cs_mode
($user, { CHAN
=> $chan }, @args)
965 notice
($user, 'Syntax: MODE <channel> <modes> [parms]');
968 elsif($cmd =~ /^copy$/i) {
969 my $chan = shift @args;
971 cs_copy
($user, { CHAN
=> $chan }, @args)
974 notice
($user, 'Syntax: COPY #chan1 [type] #chan2');
977 elsif($cmd =~ /^m(?:ode)?lock$/i) {
978 my $chan = shift @args;
980 cs_mlock
($user, { CHAN
=> $chan }, @args)
983 notice
($user, 'Syntax: MLOCK <channel> <ADD|DEL|SET|RESET> <modes> [parms]');
986 elsif($cmd =~ /^resync$/i) {
988 notice
($user, 'Syntax: RESYNC <chan1> [chan2 [chan3 [..]]]');
990 cs_resync
($user, @args);
993 elsif($cmd =~ /^JOIN$/i) {
995 notice
($user, 'Syntax: JOIN <chan1> [chan2 [chan3 [..]]]');
997 cs_join
($user, @args);
1000 elsif($cmd =~ /^topic/i) {
1001 my $cn = shift @args;
1002 my $chan = { CHAN
=> $cn };
1003 if($cmd =~ /^topicprepend$/i) {
1005 notice
($user, 'Syntax: TOPICAPPEND <#channel> <message>');
1007 $msg =~ s/^topicappend $cn //i;
1008 cs_topicappend
($user, $chan, 0, $msg);
1010 } elsif($cmd =~ /^topicprepend$/i) {
1012 notice
($user, 'Syntax: TOPICPREPEND <#channel> <message>');
1014 $msg =~ s/^topicprepend $cn //i;
1015 cs_topicappend
($user, $chan, 1, $msg);
1017 } elsif($cmd =~ /^topic$/i) {
1019 notice
($user, 'Syntax: TOPIC <#channel> <message|NONE>');
1021 $msg =~ s/^topic $cn //i;
1022 cs_topic
($user, $chan, $msg);
1027 notice
($user, "Unrecognized command \002$cmd\002.", "For help, type: \002/msg chanserv help\002");
1028 wlog
($csnick, LOG_DEBUG
(), "$src tried to use $csnick $msg");
1032 sub cs_register
($$;$$) {
1033 my ($user, $chan, $pass, $desc) = @_;
1034 # $pass is still passed in, but never used!
1035 my $src = get_user_nick
($user);
1036 my $cn = $chan->{CHAN
};
1038 unless(is_identified
($user, $src)) {
1039 notice
($user, 'You must register your nickname first.', "Type \002/msg NickServ HELP\002 for information on registering nicknames.");
1043 unless(is_in_chan
($user, $chan)) {
1044 notice
($user, "You are not in \002$cn\002.");
1048 if(services_conf_chanreg_needs_oper
&& !adminserv
::is_svsop
($user)) {
1049 notice
($user, "You must be network staff to register a channel\n");
1052 unless(get_op
($user, $chan) & ($opmodes{o
} | $opmodes{a
} | $opmodes{q
})) {
1053 # This would be preferred to be a 'opmode_mask' or something
1054 # However that might be misleading due to hop not being enough to register
1055 notice
($user, "You must have channel operator status to register \002$cn\002.");
1059 my $root = get_root_nick
($src);
1062 my $dlength = length($desc);
1063 if($dlength >= 350) {
1064 notice
($user, 'Channel description is too long by '. $dlength-350 .' character(s). Maximum length is 350 characters.');
1069 if($register->execute($cn, $desc, $root)) {
1070 notice
($user, "\002Your channel is now registered. Thank you.\002");
1071 notice
($user, ' ', "\002NOTICE:\002 Channel passwords are not used, as a security precaution.")
1073 set_acc
($root, $user, $chan, FOUNDER
);
1074 $set_modelock->execute(services_conf_default_channel_mlock
, $cn);
1076 services
::ulog
($csnick, LOG_INFO
(), "registered $cn", $user, $chan);
1077 botserv
::bs_assign
($user, $chan, services_conf_default_chanbot
) if services_conf_default_chanbot
;
1079 notice
($user, 'That channel has already been registered.');
1084 cs_command new SrSv
::AgentUI
::Simple
{
1085 COMMAND
=> [qw(uop vop hop aop sop cf cofounder cof cfounder)],
1086 SYNTAX
=> '#chan add/del/list/wipe/clear [nick/mask]',
1087 CALL
=> \
&cs_xop_dispatch
,
1091 sub cs_xop_dispatch
{
1092 my ($user, $cmd, $chan, $cmd2, @args) = @_;
1095 if($cmd2 =~ /^add$/i) {
1097 cs_xop_add
($user, $chan, $cmd, $args[0]);
1099 notice
($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
1102 elsif($cmd2 =~ /^del(ete)?$/i) {
1104 cs_xop_del
($user, $chan, $cmd, $args[0]);
1106 notice
($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
1109 elsif($cmd2 =~ /^list$/i) {
1111 cs_xop_list
($user, $chan, $cmd, $args[0]);
1113 notice
($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
1116 elsif($cmd2 =~ /^(wipe|clear)$/i) {
1118 cs_xop_wipe
($user, $chan, $cmd);
1120 notice
($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
1124 notice
($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
1128 sub cs_xop_ad_pre
($$$$$) {
1129 my ($user, $chan, $nick, $level, $del) = @_;
1131 my $old = get_acc
($nick, $chan); $old = 0 unless $old;
1132 my $slevel = get_best_acc
($user, $chan);
1134 unless(($del and is_identified
($user, $nick)) or adminserv
::can_do
($user, 'SERVOP')) {
1135 unless($level < $slevel and $old < $slevel) {
1136 notice
($user, $err_deny);
1139 my $cn = $chan->{CHAN
};
1140 my $overrideMsg = "$levels[$level] $cn ".($del ? 'DEL' : 'ADD')." $nick";
1141 can_do
($chan, 'ACCCHANGE', $user, { OVERRIDE_MSG
=> $overrideMsg }) or return undef;
1144 nickserv
::chk_registered
($user, $nick) or return undef;
1145 if (nr_chk_flag
($nick, NRF_NOACC
()) and !adminserv
::can_do
($user, 'SERVOP') and !$del) {
1146 notice
($user, "\002$nick\002 is not able to be added to access lists.");
1153 sub cs_xop_list
($$$;$) {
1154 my ($user, $chan, $cmd, $mask) = @_;
1155 chk_registered
($user, $chan) or return;
1156 my $cn = $chan->{CHAN
};
1157 my $level = xop_byname
($cmd);
1159 my $overrideMsg = "$cmd $cn LIST";
1160 can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> $overrideMsg }) or return;
1164 my ($mnick, $mident, $mhost) = glob2sql
(parse_mask
($mask));
1165 $mnick = '%' if($mnick eq '');
1166 $mident = '%' if($mident eq '');
1167 $mhost = '%' if($mhost eq '');
1169 $get_acc_list_mask->execute($mnick, $cn, $level, $mnick, $mident, $mhost);
1170 while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list_mask->fetchrow_array) {
1171 push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
1172 push @reply, ' '.($t ? 'Date/time added: '. gmtime2
($t).' ' : '').
1173 ($lu ? 'Last used '.time_ago
($lu).' ago' : '') if ($t or $lu);
1175 $get_acc_list_mask->finish();
1177 $get_acc_list->execute($cn, $level);
1178 while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list->fetchrow_array) {
1179 push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
1180 push @reply, ' '.($t ? 'Date/time added: '. gmtime2
($t).' ' : '').
1181 ($lu ? 'Last used '.time_ago
($lu).' ago' : '') if ($t or $lu);
1183 $get_acc_list->finish();
1186 notice
($user, "$levels[$level] list for \002$cn\002:", @reply);
1191 sub cs_xop_wipe
($$$) {
1192 my ($user, $chan, $cmd, $nick) = @_;
1193 chk_registered
($user, $chan) or return;
1195 my $slevel = get_best_acc
($user, $chan);
1196 my $level = xop_byname
($cmd);
1198 unless($level < $slevel) {
1199 notice
($user, $err_deny);
1202 my $cn = $chan->{CHAN
};
1203 my $overrideMsg = "$cmd $cn WIPE";
1204 my $srcnick = can_do
($chan, 'ACCCHANGE', $user, { ACC
=> $slevel, OVERRIDE_MSG
=> $overrideMsg }) or return;
1206 $wipe_acc_list->execute($cn, $level);
1208 my $log_str = "wiped the $cmd list of \002$cn\002.";
1209 my $src = get_user_nick
($user);
1210 notice
($user, "You have $log_str");
1211 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 has $log_str")
1212 if cr_chk_flag
($chan, CRF_VERBOSE
);
1213 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1215 memolog
($chan, "\002$srcnick\002 $log_str");
1218 sub cs_xop_add
($$$$) {
1219 my ($user, $chan, $cmd, $nick) = @_;
1221 chk_registered
($user, $chan) or return;
1222 my $level = xop_byname
($cmd);
1223 my $old = cs_xop_ad_pre
($user, $chan, $nick, $level, 0);
1224 return unless defined($old);
1226 my $cn = $chan->{CHAN
};
1227 if ($level == 3 && $IRCd_capabilities{"HALFOP"} eq "") {
1228 notice
($user, "m_halfop.so is required to add half ops.");
1229 notice
($user, "Please notify your friendly network administrators to enable it.");
1232 if ($level == 5 && $IRCd_capabilities{"ADMIN"} eq "") {
1233 notice
($user, "m_chanprotect.so is required to add SOPs.");
1234 notice
($user, "Please notify your friendly network administrators to enable it.");
1237 if($old == $level) {
1238 notice
($user, "\002$nick\002 already has $levels[$level] access to \002$cn\002.");
1242 if($old == FOUNDER
) {
1243 notice
($user, "\002$nick\002 is the founder of \002$cn\002 and cannot be added to access lists.",
1244 "For more information, type: \002/msg chanserv help set founder\002");
1248 my $root = get_root_nick
($nick);
1249 my $auth = nr_chk_flag
($root, NRF_AUTH
());
1250 my $src = get_user_nick
($user);
1253 $add_auth->execute($cn, "$src:".($old ? $old : 0 ).":$level:".time(), $root);
1254 del_acc
($root, $chan) if $level < $old;
1257 set_acc
($root, $user, $chan, $level);
1261 $del_nick_akick->execute($cn, $root);
1262 my $log_str = "moved $root from the AKICK list to the ${levels[$level]} list of \002$cn\002".
1263 ($auth ? ' (requires authorization)' : '');
1265 my $src = get_user_nick
($user);
1266 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1267 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1268 if cr_chk_flag
($chan, CRF_VERBOSE
);
1269 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1270 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1271 memolog
($chan, "\002$srcnick\002 $log_str");
1273 my $log_str = ($old?'moved':'added')." \002$root\002"
1274 . ($old ? " from the ${levels[$old]}" : '') .
1275 " to the ${levels[$level]} list of \002$cn\002" .
1276 ($auth ? ' (requires authorization)' : '');
1277 my $src = get_user_nick
($user);
1278 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1279 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1280 if cr_chk_flag
($chan, CRF_VERBOSE
);
1281 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1282 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1283 memolog
($chan, "\002$srcnick\002 $log_str");
1287 sub cs_xop_del
($$$) {
1288 my ($user, $chan, $cmd, $nick) = @_;
1290 chk_registered
($user, $chan) or return;
1291 my $level = xop_byname
($cmd);
1292 my $old = cs_xop_ad_pre
($user, $chan, $nick, $level, 1);
1293 return unless defined($old);
1295 my $cn = $chan->{CHAN
};
1297 unless($old == $level) {
1298 notice
($user, "\002$nick\002 is not on the ${levels[$level]} list of \002$cn\002.");
1302 my $root = get_root_nick
($nick);
1303 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1305 del_acc
($root, $chan);
1307 my $src = get_user_nick
($user);
1308 my $log_str = "removed \002$root\002 ($nick) from the ${levels[$level]} list of \002$cn\002";
1309 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1310 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1311 if cr_chk_flag
($chan, CRF_VERBOSE
);
1312 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1313 memolog
($chan, "\002$srcnick\002 $log_str");
1317 my ($user, $chan) = @_;
1319 chk_registered
($user, $chan) or return;
1321 my $cn = $chan->{CHAN
};
1322 my $overrideMsg = "COUNT $cn";
1323 if(can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> $overrideMsg })) {
1329 for (my $level = $plzero + 1; $level < COFOUNDER
+ 2; $level++) {
1330 $get_acc_count->execute($cn, $level - 1);
1331 my ($num_recs) = $get_acc_count->fetchrow_array;
1332 $reply = $reply." $plevels[$level]: ".$num_recs;
1334 notice
($user, "\002$cn Count:\002 ".$reply);
1337 sub cs_levels_pre
($$$;$) {
1338 my($user, $chan, $cmd, $listonly) = @_;
1340 chk_registered
($user, $chan) or return 0;
1341 my $cn = $chan->{CHAN
};
1342 my $overrideMsg = "LEVELS $cn $cmd";
1343 return can_do
($chan, ($listonly ? 'LEVELSLIST' : 'LEVELS'), $user, { OVERRIDE_MSG
=> $overrideMsg });
1346 sub cs_levels_set
($$$;$) {
1347 my ($user, $chan, $perm, $level) = @_;
1349 cs_levels_pre
($user, $chan, "$perm $level") or return;
1350 my $cn = $chan->{CHAN
};
1352 unless(is_level
($perm)) {
1353 notice
($user, "$perm is not a valid permission.");
1357 if(defined($level)) {
1358 $level = xop_byname
($level);
1359 unless(defined($level) and $level >= 0) {
1360 notice
($user, 'You must specify one of the following levels: '.
1361 'any, uop, vop, hop, aop, sop, cofounder, founder, nobody');
1365 $get_level_max->execute($perm);
1366 my ($max) = $get_level_max->fetchrow_array;
1367 $get_level_max->finish();
1369 if($max and $level > $max) {
1370 notice
($user, "\002$perm\002 cannot be set to " . $plevels[$level+$plzero] . '.');
1374 $add_level->execute($cn, $perm);
1375 $set_level->execute($level, $cn, $perm);
1378 notice
($user, "\002$perm\002 is now disabled in \002$cn\002.");
1380 notice
($user, "\002$perm\002 now requires " . $levels[$level] . " access in \002$cn\002.");
1383 $reset_level->execute($perm, $cn);
1385 notice
($user, "\002$perm\002 has been reset to default.");
1389 sub cs_levels_list
($$) {
1390 my ($user, $chan) = @_;
1392 cs_levels_pre
($user, $chan, 'LIST', 1) or return;
1393 my $cn = $chan->{CHAN
};
1395 $get_levels->execute($cn);
1397 while(my ($name, $def, $lvl) = $get_levels->fetchrow_array) {
1399 (defined($lvl) ? $plevels[$lvl+$plzero] : $plevels[$def+$plzero]),
1400 (defined($lvl) ? '' : '(default)')];
1403 notice
($user, columnar
{ TITLE
=> "Permission levels for \002$cn\002:",
1404 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
) }, @data);
1407 sub cs_levels_clear
($$) {
1408 my ($user, $chan) = @_;
1410 cs_levels_pre
($user, $chan, 'CLEAR') or return;
1411 my $cn = $chan->{CHAN
};
1413 $clear_levels->execute($cn);
1415 notice
($user, "All permissions have been reset to default.");
1418 sub cs_akick_pre
($$$;$) {
1419 my ($user, $chan, $overrideMsg, $list) = @_;
1421 chk_registered
($user, $chan) or return 0;
1423 return can_do
($chan, ($list ? 'AKICKLIST' : 'AKICK'), $user, { OVERRIDE_MSG
=> $overrideMsg });
1426 sub cs_akick_add
($$$$) {
1427 my ($user, $chan, $mask, $reason) = @_;
1428 my $cn = $chan->{CHAN
};
1430 my $adder = cs_akick_pre
($user, $chan, "ADD $mask $reason") or return;
1432 my ($nick, $ident, $host) = parse_mask
($mask);
1434 if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
1435 notice
($user, 'Invalid hostmask.');
1442 unless(valid_nick
($nick)) {
1443 $mask = normalize_hostmask
($mask);
1444 ($nick, $ident, $host) = parse_mask
($mask);
1448 if ($ident eq '' and $host eq '' and !nickserv
::is_registered
($nick)) {
1449 notice
($user, "\002$nick\002 is not registered");
1453 my $rlength = length($reason);
1454 if($rlength >= 350) {
1455 notice
($user, 'AKick reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
1460 my $src = get_user_nick
($user);
1461 if($ident eq '' and $host eq '' and my $old = get_acc
($nick, $chan)) {
1463 notice
($user, "\002$nick\002 is already on the AKick list in \002$cn\002");
1466 if($old < get_best_acc
($user, $chan) or adminserv
::can_do
($user, 'SERVOP')) {
1467 if ($old == FOUNDER
()) {
1468 # This is a fallthrough for the override case.
1469 # It shouldn't happen otherwise.
1470 # I didn't make it part of the previous conditional
1471 # b/c just $err_deny is a bit undescriptive in the override case.
1472 notice
($user, "You can't akick the founder!", $err_deny);
1476 my $root = get_root_nick
($nick);
1477 $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
1478 set_acc
($nick, $user, $chan, -1);
1479 $log_str = "moved \002$nick\002 (root: \002$root\002) from the $levels[$old] list".
1480 " to the AKick list of \002$cn\002";
1481 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1483 notice
($user, $err_deny);
1487 if($ident eq '' and $host eq '') {
1488 $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
1489 if (find_auth
($cn, $nick)) {
1490 # Don't allow a pending AUTH entry to potentially override an AKick entry
1491 # Believe it or not, it almost happened with #animechat on SCnet.
1492 # This would also end up leaving an orphan entry in the akick table.
1493 $nickserv::del_auth-
>execute($nick, $cn);
1494 $nickserv::del_auth-
>finish();
1496 set_acc
($nick, $user, $chan, -1);
1497 my $root = get_root_nick
($nick);
1498 $log_str = "added \002$nick\002 (root: \002$root\002) to the AKick list of \002$cn\002.";
1500 ($nick, $ident, $host) = glob2sql
($nick, $ident, $host);
1501 unless($add_akick->execute($cn, $nick, $ident, $host, $adder, $reason)) {
1502 notice
($user, "\002$mask\002 is already on the AKick list of \002$cn\002.");
1505 $log_str = "added \002$mask\002 to the AKick list of \002$cn\002.";
1509 notice
($user, "You have $log_str");
1510 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1511 if cr_chk_flag
($chan, CRF_VERBOSE
);
1512 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1513 memolog
($chan, "\002$adder\002 $log_str");
1515 akick_allchan
($chan);
1518 sub get_akick_by_num
($$) {
1519 my ($chan, $num) = @_;
1520 my $cn = $chan->{CHAN
};
1522 $get_akick_by_num->execute($cn, $num);
1523 my ($nick, $ident, $host) = $get_akick_by_num->fetchrow_array();
1524 ($nick, $ident, $host) = sql2glob
($nick, $ident, $host);
1525 $get_akick_by_num->finish();
1528 } elsif($ident eq '' and $host eq '') {
1529 # nick based akicks don't use nicks but nickreg.id
1530 # so we have to get the nickreg.nick back
1531 $nick = nickserv
::get_id_nick
($nick);
1533 return ($nick, $ident, $host);
1536 sub cs_akick_del
($$$) {
1537 my ($user, $chan, $mask) = @_;
1538 my $cn = $chan->{CHAN
};
1540 my $adder = cs_akick_pre
($user, $chan, "DEL $mask") or return;
1543 if ($mask =~ /^[0-9\.,-]+$/) {
1544 foreach my $num (makeSeqList
($mask)) {
1545 my ($nick, $ident, $host) = get_akick_by_num
($chan, $num - 1) or next;
1546 if($ident eq '' and $host eq '') {
1549 push @masks, "$nick!$ident\@$host";
1555 foreach my $mask (@masks) {
1556 my ($nick, $ident, $host) = parse_mask
($mask);
1558 if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
1559 notice
($user, 'Invalid hostmask.');
1566 unless(valid_nick
($nick)) {
1567 $mask = normalize_hostmask
($mask);
1568 ($nick, $ident, $host) = parse_mask
($mask);
1572 if ($ident eq '' and $host eq '' and !nickserv
::is_registered
($nick)) {
1573 notice
($user, "\002$nick\002 is not registered");
1577 my ($success, $log_str) = do_akick_del
($chan, $mask, $nick, $ident, $host);
1578 my $src = get_user_nick
($user);
1580 notice
($user, "\002$src\002 $log_str");
1581 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1582 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag
($chan, CRF_VERBOSE
);
1583 memolog
($chan, "\002$adder\002 $log_str");
1585 notice
($user, $log_str);
1590 sub do_akick_del
($$$$$) {
1591 my ($chan, $mask, $nick, $ident, $host) = @_;
1592 my $cn = $chan->{CHAN
};
1595 if($ident eq '' and $host eq '') {
1596 if(get_acc
($nick, $chan) == -1) {
1597 del_acc
($nick, $chan);
1598 $del_nick_akick->execute($cn, $nick); $del_nick_akick->finish();
1599 my $root = get_root_nick
($nick);
1600 return (1, "deleted \002$nick\002 (root: \002$root\002) from the AKick list of \002$cn\002.")
1602 return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
1605 ($nick, $ident, $host) = glob2sql
($nick, $ident, $host);
1606 if($del_akick->execute($cn, $nick, $ident, $host) != 0) {
1607 return (1, "deleted \002$mask\002 from the AKick list of \002$cn\002.");
1609 return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
1614 sub cs_akick_list
($$) {
1615 my ($user, $chan) = @_;
1616 my $cn = $chan->{CHAN
};
1618 cs_akick_pre
($user, $chan, 'LIST', 1) or return;
1622 $get_akick_list->execute($cn);
1624 while(my ($nick, $ident, $host, $adder, $reason, $time) = $get_akick_list->fetchrow_array) {
1626 ($nick, $ident, $host) = sql2glob
($nick, $ident, $host);
1629 if($ident eq '' and $host eq '') {
1630 $nick = nickserv
::get_id_nick
($nick);
1632 $nick = "$nick!$ident\@$host";
1635 push @data, ["\002".++$i."\002", $nick, $adder, ($time ? gmtime2
($time) : ''), $reason];
1638 notice
($user, columnar
{TITLE
=> "AKICK list of \002$cn\002:", DOUBLE
=>1,
1639 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
1642 sub cs_akick_wipe
($$$) {
1643 my ($user, $chan) = @_;
1644 my $cn = $chan->{CHAN
};
1646 my $adder = cs_akick_pre
($user, $chan, 'WIPE') or return;
1648 $drop_akick->execute($cn);
1649 $wipe_acc_list->execute($cn, -1);
1650 my $log_str = "wiped the AKICK list of \002$cn\002.";
1651 my $src = get_user_nick
($user);
1652 notice
($user, "You have $log_str");
1653 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag
($chan, CRF_VERBOSE
);
1654 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1655 memolog
($chan, "\002$adder\002 $log_str");
1658 sub cs_akick_enforce
($$) {
1659 my ($user, $chan) = @_;
1660 my $cn = $chan->{CHAN
};
1662 chk_registered
($user, $chan) or return;
1664 can_do
($chan, 'AKickEnforce', $user, { OVERRIDE_MSG
=> "AKICK $cn ENFORCE" }) or return;
1666 akick_allchan
($chan);
1670 cs_command new SrSv
::AgentUI
::Simple
{
1671 COMMAND
=> [qw(info)],
1672 SYNTAX
=> 'LIST:#chan',
1678 my ($user, @chanList) = @_;
1681 foreach my $cn (@chanList) {
1682 if(ref($cn) eq 'HASH') {
1686 push @chanList, split(',', $cn);
1689 my $chan = { CHAN
=> $cn };
1690 unless(__can_do
($chan, 'INFO', undef, 0)) {
1691 can_do
($chan, 'INFO', $user, { OVERRIDE_MSG
=> "INFO $cn" })
1695 $get_info->execute($cn);
1696 my @result = $get_info->fetchrow_array;
1698 push @reply, "The channel \002$cn\002 is not registered.";
1702 my ($descrip, $regd, $last, $topic, $topicer, $modelock, $founder, $successor, $bot, $bantype) = @result;
1704 $modelock = modes
::sanitize
($modelock) unless can_do
($chan, 'GETKEY', $user, { NOREPLY
=> 1 });
1708 my $topiclock = get_level
($chan, 'SETTOPIC');
1709 push @opts, "Topic Lock ($levels[$topiclock])" if $topiclock;
1711 if(cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
1712 push @reply, "\002$cn\002 is closed and cannot be used: ". get_close
($chan);
1717 push @extra, 'Will not expire' if cr_chk_flag
($chan, CRF_HOLD
);
1718 push @extra, 'Channel is frozen and access suspended' if cr_chk_flag
($chan, CRF_FREEZE
);
1720 push @opts, 'OpGuard' if cr_chk_flag
($chan, CRF_OPGUARD
);
1721 push @opts, 'BotStay' if cr_chk_flag
($chan, CRF_BOTSTAY
);
1722 push @opts, 'SplitOps' if cr_chk_flag
($chan, CRF_SPLITOPS
);
1723 push @opts, 'Verbose' if cr_chk_flag
($chan, CRF_VERBOSE
);
1724 push @opts, 'NeverOp' if cr_chk_flag
($chan, CRF_NEVEROP
);
1725 push @opts, 'Ban type '.$bantype if $bantype;
1726 my $opts = join(', ', @opts);
1730 push @data, ['Founder:', $founder];
1731 push @data, ['Successor:', $successor] if $successor;
1732 push @data, ['Description:', $descrip] if $descrip;
1733 push @data, ['Mode lock:', $modelock];
1734 push @data, ['Settings:', $opts] if $opts;
1735 push @data, ['ChanBot:', $bot] if $bot and $bot ne '';
1737 push @data, ['Registered:', gmtime2
($regd)],
1738 ['Last opping:', gmtime2
($last)],
1739 ['Time now:', gmtime2
(time)];
1741 push @reply, columnar
{TITLE
=> "ChanServ info for \002$cn\002:", NOHIGHLIGHT
=> 1}, @data,
1742 {COLLAPSE
=> \
@extra, BULLET
=> 1};
1744 notice
($user, @reply);
1747 sub cs_set_pre
($$$$) {
1748 my ($user, $chan, $set, $parm) = @_;
1749 my $cn = $chan->{CHAN
};
1753 'founder' => 1, 'successor' => 1, 'unsuccessor' => 1,
1754 #'mlock' => 1, 'modelock' => 1,
1756 'topiclock' => 1, 'greet' => 1, 'opguard' => 1,
1757 'freeze' => 1, 'botstay' => 1, 'verbose' => 1,
1758 'splitops' => 1, 'bantype' => 1, 'dice' => 1,
1759 'welcomeinchan' => 1, 'log' => 1,
1761 'hold' => 1, 'noexpire' => 1, 'no-expire' => 1,
1763 'autovoice' => 1, 'avoice' => 1,
1764 'neverop' => 1, 'noop' => 1,
1766 my %override_set = (
1767 'hold' => 'SERVOP', 'noexpire' => 'SERVOP', 'no-expire' => 'SERVOP',
1768 'freeze' => 'FREEZE', 'botstay' => 'BOT', 'log' => 'LOG',
1771 chk_registered
($user, $chan) or return 0;
1772 if($set =~ /m(?:ode)?lock/) {
1773 notice
($user, "CS SET MLOCK is deprecated and replaced with CS MLOCK",
1774 "For more information, please /CS HELP MLOCK");
1777 unless($valid_set{lc $set}) {
1778 notice
($user, "$set is not a valid ChanServ setting.");
1782 if($override_set{lc($set)}) {
1783 if(adminserv
::can_do
($user, $override_set{lc($set)}) ) {
1784 if(services_conf_log_overrides
) {
1785 my $src = get_user_nick
($user);
1786 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override CS SET $cn $set $parm");
1790 notice
($user, $err_deny);
1795 can_do
($chan, 'SET', $user) or return 0;
1802 my ($user, $chan, $set, $parm) = @_;
1803 my $cn = $chan->{CHAN
};
1806 cs_set_pre
($user, $chan, $set, $parm) or return;
1808 if($set =~ /^founder$/i) {
1810 unless(get_best_acc
($user, $chan) == FOUNDER
) {
1811 if(adminserv
::can_do
($user, 'SERVOP')) {
1814 notice
($user, $err_deny);
1820 unless($root = get_root_nick
($parm)) {
1821 notice
($user, "The nick \002$parm\002 is not registered.");
1825 $get_founder->execute($cn);
1826 my ($prev) = $get_founder->fetchrow_array;
1827 $get_founder->finish();
1829 if(lc($root) eq lc($prev)) {
1830 notice
($user, "\002$parm\002 is already the founder of \002$cn\002.");
1834 set_acc
($prev, $user, $chan, COFOUNDER
);
1836 $set_founder->execute($root, $cn); $set_founder->finish();
1837 set_acc
($root, $user, $chan, FOUNDER
);
1838 notice
($user, ($override ? "The previous founder, \002$prev\002, has" : "You have") . " been moved to the co-founder list of \002$cn\002.");
1839 notice_all_nicks
($user, $root, "\002$root\002 has been set as the founder of \002$cn\002.");
1840 services
::ulog
($csnick, LOG_INFO
(), "set founder of \002$cn\002 to \002$root\002", $user, $chan);
1841 $del_nick_akick->execute($cn, $root); $del_nick_akick->finish(); #just in case
1842 $get_successor->execute($cn);
1843 my $suc = $get_successor->fetchrow_array; $get_successor->finish();
1844 if(lc($suc) eq lc($root)) {
1845 $del_successor->execute($cn); $del_successor->finish();
1846 notice
($user, "Successor has been removed from \002$cn\002.");
1852 if($set eq 'successor') {
1853 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
1854 notice
($user, $err_deny);
1858 if(get_acc
($parm, $chan) == 7) {
1859 notice
($user, "The channel founder may not be the successor.");
1864 unless($root = get_root_nick
($parm)) {
1865 notice
($user, "The nick \002$parm\002 is not registered.");
1869 $set_successor->execute($root, $cn); $set_successor->finish();
1871 notice
($user, "\002$parm\002 is now the successor of \002$cn\002");
1872 services
::ulog
($csnick, LOG_INFO
(), "set successor of \002$cn\002 to \002$root\002", $user, $chan);
1876 if($set eq 'unsuccessor') {
1877 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
1878 notice
($user, $err_deny);
1882 $del_successor->execute($cn); $del_successor->finish();
1884 notice
($user, "Successor has been removed from \002$cn\002.");
1885 services
::ulog
($csnick, LOG_INFO
(), "removed successor from \002$cn\002", $user, $chan);
1889 if($set =~ /m(?:ode)?lock/) {
1890 my $modes = modes
::merge
($parm, '+r', 1);
1891 $modes = sanitize_mlockable
($modes);
1892 $set_modelock->execute($modes, $cn);
1894 notice
($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
1899 if($set eq 'desc') {
1900 $set_descrip->execute($parm, $cn);
1902 notice
($user, "Description of \002$cn\002 has been changed.");
1906 if($set eq 'topiclock') {
1907 my $perm = xop_byname
($parm);
1908 if($parm =~ /^(?:no|off|false|0)$/i) {
1909 cs_levels_set
($user, $chan, 'SETTOPIC');
1910 cs_levels_set
($user, $chan, 'TOPIC');
1911 } elsif($perm >= 0 and defined($perm)) {
1912 cs_levels_set
($user, $chan, 'SETTOPIC', $parm);
1913 cs_levels_set
($user, $chan, 'TOPIC', $parm);
1915 notice
($user, 'Syntax: SET <#chan> TOPICLOCK <off|any|uop|vop|hop|aop|sop|cf|founder>');
1920 if($set =~ /^bantype$/i) {
1921 unless (misc
::isint
($parm) and ($parm >= 0 and $parm <= 10)) {
1922 notice
($user, 'Invalid bantype');
1926 $set_bantype->execute($parm, $cn);
1928 notice
($user, "Ban-Type for \002$cn\002 now set to \002$parm\002.");
1934 if($parm =~ /^(?:no|off|false|0)$/i) { $val = 0; }
1935 elsif($parm =~ /^(?:yes|on|true|1)$/i) { $val = 1; }
1937 notice
($user, "Please say \002on\002 or \002off\002.");
1941 if($set =~ /^(?:opguard|secureops)$/i) {
1942 cr_set_flag
($chan, CRF_OPGUARD
, $val);
1946 "OpGuard is now \002ON\002.",
1947 "Channel status may not be granted by unauthorized users in \002$cn\002."#,
1948 #"Note that you must change the $csnick LEVELS settings for VOICE, HALFOP, OP, and/or ADMIN for this setting to have any effect."
1952 "OpGuard is now \002OFF\002.",
1953 "Channel status may be given freely in \002$cn\002."
1960 if($set =~ /^(?:splitops)$/i) {
1961 cr_set_flag
($chan, CRF_SPLITOPS
, $val);
1964 notice
($user, "SplitOps is now \002ON\002.");
1966 notice
($user, "SplitOps is now \002OFF\002.");
1972 if($set =~ /^(hold|no-?expire)$/i) {
1973 cr_set_flag
($chan, CRF_HOLD
, $val);
1976 notice
($user, "\002$cn\002 will not expire");
1977 services
::ulog
($csnick, LOG_INFO
(), "has held \002$cn\002", $user, $chan);
1979 notice
($user, "\002$cn\002 is no longer held from expiration");
1980 services
::ulog
($csnick, LOG_INFO
(), "has removed \002$cn\002 from hold", $user, $chan);
1986 if($set =~ /^freeze$/i) {
1987 cr_set_flag
($chan, CRF_FREEZE
, $val);
1990 notice
($user, "\002$cn\002 is now frozen and access suspended");
1991 services
::ulog
($csnick, LOG_INFO
(), "has frozen \002$cn\002", $user, $chan);
1993 notice
($user, "\002$cn\002 is now unfrozen and access restored");
1994 services
::ulog
($csnick, LOG_INFO
(), "has unfrozen \002$cn\002", $user, $chan);
2000 if($set =~ /^botstay$/i) {
2001 cr_set_flag
($chan, CRF_BOTSTAY
, $val);
2004 notice
($user, "Bot will now always stay in \002$cn");
2005 botserv
::bot_join
($chan, undef);
2007 notice
($user, "Bot will now part if less than one user is in \002$cn");
2008 botserv
::bot_part_if_needed
(undef, $chan, "Botstay turned off");
2013 if($set =~ /^verbose$/i) {
2014 cr_set_flag
($chan, CRF_VERBOSE
, $val);
2017 notice
($user, "Verbose mode enabled on \002$cn");
2020 notice
($user, "Verbose mode disabled on \002$cn");
2025 if($set =~ /^greet$/i) {
2027 notice
($user, "$csnick SET $cn GREET ON is deprecated.",
2028 "Please use $csnick LEVELS $cn SET GREET <rank>");
2030 cs_levels_set
($user, $chan, 'GREET', 'nobody');
2036 if($set =~ /^dice$/i) {
2038 notice
($user, "$csnick SET $cn DICE ON is deprecated.",
2039 "Please use $csnick LEVELS $cn SET DICE <rank>");
2041 cs_levels_set
($user, $chan, 'DICE', 'nobody');
2047 if($set =~ /^welcomeinchan$/i) {
2048 cr_set_flag
($chan, CRF_WELCOMEINCHAN
(), $val);
2051 notice
($user, "WELCOME messages will be put in the channel.");
2053 notice
($user, "WELCOME messages will be sent privately.");
2059 if($set =~ /^log$/i) {
2060 unless(module
::is_loaded
('logserv')) {
2061 notice
($user, "module logserv is not loaded, logging is not available.");
2066 logserv
::addchan
($user, $cn) and cr_set_flag
($chan, CRF_LOG
, $val);
2069 logserv
::delchan
($user, $cn) and cr_set_flag
($chan, CRF_LOG
, $val);
2074 if($set =~ /^a(?:uto)?voice$/i) {
2075 cr_set_flag
($chan, CRF_AUTOVOICE
(), $val);
2078 notice
($user, "All users w/o access will be autovoiced on join.");
2080 notice
($user, "AUTOVOICE disabled.");
2086 if($set =~ /^(?:never|no)op$/i) {
2087 cr_set_flag
($chan, CRF_NEVEROP
(), $val);
2090 notice
($user, "Users will not be automatically opped on join.");
2092 notice
($user, "Users with access will now be automatically opped on join.");
2100 my ($user, $chan, @tnicks) = @_;
2102 chk_registered
($user, $chan) or return;
2104 my $cn = $chan->{CHAN
};
2106 my ($candoNick, $override) = can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> "WHY $cn @tnicks" });
2107 return unless $candoNick;
2110 foreach my $tnick (@tnicks) {
2111 my $tuser = { NICK
=> $tnick };
2112 unless(get_user_id
($tuser)) {
2113 push @reply, "\002$tnick\002: No such user.";
2118 if(is_online
($tnick)) {
2125 $get_all_acc->execute(get_user_id
($tuser), $cn);
2126 while(my ($rnick, $acc) = $get_all_acc->fetchrow_array) {
2128 push @reply, "\002$tnick\002 $has $plevels[$acc+$plzero] access to \002$cn\002 due to identification to the nick \002$rnick\002.";
2130 $get_all_acc->finish();
2133 $check_auth_chan -> execute
($cn, $tnick);
2134 if (my ($nick, $data) = $check_auth_chan->fetchrow_array()) {
2135 my ($adder, $old, $level, $time) = split(/:/, $data);
2136 push @reply, "\002$tnick\002 is awaiting authorization to be added to the $cn \002$levels[$level]\002 list.\n";
2139 push @reply, "\002$tnick\002 has no access to \002$cn\002.";
2143 notice
($user, @reply);
2146 sub cs_setmodes
($$$@) {
2147 my ($user, $cmd, $chan, @args) = @_;
2149 my $agent = $user->{AGENT
} or $csUser;
2150 my $src = get_user_nick
($user);
2151 my $cn = $chan->{CHAN
};
2154 if (cr_chk_flag
($chan, CRF_FREEZE
())) {
2155 notice
($user, "\002$cn\002 is frozen and access suspended.");
2159 if(scalar(@args) == 0) {
2162 } elsif($args[0] =~ /^#/) {
2163 foreach my $chn ($cn, @args) {
2164 next unless $chn =~ /^#/;
2165 no warnings
'prototype'; # we call ourselves
2166 cs_setmodes
($user, $cmd, { CHAN
=> $chn });
2169 } elsif((scalar(@args) == 1) and (lc($args[0]) eq lc($src))) {
2173 # PROTECT is deprecated. remove it in a couple versions.
2174 # It should be called ADMIN under PREFIX_AQ
2175 my @mperms = ('VOICE', 'HALFOP', 'OP', 'ADMIN', 'OWNER');
2176 my @l = ('v', 'h', 'o', 'a', 'q');
2177 my ($level, @modes, $count);
2179 if($cmd =~ /voice$/i) { $level = 0 }
2180 elsif($cmd =~ /h(alf)?op$/i) { $level = 1 }
2181 elsif($cmd =~ /op$/i) { $level = 2 }
2182 elsif($cmd =~ /(protect|admin)$/i) { $level = 3 }
2183 elsif($cmd =~ /owner$/i) { $level = 4 }
2184 my $de = 1 if($cmd =~ s/^de//i);
2187 my $acc = get_best_acc
($user, $chan);
2189 # XXX I'm not sure this is the best way to do it.
2191 ($de and $self) or ($self and ($level + 2) <= $acc) or
2192 can_do
($chan, $mperms[$level], $user, { ACC
=> $acc, NOREPLY
=> 1, OVERRIDE_MSG
=> "$cmd $cn @args" }) )
2194 notice
($user, "$cn: $err_deny");
2198 my ($override, $check_override);
2200 foreach my $target (@args) {
2203 $tuser = ($self ? $user : { NICK
=> $target } );
2205 unless(is_in_chan
($tuser, $chan)) {
2206 notice
($user, "\002$target\002 is not in \002$cn\002.");
2210 my $top = get_op
($tuser, $chan);
2213 unless($top & (2**$level)) {
2214 notice
($user, "\002$target\002 has no $cmd in \002$cn\002.");
2218 if(!$override and get_best_acc
($tuser, $chan) > $acc) {
2219 unless($check_override) {
2220 $override = adminserv
::can_do
($user, 'SUPER');
2221 $check_override = 1;
2223 if($check_override and !$override) {
2224 notice
($user, "\002$target\002 outranks you in \002$cn\002.");
2229 if($top & (2**$level)) {
2231 notice
($user, "You already have $cmd in \002$cn\002.");
2233 notice
($user, "\002$target\002 already has $cmd in \002$cn\002.");
2237 if (cr_chk_flag
($chan, CRF_OPGUARD
()) and
2238 !can_keep_op
($user, $chan, $tuser, $l[$level]))
2240 notice
($user, "$target may not hold ops in $cn because OpGuard is enabled. ".
2241 "Please respect the founders wishes.");
2245 get_user_id
($tuser);
2246 push @modes, [($de ? '-' : '+').$l[$level], $tuser];
2251 ircd
::setmode2
(agent
($chan), $cn, @modes) if scalar @modes;
2252 ircd
::notice
(agent
($chan), '%'.$cn, "$src used ".($de ? "de$cmd" : $cmd).' '.join(' ', @args))
2253 if !$self and (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
2257 my ($user, $chan) = @_;
2258 my $cn = $chan->{CHAN
};
2260 chk_registered
($user, $chan) or return;
2262 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
2263 notice
($user, $err_deny);
2268 notice
($user, $cn.' has been dropped.');
2269 services
::ulog
($csnick, LOG_INFO
(), "dropped $cn", $user, $chan);
2271 undef($enforcers{lc $cn});
2272 botserv
::bot_part_if_needed
(undef(), $chan, "Channel dropped.");
2275 sub cs_kick
($$$;$$) {
2276 my ($user, $chan, $target, $ban, $reason) = @_;
2278 my $cmd = ($ban ? 'KICKBAN' : 'KICK');
2279 my $perm = ($ban ? 'BAN' : 'KICK');
2280 if(ref($chan) ne 'HASH' || !defined($chan->{CHAN
})) {
2281 notice
($user, "Invalid $cmd command, no channel specified");
2285 my $srclevel = get_best_acc
($user, $chan);
2287 my ($nick, $override) = can_do
($chan, ($ban ? 'BAN' : 'KICK'), $user, { ACC
=> $srclevel });
2292 my $src = get_user_nick
($user);
2293 my $cn = $chan->{CHAN
};
2295 $reason = "Requested by $src".($reason?": $reason":'');
2298 ["I'm sorry, $src, I'm afraid I can't do that."],
2299 ["They are not in \002$cn\002."],
2304 my $peace = ({modes
::splitmodes
(get_modelock
($chan))}->{Q
}->[0] eq '+');
2306 my @targets = split(/\,/, $target);
2307 foreach $target (@targets) {
2308 my $tuser = { NICK
=> $target };
2309 get_user_id
($tuser);
2310 my $targetlevel = get_best_acc
($tuser, $chan);
2312 if(lc $target eq lc agent
($chan) or adminserv
::is_service
($tuser)) {
2313 push @{$errors[0]}, $target;
2317 if(get_user_id
($tuser)) {
2318 unless(is_in_chan
($tuser, $chan)) {
2320 push @notinchan, $tuser;
2322 push @{$errors[1]}, $target;
2327 push @{$errors[3]}, $target;
2331 if( ( ($peace and $targetlevel > 0) or ($srclevel <= $targetlevel) )
2332 and not ($override && check_override
($user, ($ban ? 'BAN' : 'KICK'), "$cmd $cn $target")) )
2334 push @{$errors[2]}, $target;
2339 kickban
($chan, $tuser, undef, $reason, 1);
2341 ircd
::kick
(agent
($chan), $cn, $tuser, $reason) unless adminserv
::is_service
($user);
2344 ircd
::flushmodes
() if($ban);
2346 foreach my $errlist (@errors) {
2348 my $msg = shift @$errlist;
2350 foreach my $e (@$errlist) { $e = "\002$e\002" }
2354 enum
("or", @$errlist).
2359 cs_ban
($user, $chan, '', @notinchan) if ($ban and scalar (@notinchan));
2362 sub cs_kickmask
($$$;$$) {
2363 my ($user, $chan, $mask, $ban, $reason) = @_;
2365 my $srclevel = get_best_acc
($user, $chan);
2366 my $src = get_user_nick
($user);
2367 my $cn = $chan->{CHAN
};
2369 my $candoOpts = { ACC
=> $srclevel, OVERRIDE_MSG
=> 'KICK'.($ban ? 'BAN' : '')."MASK $cn $mask $reason" };
2370 my ($nick, $override) = can_do
($chan, ($ban ? 'BAN' : 'KICK'), $user, $candoOpts);
2371 return unless $nick;
2374 $reason = "Requested by $src".($reason?": $reason":'');
2376 my $count = kickmask_noacc
($chan, $mask, $reason, $ban);
2377 notice
($user, ($count ? "Users kicked from \002$cn\002: $count." : "No users in \002$cn\002 matched $mask."))
2381 my ($user, $chan, $type, @targets) = @_;
2382 my $cn = $chan->{CHAN
};
2383 my $src = get_user_nick
($user);
2385 my $srclevel = get_best_acc
($user, $chan);
2386 my ($nick, $override) = can_do
($chan, 'BAN', $user, { ACC
=> $srclevel });
2387 return unless $nick;
2390 ["I'm sorry, $src, I'm afraid I can't do that."],
2395 my (@bans, @unbans);
2396 foreach my $target (@targets) {
2402 elsif($target =~ /\,/) {
2403 push @targets, split(',', $target);
2406 elsif($target eq '') {
2407 # Should never happen
2408 # but it could, given the split above
2411 elsif($target =~ /^-/) {
2413 push @unbans, $target;
2417 elsif($target =~ /[!@]+/) {
2418 ircd
::debug
("normalizing hostmask $target");
2419 #$target = normalize_hostmask($target);
2421 my ($nick, $ident, $host) = parse_mask
($target);
2422 $nick = '*' unless length($nick);
2423 $ident = '*' unless length($ident);
2424 $host = '*' unless length($host);
2425 $target = "$nick\!$ident\@$host";
2427 ircd
::debug
("normalized hostmask: $target");
2429 push @bans, $target;
2433 elsif(valid_nick
($target)) {
2434 $tuser = { NICK
=> $target };
2436 elsif($target = validate_ban
($target)) {
2437 push @bans, $target;
2440 notice
($user, "Not a valid ban target: $target");
2443 my $targetlevel = get_best_acc
($tuser, $chan);
2445 if(lc $target eq lc agent
($chan) or adminserv
::is_service
($tuser)) {
2446 push @{$errors[0]}, get_user_nick
($tuser);
2450 unless(get_user_id
($tuser)) {
2451 push @{$errors[1]}, get_user_nick
($tuser);
2454 if( $srclevel <= $targetlevel and not ($override && check_override
($user, 'BAN', "BAN $cn $target")) ) {
2455 push @{$errors[2]}, $target;
2459 push @bans, make_banmask
($chan, $tuser, $type);
2462 foreach my $errlist (@errors) {
2464 my $msg = shift @$errlist;
2466 foreach my $e (@$errlist) { $e = "\002$e\002" }
2470 enum
("or", @$errlist).
2476 ircd
::ban_list
(agent
($chan), $cn, +1, 'b', @bans) if (scalar(@bans));
2477 ircd
::notice
(agent
($chan), $cn, "$src used BAN ".join(' ', @bans))
2478 if (lc $user->{AGENT
} eq lc $csnick) and (cr_chk_flag
($chan, CRF_VERBOSE
) and scalar(@bans));
2479 cs_unban
($user, $chan, @unbans) if scalar(@unbans);
2482 sub cs_invite
($$@) {
2483 my ($user, $chan, @targets) = @_;
2484 my $src = get_user_nick
($user);
2485 my $cn = $chan->{CHAN
};
2486 my $srclevel = get_best_acc
($user, $chan);
2489 ["They are not online."],
2490 ["They are already in \002$cn\002."],
2495 foreach my $target (@targets) {
2500 $tnick = get_user_nick
($tuser);
2501 } elsif(lc($src) eq lc($target)) {
2504 } elsif($target =~ /\,/) {
2505 push @targets, split(',', $target);
2507 } elsif($target eq '') {
2508 # Should never happen
2509 # but it could, given the split above
2512 $tuser = { NICK
=> $target };
2516 my $candoOpts = { ACC
=> $srclevel, NOREPLY
=> 1, OVERRIDE_MSG
=> "INVITE $cn $target" };
2517 if(lc($src) eq lc($tnick)) {
2518 unless(can_do
($chan, 'InviteSelf', $user, $candoOpts)) {
2519 push @{$errors[2]}, $tnick;
2524 unless(can_do
($chan, 'INVITE', $user, $candoOpts)) {
2525 push @{$errors[2]}, $tnick;
2529 unless(nickserv
::is_online
($tnick)) {
2530 push @{$errors[0]}, $tnick;
2534 # invite is annoying, so punish them mercilessly
2535 return if flood_check
($user, 2);
2538 if(is_in_chan
($tuser, $chan)) {
2539 push @{$errors[1]}, $tnick;
2543 ircd
::invite
(agent
($chan), $cn, $tuser); push @invited, $tnick;
2544 ircd
::notice
(agent
($chan), $tuser, "\002$src\002 has invited you to \002$cn\002.")
2545 unless(lc($src) eq lc($tnick));
2548 foreach my $errlist (@errors) {
2550 my $msg = shift @$errlist;
2552 foreach my $e (@$errlist) { $e = "\002$e\002" }
2556 enum
("or", @$errlist).
2562 ircd
::notice
(agent
($chan), $cn, "$src used INVITE ".join(' ', @invited))
2563 if (lc $user->{AGENT
} eq lc $csnick)and cr_chk_flag
($chan, CRF_VERBOSE
) and scalar(@invited);
2567 my ($user, $chan, $reason, $type) = @_;
2568 # $type is a flag, either CRF_CLOSE or CRF_DRONE
2569 my $cn = $chan->{CHAN
};
2572 unless($oper = adminserv
::can_do
($user, 'SERVOP')) {
2573 notice
($user, $err_deny);
2577 my $rlength = length($reason);
2578 if($rlength >= 350) {
2579 notice
($user, 'Close reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2583 if(is_registered
($chan)) {
2584 $drop_acc->execute($cn);
2585 $drop_lvl->execute($cn);
2586 $del_close->execute($cn);
2587 $drop_akick->execute($cn);
2588 $drop_welcome->execute($cn);
2589 $drop_chantext->execute($cn);
2590 $drop_nicktext->execute($cn); # Leftover channel auths
2592 $set_founder->execute($oper, $cn);
2595 $register->execute($cn, $reason, $oper);
2597 $set_modelock->execute('+rsnt', $cn);
2599 set_acc
($oper, undef, $chan, FOUNDER
);
2601 $set_close->execute($cn, $reason, $oper, $type);
2602 cr_set_flag
($chan, (CRF_FREEZE
| CRF_CLOSE
| CRF_DRONE
), 0); #unset flags
2603 cr_set_flag
($chan, CRF_HOLD
, 1); #set flags
2604 cr_set_flag
($chan, $type, 1); #set flags
2605 my $src = get_user_nick
($user);
2606 my $time = gmtime2
(time);
2607 my $cmsg = "is closed [$src $time]: $reason";
2609 if ($type == CRF_CLOSE
) {
2610 cr_set_flag
($chan, CRF_CLOSE
, 1); #set flags
2611 clear_users
($chan, "Channel $cmsg");
2612 ircd
::settopic
(agent
($chan), $cn, $src, time(), "Channel $cmsg")
2614 elsif ($type == CRF_DRONE
) {
2615 cr_set_flag
($chan, CRF_DRONE
, 1); #set flags
2616 chan_kill
($chan, "$cn $cmsg");
2619 notice
($user, "The channel \002$cn\002 is now closed.");
2620 services
::ulog
($csnick, LOG_INFO
(), "closed $cn with reason: $reason", $user, $chan);
2623 sub cs_clear_pre
($$) {
2624 my ($user, $chan) = @_;
2625 my $cn = $chan->{CHAN
};
2627 my $srclevel = get_best_acc
($user, $chan);
2629 my ($cando, $override) = can_do
($chan, 'CLEAR', $user, { ACC
=> $srclevel });
2630 return 0 unless($cando);
2632 $get_highrank->execute($cn);
2633 my ($highrank_nick, $highrank_level) = $get_highrank->fetchrow_array();
2634 $get_highrank->finish();
2636 if($highrank_level > $srclevel && !$override) {
2637 notice
($user, "$highrank_nick outranks you in $cn (level: $levels[$highrank_level])");
2644 sub cs_clear_users
($$;$) {
2645 my ($user, $chan, $reason) = @_;
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 clear_users
($chan, "CLEAR USERS by \002$src\002".($reason?" reason: $reason":''));
2659 sub cs_clear_modes
($$;$) {
2660 my ($user, $chan, $reason) = @_;
2661 my $cn = $chan->{CHAN
};
2662 my $src = get_user_nick
($user);
2664 cs_clear_pre
($user, $chan) or return;
2666 my $rlength = length($reason);
2667 if($rlength >= 350) {
2668 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2672 my $agent = agent
($chan);
2673 ircd
::notice
($agent, $cn, "CLEAR MODES by \002$src\002".($reason?" reason: $reason":''));
2675 $get_chanmodes->execute($cn);
2676 my ($curmodes) = $get_chanmodes->fetchrow_array;
2677 my $ml = get_modelock
($chan);
2679 # This method may exceed the 12-mode limit
2680 # But it seems to succeed anyway, even with more than 12.
2681 my ($modes, $parms) = split(/ /, modes
::merge
(modes
::invert
($curmodes), $ml, 1). ' * *', 2);
2682 # we split this separately,
2683 # as otherwise it insists on taking the result of the split as a scalar quantity
2684 ircd
::setmode
($agent, $cn, $modes, $parms);
2688 sub cs_clear_ops
($$;$) {
2689 my ($user, $chan, $reason) = @_;
2690 my $cn = $chan->{CHAN
};
2691 my $src = get_user_nick
($user);
2693 cs_clear_pre
($user, $chan) or return;
2695 my $rlength = length($reason);
2696 if($rlength >= 350) {
2697 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2703 ircd
::notice
(agent
($chan), $cn, "CLEAR OPS by \002$src\002".($reason?" reason: $reason":''));
2707 sub cs_clear_bans
($$;$$) {
2708 my ($user, $chan, $type, $reason) = @_;
2709 my $cn = $chan->{CHAN
};
2710 my $src = get_user_nick
($user);
2711 $type = 0 unless defined $type;
2713 cs_clear_pre
($user, $chan) or return;
2715 my $rlength = length($reason);
2716 if($rlength >= 350) {
2717 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2721 clear_bans
($chan, $type);
2723 ircd
::notice
(agent
($chan), $cn, "CLEAR BANS by \002$src\002".($reason?" reason: $reason":''));
2726 sub cs_welcome_pre
($$) {
2727 my ($user, $chan) = @_;
2729 return can_do
($chan, 'WELCOME', $user);
2732 sub cs_welcome_add
($$$) {
2733 my ($user, $chan, $msg) = @_;
2734 my $src = get_best_acc
($user, $chan, 1);
2735 my $cn = $chan->{CHAN
};
2737 cs_welcome_pre
($user, $chan) or return;
2739 my $mlength = length($msg);
2740 if($mlength >= 350) {
2741 notice
($user, 'Welcome Message is too long by '. $mlength-350 .' character(s). Maximum length is 350 characters.');
2745 $count_welcome->execute($cn);
2746 my $count = $count_welcome->fetchrow_array;
2748 notice
($user, 'There is a maximum of five (5) Channel Welcome Messages.');
2752 $add_welcome->execute($cn, ++$count, $src, $msg);
2754 notice
($user, "Welcome message number $count for \002$cn\002 set to:", " $msg");
2757 sub cs_welcome_list
($$) {
2758 my ($user, $chan) = @_;
2759 my $cn = $chan->{CHAN
};
2761 cs_welcome_pre
($user, $chan) or return;
2763 $list_welcome->execute($cn);
2767 while(my ($id, $time, $adder, $msg) = $list_welcome->fetchrow_array) {
2768 push @data, ["$id.", $adder, gmtime2
($time), $msg];
2770 $list_welcome->finish();
2772 notice
($user, columnar
{TITLE
=> "Welcome message list for \002$cn\002:", DOUBLE
=>1,
2773 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2776 sub cs_welcome_del
($$$) {
2777 my ($user, $chan, $id) = @_;
2778 my $cn = $chan->{CHAN
};
2780 cs_welcome_pre
($user, $chan) or return;
2782 if ($del_welcome->execute($cn, $id) == 1) {
2783 notice
($user, "Welcome Message \002$id\002 deleted from \002$cn\002");
2784 $consolidate_welcome->execute($cn, $id);
2788 "Welcome Message number $id for \002$cn\002 does not exist.");
2792 sub cs_alist
($$;$) {
2793 my ($user, $chan, $mask) = @_;
2794 my $cn = $chan->{CHAN
};
2796 chk_registered
($user, $chan) or return;
2798 my $slevel = get_best_acc
($user, $chan);
2800 can_do
($chan, 'ACCLIST', $user, { ACC
=> $slevel }) or return;
2805 my ($mnick, $mident, $mhost) = glob2sql
(parse_mask
($mask));
2806 $mnick = '%' if($mnick eq '');
2807 $mident = '%' if($mident eq '');
2808 $mhost = '%' if($mhost eq '');
2810 $get_acc_list2_mask->execute($mnick, $cn, $mnick, $mident, $mhost);
2811 while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2_mask->fetchrow_array) {
2812 push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
2813 push @reply, ' '.($time ? 'Date/time added: '. gmtime2
($time).' ' : '').
2814 ($last_used ? 'Last used '.time_ago
($last_used).' ago' : '') if ($time or $last_used);
2816 $get_acc_list2_mask->finish();
2818 $get_acc_list2->execute($cn);
2819 while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2->fetchrow_array) {
2820 push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
2821 push @reply, ' '.($time ? 'Date/time added: '. gmtime2
($time).' ' : '').
2822 ($last_used ? 'Last used '.time_ago
($last_used).' ago' : '') if ($time or $last_used);
2824 $get_acc_list2->finish();
2827 notice
($user, "Access list for \002$cn\002:", @reply);
2832 sub cs_banlist
($$) {
2833 my ($user, $chan) = @_;
2834 my $cn = $chan->{CHAN
};
2835 can_do
($chan, 'UnbanSelf', $user, { NOREPLY
=> 1 }) or can_do
($chan, 'BAN', $user) or return;
2837 my $i = 0; my @data;
2838 $list_bans->execute($cn, 0);
2839 while(my ($mask, $setter, $time) = $list_bans->fetchrow_array()) {
2840 push @data, ["\002".++$i."\002", sql2glob
($mask), $setter, ($time ? gmtime2
($time) : '')];
2843 notice
($user, columnar
{TITLE
=> "Ban list of \002$cn\002:", DOUBLE
=>1,
2844 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2848 my ($user, $chan, @parms) = @_;
2849 my $cn = $chan->{CHAN
};
2852 $self = 1 if ( (scalar(@parms) == 1) and ( lc($parms[0]) eq lc(get_user_nick
($user)) ) );
2853 if ($parms[0] eq '*') {
2854 cs_clear_bans
($user, $chan);
2858 can_do
($chan, ($self ? 'UnbanSelf' : 'UNBAN'), $user) or return;
2861 my (@userlist, @masklist);
2862 foreach my $parm (@parms) {
2863 if(valid_nick
($parm)) {
2864 my $tuser = ($self ? $user : { NICK
=> $parm });
2865 unless(get_user_id
($tuser)) {
2866 notice
($user, "No such user: \002$parm\002");
2869 push @userlist, $tuser;
2870 } elsif($parm =~ /^[0-9\.,-]+$/) {
2871 foreach my $num (makeSeqList
($parm)) {
2872 push @masklist, get_ban_num
($chan, $num);
2875 push @masklist, $parm;
2879 if(scalar(@userlist)) {
2880 unban_user
($chan, @userlist);
2881 notice
($user, "All bans affecting " .
2882 ( $self ? 'you' : enum
( 'and', map(get_user_nick
($_), @userlist) ) ) .
2883 " on \002$cn\002 have been removed.");
2885 if(scalar(@masklist)) {
2886 ircd
::ban_list
(agent
($chan), $cn, -1, 'b', @masklist);
2887 notice
($user, "The following bans have been removed: ".join(' ', @masklist))
2888 if scalar(@masklist);
2892 sub cs_updown
($$@) {
2893 my ($user, $cmd, @chans) = @_;
2894 return cs_updown2
($user, $cmd, { CHAN
=> shift @chans }, @chans)
2895 if (defined($chans[1]) and $chans[1] !~ "^\#" and $chans[0] =~ "^\#");
2897 @chans = get_user_chans
($user)
2900 if (uc($cmd) eq 'UP') {
2901 foreach my $cn (@chans) {
2902 next unless ($cn =~ /^\#/);
2903 my $chan = { CHAN
=> $cn };
2904 next if cr_chk_flag
($chan, (CRF_DRONE
| CRF_CLOSE
| CRF_FREEZE
), 1);
2905 chanserv
::set_modes
($user, $chan, chanserv
::get_best_acc
($user, $chan));
2908 elsif (uc($cmd) eq 'DOWN') {
2909 foreach my $cn (@chans) {
2910 next unless ($cn =~ /^\#/);
2911 chanserv
::unset_modes
($user, { CHAN
=> $cn });
2916 sub cs_updown2
($$$@) {
2917 my ($user, $cmd, $chan, @targets) = @_;
2919 my $agent = $user->{AGENT
} or $csnick;
2920 my $cn = $chan->{CHAN
};
2922 return unless chk_registered
($user, $chan);
2923 if (cr_chk_flag
($chan, CRF_FREEZE
())) {
2924 notice
($user, "\002$cn\002 is frozen and access suspended.");
2928 my $acc = get_best_acc
($user, $chan);
2929 return unless(can_do
($chan, 'UPDOWN', $user, { ACC
=> $acc }));
2931 my $updown = ((uc($cmd) eq 'UP') ? 1 : 0);
2933 my ($override, $check_override);
2935 foreach my $target (@targets) {
2937 my $tuser = { NICK
=> $target };
2939 unless(is_in_chan
($tuser, $chan)) {
2940 notice
($user, "\002$target\002 is not in \002$cn\002.");
2945 push @list, $target;
2946 chanserv
::set_modes
($tuser, $chan, chanserv
::get_best_acc
($tuser, $chan));
2949 my $top = get_op
($tuser, $chan);
2951 notice
($user, "\002$target\002 is already deopped in \002$cn\002.");
2955 if(!$override and get_best_acc
($tuser, $chan) > $acc) {
2956 unless($check_override) {
2957 $override = adminserv
::can_do
($user, 'SUPER');
2958 $check_override = 1;
2960 if($check_override and !$override) {
2961 notice
($user, "\002$target\002 outranks you in \002$cn\002.");
2965 push @list, $target;
2966 chanserv
::unset_modes
($tuser, { CHAN
=> $cn });
2971 my $src = get_user_nick
($user);
2972 ircd
::notice
(agent
($chan), '%'.$cn, "$src used $cmd ".join(' ', @list))
2973 if (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
2977 my ($user, $chan) = @_;
2978 my $cn = $chan->{CHAN
};
2980 can_do
($chan, 'GETKEY', $user) or return;
2982 $get_chanmodes->execute($cn);
2983 my $modes = $get_chanmodes->fetchrow_array; $get_chanmodes->finish();
2985 if(my $key = modes
::get_key
($modes)) {
2986 notice
($user, "Channel key for \002$cn\002: $key");
2989 notice
($user, "\002$cn\002 has no channel key.");
2994 my ($user, $chan, $cmd, @args) = @_;
2995 my $cn = $chan->{CHAN
};
2998 return unless chk_registered
($user, $chan);
2999 return unless can_do
($chan, 'AccChange', $user);
3000 my $userlevel = get_best_acc
($user, $chan);
3001 if($cmd eq 'list') {
3003 $list_auth_chan->execute($cn);
3004 while(my ($nick, $data) = $list_auth_chan->fetchrow_array()) {
3005 my ($adder, $old, $level, $time) = split(/:/, $data);
3006 push @data, ["\002$nick\002", $levels[$level], $adder, gmtime2
($time)];
3008 if ($list_auth_chan->rows()) {
3009 notice
($user, columnar
{TITLE
=> "Pending authorizations for \002$cn\002:",
3010 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
3013 notice
($user, "There are no pending authorizations for \002$cn\002");
3015 $list_auth_chan->finish();
3017 elsif($cmd eq 'remove' or $cmd eq 'delete' or $cmd eq 'del') {
3018 my ($nick, $adder, $old, $level, $time);
3019 my $parm = shift @args;
3020 if(misc
::isint
($parm) and ($nick, $adder, $old, $level, $time) = get_auth_num
($cn, $parm))
3023 elsif (($adder, $old, $level, $time) = get_auth_nick
($cn, $parm))
3028 # This should normally be an 'else' as the elsif above should prove false
3029 # For some reason, it doesn't work. the unless ($nick) fixes it.
3030 # It only doesn't work for numbered entries
3031 notice
($user, "There is no entry for \002$parm\002 in \002$cn\002's AUTH list");
3034 $nickserv::del_auth-
>execute($nick, $cn); $nickserv::del_auth-
>finish();
3035 my $log_str = "deleted AUTH entry $cn $nick $levels[$level]";
3036 my $src = get_user_nick
($user);
3037 notice
($user, "You have $log_str");
3038 ircd
::notice
(agent
($chan), '%'.$cn, "has \002$src\002 has $log_str")
3039 if cr_chk_flag
($chan, CRF_VERBOSE
);
3040 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "has $log_str", $user, $chan);
3043 notice
($user, "Unknown AUTH command \002$cmd\002");
3048 my ($user, $chan, $modes_in, @parms_in) = @_;
3049 can_do
($chan, 'MODE', $user) or return undef;
3050 ($modes_in, @parms_in) = validate_chmodes
($modes_in, @parms_in);
3059 my $sign = '+'; my $cn = $chan->{CHAN
};
3060 my ($modes_out, @parms_out, @bans);
3061 foreach my $mode (split(//, $modes_in)) {
3062 $sign = $mode if $mode =~ /[+-]/;
3063 if ($permhash{$mode}) {
3064 my $parm = shift @parms_in;
3065 cs_setmodes
($user, ($sign eq '-' ? 'de' : '').$permhash{$mode}, $chan, $parm);
3067 elsif ($mode eq 'b') {
3068 my $parm = shift @parms_in;
3074 elsif($mode =~ /[eIlLkjf]/) {
3075 $modes_out .= $mode;
3076 push @parms_out, shift @parms_in;
3078 $modes_out .= $mode;
3083 cs_ban
($user, $chan, undef, @bans);
3085 return if $modes_out =~ /^[+-]*$/;
3086 ircd
::setmode
(agent
($chan), $chan->{CHAN
}, $modes_out, join(' ', @parms_out));
3087 do_modelock
($chan, $modes_out.' '.join(' ', @parms_out));
3089 $modes_out =~ s/^[+-]*([+-].*)$/$1/;
3090 ircd
::notice
(agent
($chan), '%'.$cn, get_user_nick
($user).' used MODE '.join(' ', $modes_out, @parms_out))
3091 if (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
3095 my ($user, $chan1, @args) = @_;
3096 my $cn1 = $chan1->{CHAN
};
3099 if($args[0] =~ /^#/) {
3103 if($args[0] =~ /(?:acc(?:ess)?|akick|levels|all)/i) {
3104 $type = shift @args;
3105 $cn2 = shift @args unless $cn2;
3108 if($type =~ /^acc(?:ess)?/i) {
3110 $rank = shift @args;
3116 unless(defined $cn2 and defined $type) {
3117 notice
($user, 'Unknown COPY command', 'Syntax: COPY #chan1 [type] #chan2');
3119 my $chan2 = { CHAN
=> $cn2 };
3120 if(lc($cn1) eq lc($cn2)) {
3121 notice
($user, "You cannot copy a channel onto itself.");
3123 unless(is_registered
($chan1)) {
3124 notice
($user, "Source channel \002$cn1\002 must be registered.");
3127 can_do
($chan1, 'COPY', $user) or return undef;
3128 if(lc $type eq 'all') {
3129 if(is_registered
($chan2)) {
3130 notice
($user, "When copying all channel details, destination channel cannot be registered.");
3132 } elsif(!(get_op
($user, $chan2) & ($opmodes{o
} | $opmodes{a
} | $opmodes{q
}))) {
3133 # This would be preferred to be a 'opmode_mask' or something
3134 # However that might be misleading due to hop not being enough to register
3135 notice
($user, "You must have channel operator status to register \002$cn2\002.");
3138 cs_copy_chan_all
($user, $chan1, $chan2);
3142 unless(is_registered
($chan2)) {
3143 notice
($user, "When copying channel lists, destination channel must be registered.");
3146 can_do
($chan2, 'COPY', $user) or return undef;
3148 if(lc $type eq 'akick') {
3149 cs_copy_chan_akick
($user, $chan1, $chan2);
3150 } elsif(lc $type eq 'levels') {
3151 cs_copy_chan_levels
($user, $chan1, $chan2);
3152 } elsif($type =~ /^acc(?:ess)?/i) {
3153 cs_copy_chan_acc
($user, $chan1, $chan2, xop_byname
($rank));
3157 sub cs_copy_chan_all
($$$) {
3158 my ($user, $chan1, $chan2) = @_;
3159 cs_copy_chan_chanreg
($user, $chan1, $chan2);
3160 cs_copy_chan_levels
($user, $chan1, $chan2);
3161 cs_copy_chan_acc
($user, $chan1, $chan2);
3162 cs_copy_chan_akick
($user, $chan1, $chan2);
3166 sub cs_copy_chan_chanreg
($$$) {
3167 my ($user, $chan1, $chan2) = @_;
3168 my $cn1 = $chan1->{CHAN
};
3169 my $cn2 = $chan2->{CHAN
};
3171 copy_chan_chanreg
($cn1, $cn2);
3172 botserv
::bot_join
($chan2) unless (lc(agent
($chan2)) eq lc($csnick) );
3173 do_modelock
($chan2);
3174 notice
($user, "Registration for \002$cn1\002 copied to \002$cn2\002");
3176 my $log_str = "copied the channel registration for \002$cn1\002 to \002$cn2\002";
3177 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3179 my $src = get_user_nick
($user);
3180 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3181 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3182 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3183 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3186 sub cs_copy_chan_acc
($$$;$) {
3187 my ($user, $chan1, $chan2, $level) = @_;
3188 my $cn1 = $chan1->{CHAN
};
3189 my $cn2 = $chan2->{CHAN
};
3191 copy_chan_acc
($cn1, $cn2, $level);
3193 unless(cr_chk_flag
($chan2, CRF_NEVEROP
)) {
3194 $get_chan_users->execute($cn2); my @targets;
3195 while (my ($nick, $uid) = $get_chan_users->fetchrow_array()) {
3196 push @targets, $nick unless nr_chk_flag_user
({ NICK
=> $nick, ID
=> $uid }, NRF_NEVEROP
);
3198 cs_updown2
($user, 'UP', $chan2, @targets);
3201 notice
($user, "Access list for \002$cn1\002 ".
3202 ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
3203 "copied to \002$cn2\002");
3205 my $log_str = "copied the channel access list for \002$cn1\002 ".
3206 ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
3208 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3210 my $src = get_user_nick
($user);
3211 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3212 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3213 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3214 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3217 sub cs_copy_chan_levels
($$$) {
3218 my ($user, $chan1, $chan2) = @_;
3219 my $cn1 = $chan1->{CHAN
};
3220 my $cn2 = $chan2->{CHAN
};
3222 copy_chan_levels
($cn1, $cn2);
3223 notice
($user, "LEVELS for \002$cn1\002 copied to \002$cn2\002");
3225 my $log_str = "copied the LEVELS list for \002$cn1\002 to \002$cn2\002";
3226 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3228 my $src = get_user_nick
($user);
3229 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3230 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3231 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3232 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3235 sub cs_copy_chan_akick
($$$) {
3236 my ($user, $chan1, $chan2) = @_;
3237 my $cn1 = $chan1->{CHAN
};
3238 my $cn2 = $chan2->{CHAN
};
3240 copy_chan_akick
($cn1, $cn2);
3241 notice
($user, "Channel AKick list for \002$cn1\002 copied to \002$cn2\002");
3243 my $log_str = "copied the AKick list for \002$cn1\002 to \002$cn2\002";
3244 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3246 my $src = get_user_nick
($user);
3247 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3248 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3249 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3250 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3253 sub cs_mlock
($$$@) {
3254 my ($user, $chan, $cmd, @args) = @_;
3255 my $cn = $chan->{CHAN
};
3256 # does this need its own privilege now?
3257 can_do
($chan, 'SET', $user) or return;
3260 my ($modes_in, @parms_in) = validate_chmodes
(shift @args, @args);
3261 $modes = $modes_in.' '.join(' ', @parms_in);
3265 my $cur_modelock = get_modelock
($chan);
3266 if(lc $cmd eq 'add') {
3267 $modes = modes
::merge
($cur_modelock, $modes, 1);
3268 $modes = sanitize_mlockable
($modes);
3269 $set_modelock->execute($modes, $cn);
3271 elsif(lc $cmd eq 'del') {
3272 $modes =~ s/[+-]//g;
3273 $modes = modes
::add
($cur_modelock, "-$modes", 1);
3274 $set_modelock->execute($modes, $cn);
3276 elsif(lc $cmd eq 'set') {
3277 $modes = modes
::merge
($modes, "+r", 1);
3278 $set_modelock->execute($modes, $cn);
3280 elsif(lc $cmd eq 'reset') {
3281 $set_modelock->execute(services_conf_default_channel_mlock
, $cn);
3283 notice
($user, "Unknown MLOCK command \"$cmd\"");
3287 notice
($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
3291 notice
($user, columnar
{TITLE
=> "Ban list of \002$cn\002:", DOUBLE
=>1,
3292 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
3296 use SrSv
::MySQL
::Stub
{
3297 getChanUsers
=> ['COLUMN', "SELECT user.nick FROM chanuser JOIN user ON (user.id=chanuser.nickid)
3298 WHERE chanuser.chan=? AND chanuser.joined=1"]
3302 my ($user, @cns) = @_;
3303 foreach my $cn (@cns) {
3304 my $chan = { CHAN
=> $cn };
3305 next unless cs_clear_ops
($user, $chan, 'Resync');
3306 cs_updown2
($user, 'up', $chan, getChanUsers
($cn));
3307 if(can_do
($chan, 'AKickEnforce', $user, { OVERRIDE_MSG
=> "AKICK $cn ENFORCE", NOREPLY
=> 1 })) {
3308 cs_akick_enforce
($user, $chan);
3314 my ($user, @cns) = @_;
3317 foreach my $cn (@cns) {
3319 push @cns, split(',', $cn);
3324 my $chan = { CHAN
=> $cn };
3325 my $cando_opts = { NOREPLY
=> 1 };
3326 if(check_akick
($user, $chan, 1)) {
3327 push @reply, "You are banned from $cn";
3329 } elsif(!can_do
($chan, 'JOIN', $user, $cando_opts)) {
3330 push @reply, "$cn is a private channel.";
3333 if(is_in_chan
($user, $chan)) {
3336 if(can_do
($chan, 'InviteSelf', $user, $cando_opts)) {
3337 cs_invite
($user, $chan, $user);
3342 ircd
::svsjoin
(get_user_agent
($user), $user, @out_cns) if scalar @out_cns;
3343 notice
($user, @reply) if scalar @reply;
3347 my ($user, $cn, @args) = @_;
3348 my ($chan, $msg) = ($cn->{CHAN
}, join(" ", @args));
3349 can_do
($cn, 'SETTOPIC', $user) or return undef;
3350 ircd
::settopic
(agent
($cn), $chan, get_user_nick
($user), time, ($msg =~ /^none/i ? "" : $msg));
3353 sub cs_topicappend
{
3354 my ($user, $chan, $front, $topicappend) = @_;
3355 my $cn = $chan->{CHAN
};
3356 $get_topic->execute($cn);
3357 my ($cur_topic, undef, undef) = $get_topic->fetchrow_array;
3358 $get_topic->finish();
3359 my $new_topic = $cur_topic;
3362 $new_topic = $topicappend . ' | ' . $cur_topic;
3364 $new_topic .= ' | ' . $topicappend;
3367 cs_topic
($user, $chan, $new_topic);
3372 # these are helpers and do NOT check if $cn1 or $cn2 is reg'd
3373 sub copy_chan_acc
($$;$) {
3374 my ($cn1, $cn2, $level) = @_;
3376 $copy_acc_rank->execute($cn2, $cn1, $level);
3377 $copy_acc_rank->finish();
3379 $get_founder->execute($cn2);
3380 my ($founder) = $get_founder->fetchrow_array;
3381 $get_founder->finish();
3383 $copy_acc->execute($cn2, $cn1, $founder);
3384 $copy_acc->finish();
3388 sub copy_chan_akick
($$;$) {
3389 my ($cn1, $cn2) = @_;
3390 $copy_akick->execute($cn2, $cn1);
3391 $copy_akick->finish();
3392 copy_chan_acc
($cn1, $cn2, -1);
3395 sub copy_chan_levels
($$) {
3396 my ($cn1, $cn2) = @_;
3397 $copy_levels->execute($cn2, $cn1);
3398 $copy_levels->finish();
3401 sub copy_chan_chanreg
($$) {
3402 my ($cn1, $cn2) = @_;
3403 $get_founder->execute($cn1);
3404 my ($founder) = $get_founder->fetchrow_array;
3405 $get_founder->finish();
3406 set_acc
($founder, undef, { CHAN
=> $cn2 }, FOUNDER
);
3407 $copy_chanreg->execute($cn2, $cn1);
3408 $copy_chanreg->finish();
3411 sub do_welcome
($$) {
3412 my ($user, $chan) = @_;
3413 my $cn = $chan->{CHAN
};
3415 $get_welcomes->execute($cn);
3416 if($get_welcomes->rows) {
3418 while(my ($msg) = $get_welcomes->fetchrow_array) {
3419 push @welcomes, (cr_chk_flag
($chan, CRF_WELCOMEINCHAN
) ? '' : "[$cn] " ).$msg;
3421 if(cr_chk_flag
($chan, CRF_WELCOMEINCHAN
)) {
3422 ircd
::privmsg
(agent
($chan), $cn, @welcomes);
3424 notice
($user, @welcomes);
3427 $get_welcomes->finish();
3431 my ($user, $chan) = @_;
3432 my $cn = $chan->{CHAN
};
3434 if(can_do
($chan, 'GREET', $user)) {
3435 my $src = get_user_nick
($user);
3436 $nickserv::get_greet-
>execute(get_user_id
($user));
3437 my ($greet) = $nickserv::get_greet-
>fetchrow_array();
3438 $nickserv::get_greet-
>finish();
3439 ircd
::privmsg
(agent
($chan), $cn, "[\002$src\002] $greet") if $greet;
3443 sub chk_registered
($$) {
3444 my ($user, $chan) = @_;
3446 unless(is_registered
($chan)) {
3447 my $cn = $chan->{CHAN
};
3449 notice
($user, "The channel \002$cn\002 is not registered.");
3456 sub make_banmask
($$;$) {
3457 my ($chan, $tuser, $type) = @_;
3458 my $nick = get_user_nick
($tuser);
3460 my ($ident, $vhost) = get_vhost
($tuser);
3462 my ($nick, $ident, $vhost) = make_hostmask
(get_bantype
($chan), $nick, $ident, $vhost);
3465 } elsif($type eq 'n') {
3470 return $type."$nick!$ident\@$vhost";
3473 sub kickban
($$$$;$) {
3474 my ($chan, $user, $mask, $reason, $noflush) = @_;
3475 my $cn = $chan->{CHAN
};
3476 my $nick = get_user_nick
($user);
3478 return 0 if adminserv
::is_service
($user);
3480 my $agent = agent
($chan);
3483 $mask = make_banmask
($chan, $user);
3486 enforcer_join
($chan) if (get_user_count
($chan) <= 1);
3487 ircd
::setmode
($agent, $cn, '+b', $mask);
3488 ircd
::flushmodes
() unless $noflush;
3489 ircd
::kick
($agent, $cn, $user, $reason);
3493 sub kickban_multi
($$$) {
3494 my ($chan, $users, $reason) = @_;
3495 my $cn = $chan->{CHAN
};
3496 my $agent = agent
($chan);
3498 enforcer_join
($chan);
3499 ircd
::setmode
($agent, $cn, '+b', '*!*@*');
3502 foreach my $user (@$users) {
3503 next if adminserv
::is_ircop
($user) or adminserv
::is_svsop
($user, adminserv
::S_HELP
());
3504 ircd
::kick
($agent, $cn, $user, $reason);
3508 sub clear_users
($$) {
3509 my ($chan, $reason) = @_;
3510 my $cn = $chan->{CHAN
};
3511 my $agent = agent
($chan);
3514 enforcer_join
($chan);
3515 ircd
::setmode
($agent, $cn, '+b', '*!*@*');
3517 $get_chan_users->execute($cn);
3518 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3519 my $user = { NICK
=> $nick, ID
=> $uid };
3520 ircd
::kick
($agent, $cn, $user, $reason)
3521 unless adminserv
::is_ircop
($user) or adminserv
::is_svsop
($user, adminserv
::S_HELP
());
3528 sub kickmask
($$$$) {
3529 my ($chan, $mask, $reason, $ban) = @_;
3530 my $cn = $chan->{CHAN
};
3531 my $agent = agent
($chan);
3533 my ($nick, $ident, $host) = glob2sql
(parse_mask
($mask));
3534 $nick = '%' if ($nick eq '');
3535 $ident = '%' if ($ident eq '');
3536 $host = '%' if ($host eq '');
3539 my $banmask = $nick.'!'.$ident.'@'.$host;
3540 $banmask =~ tr/%_/*?/;
3541 ircd
::setmode
($agent, $cn, '+b', $banmask);
3546 $get_chan_users_mask->execute($cn, $nick, $ident, $host, $host, $host);
3547 while(my ($nick, $uid) = $get_chan_users_mask->fetchrow_array) {
3548 my $user = { NICK
=> $nick, ID
=> $uid };
3549 ircd
::kick
($agent, $cn, $user, $reason)
3550 unless adminserv
::is_service
($user);
3553 $get_chan_users_mask->finish();
3558 sub kickmask_noacc
($$$$) {
3559 my ($chan, $mask, $reason, $ban) = @_;
3560 my $cn = $chan->{CHAN
};
3561 my $agent = agent
($chan);
3563 my ($nick, $ident, $host) = glob2sql
(parse_mask
($mask));
3564 $nick = '%' if ($nick eq '');
3565 $ident = '%' if ($ident eq '');
3566 $host = '%' if ($host eq '');
3569 my $banmask = $nick.'!'.$ident.'@'.$host;
3570 $banmask =~ tr/%_/*?/;
3571 ircd
::setmode
($agent, $cn, '+b', $banmask);
3576 $get_chan_users_mask_noacc->execute($cn, $nick, $ident, $host, $host, $host);
3577 while(my ($nick, $uid) = $get_chan_users_mask_noacc->fetchrow_array) {
3578 my $user = { NICK
=> $nick, ID
=> $uid };
3579 ircd
::kick
($agent, $cn, $user, $reason)
3580 unless adminserv
::is_service
($user);
3583 $get_chan_users_mask_noacc->finish();
3590 my $cn = $chan->{CHAN
};
3592 my $agent = agent
($chan);
3594 $get_chan_users->execute($cn);
3595 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3596 my $user = { NICK
=> $nick };
3597 get_user_id
($user);
3598 my $opmodes = get_op
($user, $chan);
3599 for(my $i; $i < 5; $i++) {
3600 if($opmodes & 2**$i) {
3601 push @modelist, ['-'.$opmodes[$i], $user];
3606 ircd
::setmode2
($agent, $cn, @modelist);
3609 sub clear_bans
($;$) {
3610 my ($chan, $type) = @_;
3611 my $cn = $chan->{CHAN
};
3613 my $agent = agent
($chan);
3614 $type = 0 unless defined $type;
3615 my $mode = ($type == 128 ? 'e' : 'b');
3618 $get_all_bans->execute($cn, $type);
3619 while(my ($mask) = $get_all_bans->fetchrow_array) {
3620 $mask =~ tr/\%\_/\*\?/;
3621 push @banlist, $mask;
3624 ircd
::ban_list
($agent, $cn, -1, $mode, @banlist);
3628 sub unban_user
($@) {
3629 my ($chan, @userlist) = @_;
3630 my $cn = $chan->{CHAN
};
3632 if (defined(&ircd
::unban_users
)) {
3633 ircd
::unban_users
(@userlist);
3636 foreach my $tuser (@userlist) {
3638 unless($tuid = get_user_id
($tuser)) {
3642 # We don't handle extended bans. Yet.
3643 $find_bans_chan_user->execute($cn, $tuid, 0);
3644 while (my ($mask) = $find_bans_chan_user->fetchrow_array) {
3645 $mask =~ tr/\%\_/\*\?/;
3648 $find_bans_chan_user->finish();
3650 ircd
::ban_list
(agent
($chan), $cn, -1, 'b', @bans) if scalar(@bans);
3651 $delete_bans_chan_user->execute($cn, $tuid, 0); $delete_bans_chan_user->finish();
3657 sub chan_kill
($$;$) {
3658 my ($chan, $reason, $tusers) = @_;
3659 my $cn = $chan->{CHAN
};
3660 my $agent = agent
($chan);
3663 enforcer_join
($chan);
3665 foreach my $tuser (@$tusers) {
3666 $tuser->{ID
} = $tuser->{__ID
} if defined($tuser->{__ID
}); # user_join_multi does this.
3667 nickserv
::kline_user
($tuser, services_conf_chankilltime
, $reason)
3668 unless adminserv
::is_ircop
($tuser) or adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3673 $get_chan_users->execute($cn);
3674 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3675 my $tuser = { NICK
=> $nick, ID
=> $uid, AGENT
=> $agent };
3676 nickserv
::kline_user
($tuser, services_conf_chankilltime
, $reason)
3677 unless adminserv
::is_ircop
($tuser) or adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3685 sub do_nick_akick
($$;$) {
3686 my ($tuser, $chan, $root) = @_;
3687 my $cn = $chan->{CHAN
};
3688 unless(defined($root)) {
3689 (undef, $root) = get_best_acc
($tuser, $chan, 2);
3691 $get_nick_akick->execute($cn, $root);
3692 my ($reason) = $get_nick_akick->fetchrow_array(); $get_nick_akick->finish();
3693 return 0 if adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3694 if(defined($reason) && $reason =~ /\|/) {
3695 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3697 kickban
($chan, $tuser, undef, "User has been banned from ".$cn.($reason?": $reason":''));
3700 sub check_akick
($$;$) {
3701 my ($user, $chan, $check_only) = @_;
3702 if(adminserv
::is_svsop
($user, adminserv
::S_HELP
())) {
3705 my ($acc, $root) = get_best_acc
($user, $chan, 2);
3707 do_nick_akick
($user, $chan, $root) unless $check_only;
3710 my $cn = $chan->{CHAN
};
3711 my $uid = get_user_id
($user);
3713 $get_akick->execute($uid, $cn);
3714 if(my @akick = $get_akick->fetchrow_array) {
3715 akickban
($cn, @akick) unless $check_only;
3722 sub do_status
($$;$) {
3723 my ($user, $chan, $check_only) = @_;
3725 return 0 if cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
));
3727 my $nick = get_user_nick
($user);
3729 if(check_akick
($user, $chan, $check_only)) {
3732 my ($acc, $root) = get_best_acc
($user, $chan, 2);
3733 if(!can_do
($chan, 'JOIN', $user, { ACC
=> $acc, NOREPLY
=> 1 })) {
3734 if (!is_agent
($user->{NICK
})) {
3735 kickban
($chan, $user, undef, 'This is a private channel.')
3741 if( !$check_only && is_registered
($chan) &&
3742 !cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
)) )
3744 my $neverop = (is_neverop_user
($user) || cr_chk_flag
($chan, CRF_NEVEROP
, 1));
3745 my $no_deop = cr_chk_flag
($chan, CRF_SPLITOPS
, 0);
3747 if($neverop && cr_chk_flag
($chan, CRF_AUTOVOICE
, 1) && $acc > 2) {
3752 set_modes
($user, $chan, $acc,
3754 # this probably needs to be configurable for ports
3755 # also Unreal may [optionally] set +q on join.
3757 !$neverop || $op_anyway,
3764 sub akick_alluser
($) {
3766 my $uid = get_user_id
($user);
3768 $get_akick_alluser->execute($uid);
3769 while(my @akick = $get_akick_alluser->fetchrow_array) {
3774 sub akick_allchan
($) {
3776 my $cn = $chan->{CHAN
};
3778 $get_akick_allchan->execute($cn);
3779 while(my @akick = $get_akick_allchan->fetchrow_array) {
3780 akickban
($cn, @akick);
3785 my ($cn, $knick, $bnick, $ident, $host, $reason, $bident) = @_;
3786 my $target = { NICK
=> $knick };
3787 my $chan = { CHAN
=> $cn };
3788 return 0 if adminserv
::is_svsop
($target, adminserv
::S_HELP
());
3791 ($bnick, $ident, $host) = make_hostmask
(get_bantype
($chan), $knick, $bident, $host);
3792 } elsif($host =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
3793 ($bnick, $ident, $host) = make_hostmask
(4, $knick, $bident, $host);
3795 $bnick =~ tr/\%\_/\*\?/;
3796 $ident =~ tr/\%\_/\*\?/;
3797 $host =~ tr/\%\_/\*\?/;
3799 if(defined($reason) && $reason =~ /\|/) {
3800 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3803 if(defined($reason) && $reason =~ /\|/) {
3804 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3807 return kickban
($chan, $target, "$bnick!$ident\@$host", "User has been banned from ".$cn.($reason?": $reason":''));
3810 sub notice_all_nicks
($$$) {
3811 my ($user, $nick, $msg) = @_;
3812 my $src = get_user_nick
($user);
3814 notice
($user, $msg);
3815 foreach my $u (get_nick_user_nicks
$nick) {
3816 notice
({ NICK
=> $u, AGENT
=> $csUser }, $msg) unless lc $src eq lc $u;
3824 if($name =~ /^uop$/i) { $level=1; }
3825 elsif($name =~ /^vop$/i) { $level=2; }
3826 elsif($name =~ /^hop$/i) { $level=3; }
3827 elsif($name =~ /^aop$/i) { $level=4; }
3828 elsif($name =~ /^sop$/i) { $level=5; }
3829 elsif($name =~ /^co?f(ounder)?$/i) { $level=6; }
3830 elsif($name =~ /^founder$/i) { $level=7; }
3831 elsif($name =~ /^(any|all|user)/i) { $level=0; }
3832 elsif($name =~ /^akick$/i) { $level=-1; }
3833 elsif($name =~ /^(none|disabled?|nobody)$/i) { $level=8; }
3839 return if services_conf_noexpire
;
3841 $get_expired->execute(time() - (86400 * services_conf_chanexpire
));
3842 while(my ($cn, $founder) = $get_expired->fetchrow_array) {
3843 drop
({ CHAN
=> $cn });
3844 wlog
($csnick, LOG_INFO
(), "\002$cn\002 has expired. Founder: $founder");
3848 sub enforcer_join
($) {
3850 my $cn = $chan->{CHAN
};
3851 my $bot = agent
($chan);
3853 return if $enforcers{lc $cn};
3854 $enforcers{lc $cn} = lc $bot;
3856 botserv
::bot_join
($chan);
3858 add_timer
("CSEnforce $bot $cn", 60, __PACKAGE__
, 'chanserv::enforcer_part');
3861 sub enforcer_part
($) {
3863 my ($junk, $bot, $cn) = split(/ /, $cookie);
3865 return unless $enforcers{lc $cn};
3866 undef($enforcers{lc $cn});
3868 botserv
::bot_part_if_needed
($bot, {CHAN
=> $cn}, 'Enforcer Leaving');
3871 sub fix_private_join_before_id
($) {
3874 my @cns = get_recent_private_chans
(get_user_id
($user));
3875 foreach my $cn (@cns) {
3876 my $chan = { CHAN
=> $cn };
3877 unban_user
($chan, $user);
3880 ircd
::svsjoin
($csUser, $user, @cns) if @cns;
3883 ### DATABASE UTILITY FUNCTIONS ###
3885 sub get_user_count
($) {
3887 my $cn = $chan->{CHAN
};
3889 $get_user_count->execute($cn);
3891 return $get_user_count->fetchrow_array;
3902 if($cur_lock ne $chan) {
3903 really_release_lock
($chan);
3905 die("Tried to get two locks at the same time: $cur_lock, $chan")
3910 $get_lock->execute(sql_conf_mysql_db
.".chan.$chan");
3915 sub release_lock
($) {
3922 if($cur_lock and $cur_lock ne $chan) {
3923 really_release_lock
($cur_lock);
3925 die("Tried to release the wrong lock");
3931 really_release_lock
($chan);
3935 sub really_release_lock
($) {
3939 $release_lock->execute(sql_conf_mysql_db
.".chan.$chan");
3940 $release_lock->finish;
3944 #sub is_free_lock($) {
3945 # $is_free_lock->execute($_[0]);
3946 # return $is_free_lock->fetchrow_array;
3949 sub get_modelock
($) {
3958 $get_modelock->execute($cn);
3959 my ($ml) = $get_modelock->fetchrow_array;
3960 $get_modelock->finish();
3964 sub do_modelock
($;$) {
3965 my ($chan, $modes) = @_;
3966 my $cn = $chan->{CHAN
};
3970 $get_modelock_lock->execute; $get_modelock_lock->finish;
3972 $get_chanmodes->execute($cn);
3973 my ($omodes) = $get_chanmodes->fetchrow_array;
3974 my $ml = get_modelock
($chan);
3976 $ml = do_modelock_fast
($cn, $modes, $omodes, $ml);
3978 $unlock_tables->execute; $unlock_tables->finish;
3980 ircd
::setmode
(agent
($chan), $cn, $ml) if($ml);
3983 sub do_modelock_fast
($$$$) {
3984 my ($cn, $modes, $omodes, $ml) = @_;
3985 my $nmodes = modes
::add
($omodes, $modes, 1);
3986 $ml = modes
::diff
($nmodes, $ml, 1);
3987 $set_chanmodes->execute(modes
::add
($nmodes, $ml, 1), $cn);
3992 sub update_modes
($$) {
3993 my ($cn, $modes) = @_;
3995 $get_update_modes_lock->execute; $get_update_modes_lock->finish;
3996 $get_chanmodes->execute($cn);
3997 my ($omodes) = $get_chanmodes->fetchrow_array;
3999 $set_chanmodes->execute(modes
::add
($omodes, $modes, 1), $cn);
4000 $unlock_tables->execute; $unlock_tables->finish;
4006 $is_level->execute($perm);
4008 return $is_level->fetchrow_array;
4012 return nr_chk_flag
($_[0], NRF_NEVEROP
(), 1);
4015 sub is_neverop_user
($) {
4016 return nr_chk_flag_user
($_[0], NRF_NEVEROP
(), 1);
4019 sub is_in_chan
($$) {
4020 my ($user, $chan) = @_;
4021 my $cn = $chan->{CHAN
};
4022 my $uid = get_user_id
($user);
4024 $is_in_chan->execute($uid, $cn);
4025 if($is_in_chan->fetchrow_array) {
4032 sub is_registered
($) {
4034 my $cn = $chan->{CHAN
};
4036 $is_registered->execute($cn);
4037 if($is_registered->fetchrow_array) {
4044 sub get_user_chans
($) {
4046 my $uid = get_user_id
($user);
4049 $get_user_chans->execute($uid, $ircline, $ircline+1000);
4050 while(my ($chan) = $get_user_chans->fetchrow_array) {
4057 sub get_user_chans_recent
($) {
4059 my $uid = get_user_id
($user);
4060 my (@curchans, @oldchans);
4062 $get_user_chans_recent->execute($uid);
4063 while(my ($cn, $joined, $op) = $get_user_chans_recent->fetchrow_array) {
4065 push @curchans, make_op_prefix
($op).$cn;
4068 push @oldchans, $cn;
4072 return (\
@curchans, \
@oldchans);
4075 my ($prefixes, $modes);
4076 sub make_op_prefix
($) {
4080 unless(defined($prefixes) and defined($modes)) {
4081 $IRCd_capabilities{PREFIX
} =~ /^\((\S+)\)(\S+)$/;
4082 ($modes, $prefixes) = ($1, $2);
4083 $modes = reverse $modes;
4084 $prefixes = reverse $prefixes;
4088 for(my $i = 0; $i < length($prefixes); $i++) {
4089 $op_prefix = substr($prefixes, $i, 1).$op_prefix if ($op & (2**$i));
4095 my ($user, $chan) = @_;
4096 my $cn = $chan->{CHAN
};
4097 my $uid = get_user_id
($user);
4099 $get_op->execute($uid, $cn);
4100 my ($op) = $get_op->fetchrow_array;
4105 sub get_best_acc
($$;$) {
4106 my ($user, $chan, $retnick) = @_;
4107 my $uid = get_user_id
($user);
4108 my $cn = $chan->{CHAN
};
4110 $get_best_acc->execute($uid, $cn);
4111 my ($bnick, $best) = $get_best_acc->fetchrow_array;
4112 $get_best_acc->finish();
4115 return ($best, $bnick);
4116 } elsif($retnick == 1) {
4124 my ($nick, $chan) = @_;
4125 my $cn = $chan->{CHAN
};
4128 if cr_chk_flag
($chan, (CRF_DRONE
| CRF_CLOSE
| CRF_FREEZE
), 1);
4130 $get_acc->execute($cn, $nick);
4131 my ($acc) = $get_acc->fetchrow_array;
4137 my ($nick, $user, $chan, $level) = @_;
4138 my $cn = $chan->{CHAN
};
4140 $adder = get_best_acc
($user, $chan, 1) if $user;
4142 $set_acc1->execute($cn, $level, $nick);
4143 $set_acc2->execute($level, $adder, $cn, $nick);
4145 if ( ( $level > 0 and !is_neverop
($nick) and !cr_chk_flag
($chan, CRF_NEVEROP
) )
4148 set_modes_allnick
($nick, $chan, $level);
4153 my ($nick, $chan) = @_;
4154 my $cn = $chan->{CHAN
};
4156 $del_acc->execute($cn, $nick);
4158 foreach my $user (get_nick_users
$nick) {
4159 set_modes
($user, $chan, 0, 1) if is_in_chan
($user, $chan);
4163 sub get_auth_nick
($$) {
4164 my ($cn, $nick) = @_;
4166 $get_auth_nick->execute($cn, $nick);
4167 my ($data) = $get_auth_nick->fetchrow_array();
4168 $get_auth_nick->finish();
4170 return split(/:/, $data);
4172 sub get_auth_num
($$) {
4173 my ($cn, $num) = @_;
4175 $get_auth_num->execute($cn, $num - 1);
4176 my ($nick, $data) = $get_auth_num->fetchrow_array();
4177 $get_auth_num->finish();
4179 return ($nick, split(/:/, $data));
4182 my ($cn, $nick) = @_;
4184 $find_auth->execute($cn, $nick);
4185 my ($ret) = $find_auth->fetchrow_array();
4186 $find_auth->finish();
4191 # Only call this if you've checked the user for NEVEROP already.
4192 sub set_modes_allchan
($;$) {
4193 my ($user, $neverop) = @_;
4194 my $uid = get_user_id
($user);
4196 $get_user_chans->execute($uid, $ircline, $ircline+1000);
4197 while(my ($cn) = $get_user_chans->fetchrow_array) {
4198 my $chan = { CHAN
=> $cn };
4199 my $acc = get_best_acc
($user, $chan);
4201 set_modes
($user, $chan, $acc) unless ($neverop or cr_chk_flag
($chan, CRF_NEVEROP
));
4203 do_nick_akick
($user, $chan);
4208 # Only call this if you've checked for NEVEROP already.
4209 sub set_modes_allnick
($$$) {
4210 my ($nick, $chan, $level) = @_;
4211 my $cn = $chan->{CHAN
};
4213 $get_using_nick_chans->execute($nick, $cn);
4214 while(my ($n) = $get_using_nick_chans->fetchrow_array) {
4215 my $user = { NICK
=> $n };
4216 my $l = get_best_acc
($user, $chan);
4218 set_modes
($user, $chan, $level, 1) if($level == $l);
4220 do_nick_akick
($user, $chan);
4225 # If channel has OPGUARD, $doneg is true.
4226 sub set_modes
($$$;$$) {
4227 my ($user, $chan, $acc, $doneg, $dopos) = @_;
4229 $dopos = 1 unless defined($dopos);
4230 $doneg = 0 unless defined($doneg);
4231 my $cn = $chan->{CHAN
};
4234 # Do akick stuff here.
4237 my $dst = ( $acc > 0 ? $ops[$acc] : 0 );
4238 my $cur = get_op
($user, $chan);
4241 if (cr_chk_flag
($chan, CRF_FREEZE
)) {
4242 set_mode_mask
($user, $chan, $cur, undef);
4245 if (($acc == 0) and cr_chk_flag
($chan, CRF_AUTOVOICE
)) {
4246 set_mode_mask
($user, $chan, $cur, 1);
4250 $pos = $dst ^ ($dst & $cur);
4251 $neg = ($dst ^ $cur) & $cur if $doneg;
4254 set_mode_mask
($user, $chan, ($doneg ? $neg : '-'), ($dopos ? $pos : '+'));
4259 set_lastused
($cn, get_user_id
($user));
4263 sub unset_modes
($$) {
4264 my ($user, $chan) = @_;
4266 my $mask = get_op
($user, $chan);
4268 set_mode_mask
($user, $chan, $mask, 0);
4271 sub set_mode_mask
($$$$) {
4272 my ($user, $chan, @masks) = @_;
4273 my $nick = get_user_nick
($user);
4274 my $cn = $chan->{CHAN
};
4277 for(my $sign; $sign < 2; $sign++) {
4278 next if($masks[$sign] == 0);
4280 $out .= '-' if $sign == 0;
4281 $out .= '+' if $sign == 1;
4283 for(my $i; $i < 5; $i++) {
4284 my @l = ('v', 'h', 'o', 'a', 'q');
4285 if ($IRCd_capabilities{"FOUNDER"} eq "") {
4288 if ($IRCd_capabilities{"ADMIN"} eq "") {
4291 if ($IRCd_capabilities{"HALFOP"} eq "") {
4294 if($masks[$sign] & 2**$i) {
4296 my $user_ = { NICK
=> $nick, AGENT
=> $csnick};
4297 get_user_id
($user_);
4304 ircd
::setmode_many
(agent
($chan), $cn, $out, @args);
4309 my ($chan, $perm) = @_;
4310 my $cn = $chan->{CHAN
};
4312 $get_level->execute($cn, $perm);
4313 my ($level, $isnotnull) = $get_level->fetchrow_array;
4314 $get_level->finish();
4317 return ($level, $isnotnull);
4324 sub check_override
($$;$) {
4325 my ($user, $perm, $logMsg) = @_;
4328 #{OVERRIDE::$perm} produces funny package problems, so wrap it in double-quotes.
4329 if(exists($user->{"OVERRIDE::$perm"}) && (my $nick = $user->{"OVERRIDE::$perm"})) {
4330 if(defined($nick)) {
4331 if(services_conf_log_overrides
&& $logMsg) {
4332 my $src = get_user_nick
($user);
4333 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override $logMsg");
4335 return (wantarray ? ($nick, 1) : $nick);
4340 foreach my $o (@override) {
4341 my ($operRank, $permHashRef) = @$o;
4342 if($permHashRef->{$perm} and my $nick = adminserv
::can_do
($user, $operRank)) {
4343 $user->{"OVERRIDE::$perm"} = $nick;
4344 if(services_conf_log_overrides
&& $logMsg) {
4345 my $src = get_user_nick
($user);
4346 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override $logMsg");
4348 return (wantarray ? ($nick, 1) : $nick);
4351 $user->{"OVERRIDE::$perm"} = undef;
4355 my ($chan, $perm, $user, $data) = @_;
4356 $data = {} unless defined $data;
4357 # $data is a hashref/struct
4358 my $noreply = $data->{NOREPLY
};
4359 my $acc = $data->{ACC
};
4360 my $overrideMsg = $data->{OVERRIDE_MSG
};
4362 if(my $nick = __can_do
($chan, $perm, $user, $acc)) {
4363 # This is becoming increasingly complicated
4364 # and checking if an override was used is becoming tricky.
4365 # We had a case in cs_kick where an oper should be able to override +Q/$peace
4366 # but cannot b/c they have regular access in that channel.
4368 if(defined($user)) {
4369 (undef, $override) = check_override
($user, $perm);
4371 return (wantarray ? ($nick, $override) : $nick);
4372 } elsif ( $user and adminserv
::is_svsop
($user, adminserv
::S_HELP
()) ) {
4373 #set_lastused($cn, get_user_id($user));
4374 my ($nick, $override) = check_override
($user, $perm, $overrideMsg);
4375 return (wantarray ? ($nick, $override) : $nick) if $override;
4377 if($user and !$noreply) {
4378 my $cn = $chan->{CHAN
};
4379 if (cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
4380 notice
($user, "\002$cn\002 is closed and cannot be used".
4381 ((uc $perm eq 'INFO') ? ': '.get_close
($chan) : '.'));
4383 elsif(cr_chk_flag
($chan, CRF_FREEZE
)) {
4384 notice
($user, "\002$cn\002 is frozen and access suspended.");
4387 notice
($user, "$cn: $err_deny");
4393 sub __can_do
($$$;$) {
4394 my ($chan, $perm, $user, $acc) = @_;
4396 my $cn = $chan->{CHAN
};
4400 unless(exists($chan->{"PERM::$perm"})) {
4401 $level = $chan->{"PERM::$perm"} = get_level
($chan, $perm);
4403 $level = $chan->{"PERM::$perm"};
4406 unless(defined($acc)) {
4407 unless (defined $user && ref($user) eq 'HASH') {
4408 die "invalid __can_do call";
4410 my $chanuser = $user->{lc $cn};
4411 unless (defined($chanuser) && exists($chanuser->{ACC
})) {
4412 ($acc, $nick) = get_best_acc
($user, $chan, 2);
4413 ($chanuser->{ACC
}, $chanuser->{ACCNICK
}) = ($acc, $nick);
4415 ($acc, $nick) = ($chanuser->{ACC
}, $chanuser->{ACCNICK
});
4418 $nick = 1 unless $nick;
4420 if($acc >= $level and !cr_chk_flag
($chan, (CRF_CLOSE
| CRF_FREEZE
| CRF_DRONE
))) {
4421 set_lastused
($cn, get_user_id
($user)) if $user;
4422 return (wantarray ? ($nick, 0) : $nick);
4425 if(cr_chk_flag
($chan, CRF_FREEZE
) and ($perm eq 'JOIN')) {
4426 return (wantarray ? ($nick, 0) : $nick);
4432 sub can_keep_op
($$$$) {
4433 # This is a naïve implemenation using a loop.
4434 # If we ever do a more flexible version that further restricts how
4435 # LEVELS affect opguard, the loop will have to be unrolled.
4437 # Only call this if you've already checked opguard, as we do not check it here.
4439 # Remember, this isn't a permission check if someone is allowed to op someone [else],
4440 # rather this checks if the person being opped is allowed to keep/have it.
4441 my ($user, $chan, $tuser, $opmode) = @_;
4442 return 1 if $opmode eq 'v'; # why remove a voice?
4444 'q' => ['OWNER', 4],
4445 'a' => ['ADMIN', 3],
4447 'h' => ['HALFOP', 1],
4451 my $self = (lc(get_user_nick
($user)) eq lc(get_user_nick
($tuser)));
4453 #my ($level, $isnotnull) = get_level($chan, $permhash{$opmode}[1]);
4454 my $level = get_level
($chan, $permhash{$opmode}[0]);
4456 foreach my $luser ($tuser, $user) {
4457 # We check target first, as there seems no reason that
4458 # someone who has access can't be opped by someone
4459 # who technically doesn't.
4460 return 1 if (adminserv
::is_svsop
($luser, adminserv
::S_HELP
()) and
4461 check_override
($luser, $permhash{$opmode}[0]));
4463 my $acc = get_best_acc
($luser, $chan);
4464 return 1 if ($self and ($permhash{opmode
}[2] + 2) <= $acc);
4477 if($chan->{AGENT
}) {
4478 my $a = $chan->{AGENT
};
4479 $a->{ID
} = ircd
::getAgentUuid
($a->{NICK
});
4483 unless(initial_synced
()) {
4486 $botserv::get_chan_bot-
>execute($chan->{CHAN
});
4487 my $agentnick = $botserv::get_chan_bot-
>fetchrow_array;
4488 my ($agent) = { NICK
=> $agentnick, ID
=> ircd
::getAgentUuid
($agentnick)};
4489 $agent = $csUser unless $agentnick;
4490 return $chan->{AGENT
} = $agent;
4495 my $cn = $chan->{CHAN
};
4497 undef($enforcers{lc $cn});
4498 my $agent = agent
($chan);
4499 agent_part
($agent, $cn, 'Channel dropped') unless (lc($agent) eq lc($csnick));
4500 if (module
::is_loaded
('logserv')) {
4501 eval { logserv
::delchan
(undef, $cn); }
4504 $drop_acc->execute($cn);
4505 $drop_lvl->execute($cn);
4506 $del_close->execute($cn);
4507 $drop_akick->execute($cn);
4508 $drop_welcome->execute($cn);
4509 $drop_chantext->execute($cn);
4510 $drop_nicktext->execute($cn); # Leftover channel auths
4511 $drop->execute($cn);
4512 ircd
::setmode
($csUser, $cn, '-r');
4515 sub drop_nick_chans
($) {
4518 $delete_successors->execute($nick);
4520 $get_nick_own_chans->execute($nick);
4521 while(my ($cn) = $get_nick_own_chans->fetchrow_array) {
4522 succeed_chan
($cn, $nick);
4526 sub succeed_chan
($$) {
4527 my ($cn, $nick) = @_;
4529 $get_successor->execute($cn);
4530 my ($suc) = $get_successor->fetchrow_array;
4533 $set_founder->execute($suc, $cn);
4534 set_acc
($suc, undef, {CHAN
=> $cn}, FOUNDER
);
4535 $del_successor->execute($cn);
4537 drop
({CHAN
=> $cn});
4538 wlog
($csnick, LOG_INFO
(), "\002$cn\002 has been dropped due to expiry/drop of \002$nick\002");
4544 my $cn = $chan->{CHAN
};
4545 return undef unless cr_chk_flag
($chan, CRF_CLOSE
| CRF_DRONE
);
4547 $get_close->execute($cn);
4548 my ($reason, $opnick, $time) = $get_close->fetchrow_array();
4549 $get_close->finish();
4551 $reason = "[$opnick ".gmtime2
($time)."] - $reason";
4553 return (wantarray ? ($reason, $opnick, $time) : $reason);
4556 sub get_users_nochans
(;$) {
4561 $get_users_nochans_noid->execute();
4562 while (my ($usernick, $userid) = $get_users_nochans_noid->fetchrow_array()) {
4563 push @users, { NICK
=> $usernick, ID
=> $userid };
4565 $get_users_nochans_noid->finish();
4568 $get_users_nochans->execute();
4569 while (my ($usernick, $userid) = $get_users_nochans->fetchrow_array()) {
4570 push @users, { NICK
=> $usernick, ID
=> $userid };
4572 $get_users_nochans->finish();
4578 sub get_bantype
($) {
4580 my $cn = $chan->{CHAN
};
4582 unless (exists($chan->{BANTYPE
})) {
4583 $get_bantype->execute($cn);
4584 ($chan->{BANTYPE
}) = $get_bantype->fetchrow_array();
4585 $get_bantype->finish();
4588 return $chan->{BANTYPE
};
4592 my ($chan, $log) = @_;
4594 my $level = get_level
($chan, "MemoAccChange");
4595 return if $level == 8; # 8 is 'disable'
4596 $level = 1 if $level == 0;
4597 memoserv
::send_chan_memo
($csnick, $chan, $log, $level);
4600 sub get_ban_num
($$) {
4601 my ($chan, $num) = @_;
4602 $get_ban_num->execute($chan->{CHAN
}, 0, $num-1);
4603 my ($mask) = $get_ban_num->fetchrow_array();
4604 $get_ban_num->finish();
4605 return sql2glob
($mask);
4611 # Due to special casing of '0' this wrapper should be used
4612 # by anyone handling a JOIN (not SJOIN, it's a JOIN) event.
4613 # This is an RFC1459 requirement.
4614 my ($user, $cn) = @_;
4615 my $chan = { CHAN
=> $cn };
4618 # This should be treated as a number
4619 # Just in case we ever got passed '000', not that Unreal does.
4620 # In C, you could check that chan[0] != '#' && chan[0] == '0'
4621 user_part_multi
($user, [ get_user_chans
($user) ], 'Left all channels');
4624 user_join_multi
($chan, [$user]);
4628 sub handle_sjoin
($$$$$$$) {
4629 my ($server, $cn, $ts, $chmodes, $chmodeparms, $userarray, $banarray, $exceptarray) = @_;
4630 my $chan = { CHAN
=> $cn };
4632 chan_mode
($server, $cn, $chmodes, $chmodeparms) if $chmodes;
4634 update_modes
($cn, "$chmodes $chmodeparms") if $chmodes;
4636 user_join_multi
($chan, $userarray) if scalar @$userarray;
4638 foreach my $ban (@$banarray) {
4639 process_ban
($cn, $ban, $server, 0, 1);
4641 foreach my $except (@$exceptarray) {
4642 process_ban
($cn, $except, $server, 128, 1);
4646 sub user_join_multi
($$) {
4647 my ($chan, $users) = @_;
4648 my $cn = $chan->{CHAN
};
4650 my $multi_tradeoff = 2; # could use some synthetic-benchmark tuning
4651 foreach my $user (@$users) {
4652 if ($user->{ID
} && !$user->{NICK
}) {
4653 get_user_nick
($user); # INSP
4655 $user->{__ID
} = get_user_id
($user);
4656 unless (defined($user->{__ID
})) {
4657 # This does happen occasionally. it's a BUG.
4658 # At least we have a diagnostic for it now.
4659 # Normally we'd just get a [useless] warning from the SQL server
4660 ircd
::debug
($user->{NICK
}.' has a NULL user->{__ID} in user_join_multi('.$cn.', ...');
4664 $get_joinpart_lock->execute; $get_joinpart_lock->finish;
4666 $chan_create->execute($seq, $cn);
4668 $get_user_count->execute($cn);
4669 my ($count) = $get_user_count->fetchrow_array;
4671 if(scalar(@$users) < $multi_tradeoff) {
4672 foreach my $user (@$users) {
4673 # see note above in get_user_id loop
4674 if (defined($user->{__ID
})) {
4675 $chanjoin->execute($seq, $user->{__ID
}, $cn, $user->{__OP
});
4680 my $query = "REPLACE INTO chanuser (seq, nickid, chan, op, joined) VALUES ";
4681 foreach my $user (@$users) {
4682 # a join(',', list) would be nice but would involve preparing the list first.
4683 # I think this will be faster.
4684 if (defined($user->{__ID
})) {
4685 # see note above in get_user_id loop
4686 $query .= '('.$dbh->quote($seq).','.
4687 $dbh->quote($user->{__ID
}).','.
4688 $dbh->quote($cn).','.
4689 $dbh->quote($user->{__OP
}).', 1),';
4696 $unlock_tables->execute; $unlock_tables->finish;
4698 my $bot = agent
($chan);
4699 foreach my $user (@$users) {
4700 $user->{AGENT
} = $bot;
4703 if(initial_synced
() and cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
4704 my ($reason, $opnick, $time) = get_close
($chan);
4705 my $cmsg = "$cn is closed: $reason";
4706 my $preenforce = $enforcers{lc $chan};
4707 if (cr_chk_flag
($chan, CRF_CLOSE
)) {
4708 kickban_multi
($chan, $users, $cmsg);
4710 elsif (cr_chk_flag
($chan, CRF_DRONE
)) {
4711 chan_kill
($chan, $cmsg, $users);
4714 unless($preenforce) {
4715 ircd
::settopic
($bot, $cn, $opnick, $time, $cmsg);
4717 my $ml = get_modelock
($chan);
4718 ircd
::setmode
($bot, $cn, $ml) if($ml);
4722 if(($count == 0 or !is_agent_in_chan
($bot, $cn)) and initial_synced
()) {
4723 unless ($bot eq $csUser) {
4724 unless(is_agent_in_chan
($bot, $cn)) {
4725 botserv
::bot_join
($chan);
4730 #return unless synced() and not cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
4731 return unless not cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
));
4732 #commands aren't sent before synced() anyway
4734 foreach my $user (@$users) {
4735 if(do_status
($user, $chan)) {
4737 $user->{__DO_WELCOME
} = 1;
4741 if($count == 0 and $n) {
4742 my ($ml) = get_modelock
($chan);
4743 ircd
::setmode
($bot, $cn, $ml) if($ml);
4745 $get_topic->execute($cn);
4746 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4747 ircd
::settopic
($bot, $cn, $nsetter, $ntime, $ntopic) if $ntopic;
4753 foreach my $user (@$users) {
4754 if ($user->{__DO_WELCOME
} and chk_user_flag
($user, UF_FINISHED
())) {
4755 do_welcome
($user, $chan);
4756 do_greet
($user, $chan)
4757 if can_do
($chan, 'GREET', $user, { NOREPLY
=> 1 });
4763 sub user_part
($$$) {
4764 my ($nick, $cn, $reason) = @_;
4765 my $user = ( ref $nick eq 'HASH' ? $nick : { NICK
=> $nick });
4766 user_part_multi
($user, [ $cn ], $reason);
4769 sub user_part_multi
($$$) {
4770 # user_join_multi takes a channel and multiple users
4771 # user_part_multi takes a user and multiple channels
4772 # There should probably be a user_join_* that takes one user, multiple channels
4773 # However, it seems that so far, Unreal splits both PART and JOIN (non-SJOIN)
4774 # into multiple events/cmds. The reason is unclear.
4775 # Other ircds may not do so.
4776 # There is also KICK. some IRCds allow KICK #chan user1,user2,...
4777 # Unreal it's _supposed_ to work, but it does not.
4778 my ($user, $chanlist, $reason) = @_;
4780 foreach my $cn (@$chanlist) {
4781 push @chans, { CHAN
=> $cn };
4784 my $uid = get_user_id
($user);
4787 $get_joinpart_lock->execute; $get_joinpart_lock->finish;
4789 foreach my $chan (@chans) {
4790 my $cn = $chan->{CHAN
};
4791 $chanpart->execute($seq, $uid, $cn, $seq, $seq+1000);
4792 $get_user_count->execute($cn);
4793 $chan->{COUNT
} = $get_user_count->fetchrow_array;
4796 $unlock_tables->execute; $unlock_tables->finish;
4798 foreach my $chan (@chans) {
4799 channel_emptied
($chan) if $chan->{COUNT
} == 0;
4803 sub channel_emptied
($) {
4806 botserv
::bot_part_if_needed
(undef, $chan, 'Nobody\'s here', 1);
4807 $chan_delete->execute($chan->{CHAN
});
4808 $wipe_bans->execute($chan->{CHAN
});
4811 sub process_kick
($$$$) {
4812 my ($src, $cn, $target, $reason) = @_;
4813 my $tuser = { NICK
=> $target };
4814 get_user_id
($tuser);
4815 user_part
($tuser, $cn, 'Kicked by '.$src.' ('.$reason.')');
4817 my $chan = { CHAN
=> $cn };
4818 if ( !(is_agent
($src) or $src =~ /\./ or adminserv
::is_ircop
({ NICK
=> $src })) and
4819 ({modes
::splitmodes
(get_modelock
($chan))}->{Q
}->[0] eq '+') )
4821 my $srcUser = { NICK
=> $src };
4822 get_user_id
($srcUser);
4823 #ircd::irckill(agent($chan), $src, "War script detected (kicked $target past +Q in $cn)");
4824 nickserv
::kline_user
($srcUser, 300, "War script detected (kicked $target past +Q in $cn)");
4825 # SVSJOIN won't work while they're banned, unless you invite.
4826 ircd
::invite
(agent
($chan), $cn, $tuser);
4827 ircd
::svsjoin
(undef, $tuser, $cn);
4828 unban_user
($chan, $tuser);
4832 sub chan_mode
($$$$@) {
4833 my ($user, $cn, $modes, $args, @userargs) = @_;
4835 if (ref($user) eq "HASH") {
4836 $src = $user->{NICK
};
4841 my $chan = { CHAN
=> $cn };
4844 # XXX This is not quite right, but maybe it's good enough.
4845 my $mysync = ($src =~ /\./ ? 0 : 1);
4847 if($modes !~ /^[beIvhoaq+-]+$/ and (!synced
() or $mysync)) {
4848 do_modelock
($chan, "$modes $args");
4851 my $opguard = (!current_message-
>{SYNC
} and cr_chk_flag
($chan, CRF_OPGUARD
, 1));
4853 my @perms = ('VOICE', 'HALFOP', 'OP', 'PROTECT');
4857 my @modes = split(//, $modes);
4858 my @args = split(/ /, $args);
4860 foreach my $mode (@modes) {
4861 if($mode eq '+') { $sign = 1; next; }
4862 if($mode eq '-') { $sign = 0; next; }
4864 my $arg = shift(@args) if($mode =~ $scm or $mode =~ $ocm);
4865 if($mode =~ /^[vhoaq]$/) {
4867 next if is_agent
($arg);
4868 my $auser = shift (@userargs);
4869 $num = 0 if $mode eq 'v';
4870 $num = 1 if $mode eq 'h';
4871 $num = 2 if $mode eq 'o';
4872 $num = 3 if $mode eq 'a';
4873 $num = 4 if $mode eq 'q';
4874 if($opguard and $sign == 1 and!can_keep_op
($user, $chan, $auser, $mode)) {
4875 push @unargs, ["-" . $mode, $auser];
4878 $nid = get_user_id
($auser) or next;
4882 $r = $chop->execute((2**$num), (2**$num), $nid, $cn);
4884 $r = $chdeop->execute((2**$num), (2**$num), $nid, $cn);
4887 } while($r==0 and $i<10);
4892 process_ban
($cn, $arg, $src, 0, $sign);
4896 process_ban
($cn, $arg, $src, 128, $sign);
4899 next;# if $arg eq '';
4900 #process_ban($cn, $arg, $src, 128, $sign);
4903 ircd
::setmode2
(agent
($chan), $cn, @unargs) if($opguard and @unargs);
4906 sub process_ban
($$$$) {
4907 my ($cn, $arg, $src, $type, $sign) = @_;
4909 $arg =~ tr/\*\?/\%\_/;
4912 $add_ban->execute($cn, $arg, $src, $type);
4914 $delete_ban->execute($cn, $arg, $type);
4919 my ($src, $cn, $suser, $time, $topic) = @_;
4920 my $chan = { CHAN
=> $cn };
4921 $suser -> {AGENT
} = agent
($chan);
4922 my $setter = $suser -> {NICK
};
4923 return if cr_chk_flag
($chan, CRF_CLOSE
, 1);
4925 if(current_message-
>{SYNC
}) { # We don't need to undo our own topic changes.
4926 print "Line @{[__LINE__]}\n";
4927 $set_topic1->execute($setter, $time, $cn);
4928 $set_topic2->execute($cn, $topic);
4932 print "Line @{[__LINE__]}\n";
4933 $get_topic->execute($cn);
4934 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4935 if($topic ne '' and $time == $ntime or can_do
($chan, 'SETTOPIC', undef, { ACC
=> 0 })) {
4936 print "Line @{[__LINE__]}\n";
4937 $set_topic1->execute($setter, $time, $cn);
4938 $set_topic2->execute($cn, $topic);
4940 print "Line @{[__LINE__]}\n";
4941 ircd
::settopic
(agent
($chan), $cn, $nsetter, $ntime, $ntopic);
4944 #lc($src) ne lc($setter) or - removed from previous line because
4945 #i think it was breaking it for insp & idk what it did
4948 # as explained on IRC, the intent is to determine whether it's being set due to a
4949 # net.unsplit, or b/c a user is doing it. You can probably do something more useful
4950 # by paying attn to timestamp
4952 elsif(can_do
($chan, 'SETTOPIC', $suser)) {
4953 print "Line @{[__LINE__]}\n",
4954 "condition : ", can_do
($chan, 'SETTOPIC', $suser),$/;
4955 $set_topic1->execute($setter, $time, $cn);
4956 $set_topic2->execute($cn, $topic);
4958 print "Line @{[__LINE__]}\n";
4959 $get_topic->execute($cn);
4960 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4961 ircd
::settopic
(agent
($chan), $cn, $nsetter, $ntime, $ntopic);
4969 $get_all_closed_chans->execute(CRF_DRONE
|CRF_CLOSE
);
4970 while(my ($cn, $type, $reason, $opnick, $time) = $get_all_closed_chans->fetchrow_array) {
4971 my $chan = { CHAN
=> $cn };
4973 my $cmsg = " is closed [$opnick ".gmtime2
($time)."]: $reason";
4974 if($type == CRF_DRONE
) {
4975 chan_kill
($chan, $cn.$cmsg);
4977 my $user = { NICK
=> $opnick};
4978 get_user_id
($user);
4979 ircd
::settopic
(agent
($chan), $cn, $opnick, $time, "Channel".$cmsg);
4980 clear_users
($chan, "Channel".$cmsg);
4984 while($chanuser_table > 0) { }
4986 $get_eos_lock->execute(); $get_eos_lock->finish;
4987 $get_akick_all->execute();
4989 $get_status_all_server->execute($server);
4990 $gsa = $get_status_all_server;
4992 $get_status_all->execute();
4993 $gsa = $get_status_all;
4995 #$unlock_tables->execute(); $unlock_tables->finish;
4997 while(my @akick = $get_akick_all->fetchrow_array) {
5001 $get_modelock_all->execute();
5002 while(my ($cn, $modes, $ml) = $get_modelock_all->fetchrow_array) {
5003 $ml = do_modelock_fast
($cn, '', $modes, $ml);
5004 ircd
::setmode
(agent
({CHAN
=>$cn}), $cn, $ml) if $ml;
5007 while(my ($cn, $cflags, $agent, $nick, $uid, $uflags, $level, $op, $neverop) = $gsa->fetchrow_array) {
5008 my $user = { NICK
=> $nick, ID
=> $uid };
5009 #next if chk_user_flag($user, UF_FINISHED);
5010 $agent = $csnick unless $agent;
5011 my $chan = { CHAN
=> $cn, FLAGS
=> $cflags, AGENT
=> $agent };
5013 set_modes
($user, $chan, $level, ($cflags & CRF_OPGUARD
)) if not $neverop and $ops[$level] != $op and not $cflags & (CRF_FREEZE
| CRF_CLOSE
| CRF_DRONE
);
5014 do_welcome
($user, $chan);
5017 set_user_flag_all
(UF_FINISHED
());
5018 $unlock_tables->execute(); $unlock_tables->finish;