1 # This file is part of SurrealServices.
3 # SurrealServices is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # SurrealServices is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with SurrealServices; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 use SrSv
::Timer
qw(add_timer);
22 use SrSv
::Message
qw(current_message);
23 use SrSv
::IRCd
::State
qw($ircline synced initial_synced %IRCd_capabilities);
24 use SrSv
::Message
qw(message current_message);
25 use SrSv
::HostMask
qw(normalize_hostmask make_hostmask parse_mask);
27 #FIXME: This needs to be abstracted into a proper SrSv::IRCd module
28 use SrSv
::Unreal
::Modes
qw(@opmodes %opmodes $scm $ocm $acm sanitize_mlockable);
29 use SrSv
::IRCd
::Validate
qw( valid_nick validate_chmodes validate_ban );
32 use SrSv
::Shared
qw(%enforcers $chanuser_table);
34 #use SrSv::Conf qw(services);
35 use SrSv
::Conf2Consts
qw( services sql main );
38 use SrSv
::Text
::Format
qw( columnar enum );
44 get_user_nick get_user_agent get_user_id
45 is_online :user_flags get_host get_vhost
48 use SrSv
::User
::Notice
;
49 use SrSv
::Help
qw( sendhelp );
51 use SrSv
::ChanReg
::Flags
;
53 use SrSv
::NickReg
::Flags
;
54 use SrSv
::NickReg
::NickText
;
55 use SrSv
::NickReg
::User
qw(is_identified get_nick_users get_nick_user_nicks);
57 use SrSv
::MySQL
qw( $dbh :sql_types );
58 use SrSv
::MySQL
::Glob
;
60 use SrSv
::Util
qw( makeSeqList );
71 # Maybe this should be a config option
72 DEFAULT_BANTYPE
=> 10,
78 *get_root_nick
= \
&nickserv
::get_root_nick
;
80 our @levels = ("no", "UOp", "VOp", "HOp", "AOp", "SOp", "co-founder", "founder");
82 if(!ircd
::PREFIXAQ_DISABLE
()) {
83 @ops = (0, 0, 1, 2, 4, 8, 16, 16); # PREFIX_AQ
84 } else { # lame IRC scripts and admins who don't enable PREFIX_AQ
85 @ops = (0, 0, 1, 2, 4, 12, 20, 20); # normal
87 our @plevels = ('AKICK', 'anyone', 'UOp', 'VOp', 'HOp', 'AOp', 'SOp', 'co-founder', 'founder', 'disabled');
143 our $csnick_default = 'ChanServ';
144 our $csnick = $csnick_default;
145 our $csUser = { NICK
=> $csnick, ID
=> ircd
::getAgentUuid
($csnick) };
147 our ($cur_lock, $cnt_lock);
150 $get_joinpart_lock, $get_modelock_lock, $get_update_modes_lock,
152 $chanjoin, $chanpart, $chop, $chdeop, $get_op, $get_user_chans, $get_user_chans_recent,
153 $get_all_closed_chans, $get_user_count,
157 #$lock_chanuser, $get_all_chan_users,
159 $get_chan_users, $get_chan_users_noacc, $get_chan_users_mask, $get_chan_users_mask_noacc,
161 $get_users_nochans, $get_users_nochans_noid,
163 $get_using_nick_chans,
165 $get_lock, $release_lock, $is_free_lock,
167 $chan_create, $chan_delete, $get_chanmodes, $set_chanmodes,
169 $is_registered, $get_modelock, $set_modelock, $set_descrip,
171 $get_topic, $set_topic1, $set_topic2,
173 $get_acc, $set_acc1, $set_acc2, $del_acc, $get_acc_list, $get_acc_list2, $get_acc_list_mask, $get_acc_list2_mask,
175 $get_best_acc, $get_all_acc, $get_highrank, $get_acc_count,
176 $copy_acc, $copy_acc_rank,
178 $get_eos_lock, $get_status_all, $get_status_all_server, $get_modelock_all,
180 $get_akick, $get_akick_allchan, $get_akick_alluser, $get_akick_all, $add_akick, $del_akick,
181 $get_akick_list, $get_akick_by_num,
183 $add_nick_akick, $del_nick_akick, $get_nick_akick, $drop_nick_akick,
186 $is_level, $get_level, $get_levels, $add_level, $set_level, $reset_level, $clear_levels, $get_level_max,
189 $get_founder, $get_successor,
190 $set_founder, $set_successor, $del_successor,
192 $get_nick_own_chans, $delete_successors,
196 $register, $drop_acc, $drop_lvl, $drop_akick, $drop,
201 $get_close, $set_close, $del_close,
203 $add_welcome, $del_welcome, $list_welcome, $get_welcomes, $drop_welcome,
204 $count_welcome, $consolidate_welcome,
206 $add_ban, $delete_bans, $delete_ban,
207 $get_all_bans, $get_ban_num,
208 $find_bans, $list_bans, $wipe_bans,
209 $find_bans_chan_user, $delete_bans_chan_user,
211 $add_auth, $list_auth_chan, $check_auth_chan, $get_auth_nick, $get_auth_num, $find_auth,
213 $set_bantype, $get_bantype,
215 $drop_chantext, $drop_nicktext,
219 #$chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET id=(RAND()*294967293)+1, chan=?");
220 $get_joinpart_lock = $dbh->prepare("LOCK TABLES chan WRITE, chanuser WRITE");
221 $get_modelock_lock = $dbh->prepare("LOCK TABLES chanreg READ LOCAL, chan WRITE");
222 $get_update_modes_lock = $dbh->prepare("LOCK TABLES chan WRITE");
224 $chanjoin = $dbh->prepare("REPLACE INTO chanuser (seq,nickid,chan,op,joined) VALUES (?, ?, ?, ?, 1)");
225 $chanpart = $dbh->prepare("UPDATE chanuser SET joined=0, seq=?
226 WHERE nickid=? AND chan=? AND (seq <= ? OR seq > ?)");
227 #$chop = $dbh->prepare("UPDATE chanuser SET op=op+? WHERE nickid=? AND chan=?");
228 $chop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op, op ^ ?) WHERE nickid=? AND chan=?");
229 $chdeop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op ^ ?, op) WHERE nickid=? AND chan=?");
230 $get_op = $dbh->prepare("SELECT op FROM chanuser WHERE nickid=? AND chan=?");
231 $get_user_chans = $dbh->prepare("SELECT chan, op FROM chanuser WHERE nickid=? AND joined=1 AND (seq <= ? OR seq > ?)");
232 $get_user_chans_recent = $dbh->prepare("SELECT chan, joined, op FROM chanuser WHERE nickid=?");
234 $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");
235 $get_user_count = $dbh->prepare("SELECT COUNT(*) FROM chanuser WHERE chan=? AND joined=1");
237 $is_in_chan = $dbh->prepare("SELECT 1 FROM chanuser WHERE nickid=? AND chan=? AND joined=1");
239 #$lock_chanuser = $dbh->prepare("LOCK TABLES chanuser READ, user READ");
240 #$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");
241 $unlock_tables = $dbh->prepare("UNLOCK TABLES");
243 $get_chan_users = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
244 WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1");
245 my $chan_users_noacc_tables = 'user '.
246 'JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1 AND user.online=1) '.
247 'LEFT JOIN nickid ON (chanuser.nickid=nickid.id) '.
248 'LEFT JOIN chanacc ON (nickid.nrid=chanacc.nrid AND chanuser.chan=chanacc.chan)';
249 $get_chan_users_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
250 WHERE chanuser.chan=?
251 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
253 my $check_mask = "((user.nick LIKE ?) AND (user.ident LIKE ?)
254 AND ((user.vhost LIKE ?) OR (user.host LIKE ?) OR (user.cloakhost LIKE ?)))";
255 $get_chan_users_mask = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
256 WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1 AND $check_mask");
257 $get_chan_users_mask_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
258 WHERE chanuser.chan=? AND $check_mask
259 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
262 $get_users_nochans = $dbh->prepare("SELECT user.nick, user.id
263 FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
264 WHERE chanuser.chan IS NULL AND user.online=1");
265 $get_users_nochans_noid = $dbh->prepare("SELECT user.nick, user.id
266 FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
267 LEFT JOIN nickid ON (nickid.id=user.id)
268 WHERE chanuser.chan IS NULL AND nickid.id IS NULL
271 $get_using_nick_chans = $dbh->prepare("SELECT user.nick FROM user, nickid, nickreg, chanuser
272 WHERE user.id=nickid.id AND user.id=chanuser.nickid AND nickid.nrid=nickreg.id AND chanuser.joined=1
273 AND nickreg.nick=? AND chanuser.chan=?");
275 $get_lock = $dbh->prepare("SELECT GET_LOCK(?, 3)");
276 $release_lock = $dbh->prepare("DO RELEASE_LOCK(?)");
277 $is_free_lock = $dbh->prepare("SELECT IS_FREE_LOCK(?)");
279 $chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET seq=?, chan=?");
280 $chan_delete = $dbh->prepare("DELETE FROM chan WHERE chan=?");
281 $get_chanmodes = $dbh->prepare("SELECT modes FROM chan WHERE chan=?");
282 $set_chanmodes = $dbh->prepare("REPLACE INTO chan SET modes=?, chan=?");
284 $is_registered = $dbh->prepare("SELECT 1 FROM chanreg WHERE chan=?");
285 $get_modelock = $dbh->prepare("SELECT modelock FROM chanreg WHERE chan=?");
286 $set_modelock = $dbh->prepare("UPDATE chanreg SET modelock=? WHERE chan=?");
288 $set_descrip = $dbh->prepare("UPDATE chanreg SET descrip=? WHERE chan=?");
290 $get_topic = $dbh->prepare("SELECT chantext.data, topicer, topicd FROM chanreg, chantext
291 WHERE chanreg.chan=chantext.chan AND chantext.chan=?");
292 $set_topic1 = $dbh->prepare("UPDATE chanreg SET chanreg.topicer=?, chanreg.topicd=?
293 WHERE chanreg.chan=?");
294 $set_topic2 = $dbh->prepare("REPLACE INTO chantext SET chan=?, type=".CRT_TOPIC
().", data=?");
296 $get_acc = $dbh->prepare("SELECT chanacc.level FROM chanacc, nickalias
297 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
298 $set_acc1 = $dbh->prepare("INSERT IGNORE INTO chanacc SELECT ?, nrid, ?, NULL, UNIX_TIMESTAMP(), 0
299 FROM nickalias WHERE alias=?");
300 $set_acc2 = $dbh->prepare("UPDATE chanacc, nickalias
301 SET chanacc.level=?, chanacc.adder=?, chanacc.time=UNIX_TIMESTAMP()
302 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
303 $del_acc = $dbh->prepare("DELETE FROM chanacc USING chanacc, nickalias
304 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
305 $wipe_acc_list = $dbh->prepare("DELETE FROM chanacc WHERE chan=? AND level=?");
306 $get_acc_list = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.time,
307 chanacc.last, nickreg.ident, nickreg.vhost
308 FROM chanacc, nickreg
309 WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
310 $get_acc_list2 = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.level, chanacc.time,
311 chanacc.last, nickreg.ident, nickreg.vhost
312 FROM chanacc, nickreg
313 WHERE chanacc.chan=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
314 $get_acc_list_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias), chanacc.adder, chanacc.time,
315 chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
316 FROM chanacc, nickalias, nickreg
317 WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
318 AND chanacc.level > 0
319 AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
321 ORDER BY nickalias.alias");
322 $get_acc_list2_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias),
323 chanacc.adder, chanacc.level, chanacc.time,
324 chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
325 FROM chanacc, nickalias, nickreg
326 WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
327 AND chanacc.level > 0
328 AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
330 ORDER BY nickalias.alias");
332 $get_best_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
333 FROM nickid, nickalias, nickreg, chanacc
334 WHERE nickid.nrid=nickreg.id AND nickalias.nrid=nickreg.id AND nickid.id=?
335 AND chanacc.nrid=nickreg.id AND chanacc.chan=? ORDER BY chanacc.level DESC LIMIT 1");
336 $get_all_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
337 FROM nickid, nickreg, chanacc
338 WHERE nickid.nrid=nickreg.id AND nickid.id=? AND chanacc.nrid=nickreg.id
339 AND chanacc.chan=? ORDER BY chanacc.level");
340 $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");
341 $get_acc_count = $dbh->prepare("SELECT COUNT(*) FROM chanacc WHERE chan=? AND level=?");
342 $copy_acc = $dbh->prepare("REPLACE INTO chanacc
343 ( chan, nrid, level, adder, time)
344 SELECT ?, nrid, level, adder, time FROM chanacc JOIN nickreg ON (chanacc.nrid=nickreg.id)
345 WHERE chan=? AND nickreg.nick!=? AND chanacc.level!=7");
346 $copy_acc_rank = $dbh->prepare("REPLACE INTO chanacc
347 ( chan, nrid, level, adder, time)
348 SELECT ?, nrid, level, adder, time FROM chanacc
349 WHERE chan=? AND chanacc.level=?");
351 $get_eos_lock = $dbh->prepare("LOCK TABLES akick READ LOCAL, welcome READ LOCAL, chanuser WRITE, user WRITE,
352 user AS u1 READ, user AS u2 READ, chan WRITE, chanreg WRITE, nickid READ LOCAL, nickreg READ LOCAL,
353 nickalias READ LOCAL, chanacc READ LOCAL, chanban WRITE, svsop READ");
354 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
().")
355 FROM user, chanreg, chanuser
356 LEFT JOIN nickid ON(nickid.id=chanuser.nickid)
357 LEFT JOIN nickreg ON(nickid.nrid=nickreg.id)
358 LEFT JOIN chanacc ON(chanacc.chan=chanuser.chan AND chanacc.nrid=nickid.nrid AND (nickreg.flags & ".NRF_NEVEROP
().")=0)
360 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)
361 GROUP BY chanuser.chan, chanuser.nickid ORDER BY NULL";
362 $get_status_all = $dbh->prepare("$get_status_all_1 $get_status_all_2");
363 $get_status_all_server = $dbh->prepare("$get_status_all_1 user.server=? AND $get_status_all_2");
365 $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");
367 my $akick_rows = "user.nick, akick.nick, akick.ident, akick.host, akick.reason";
368 my $akick_no_zerolen = "(akick.ident != '' AND akick.host != '')";
369 my $akick_single_cond = "$akick_no_zerolen AND user.nick LIKE akick.nick AND user.ident LIKE akick.ident ".
370 "AND ( (user.host LIKE akick.host) OR (user.vhost LIKE akick.host) OR ".
371 "(IF((user.ip IS NOT NULL) AND (user.ip != 0), INET_NTOA(user.ip) LIKE akick.host, 0)) OR ".
372 "(IF(user.cloakhost IS NOT NULL, user.cloakhost LIKE akick.host, 0)) )";
373 my $akick_multi_cond = "chanuser.chan=akick.chan AND $akick_single_cond";
375 $get_akick = $dbh->prepare("SELECT $akick_rows FROM akick, user ".
376 "WHERE user.id=? AND akick.chan=? AND $akick_single_cond LIMIT 1");
377 $get_akick_allchan = $dbh->prepare("SELECT $akick_rows FROM $chan_users_noacc_tables
378 JOIN akick ON($akick_multi_cond)
380 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
382 $get_akick_alluser = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
383 JOIN akick ON($akick_multi_cond)
384 WHERE chanuser.nickid=?
385 GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
387 $get_akick_all = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
388 JOIN akick ON($akick_multi_cond)
389 GROUP BY akick.chan, user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
392 $add_akick = $dbh->prepare("INSERT INTO akick SET chan=?, nick=?, ident=?, host=?, adder=?, reason=?, time=UNIX_TIMESTAMP()");
393 $add_akick->{PrintError
} = 0;
394 $del_akick = $dbh->prepare("DELETE FROM akick WHERE chan=? AND nick=? AND ident=? AND host=?");
395 $get_akick_list = $dbh->prepare("SELECT nick, ident, host, adder, reason, time FROM akick WHERE chan=? ORDER BY time");
397 $add_nick_akick = $dbh->prepare("INSERT INTO akick SELECT ?, nickalias.nrid, '', '', ?, ?, UNIX_TIMESTAMP()
398 FROM nickalias WHERE alias=?");
399 $del_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickalias
400 WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
401 $get_nick_akick = $dbh->prepare("SELECT reason FROM akick, nickalias
402 WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
403 $drop_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickreg
404 WHERE akick.nick=nickreg.id AND akick.ident='' AND akick.host='' AND nickreg.nick=?");
405 $copy_akick = $dbh->prepare("REPLACE INTO akick
406 ( chan, nick, ident, host, adder, reason, time)
407 SELECT ?, nick, ident, host, adder, reason, time FROM akick WHERE chan=?");
408 $get_akick_by_num = $dbh->prepare("SELECT akick.nick, akick.ident, akick.host FROM akick WHERE chan=?
409 ORDER BY time LIMIT 1 OFFSET ?");
410 $get_akick_by_num->bind_param(2, 0, SQL_INTEGER
);
412 $is_level = $dbh->prepare("SELECT 1 FROM chanperm WHERE chanperm.name=?");
413 $get_level = $dbh->prepare("SELECT IF(chanlvl.level IS NULL, chanperm.level, chanlvl.level), chanlvl.level
414 FROM chanperm LEFT JOIN chanlvl ON chanlvl.perm=chanperm.id AND chanlvl.chan=?
415 WHERE chanperm.name=?");
416 $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");
417 $add_level = $dbh->prepare("INSERT IGNORE INTO chanlvl SELECT ?, chanperm.id, chanperm.level FROM chanperm WHERE chanperm.name=?");
418 $set_level = $dbh->prepare("UPDATE chanlvl, chanperm SET chanlvl.level=? WHERE chanlvl.chan=? AND chanperm.id=chanlvl.perm AND chanperm.name=?");
419 $reset_level = $dbh->prepare("DELETE FROM chanlvl USING chanlvl, chanperm WHERE chanperm.name=? AND chanlvl.perm=chanperm.id AND chanlvl.chan=?");
420 $clear_levels = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
421 $get_level_max = $dbh->prepare("SELECT max FROM chanperm WHERE name=?");
422 $copy_levels = $dbh->prepare("REPLACE INTO chanlvl
424 SELECT ?, perm, level FROM chanlvl WHERE chan=?");
426 $get_founder = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.founderid=nickreg.id");
427 $get_successor = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.successorid=nickreg.id");
428 $set_founder = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.founderid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
429 $set_successor = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
430 $del_successor = $dbh->prepare("UPDATE chanreg SET chanreg.successorid=NULL WHERE chanreg.chan=?");
432 $get_nick_own_chans = $dbh->prepare("SELECT chanreg.chan FROM chanreg, nickreg WHERE nickreg.nick=? AND chanreg.founderid=nickreg.id");
433 $delete_successors = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=NULL WHERE nickreg.nick=? AND chanreg.successorid=nickreg.id");
436 $get_info = $dbh->prepare("SELECT chanreg.descrip, chanreg.regd, chanreg.last, chantext.data,
437 chanreg.topicer, chanreg.modelock, foundernick.nick, successornick.nick, chanreg.bot, chanreg.bantype
438 FROM nickreg AS foundernick, chanreg
439 LEFT JOIN nickreg AS successornick ON(successornick.id=chanreg.successorid)
440 LEFT JOIN chantext ON (chanreg.chan=chantext.chan AND chantext.type=".CRT_TOPIC
().")
441 WHERE chanreg.chan=? AND foundernick.id=chanreg.founderid");
443 $register = $dbh->prepare("INSERT INTO chanreg
444 SELECT ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), NULL, NULL,
445 NULL, id, NULL, NULL, NULL, ".DEFAULT_BANTYPE
()." FROM nickreg WHERE nick=?");
446 $register->{PrintError
} = 0;
447 $copy_chanreg = $dbh->prepare("INSERT INTO chanreg
448 ( chan, descrip, regd, last, modelock, founderid, successorid, bot, flags, bantype)
449 SELECT ?, descrip, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), modelock, founderid, successorid, bot, flags, bantype
450 FROM chanreg WHERE chan=?");
452 $drop_acc = $dbh->prepare("DELETE FROM chanacc WHERE chan=?");
453 $drop_lvl = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
454 $drop_akick = $dbh->prepare("DELETE FROM akick WHERE chan=?");
455 $drop = $dbh->prepare("DELETE FROM chanreg WHERE chan=?");
457 $get_expired = $dbh->prepare("SELECT chanreg.chan, nickreg.nick FROM nickreg, chanreg
458 LEFT JOIN chanuser ON(chanreg.chan=chanuser.chan AND chanuser.op!=0)
459 WHERE chanreg.founderid=nickreg.id AND chanuser.chan IS NULL AND chanreg.last<? AND
460 !(chanreg.flags & " . CRF_HOLD
. ")");
462 $get_close = $dbh->prepare("SELECT reason, nick, time FROM chanclose WHERE chan=?");
463 $set_close = $dbh->prepare("REPLACE INTO chanclose SET chan=?, reason=?, nick=?, time=UNIX_TIMESTAMP(), type=?");
464 $del_close = $dbh->prepare("DELETE FROM chanclose WHERE chan=?");
466 $add_welcome = $dbh->prepare("REPLACE INTO welcome SET chan=?, id=?, adder=?, time=UNIX_TIMESTAMP(), msg=?");
467 $del_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=? AND id=?");
468 $list_welcome = $dbh->prepare("SELECT id, time, adder, msg FROM welcome WHERE chan=? ORDER BY id");
469 $get_welcomes = $dbh->prepare("SELECT msg FROM welcome WHERE chan=? ORDER BY id");
470 $drop_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=?");
471 $count_welcome = $dbh->prepare("SELECT COUNT(*) FROM welcome WHERE chan=?");
472 $consolidate_welcome = $dbh->prepare("UPDATE welcome SET id=id-1 WHERE chan=? AND id>?");
474 $add_ban = $dbh->prepare("INSERT IGNORE INTO chanban SET chan=?, mask=?, setter=?, type=?, time=UNIX_TIMESTAMP()");
475 $delete_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
476 # likely need a better name for this or for the above.
477 $delete_ban = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND mask=? AND type=?");
478 $find_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
479 $get_all_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=?");
480 $get_ban_num = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=? ORDER BY time, mask LIMIT 1 OFFSET ?");
481 $get_ban_num->bind_param(3, 0, SQL_INTEGER
);
482 $list_bans = $dbh->prepare("SELECT mask, setter, time FROM chanban WHERE chan=? AND type=? ORDER BY time, mask");
483 $wipe_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=?");
485 my $chanban_mask = "((CONCAT(user.nick, '!', user.ident, '\@', user.host) LIKE chanban.mask) ".
486 "OR (CONCAT(user.nick , '!' , user.ident , '\@' , user.vhost) LIKE chanban.mask) ".
487 "OR IF(user.cloakhost IS NOT NULL, ".
488 "(CONCAT(user.nick , '!' , user.ident , '\@' , user.cloakhost) LIKE chanban.mask), 0))";
489 $find_bans_chan_user = $dbh->prepare("SELECT mask FROM chanban,user
490 WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
491 $delete_bans_chan_user = $dbh->prepare("DELETE FROM chanban USING chanban,user
492 WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
494 $add_auth = $dbh->prepare("REPLACE INTO nicktext
495 SELECT nickalias.nrid, (".NTF_AUTH
()."), 1, ?, ? FROM nickalias WHERE nickalias.alias=?");
496 $list_auth_chan = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nicktext
497 WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".NTF_AUTH
().") AND nicktext.chan=?");
498 $check_auth_chan = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nicktext
499 WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".nickserv
::NTF_AUTH
().") AND nicktext.chan=? AND nickreg.nick=?");
500 $get_auth_nick = $dbh->prepare("SELECT nicktext.data FROM nickreg, nickalias, nicktext
501 WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH
().")
502 AND nicktext.chan=? AND nickalias.alias=?");
503 $get_auth_num = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nickalias, nicktext
504 WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH
().")
505 AND nicktext.chan=? LIMIT 1 OFFSET ?");
506 $get_auth_num->bind_param(2, 0, SQL_INTEGER
);
507 $find_auth = $dbh->prepare("SELECT 1 FROM nickalias, nicktext
508 WHERE nickalias.nrid=nicktext.nrid AND nicktext.type=(".NTF_AUTH
().")
509 AND nicktext.chan=? AND nickalias.alias=?");
511 $set_bantype = $dbh->prepare("UPDATE chanreg SET bantype=? WHERE chan=?");
512 $get_bantype = $dbh->prepare("SELECT bantype FROM chanreg WHERE chan=?");
514 $drop_chantext = $dbh->prepare("DELETE FROM chantext WHERE chan=?");
515 $drop_nicktext = $dbh->prepare("DELETE nicktext.* FROM nicktext WHERE nicktext.chan=?");
518 use SrSv
::MySQL
::Stub
{
519 set_lastop
=> ['NULL', "UPDATE chanreg SET last=UNIX_TIMESTAMP() WHERE chan=?"],
520 set_lastused
=> ['NULL', "UPDATE chanacc, nickid SET chanacc.last=UNIX_TIMESTAMP() WHERE
521 chanacc.chan=? AND nickid.id=? AND chanacc.nrid=nickid.nrid AND chanacc.level > 0"],
522 get_recent_private_chans
=> ['COLUMN', "SELECT DISTINCT chanuser.chan FROM chanuser
523 JOIN chanacc ON (chanuser.chan=chanacc.chan AND chanuser.joined=0)
524 JOIN chanlvl ON (chanlvl.level <= chanacc.level AND chanlvl.level > 0 AND chanuser.chan=chanlvl.chan)
525 JOIN chanperm ON (chanlvl.perm=chanperm.id)
526 JOIN nickid ON (chanuser.nickid=nickid.id AND chanacc.nrid=nickid.nrid)
527 WHERE chanperm.name='Join'
531 ### CHANSERV COMMANDS ###
533 our %high_priority_cmds = (
544 our $csUser = { NICK
=> $csnick, ID
=> ircd
::getAgentUuid
($csnick) };
545 my ($user, $dstUser, $msg) = @_;
546 my $src = $user->{NICK
};
548 my @args = split(/\s+/, $msg);
549 my $cmd = shift @args;
550 $user->{AGENT
} = $csUser;
552 return if flood_check
($user);
553 return unless (lc $dstUser->{NICK
} eq lc $csnick);
554 if(!defined($high_priority_cmds{lc $cmd}) &&
555 !adminserv
::is_svsop
($user) &&
556 $SrSv::IRCd
::State
::queue_depth
> main_conf_highqueue
)
558 notice
($user, get_user_agent
($user)." is too busy right now. Please try your command again later.");
562 if($cmd =~ /^register$/i) {
564 my @args = split(/\s+/, $msg, 4);
565 cs_register
($user, { CHAN
=> $args[1] }, $args[2], $args[3]);
567 notice
($user, 'Syntax: REGISTER <#channel> [password] [description]');
570 elsif($cmd =~ /^(?:[uvhas]op|co?f(ounder)?)$/i) {
571 my ($cn, $cmd2) = splice(@args, 0, 2);
572 my $chan = { CHAN
=> $cn };
574 if($cmd2 =~ /^add$/i) {
576 cs_xop_add
($user, $chan, $cmd, $args[0]);
578 notice
($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
581 elsif($cmd2 =~ /^del(ete)?$/i) {
583 cs_xop_del
($user, $chan, $cmd, $args[0]);
585 notice
($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
588 elsif($cmd2 =~ /^list$/i) {
590 cs_xop_list
($user, $chan, $cmd, $args[0]);
592 notice
($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
595 elsif($cmd2 =~ /^(wipe|clear)$/i) {
597 cs_xop_wipe
($user, $chan, $cmd);
599 notice
($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
603 notice
($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
606 elsif($cmd =~ /^levels$/i) {
608 notice
($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
612 my $cmd2 = lc(splice(@args, 1, 1));
616 cs_levels_set
($user, { CHAN
=> $args[0] }, $args[1], $args[2]);
618 notice
($user, 'Syntax: LEVELS <#channel> SET <permission> <level>');
621 elsif($cmd2 eq 'reset') {
623 cs_levels_set
($user, { CHAN
=> $args[0] }, $args[1]);
625 notice
($user, 'Syntax: LEVELS <#channel> RESET <permission>');
628 elsif($cmd2 eq 'list') {
630 cs_levels_list
($user, { CHAN
=> $args[0] });
632 notice
($user, 'Syntax: LEVELS <#channel> LIST');
635 elsif($cmd2 eq 'clear') {
637 cs_levels_clear
($user, { CHAN
=> $args[0] });
639 notice
($user, 'Syntax: LEVELS <#channel> CLEAR');
643 notice
($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
646 elsif($cmd =~ /^akick$/i) {
648 notice
($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
652 #my $cmd2 = lc($args[1]);
653 my $cmd2 = lc(splice(@args, 1, 1));
657 my @args = split(/\s+/, $msg, 5);
658 cs_akick_add
($user, { CHAN
=> $args[1] }, $args[3], $args[4]);
660 notice
($user, 'Syntax: AKICK <#channel> ADD <nick|mask> <reason>');
663 elsif($cmd2 eq 'del') {
665 cs_akick_del
($user, { CHAN
=> $args[0] }, $args[1]);
667 notice
($user, 'Syntax: AKICK <#channel> DEL <nick|mask|num|seq>');
670 elsif($cmd2 eq 'list') {
672 cs_akick_list
($user, { CHAN
=> $args[0] });
674 notice
($user, 'Syntax: AKICK <#channel> LIST');
677 elsif($cmd2 =~ /^(wipe|clear)$/i) {
679 cs_akick_wipe
($user, { CHAN
=> $args[0] });
681 notice
($user, 'Syntax: AKICK <#channel> WIPE');
684 elsif($cmd2 =~ /^enforce$/i) {
686 cs_akick_enforce
($user, { CHAN
=> $args[0] });
688 notice
($user, 'Syntax: AKICK <#channel> ENFORCE');
692 notice
($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
695 elsif($cmd =~ /^info$/i) {
697 cs_info
($user, { CHAN
=> $args[0] });
699 notice
($user, 'Syntax: INFO <channel>');
702 elsif($cmd =~ /^set$/i) {
703 if(@args == 2 and lc($args[1]) eq 'unsuccessor') {
704 cs_set
($user, { CHAN
=> $args[0] }, $args[1]);
706 elsif(@args >= 3 and (
707 $args[1] =~ /m(?:ode)?lock/i or
708 lc($args[1]) eq 'desc'
710 my @args = split(/\s+/, $msg, 4);
711 cs_set
($user, { CHAN
=> $args[1] }, $args[2], $args[3]);
714 cs_set
($user, { CHAN
=> $args[0] }, $args[1], $args[2]);
717 notice
($user, 'Syntax: SET <channel> <option> <value>');
720 elsif($cmd =~ /^why$/i) {
722 cs_why
($user, { CHAN
=> shift @args }, $src);
725 cs_why
($user, { CHAN
=> shift @args }, @args);
727 notice
($user, 'Syntax: WHY <channel> <nick> [nick [nick ...]]');
731 elsif($cmd =~ /^(de)?(voice|h(alf)?op|op|protect|admin|owner)$/i) {
733 cs_setmodes
($user, $cmd, { CHAN
=> shift(@args) }, @args);
735 notice
($user, 'Syntax: '.uc($cmd).' <channel> [nick [nick ...]]');
738 elsif($cmd =~ /^(up|down)$/i) {
739 cs_updown
($user, $cmd, @args);
741 elsif($cmd =~ /^drop$/i) {
743 cs_drop
($user, { CHAN
=> $args[0] });
745 notice
($user, 'Syntax: DROP <channel>');
748 elsif($cmd =~ /^help$/i) {
749 sendhelp
($user, 'chanserv', @args)
751 elsif($cmd =~ /^count$/i) {
753 cs_count
($user, { CHAN
=> $args[0] });
755 notice
($user, 'Syntax: COUNT <channel>');
758 elsif($cmd =~ /^k(?:ick)?$/i) {
759 my @args = split(/\s+/, $msg, 4); shift @args;
761 cs_kick
($user, { CHAN
=> $args[0] }, $args[1], 0, $args[2])
764 notice
($user, 'Syntax: KICK <channel> <nick> [reason]');
767 elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)$/i) {
768 my @args = split(/\s+/, $msg, 4); shift @args;
770 cs_kick
($user, { CHAN
=> $args[0] }, $args[1], 1, $args[2]);
772 notice
($user, 'Syntax: KICKBAN <channel> <nick> [reason]');
775 elsif($cmd =~ /^k(ick)?m(ask)?$/i) {
776 my @args = split(/\s+/, $msg, 4); shift @args;
778 cs_kickmask
($user, { CHAN
=> $args[0] }, $args[1], 0, $args[2])
781 notice
($user, 'Syntax: KICKMASK <channel> <mask> [reason]');
784 elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)m(ask)?$/i) {
785 my @args = split(/\s+/, $msg, 4); shift @args;
787 cs_kickmask
($user, { CHAN
=> $args[0] }, $args[1], 1, $args[2]);
789 notice
($user, 'Syntax: KICKBANMASK <channel> <mask> [reason]');
792 elsif($cmd =~ /^invite$/i) {
793 my $chan = shift @args;
795 cs_invite
($user, { CHAN
=> $chan }, $src)
798 cs_invite
($user, { CHAN
=> $chan }, @args)
801 notice
($user, 'Syntax: INVITE <channel> <nick>');
804 elsif($cmd =~ /^(close|forbid)$/i) {
806 my @args = split(/\s+/, $msg, 3);
807 cs_close
($user, { CHAN
=> $args[1] }, $args[2], CRF_CLOSE
);
810 notice
($user, 'Syntax: CLOSE <chan> <reason>');
813 elsif($cmd =~ /^drone$/i) {
815 my @args = split(/\s+/, $msg, 3);
816 cs_close
($user, { CHAN
=> $args[1] }, $args[2], CRF_DRONE
);
819 notice
($user, 'Syntax: DRONE <chan> <reason>');
822 elsif($cmd =~ /^clear$/i) {
823 my ($cmd, $chan, $clearcmd, $reason) = split(/\s+/, $msg, 4);
824 unless ($chan and $clearcmd) {
825 notice
($user, 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
828 if($clearcmd =~ /^modes$/i) {
829 cs_clear_modes
($user, { CHAN
=> $chan }, $reason);
831 elsif($clearcmd =~ /^ops$/i) {
832 cs_clear_ops
($user, { CHAN
=> $chan }, $reason);
834 elsif($clearcmd =~ /^users$/i) {
835 cs_clear_users
($user, { CHAN
=> $chan }, $reason);
837 elsif($clearcmd =~ /^bans?$/i) {
838 cs_clear_bans
($user, { CHAN
=> $chan }, 0, $reason);
840 elsif($clearcmd =~ /^excepts?$/i) {
841 cs_clear_bans
($user, { CHAN
=> $chan }, 128, $reason);
844 notice
($user, "Unknown CLEAR command \002$clearcmd\002",
845 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
848 elsif($cmd =~ /^mkick$/i) {
849 my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
851 cs_clear_users
($user, { CHAN
=> $chan }, $reason);
854 notice
($user, 'Syntax: MKICK <chan> [reason]');
857 elsif($cmd =~ /^mdeop$/i) {
858 my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
860 cs_clear_ops
($user, { CHAN
=> $chan }, $reason);
863 notice
($user, 'Syntax: MDEOP <chan> [reason]');
866 elsif($cmd =~ /^welcome$/i) {
867 my $wcmd = splice(@args, 1, 1);
868 if(lc($wcmd) eq 'add') {
869 my ($chan, $wmsg) = (splice(@args, 0, 1), join(' ', @args));
870 unless ($chan and $wmsg) {
871 notice
($user, 'Syntax: WELCOME <channel> ADD <message>');
874 cs_welcome_add
($user, { CHAN
=> $chan }, $wmsg);
876 elsif(lc($wcmd) eq 'del') {
877 if (@args != 2 or !misc
::isint
($args[1])) {
878 notice
($user, 'Syntax: WELCOME <channnel> DEL <number>');
881 cs_welcome_del
($user, { CHAN
=> $args[0] }, $args[1]);
883 elsif(lc($wcmd) eq 'list') {
885 notice
($user, 'Syntax: WELCOME <channel> LIST');
888 cs_welcome_list
($user, { CHAN
=> $args[0] });
891 notice
($user, 'Syntax: WELCOME <channel> <ADD|DEL|LIST>');
894 elsif($cmd =~ /^alist$/i) {
896 cs_alist
($user, { CHAN
=> shift @args }, shift @args);
898 notice
($user, 'Syntax: ALIST <channel> [mask]');
901 elsif($cmd =~ /^unban$/i) {
903 cs_unban
($user, { CHAN
=> shift @args }, $src);
906 cs_unban
($user, { CHAN
=> shift @args }, @args);
908 notice
($user, 'Syntax: UNBAN <channel> [nick]');
911 elsif($cmd =~ /^getkey$/i) {
913 cs_getkey
($user, { CHAN
=> $args[0] });
915 notice
($user, 'Syntax: GETKEY <channel>');
918 elsif($cmd =~ /^auth$/i) {
920 notice
($user, 'Syntax: AUTH <channel> <LIST|DELETE> [param]');
922 cs_auth
($user, { CHAN
=> shift @args }, shift @args, @args);
925 elsif($cmd =~ /^dice$/i) {
926 notice
($user, botserv
::get_dice
($args[0]));
928 elsif($cmd =~ /^(q|n)?ban$/i) {
930 my $chan = shift @args;
932 cs_ban
($user, { CHAN
=> $chan }, $type, @args)
935 notice
($user, 'Syntax: BAN <channel> <nick|mask>');
938 elsif($cmd =~ /^banlist$/i) {
939 my $chan = shift @args;
941 cs_banlist
($user, { CHAN
=> $chan });
944 notice
($user, 'Syntax: BANLIST <channel>');
947 elsif($cmd =~ /^assign$/i) {
948 my $chan = shift @args;
949 notice
($user, "$csnick ASSIGN is deprecated. Please use $botserv::bsnick ASSIGN");
951 botserv
::bs_assign
($user, { CHAN
=> shift @args }, shift @args);
954 notice
($user, 'Syntax: ASSIGN <#channel> <bot>');
957 elsif($cmd =~ /^mode$/i) {
958 my $chan = shift @args;
960 cs_mode
($user, { CHAN
=> $chan }, @args)
963 notice
($user, 'Syntax: MODE <channel> <modes> [parms]');
966 elsif($cmd =~ /^copy$/i) {
967 my $chan = shift @args;
969 cs_copy
($user, { CHAN
=> $chan }, @args)
972 notice
($user, 'Syntax: COPY #chan1 [type] #chan2');
975 elsif($cmd =~ /^m(?:ode)?lock$/i) {
976 my $chan = shift @args;
978 cs_mlock
($user, { CHAN
=> $chan }, @args)
981 notice
($user, 'Syntax: MLOCK <channel> <ADD|DEL|SET|RESET> <modes> [parms]');
984 elsif($cmd =~ /^resync$/i) {
986 notice
($user, 'Syntax: RESYNC <chan1> [chan2 [chan3 [..]]]');
988 cs_resync
($user, @args);
991 elsif($cmd =~ /^JOIN$/i) {
993 notice
($user, 'Syntax: JOIN <chan1> [chan2 [chan3 [..]]]');
995 cs_join
($user, @args);
998 elsif($cmd =~ /^topic$/i) {
999 my $chan = shift @args;
1001 notice
($user, 'Syntax: TOPIC <#channel> <message|NONE>');
1003 $msg =~ s/^topic #(?:\S+)? //i;
1004 cs_topic
($user, { CHAN
=> $chan }, $msg);
1008 notice
($user, "Unrecognized command \002$cmd\002.", "For help, type: \002/msg chanserv help\002");
1009 wlog
($csnick, LOG_DEBUG
(), "$src tried to use $csnick $msg");
1013 sub cs_register
($$;$$) {
1014 my ($user, $chan, $pass, $desc) = @_;
1015 # $pass is still passed in, but never used!
1016 my $src = get_user_nick
($user);
1017 my $cn = $chan->{CHAN
};
1019 unless(is_identified
($user, $src)) {
1020 notice
($user, 'You must register your nickname first.', "Type \002/msg NickServ HELP\002 for information on registering nicknames.");
1024 unless(is_in_chan
($user, $chan)) {
1025 notice
($user, "You are not in \002$cn\002.");
1029 if(services_conf_chanreg_needs_oper
&& !adminserv
::is_svsop
($user)) {
1030 notice
($user, "You must be network staff to register a channel\n");
1033 unless(get_op
($user, $chan) & ($opmodes{o
} | $opmodes{a
} | $opmodes{q
})) {
1034 # This would be preferred to be a 'opmode_mask' or something
1035 # However that might be misleading due to hop not being enough to register
1036 notice
($user, "You must have channel operator status to register \002$cn\002.");
1040 my $root = get_root_nick
($src);
1043 my $dlength = length($desc);
1044 if($dlength >= 350) {
1045 notice
($user, 'Channel description is too long by '. $dlength-350 .' character(s). Maximum length is 350 characters.');
1050 if($register->execute($cn, $desc, $root)) {
1051 notice
($user, "\002Your channel is now registered. Thank you.\002");
1052 notice
($user, ' ', "\002NOTICE:\002 Channel passwords are not used, as a security precaution.")
1054 set_acc
($root, $user, $chan, FOUNDER
);
1055 $set_modelock->execute(services_conf_default_channel_mlock
, $cn);
1057 services
::ulog
($csnick, LOG_INFO
(), "registered $cn", $user, $chan);
1058 botserv
::bs_assign
($user, $chan, services_conf_default_chanbot
) if services_conf_default_chanbot
;
1060 notice
($user, 'That channel has already been registered.');
1065 cs_command new SrSv
::AgentUI
::Simple
{
1066 COMMAND
=> [qw(uop vop hop aop sop cf cofounder cof cfounder)],
1067 SYNTAX
=> '#chan add/del/list/wipe/clear [nick/mask]',
1068 CALL
=> \
&cs_xop_dispatch
,
1072 sub cs_xop_dispatch
{
1073 my ($user, $cmd, $chan, $cmd2, @args) = @_;
1076 if($cmd2 =~ /^add$/i) {
1078 cs_xop_add
($user, $chan, $cmd, $args[0]);
1080 notice
($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
1083 elsif($cmd2 =~ /^del(ete)?$/i) {
1085 cs_xop_del
($user, $chan, $cmd, $args[0]);
1087 notice
($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
1090 elsif($cmd2 =~ /^list$/i) {
1092 cs_xop_list
($user, $chan, $cmd, $args[0]);
1094 notice
($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
1097 elsif($cmd2 =~ /^(wipe|clear)$/i) {
1099 cs_xop_wipe
($user, $chan, $cmd);
1101 notice
($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
1105 notice
($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
1109 sub cs_xop_ad_pre
($$$$$) {
1110 my ($user, $chan, $nick, $level, $del) = @_;
1112 my $old = get_acc
($nick, $chan); $old = 0 unless $old;
1113 my $slevel = get_best_acc
($user, $chan);
1115 unless(($del and is_identified
($user, $nick)) or adminserv
::can_do
($user, 'SERVOP')) {
1116 unless($level < $slevel and $old < $slevel) {
1117 notice
($user, $err_deny);
1120 my $cn = $chan->{CHAN
};
1121 my $overrideMsg = "$levels[$level] $cn ".($del ? 'DEL' : 'ADD')." $nick";
1122 can_do
($chan, 'ACCCHANGE', $user, { OVERRIDE_MSG
=> $overrideMsg }) or return undef;
1125 nickserv
::chk_registered
($user, $nick) or return undef;
1126 if (nr_chk_flag
($nick, NRF_NOACC
()) and !adminserv
::can_do
($user, 'SERVOP') and !$del) {
1127 notice
($user, "\002$nick\002 is not able to be added to access lists.");
1134 sub cs_xop_list
($$$;$) {
1135 my ($user, $chan, $cmd, $mask) = @_;
1136 chk_registered
($user, $chan) or return;
1137 my $cn = $chan->{CHAN
};
1138 my $level = xop_byname
($cmd);
1140 my $overrideMsg = "$cmd $cn LIST";
1141 can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> $overrideMsg }) or return;
1145 my ($mnick, $mident, $mhost) = glob2sql
(parse_mask
($mask));
1146 $mnick = '%' if($mnick eq '');
1147 $mident = '%' if($mident eq '');
1148 $mhost = '%' if($mhost eq '');
1150 $get_acc_list_mask->execute($mnick, $cn, $level, $mnick, $mident, $mhost);
1151 while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list_mask->fetchrow_array) {
1152 push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
1153 push @reply, ' '.($t ? 'Date/time added: '. gmtime2
($t).' ' : '').
1154 ($lu ? 'Last used '.time_ago
($lu).' ago' : '') if ($t or $lu);
1156 $get_acc_list_mask->finish();
1158 $get_acc_list->execute($cn, $level);
1159 while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list->fetchrow_array) {
1160 push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
1161 push @reply, ' '.($t ? 'Date/time added: '. gmtime2
($t).' ' : '').
1162 ($lu ? 'Last used '.time_ago
($lu).' ago' : '') if ($t or $lu);
1164 $get_acc_list->finish();
1167 notice
($user, "$levels[$level] list for \002$cn\002:", @reply);
1172 sub cs_xop_wipe
($$$) {
1173 my ($user, $chan, $cmd, $nick) = @_;
1174 chk_registered
($user, $chan) or return;
1176 my $slevel = get_best_acc
($user, $chan);
1177 my $level = xop_byname
($cmd);
1179 unless($level < $slevel) {
1180 notice
($user, $err_deny);
1183 my $cn = $chan->{CHAN
};
1184 my $overrideMsg = "$cmd $cn WIPE";
1185 my $srcnick = can_do
($chan, 'ACCCHANGE', $user, { ACC
=> $slevel, OVERRIDE_MSG
=> $overrideMsg }) or return;
1187 $wipe_acc_list->execute($cn, $level);
1189 my $log_str = "wiped the $cmd list of \002$cn\002.";
1190 my $src = get_user_nick
($user);
1191 notice
($user, "You have $log_str");
1192 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 has $log_str")
1193 if cr_chk_flag
($chan, CRF_VERBOSE
);
1194 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1196 memolog
($chan, "\002$srcnick\002 $log_str");
1199 sub cs_xop_add
($$$$) {
1200 my ($user, $chan, $cmd, $nick) = @_;
1202 chk_registered
($user, $chan) or return;
1203 my $level = xop_byname
($cmd);
1204 my $old = cs_xop_ad_pre
($user, $chan, $nick, $level, 0);
1205 return unless defined($old);
1207 my $cn = $chan->{CHAN
};
1209 if($old == $level) {
1210 notice
($user, "\002$nick\002 already has $levels[$level] access to \002$cn\002.");
1214 if($old == FOUNDER
) {
1215 notice
($user, "\002$nick\002 is the founder of \002$cn\002 and cannot be added to access lists.",
1216 "For more information, type: \002/msg chanserv help set founder\002");
1220 my $root = get_root_nick
($nick);
1221 my $auth = nr_chk_flag
($root, NRF_AUTH
());
1222 my $src = get_user_nick
($user);
1225 $add_auth->execute($cn, "$src:".($old ? $old : 0 ).":$level:".time(), $root);
1226 del_acc
($root, $chan) if $level < $old;
1229 set_acc
($root, $user, $chan, $level);
1233 $del_nick_akick->execute($cn, $root);
1234 my $log_str = "moved $root from the AKICK list to the ${levels[$level]} list of \002$cn\002".
1235 ($auth ? ' (requires authorization)' : '');
1237 my $src = get_user_nick
($user);
1238 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1239 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1240 if cr_chk_flag
($chan, CRF_VERBOSE
);
1241 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1242 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1243 memolog
($chan, "\002$srcnick\002 $log_str");
1245 my $log_str = ($old?'moved':'added')." \002$root\002"
1246 . ($old ? " from the ${levels[$old]}" : '') .
1247 " to the ${levels[$level]} list of \002$cn\002" .
1248 ($auth ? ' (requires authorization)' : '');
1249 my $src = get_user_nick
($user);
1250 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1251 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1252 if cr_chk_flag
($chan, CRF_VERBOSE
);
1253 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1254 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1255 memolog
($chan, "\002$srcnick\002 $log_str");
1259 sub cs_xop_del
($$$) {
1260 my ($user, $chan, $cmd, $nick) = @_;
1262 chk_registered
($user, $chan) or return;
1263 my $level = xop_byname
($cmd);
1264 my $old = cs_xop_ad_pre
($user, $chan, $nick, $level, 1);
1265 return unless defined($old);
1267 my $cn = $chan->{CHAN
};
1269 unless($old == $level) {
1270 notice
($user, "\002$nick\002 is not on the ${levels[$level]} list of \002$cn\002.");
1274 my $root = get_root_nick
($nick);
1275 my $srcnick = can_do
($chan, 'ACCLIST', $user);
1277 del_acc
($root, $chan);
1279 my $src = get_user_nick
($user);
1280 my $log_str = "removed \002$root\002 ($nick) from the ${levels[$level]} list of \002$cn\002";
1281 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1282 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1283 if cr_chk_flag
($chan, CRF_VERBOSE
);
1284 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1285 memolog
($chan, "\002$srcnick\002 $log_str");
1289 my ($user, $chan) = @_;
1291 chk_registered
($user, $chan) or return;
1293 my $cn = $chan->{CHAN
};
1294 my $overrideMsg = "COUNT $cn";
1295 if(can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> $overrideMsg })) {
1301 for (my $level = $plzero + 1; $level < COFOUNDER
+ 2; $level++) {
1302 $get_acc_count->execute($cn, $level - 1);
1303 my ($num_recs) = $get_acc_count->fetchrow_array;
1304 $reply = $reply." $plevels[$level]: ".$num_recs;
1306 notice
($user, "\002$cn Count:\002 ".$reply);
1309 sub cs_levels_pre
($$$;$) {
1310 my($user, $chan, $cmd, $listonly) = @_;
1312 chk_registered
($user, $chan) or return 0;
1313 my $cn = $chan->{CHAN
};
1314 my $overrideMsg = "LEVELS $cn $cmd";
1315 return can_do
($chan, ($listonly ? 'LEVELSLIST' : 'LEVELS'), $user, { OVERRIDE_MSG
=> $overrideMsg });
1318 sub cs_levels_set
($$$;$) {
1319 my ($user, $chan, $perm, $level) = @_;
1321 cs_levels_pre
($user, $chan, "$perm $level") or return;
1322 my $cn = $chan->{CHAN
};
1324 unless(is_level
($perm)) {
1325 notice
($user, "$perm is not a valid permission.");
1329 if(defined($level)) {
1330 $level = xop_byname
($level);
1331 unless(defined($level) and $level >= 0) {
1332 notice
($user, 'You must specify one of the following levels: '.
1333 'any, uop, vop, hop, aop, sop, cofounder, founder, nobody');
1337 $get_level_max->execute($perm);
1338 my ($max) = $get_level_max->fetchrow_array;
1339 $get_level_max->finish();
1341 if($max and $level > $max) {
1342 notice
($user, "\002$perm\002 cannot be set to " . $plevels[$level+$plzero] . '.');
1346 $add_level->execute($cn, $perm);
1347 $set_level->execute($level, $cn, $perm);
1350 notice
($user, "\002$perm\002 is now disabled in \002$cn\002.");
1352 notice
($user, "\002$perm\002 now requires " . $levels[$level] . " access in \002$cn\002.");
1355 $reset_level->execute($perm, $cn);
1357 notice
($user, "\002$perm\002 has been reset to default.");
1361 sub cs_levels_list
($$) {
1362 my ($user, $chan) = @_;
1364 cs_levels_pre
($user, $chan, 'LIST', 1) or return;
1365 my $cn = $chan->{CHAN
};
1367 $get_levels->execute($cn);
1369 while(my ($name, $def, $lvl) = $get_levels->fetchrow_array) {
1371 (defined($lvl) ? $plevels[$lvl+$plzero] : $plevels[$def+$plzero]),
1372 (defined($lvl) ? '' : '(default)')];
1375 notice
($user, columnar
{ TITLE
=> "Permission levels for \002$cn\002:",
1376 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
) }, @data);
1379 sub cs_levels_clear
($$) {
1380 my ($user, $chan) = @_;
1382 cs_levels_pre
($user, $chan, 'CLEAR') or return;
1383 my $cn = $chan->{CHAN
};
1385 $clear_levels->execute($cn);
1387 notice
($user, "All permissions have been reset to default.");
1390 sub cs_akick_pre
($$$;$) {
1391 my ($user, $chan, $overrideMsg, $list) = @_;
1393 chk_registered
($user, $chan) or return 0;
1395 return can_do
($chan, ($list ? 'AKICKLIST' : 'AKICK'), $user, { OVERRIDE_MSG
=> $overrideMsg });
1398 sub cs_akick_add
($$$$) {
1399 my ($user, $chan, $mask, $reason) = @_;
1400 my $cn = $chan->{CHAN
};
1402 my $adder = cs_akick_pre
($user, $chan, "ADD $mask $reason") or return;
1404 my ($nick, $ident, $host) = parse_mask
($mask);
1406 if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
1407 notice
($user, 'Invalid hostmask.');
1414 unless(valid_nick
($nick)) {
1415 $mask = normalize_hostmask
($mask);
1416 ($nick, $ident, $host) = parse_mask
($mask);
1420 if ($ident eq '' and $host eq '' and !nickserv
::is_registered
($nick)) {
1421 notice
($user, "\002$nick\002 is not registered");
1425 my $rlength = length($reason);
1426 if($rlength >= 350) {
1427 notice
($user, 'AKick reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
1432 my $src = get_user_nick
($user);
1433 if($ident eq '' and $host eq '' and my $old = get_acc
($nick, $chan)) {
1435 notice
($user, "\002$nick\002 is already on the AKick list in \002$cn\002");
1438 if($old < get_best_acc
($user, $chan) or adminserv
::can_do
($user, 'SERVOP')) {
1439 if ($old == FOUNDER
()) {
1440 # This is a fallthrough for the override case.
1441 # It shouldn't happen otherwise.
1442 # I didn't make it part of the previous conditional
1443 # b/c just $err_deny is a bit undescriptive in the override case.
1444 notice
($user, "You can't akick the founder!", $err_deny);
1448 my $root = get_root_nick
($nick);
1449 $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
1450 set_acc
($nick, $user, $chan, -1);
1451 $log_str = "moved \002$nick\002 (root: \002$root\002) from the $levels[$old] list".
1452 " to the AKick list of \002$cn\002";
1453 notice_all_nicks
($user, $root, "\002$src\002 $log_str");
1455 notice
($user, $err_deny);
1459 if($ident eq '' and $host eq '') {
1460 $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
1461 if (find_auth
($cn, $nick)) {
1462 # Don't allow a pending AUTH entry to potentially override an AKick entry
1463 # Believe it or not, it almost happened with #animechat on SCnet.
1464 # This would also end up leaving an orphan entry in the akick table.
1465 $nickserv::del_auth-
>execute($nick, $cn);
1466 $nickserv::del_auth-
>finish();
1468 set_acc
($nick, $user, $chan, -1);
1469 my $root = get_root_nick
($nick);
1470 $log_str = "added \002$nick\002 (root: \002$root\002) to the AKick list of \002$cn\002.";
1472 ($nick, $ident, $host) = glob2sql
($nick, $ident, $host);
1473 unless($add_akick->execute($cn, $nick, $ident, $host, $adder, $reason)) {
1474 notice
($user, "\002$mask\002 is already on the AKick list of \002$cn\002.");
1477 $log_str = "added \002$mask\002 to the AKick list of \002$cn\002.";
1481 notice
($user, "You have $log_str");
1482 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str")
1483 if cr_chk_flag
($chan, CRF_VERBOSE
);
1484 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1485 memolog
($chan, "\002$adder\002 $log_str");
1487 akick_allchan
($chan);
1490 sub get_akick_by_num
($$) {
1491 my ($chan, $num) = @_;
1492 my $cn = $chan->{CHAN
};
1494 $get_akick_by_num->execute($cn, $num);
1495 my ($nick, $ident, $host) = $get_akick_by_num->fetchrow_array();
1496 ($nick, $ident, $host) = sql2glob
($nick, $ident, $host);
1497 $get_akick_by_num->finish();
1500 } elsif($ident eq '' and $host eq '') {
1501 # nick based akicks don't use nicks but nickreg.id
1502 # so we have to get the nickreg.nick back
1503 $nick = nickserv
::get_id_nick
($nick);
1505 return ($nick, $ident, $host);
1508 sub cs_akick_del
($$$) {
1509 my ($user, $chan, $mask) = @_;
1510 my $cn = $chan->{CHAN
};
1512 my $adder = cs_akick_pre
($user, $chan, "DEL $mask") or return;
1515 if ($mask =~ /^[0-9\.,-]+$/) {
1516 foreach my $num (makeSeqList
($mask)) {
1517 my ($nick, $ident, $host) = get_akick_by_num
($chan, $num - 1) or next;
1518 if($ident eq '' and $host eq '') {
1521 push @masks, "$nick!$ident\@$host";
1527 foreach my $mask (@masks) {
1528 my ($nick, $ident, $host) = parse_mask
($mask);
1530 if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
1531 notice
($user, 'Invalid hostmask.');
1538 unless(valid_nick
($nick)) {
1539 $mask = normalize_hostmask
($mask);
1540 ($nick, $ident, $host) = parse_mask
($mask);
1544 if ($ident eq '' and $host eq '' and !nickserv
::is_registered
($nick)) {
1545 notice
($user, "\002$nick\002 is not registered");
1549 my ($success, $log_str) = do_akick_del
($chan, $mask, $nick, $ident, $host);
1550 my $src = get_user_nick
($user);
1552 notice
($user, "\002$src\002 $log_str");
1553 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1554 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag
($chan, CRF_VERBOSE
);
1555 memolog
($chan, "\002$adder\002 $log_str");
1557 notice
($user, $log_str);
1562 sub do_akick_del
($$$$$) {
1563 my ($chan, $mask, $nick, $ident, $host) = @_;
1564 my $cn = $chan->{CHAN
};
1567 if($ident eq '' and $host eq '') {
1568 if(get_acc
($nick, $chan) == -1) {
1569 del_acc
($nick, $chan);
1570 $del_nick_akick->execute($cn, $nick); $del_nick_akick->finish();
1571 my $root = get_root_nick
($nick);
1572 return (1, "deleted \002$nick\002 (root: \002$root\002) from the AKick list of \002$cn\002.")
1574 return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
1577 ($nick, $ident, $host) = glob2sql
($nick, $ident, $host);
1578 if($del_akick->execute($cn, $nick, $ident, $host) != 0) {
1579 return (1, "deleted \002$mask\002 from the AKick list of \002$cn\002.");
1581 return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
1586 sub cs_akick_list
($$) {
1587 my ($user, $chan) = @_;
1588 my $cn = $chan->{CHAN
};
1590 cs_akick_pre
($user, $chan, 'LIST', 1) or return;
1594 $get_akick_list->execute($cn);
1596 while(my ($nick, $ident, $host, $adder, $reason, $time) = $get_akick_list->fetchrow_array) {
1598 ($nick, $ident, $host) = sql2glob
($nick, $ident, $host);
1601 if($ident eq '' and $host eq '') {
1602 $nick = nickserv
::get_id_nick
($nick);
1604 $nick = "$nick!$ident\@$host";
1607 push @data, ["\002".++$i."\002", $nick, $adder, ($time ? gmtime2
($time) : ''), $reason];
1610 notice
($user, columnar
{TITLE
=> "AKICK list of \002$cn\002:", DOUBLE
=>1,
1611 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
1614 sub cs_akick_wipe
($$$) {
1615 my ($user, $chan) = @_;
1616 my $cn = $chan->{CHAN
};
1618 my $adder = cs_akick_pre
($user, $chan, 'WIPE') or return;
1620 $drop_akick->execute($cn);
1621 $wipe_acc_list->execute($cn, -1);
1622 my $log_str = "wiped the AKICK list of \002$cn\002.";
1623 my $src = get_user_nick
($user);
1624 notice
($user, "You have $log_str");
1625 ircd
::notice
(agent
($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag
($chan, CRF_VERBOSE
);
1626 services
::ulog
($csnick, LOG_INFO
(), $log_str, $user, $chan);
1627 memolog
($chan, "\002$adder\002 $log_str");
1630 sub cs_akick_enforce
($$) {
1631 my ($user, $chan) = @_;
1632 my $cn = $chan->{CHAN
};
1634 chk_registered
($user, $chan) or return;
1636 can_do
($chan, 'AKickEnforce', $user, { OVERRIDE_MSG
=> "AKICK $cn ENFORCE" }) or return;
1638 akick_allchan
($chan);
1642 cs_command new SrSv
::AgentUI
::Simple
{
1643 COMMAND
=> [qw(info)],
1644 SYNTAX
=> 'LIST:#chan',
1650 my ($user, @chanList) = @_;
1653 foreach my $cn (@chanList) {
1654 if(ref($cn) eq 'HASH') {
1658 push @chanList, split(',', $cn);
1661 my $chan = { CHAN
=> $cn };
1662 unless(__can_do
($chan, 'INFO', undef, 0)) {
1663 can_do
($chan, 'INFO', $user, { OVERRIDE_MSG
=> "INFO $cn" })
1667 $get_info->execute($cn);
1668 my @result = $get_info->fetchrow_array;
1670 push @reply, "The channel \002$cn\002 is not registered.";
1674 my ($descrip, $regd, $last, $topic, $topicer, $modelock, $founder, $successor, $bot, $bantype) = @result;
1676 $modelock = modes
::sanitize
($modelock) unless can_do
($chan, 'GETKEY', $user, { NOREPLY
=> 1 });
1680 my $topiclock = get_level
($chan, 'SETTOPIC');
1681 push @opts, "Topic Lock ($levels[$topiclock])" if $topiclock;
1683 if(cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
1684 push @reply, "\002$cn\002 is closed and cannot be used: ". get_close
($chan);
1689 push @extra, 'Will not expire' if cr_chk_flag
($chan, CRF_HOLD
);
1690 push @extra, 'Channel is frozen and access suspended' if cr_chk_flag
($chan, CRF_FREEZE
);
1692 push @opts, 'OpGuard' if cr_chk_flag
($chan, CRF_OPGUARD
);
1693 push @opts, 'BotStay' if cr_chk_flag
($chan, CRF_BOTSTAY
);
1694 push @opts, 'SplitOps' if cr_chk_flag
($chan, CRF_SPLITOPS
);
1695 push @opts, 'Verbose' if cr_chk_flag
($chan, CRF_VERBOSE
);
1696 push @opts, 'NeverOp' if cr_chk_flag
($chan, CRF_NEVEROP
);
1697 push @opts, 'Ban type '.$bantype if $bantype;
1698 my $opts = join(', ', @opts);
1702 push @data, ['Founder:', $founder];
1703 push @data, ['Successor:', $successor] if $successor;
1704 push @data, ['Description:', $descrip] if $descrip;
1705 push @data, ['Mode lock:', $modelock];
1706 push @data, ['Settings:', $opts] if $opts;
1707 push @data, ['ChanBot:', $bot] if $bot and $bot ne '';
1709 push @data, ['Registered:', gmtime2
($regd)],
1710 ['Last opping:', gmtime2
($last)],
1711 ['Time now:', gmtime2
(time)];
1713 push @reply, columnar
{TITLE
=> "ChanServ info for \002$cn\002:", NOHIGHLIGHT
=> 1}, @data,
1714 {COLLAPSE
=> \
@extra, BULLET
=> 1};
1716 notice
($user, @reply);
1719 sub cs_set_pre
($$$$) {
1720 my ($user, $chan, $set, $parm) = @_;
1721 my $cn = $chan->{CHAN
};
1725 'founder' => 1, 'successor' => 1, 'unsuccessor' => 1,
1726 #'mlock' => 1, 'modelock' => 1,
1728 'topiclock' => 1, 'greet' => 1, 'opguard' => 1,
1729 'freeze' => 1, 'botstay' => 1, 'verbose' => 1,
1730 'splitops' => 1, 'bantype' => 1, 'dice' => 1,
1731 'welcomeinchan' => 1, 'log' => 1,
1733 'hold' => 1, 'noexpire' => 1, 'no-expire' => 1,
1735 'autovoice' => 1, 'avoice' => 1,
1736 'neverop' => 1, 'noop' => 1,
1738 my %override_set = (
1739 'hold' => 'SERVOP', 'noexpire' => 'SERVOP', 'no-expire' => 'SERVOP',
1740 'freeze' => 'FREEZE', 'botstay' => 'BOT', 'log' => 'LOG',
1743 chk_registered
($user, $chan) or return 0;
1744 if($set =~ /m(?:ode)?lock/) {
1745 notice
($user, "CS SET MLOCK is deprecated and replaced with CS MLOCK",
1746 "For more information, please /CS HELP MLOCK");
1749 unless($valid_set{lc $set}) {
1750 notice
($user, "$set is not a valid ChanServ setting.");
1754 if($override_set{lc($set)}) {
1755 if(adminserv
::can_do
($user, $override_set{lc($set)}) ) {
1756 if(services_conf_log_overrides
) {
1757 my $src = get_user_nick
($user);
1758 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override CS SET $cn $set $parm");
1762 notice
($user, $err_deny);
1767 can_do
($chan, 'SET', $user) or return 0;
1774 my ($user, $chan, $set, $parm) = @_;
1775 my $cn = $chan->{CHAN
};
1778 cs_set_pre
($user, $chan, $set, $parm) or return;
1780 if($set =~ /^founder$/i) {
1782 unless(get_best_acc
($user, $chan) == FOUNDER
) {
1783 if(adminserv
::can_do
($user, 'SERVOP')) {
1786 notice
($user, $err_deny);
1792 unless($root = get_root_nick
($parm)) {
1793 notice
($user, "The nick \002$parm\002 is not registered.");
1797 $get_founder->execute($cn);
1798 my ($prev) = $get_founder->fetchrow_array;
1799 $get_founder->finish();
1801 if(lc($root) eq lc($prev)) {
1802 notice
($user, "\002$parm\002 is already the founder of \002$cn\002.");
1806 set_acc
($prev, $user, $chan, COFOUNDER
);
1808 $set_founder->execute($root, $cn); $set_founder->finish();
1809 set_acc
($root, $user, $chan, FOUNDER
);
1810 notice
($user, ($override ? "The previous founder, \002$prev\002, has" : "You have") . " been moved to the co-founder list of \002$cn\002.");
1811 notice_all_nicks
($user, $root, "\002$root\002 has been set as the founder of \002$cn\002.");
1812 services
::ulog
($csnick, LOG_INFO
(), "set founder of \002$cn\002 to \002$root\002", $user, $chan);
1813 $del_nick_akick->execute($cn, $root); $del_nick_akick->finish(); #just in case
1814 $get_successor->execute($cn);
1815 my $suc = $get_successor->fetchrow_array; $get_successor->finish();
1816 if(lc($suc) eq lc($root)) {
1817 $del_successor->execute($cn); $del_successor->finish();
1818 notice
($user, "Successor has been removed from \002$cn\002.");
1824 if($set eq 'successor') {
1825 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
1826 notice
($user, $err_deny);
1830 if(get_acc
($parm, $chan) == 7) {
1831 notice
($user, "The channel founder may not be the successor.");
1836 unless($root = get_root_nick
($parm)) {
1837 notice
($user, "The nick \002$parm\002 is not registered.");
1841 $set_successor->execute($root, $cn); $set_successor->finish();
1843 notice
($user, "\002$parm\002 is now the successor of \002$cn\002");
1844 services
::ulog
($csnick, LOG_INFO
(), "set successor of \002$cn\002 to \002$root\002", $user, $chan);
1848 if($set eq 'unsuccessor') {
1849 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
1850 notice
($user, $err_deny);
1854 $del_successor->execute($cn); $del_successor->finish();
1856 notice
($user, "Successor has been removed from \002$cn\002.");
1857 services
::ulog
($csnick, LOG_INFO
(), "removed successor from \002$cn\002", $user, $chan);
1861 if($set =~ /m(?:ode)?lock/) {
1862 my $modes = modes
::merge
($parm, '+r', 1);
1863 $modes = sanitize_mlockable
($modes);
1864 $set_modelock->execute($modes, $cn);
1866 notice
($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
1871 if($set eq 'desc') {
1872 $set_descrip->execute($parm, $cn);
1874 notice
($user, "Description of \002$cn\002 has been changed.");
1878 if($set eq 'topiclock') {
1879 my $perm = xop_byname
($parm);
1880 if($parm =~ /^(?:no|off|false|0)$/i) {
1881 cs_levels_set
($user, $chan, 'SETTOPIC');
1882 cs_levels_set
($user, $chan, 'TOPIC');
1883 } elsif($perm >= 0 and defined($perm)) {
1884 cs_levels_set
($user, $chan, 'SETTOPIC', $parm);
1885 cs_levels_set
($user, $chan, 'TOPIC', $parm);
1887 notice
($user, 'Syntax: SET <#chan> TOPICLOCK <off|any|uop|vop|hop|aop|sop|cf|founder>');
1892 if($set =~ /^bantype$/i) {
1893 unless (misc
::isint
($parm) and ($parm >= 0 and $parm <= 10)) {
1894 notice
($user, 'Invalid bantype');
1898 $set_bantype->execute($parm, $cn);
1900 notice
($user, "Ban-Type for \002$cn\002 now set to \002$parm\002.");
1906 if($parm =~ /^(?:no|off|false|0)$/i) { $val = 0; }
1907 elsif($parm =~ /^(?:yes|on|true|1)$/i) { $val = 1; }
1909 notice
($user, "Please say \002on\002 or \002off\002.");
1913 if($set =~ /^(?:opguard|secureops)$/i) {
1914 cr_set_flag
($chan, CRF_OPGUARD
, $val);
1918 "OpGuard is now \002ON\002.",
1919 "Channel status may not be granted by unauthorized users in \002$cn\002."#,
1920 #"Note that you must change the $csnick LEVELS settings for VOICE, HALFOP, OP, and/or ADMIN for this setting to have any effect."
1924 "OpGuard is now \002OFF\002.",
1925 "Channel status may be given freely in \002$cn\002."
1932 if($set =~ /^(?:splitops)$/i) {
1933 cr_set_flag
($chan, CRF_SPLITOPS
, $val);
1936 notice
($user, "SplitOps is now \002ON\002.");
1938 notice
($user, "SplitOps is now \002OFF\002.");
1944 if($set =~ /^(hold|no-?expire)$/i) {
1945 cr_set_flag
($chan, CRF_HOLD
, $val);
1948 notice
($user, "\002$cn\002 will not expire");
1949 services
::ulog
($csnick, LOG_INFO
(), "has held \002$cn\002", $user, $chan);
1951 notice
($user, "\002$cn\002 is no longer held from expiration");
1952 services
::ulog
($csnick, LOG_INFO
(), "has removed \002$cn\002 from hold", $user, $chan);
1958 if($set =~ /^freeze$/i) {
1959 cr_set_flag
($chan, CRF_FREEZE
, $val);
1962 notice
($user, "\002$cn\002 is now frozen and access suspended");
1963 services
::ulog
($csnick, LOG_INFO
(), "has frozen \002$cn\002", $user, $chan);
1965 notice
($user, "\002$cn\002 is now unfrozen and access restored");
1966 services
::ulog
($csnick, LOG_INFO
(), "has unfrozen \002$cn\002", $user, $chan);
1972 if($set =~ /^botstay$/i) {
1973 cr_set_flag
($chan, CRF_BOTSTAY
, $val);
1976 notice
($user, "Bot will now always stay in \002$cn");
1977 botserv
::bot_join
($chan, undef);
1979 notice
($user, "Bot will now part if less than one user is in \002$cn");
1980 botserv
::bot_part_if_needed
(undef, $chan, "Botstay turned off");
1985 if($set =~ /^verbose$/i) {
1986 cr_set_flag
($chan, CRF_VERBOSE
, $val);
1989 notice
($user, "Verbose mode enabled on \002$cn");
1992 notice
($user, "Verbose mode disabled on \002$cn");
1997 if($set =~ /^greet$/i) {
1999 notice
($user, "$csnick SET $cn GREET ON is deprecated.",
2000 "Please use $csnick LEVELS $cn SET GREET <rank>");
2002 cs_levels_set
($user, $chan, 'GREET', 'nobody');
2008 if($set =~ /^dice$/i) {
2010 notice
($user, "$csnick SET $cn DICE ON is deprecated.",
2011 "Please use $csnick LEVELS $cn SET DICE <rank>");
2013 cs_levels_set
($user, $chan, 'DICE', 'nobody');
2019 if($set =~ /^welcomeinchan$/i) {
2020 cr_set_flag
($chan, CRF_WELCOMEINCHAN
(), $val);
2023 notice
($user, "WELCOME messages will be put in the channel.");
2025 notice
($user, "WELCOME messages will be sent privately.");
2031 if($set =~ /^log$/i) {
2032 unless(module
::is_loaded
('logserv')) {
2033 notice
($user, "module logserv is not loaded, logging is not available.");
2038 logserv
::addchan
($user, $cn) and cr_set_flag
($chan, CRF_LOG
, $val);
2041 logserv
::delchan
($user, $cn) and cr_set_flag
($chan, CRF_LOG
, $val);
2046 if($set =~ /^a(?:uto)?voice$/i) {
2047 cr_set_flag
($chan, CRF_AUTOVOICE
(), $val);
2050 notice
($user, "All users w/o access will be autovoiced on join.");
2052 notice
($user, "AUTOVOICE disabled.");
2058 if($set =~ /^(?:never|no)op$/i) {
2059 cr_set_flag
($chan, CRF_NEVEROP
(), $val);
2062 notice
($user, "Users will not be automatically opped on join.");
2064 notice
($user, "Users with access will now be automatically opped on join.");
2072 my ($user, $chan, @tnicks) = @_;
2074 chk_registered
($user, $chan) or return;
2076 my $cn = $chan->{CHAN
};
2078 my ($candoNick, $override) = can_do
($chan, 'ACCLIST', $user, { OVERRIDE_MSG
=> "WHY $cn @tnicks" });
2079 return unless $candoNick;
2082 foreach my $tnick (@tnicks) {
2083 my $tuser = { NICK
=> $tnick };
2084 unless(get_user_id
($tuser)) {
2085 push @reply, "\002$tnick\002: No such user.";
2090 if(is_online
($tnick)) {
2097 $get_all_acc->execute(get_user_id
($tuser), $cn);
2098 while(my ($rnick, $acc) = $get_all_acc->fetchrow_array) {
2100 push @reply, "\002$tnick\002 $has $plevels[$acc+$plzero] access to \002$cn\002 due to identification to the nick \002$rnick\002.";
2102 $get_all_acc->finish();
2105 $check_auth_chan -> execute
($cn, $tnick);
2106 if (my ($nick, $data) = $check_auth_chan->fetchrow_array()) {
2107 my ($adder, $old, $level, $time) = split(/:/, $data);
2108 push @reply, "\002$tnick\002 is awaiting authorization to be added to the $cn \002$levels[$level]\002 list.\n";
2111 push @reply, "\002$tnick\002 has no access to \002$cn\002.";
2115 notice
($user, @reply);
2118 sub cs_setmodes
($$$@) {
2119 my ($user, $cmd, $chan, @args) = @_;
2121 my $agent = $user->{AGENT
} or $csUser;
2122 my $src = get_user_nick
($user);
2123 my $cn = $chan->{CHAN
};
2126 if (cr_chk_flag
($chan, CRF_FREEZE
())) {
2127 notice
($user, "\002$cn\002 is frozen and access suspended.");
2131 if(scalar(@args) == 0) {
2134 } elsif($args[0] =~ /^#/) {
2135 foreach my $chn ($cn, @args) {
2136 next unless $chn =~ /^#/;
2137 no warnings
'prototype'; # we call ourselves
2138 cs_setmodes
($user, $cmd, { CHAN
=> $chn });
2141 } elsif((scalar(@args) == 1) and (lc($args[0]) eq lc($src))) {
2145 # PROTECT is deprecated. remove it in a couple versions.
2146 # It should be called ADMIN under PREFIX_AQ
2147 my @mperms = ('VOICE', 'HALFOP', 'OP', 'ADMIN', 'OWNER');
2148 my @l = ('v', 'h', 'o', 'a', 'q');
2149 my ($level, @modes, $count);
2151 if($cmd =~ /voice$/i) { $level = 0 }
2152 elsif($cmd =~ /h(alf)?op$/i) { $level = 1 }
2153 elsif($cmd =~ /op$/i) { $level = 2 }
2154 elsif($cmd =~ /(protect|admin)$/i) { $level = 3 }
2155 elsif($cmd =~ /owner$/i) { $level = 4 }
2156 my $de = 1 if($cmd =~ s/^de//i);
2159 my $acc = get_best_acc
($user, $chan);
2161 # XXX I'm not sure this is the best way to do it.
2163 ($de and $self) or ($self and ($level + 2) <= $acc) or
2164 can_do
($chan, $mperms[$level], $user, { ACC
=> $acc, NOREPLY
=> 1, OVERRIDE_MSG
=> "$cmd $cn @args" }) )
2166 notice
($user, "$cn: $err_deny");
2170 my ($override, $check_override);
2172 foreach my $target (@args) {
2175 $tuser = ($self ? $user : { NICK
=> $target } );
2177 unless(is_in_chan
($tuser, $chan)) {
2178 notice
($user, "\002$target\002 is not in \002$cn\002.");
2182 my $top = get_op
($tuser, $chan);
2185 unless($top & (2**$level)) {
2186 notice
($user, "\002$target\002 has no $cmd in \002$cn\002.");
2190 if(!$override and get_best_acc
($tuser, $chan) > $acc) {
2191 unless($check_override) {
2192 $override = adminserv
::can_do
($user, 'SUPER');
2193 $check_override = 1;
2195 if($check_override and !$override) {
2196 notice
($user, "\002$target\002 outranks you in \002$cn\002.");
2201 if($top & (2**$level)) {
2203 notice
($user, "You already have $cmd in \002$cn\002.");
2205 notice
($user, "\002$target\002 already has $cmd in \002$cn\002.");
2209 if (cr_chk_flag
($chan, CRF_OPGUARD
()) and
2210 !can_keep_op
($user, $chan, $tuser, $l[$level]))
2212 notice
($user, "$target may not hold ops in $cn because OpGuard is enabled. ".
2213 "Please respect the founders wishes.");
2217 get_user_id
($tuser);
2218 push @modes, [($de ? '-' : '+').$l[$level], $tuser];
2223 ircd
::setmode2
(agent
($chan), $cn, @modes) if scalar @modes;
2224 ircd
::notice
(agent
($chan), '%'.$cn, "$src used ".($de ? "de$cmd" : $cmd).' '.join(' ', @args))
2225 if !$self and (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
2229 my ($user, $chan) = @_;
2230 my $cn = $chan->{CHAN
};
2232 chk_registered
($user, $chan) or return;
2234 unless(get_best_acc
($user, $chan) == FOUNDER
or adminserv
::can_do
($user, 'SERVOP')) {
2235 notice
($user, $err_deny);
2240 notice
($user, $cn.' has been dropped.');
2241 services
::ulog
($csnick, LOG_INFO
(), "dropped $cn", $user, $chan);
2243 undef($enforcers{lc $cn});
2244 botserv
::bot_part_if_needed
(undef(), $chan, "Channel dropped.");
2247 sub cs_kick
($$$;$$) {
2248 my ($user, $chan, $target, $ban, $reason) = @_;
2250 my $cmd = ($ban ? 'KICKBAN' : 'KICK');
2251 my $perm = ($ban ? 'BAN' : 'KICK');
2252 if(ref($chan) ne 'HASH' || !defined($chan->{CHAN
})) {
2253 notice
($user, "Invalid $cmd command, no channel specified");
2257 my $srclevel = get_best_acc
($user, $chan);
2259 my ($nick, $override) = can_do
($chan, ($ban ? 'BAN' : 'KICK'), $user, { ACC
=> $srclevel });
2260 return unless $nick;
2262 my $src = get_user_nick
($user);
2263 my $cn = $chan->{CHAN
};
2265 $reason = "Requested by $src".($reason?": $reason":'');
2268 ["I'm sorry, $src, I'm afraid I can't do that."],
2269 ["They are not in \002$cn\002."],
2274 my $peace = ({modes
::splitmodes
(get_modelock
($chan))}->{Q
}->[0] eq '+');
2276 my @targets = split(/\,/, $target);
2277 foreach $target (@targets) {
2278 my $tuser = { NICK
=> $target };
2279 get_user_id
($tuser);
2280 my $targetlevel = get_best_acc
($tuser, $chan);
2282 if(lc $target eq lc agent
($chan) or adminserv
::is_service
($tuser)) {
2283 push @{$errors[0]}, $target;
2287 if(get_user_id
($tuser)) {
2288 unless(is_in_chan
($tuser, $chan)) {
2290 push @notinchan, $tuser;
2292 push @{$errors[1]}, $target;
2297 push @{$errors[3]}, $target;
2301 if( ( ($peace and $targetlevel > 0) or ($srclevel <= $targetlevel) )
2302 and not ($override && check_override
($user, ($ban ? 'BAN' : 'KICK'), "$cmd $cn $target")) )
2304 push @{$errors[2]}, $target;
2309 kickban
($chan, $tuser, undef, $reason, 1);
2311 ircd
::kick
(agent
($chan), $cn, $tuser, $reason) unless adminserv
::is_service
($user);
2314 ircd
::flushmodes
() if($ban);
2316 foreach my $errlist (@errors) {
2318 my $msg = shift @$errlist;
2320 foreach my $e (@$errlist) { $e = "\002$e\002" }
2324 enum
("or", @$errlist).
2329 cs_ban
($user, $chan, '', @notinchan) if ($ban and scalar (@notinchan));
2332 sub cs_kickmask
($$$;$$) {
2333 my ($user, $chan, $mask, $ban, $reason) = @_;
2335 my $srclevel = get_best_acc
($user, $chan);
2336 my $src = get_user_nick
($user);
2337 my $cn = $chan->{CHAN
};
2339 my $candoOpts = { ACC
=> $srclevel, OVERRIDE_MSG
=> 'KICK'.($ban ? 'BAN' : '')."MASK $cn $mask $reason" };
2340 my ($nick, $override) = can_do
($chan, ($ban ? 'BAN' : 'KICK'), $user, $candoOpts);
2341 return unless $nick;
2344 $reason = "Requested by $src".($reason?": $reason":'');
2346 my $count = kickmask_noacc
($chan, $mask, $reason, $ban);
2347 notice
($user, ($count ? "Users kicked from \002$cn\002: $count." : "No users in \002$cn\002 matched $mask."))
2351 my ($user, $chan, $type, @targets) = @_;
2352 my $cn = $chan->{CHAN
};
2353 my $src = get_user_nick
($user);
2355 my $srclevel = get_best_acc
($user, $chan);
2356 my ($nick, $override) = can_do
($chan, 'BAN', $user, { ACC
=> $srclevel });
2357 return unless $nick;
2360 ["I'm sorry, $src, I'm afraid I can't do that."],
2365 my (@bans, @unbans);
2366 foreach my $target (@targets) {
2372 elsif($target =~ /\,/) {
2373 push @targets, split(',', $target);
2376 elsif($target eq '') {
2377 # Should never happen
2378 # but it could, given the split above
2381 elsif($target =~ /^-/) {
2383 push @unbans, $target;
2387 elsif($target =~ /[!@]+/) {
2388 ircd
::debug
("normalizing hostmask $target");
2389 #$target = normalize_hostmask($target);
2391 my ($nick, $ident, $host) = parse_mask
($target);
2392 $nick = '*' unless length($nick);
2393 $ident = '*' unless length($ident);
2394 $host = '*' unless length($host);
2395 $target = "$nick\!$ident\@$host";
2397 ircd
::debug
("normalized hostmask: $target");
2399 push @bans, $target;
2403 elsif(valid_nick
($target)) {
2404 $tuser = { NICK
=> $target };
2406 elsif($target = validate_ban
($target)) {
2407 push @bans, $target;
2410 notice
($user, "Not a valid ban target: $target");
2413 my $targetlevel = get_best_acc
($tuser, $chan);
2415 if(lc $target eq lc agent
($chan) or adminserv
::is_service
($tuser)) {
2416 push @{$errors[0]}, get_user_nick
($tuser);
2420 unless(get_user_id
($tuser)) {
2421 push @{$errors[1]}, get_user_nick
($tuser);
2424 if( $srclevel <= $targetlevel and not ($override && check_override
($user, 'BAN', "BAN $cn $target")) ) {
2425 push @{$errors[2]}, $target;
2429 push @bans, make_banmask
($chan, $tuser, $type);
2432 foreach my $errlist (@errors) {
2434 my $msg = shift @$errlist;
2436 foreach my $e (@$errlist) { $e = "\002$e\002" }
2440 enum
("or", @$errlist).
2446 ircd
::ban_list
(agent
($chan), $cn, +1, 'b', @bans) if (scalar(@bans));
2447 ircd
::notice
(agent
($chan), $cn, "$src used BAN ".join(' ', @bans))
2448 if (lc $user->{AGENT
} eq lc $csnick) and (cr_chk_flag
($chan, CRF_VERBOSE
) and scalar(@bans));
2449 cs_unban
($user, $chan, @unbans) if scalar(@unbans);
2452 sub cs_invite
($$@) {
2453 my ($user, $chan, @targets) = @_;
2454 my $src = get_user_nick
($user);
2455 my $cn = $chan->{CHAN
};
2456 my $srclevel = get_best_acc
($user, $chan);
2459 ["They are not online."],
2460 ["They are already in \002$cn\002."],
2465 foreach my $target (@targets) {
2470 $tnick = get_user_nick
($tuser);
2471 } elsif(lc($src) eq lc($target)) {
2474 } elsif($target =~ /\,/) {
2475 push @targets, split(',', $target);
2477 } elsif($target eq '') {
2478 # Should never happen
2479 # but it could, given the split above
2482 $tuser = { NICK
=> $target };
2486 my $candoOpts = { ACC
=> $srclevel, NOREPLY
=> 1, OVERRIDE_MSG
=> "INVITE $cn $target" };
2487 if(lc($src) eq lc($tnick)) {
2488 unless(can_do
($chan, 'InviteSelf', $user, $candoOpts)) {
2489 push @{$errors[2]}, $tnick;
2494 unless(can_do
($chan, 'INVITE', $user, $candoOpts)) {
2495 push @{$errors[2]}, $tnick;
2499 unless(nickserv
::is_online
($tnick)) {
2500 push @{$errors[0]}, $tnick;
2504 # invite is annoying, so punish them mercilessly
2505 return if flood_check
($user, 2);
2508 if(is_in_chan
($tuser, $chan)) {
2509 push @{$errors[1]}, $tnick;
2513 ircd
::invite
(agent
($chan), $cn, $tuser); push @invited, $tnick;
2514 ircd
::notice
(agent
($chan), $tuser, "\002$src\002 has invited you to \002$cn\002.")
2515 unless(lc($src) eq lc($tnick));
2518 foreach my $errlist (@errors) {
2520 my $msg = shift @$errlist;
2522 foreach my $e (@$errlist) { $e = "\002$e\002" }
2526 enum
("or", @$errlist).
2532 ircd
::notice
(agent
($chan), $cn, "$src used INVITE ".join(' ', @invited))
2533 if (lc $user->{AGENT
} eq lc $csnick)and cr_chk_flag
($chan, CRF_VERBOSE
) and scalar(@invited);
2537 my ($user, $chan, $reason, $type) = @_;
2538 # $type is a flag, either CRF_CLOSE or CRF_DRONE
2539 my $cn = $chan->{CHAN
};
2542 unless($oper = adminserv
::can_do
($user, 'SERVOP')) {
2543 notice
($user, $err_deny);
2547 my $rlength = length($reason);
2548 if($rlength >= 350) {
2549 notice
($user, 'Close reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2553 if(is_registered
($chan)) {
2554 $drop_acc->execute($cn);
2555 $drop_lvl->execute($cn);
2556 $del_close->execute($cn);
2557 $drop_akick->execute($cn);
2558 $drop_welcome->execute($cn);
2559 $drop_chantext->execute($cn);
2560 $drop_nicktext->execute($cn); # Leftover channel auths
2562 $set_founder->execute($oper, $cn);
2565 $register->execute($cn, $reason, $oper);
2567 $set_modelock->execute('+rsnt', $cn);
2569 set_acc
($oper, undef, $chan, FOUNDER
);
2571 $set_close->execute($cn, $reason, $oper, $type);
2572 cr_set_flag
($chan, (CRF_FREEZE
| CRF_CLOSE
| CRF_DRONE
), 0); #unset flags
2573 cr_set_flag
($chan, CRF_HOLD
, 1); #set flags
2574 cr_set_flag
($chan, $type, 1); #set flags
2575 my $src = get_user_nick
($user);
2576 my $time = gmtime2
(time);
2577 my $cmsg = "is closed [$src $time]: $reason";
2579 if ($type == CRF_CLOSE
) {
2580 cr_set_flag
($chan, CRF_CLOSE
, 1); #set flags
2581 clear_users
($chan, "Channel $cmsg");
2582 ircd
::settopic
(agent
($chan), $cn, $src, time(), "Channel $cmsg")
2584 elsif ($type == CRF_DRONE
) {
2585 cr_set_flag
($chan, CRF_DRONE
, 1); #set flags
2586 chan_kill
($chan, "$cn $cmsg");
2589 notice
($user, "The channel \002$cn\002 is now closed.");
2590 services
::ulog
($csnick, LOG_INFO
(), "closed $cn with reason: $reason", $user, $chan);
2593 sub cs_clear_pre
($$) {
2594 my ($user, $chan) = @_;
2595 my $cn = $chan->{CHAN
};
2597 my $srclevel = get_best_acc
($user, $chan);
2599 my ($cando, $override) = can_do
($chan, 'CLEAR', $user, { ACC
=> $srclevel });
2600 return 0 unless($cando);
2602 $get_highrank->execute($cn);
2603 my ($highrank_nick, $highrank_level) = $get_highrank->fetchrow_array();
2604 $get_highrank->finish();
2606 if($highrank_level > $srclevel && !$override) {
2607 notice
($user, "$highrank_nick outranks you in $cn (level: $levels[$highrank_level])");
2614 sub cs_clear_users
($$;$) {
2615 my ($user, $chan, $reason) = @_;
2616 my $src = get_user_nick
($user);
2618 cs_clear_pre
($user, $chan) or return;
2620 my $rlength = length($reason);
2621 if($rlength >= 350) {
2622 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2626 clear_users
($chan, "CLEAR USERS by \002$src\002".($reason?" reason: $reason":''));
2629 sub cs_clear_modes
($$;$) {
2630 my ($user, $chan, $reason) = @_;
2631 my $cn = $chan->{CHAN
};
2632 my $src = get_user_nick
($user);
2634 cs_clear_pre
($user, $chan) or return;
2636 my $rlength = length($reason);
2637 if($rlength >= 350) {
2638 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2642 my $agent = agent
($chan);
2643 ircd
::notice
($agent, $cn, "CLEAR MODES by \002$src\002".($reason?" reason: $reason":''));
2645 $get_chanmodes->execute($cn);
2646 my ($curmodes) = $get_chanmodes->fetchrow_array;
2647 my $ml = get_modelock
($chan);
2649 # This method may exceed the 12-mode limit
2650 # But it seems to succeed anyway, even with more than 12.
2651 my ($modes, $parms) = split(/ /, modes
::merge
(modes
::invert
($curmodes), $ml, 1). ' * *', 2);
2652 # we split this separately,
2653 # as otherwise it insists on taking the result of the split as a scalar quantity
2654 ircd
::setmode
($agent, $cn, $modes, $parms);
2658 sub cs_clear_ops
($$;$) {
2659 my ($user, $chan, $reason) = @_;
2660 my $cn = $chan->{CHAN
};
2661 my $src = get_user_nick
($user);
2663 cs_clear_pre
($user, $chan) or return;
2665 my $rlength = length($reason);
2666 if($rlength >= 350) {
2667 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2673 ircd
::notice
(agent
($chan), $cn, "CLEAR OPS by \002$src\002".($reason?" reason: $reason":''));
2677 sub cs_clear_bans
($$;$$) {
2678 my ($user, $chan, $type, $reason) = @_;
2679 my $cn = $chan->{CHAN
};
2680 my $src = get_user_nick
($user);
2681 $type = 0 unless defined $type;
2683 cs_clear_pre
($user, $chan) or return;
2685 my $rlength = length($reason);
2686 if($rlength >= 350) {
2687 notice
($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
2691 clear_bans
($chan, $type);
2693 ircd
::notice
(agent
($chan), $cn, "CLEAR BANS by \002$src\002".($reason?" reason: $reason":''));
2696 sub cs_welcome_pre
($$) {
2697 my ($user, $chan) = @_;
2699 return can_do
($chan, 'WELCOME', $user);
2702 sub cs_welcome_add
($$$) {
2703 my ($user, $chan, $msg) = @_;
2704 my $src = get_best_acc
($user, $chan, 1);
2705 my $cn = $chan->{CHAN
};
2707 cs_welcome_pre
($user, $chan) or return;
2709 my $mlength = length($msg);
2710 if($mlength >= 350) {
2711 notice
($user, 'Welcome Message is too long by '. $mlength-350 .' character(s). Maximum length is 350 characters.');
2715 $count_welcome->execute($cn);
2716 my $count = $count_welcome->fetchrow_array;
2718 notice
($user, 'There is a maximum of five (5) Channel Welcome Messages.');
2722 $add_welcome->execute($cn, ++$count, $src, $msg);
2724 notice
($user, "Welcome message number $count for \002$cn\002 set to:", " $msg");
2727 sub cs_welcome_list
($$) {
2728 my ($user, $chan) = @_;
2729 my $cn = $chan->{CHAN
};
2731 cs_welcome_pre
($user, $chan) or return;
2733 $list_welcome->execute($cn);
2737 while(my ($id, $time, $adder, $msg) = $list_welcome->fetchrow_array) {
2738 push @data, ["$id.", $adder, gmtime2
($time), $msg];
2740 $list_welcome->finish();
2742 notice
($user, columnar
{TITLE
=> "Welcome message list for \002$cn\002:", DOUBLE
=>1,
2743 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2746 sub cs_welcome_del
($$$) {
2747 my ($user, $chan, $id) = @_;
2748 my $cn = $chan->{CHAN
};
2750 cs_welcome_pre
($user, $chan) or return;
2752 if ($del_welcome->execute($cn, $id) == 1) {
2753 notice
($user, "Welcome Message \002$id\002 deleted from \002$cn\002");
2754 $consolidate_welcome->execute($cn, $id);
2758 "Welcome Message number $id for \002$cn\002 does not exist.");
2762 sub cs_alist
($$;$) {
2763 my ($user, $chan, $mask) = @_;
2764 my $cn = $chan->{CHAN
};
2766 chk_registered
($user, $chan) or return;
2768 my $slevel = get_best_acc
($user, $chan);
2770 can_do
($chan, 'ACCLIST', $user, { ACC
=> $slevel }) or return;
2775 my ($mnick, $mident, $mhost) = glob2sql
(parse_mask
($mask));
2776 $mnick = '%' if($mnick eq '');
2777 $mident = '%' if($mident eq '');
2778 $mhost = '%' if($mhost eq '');
2780 $get_acc_list2_mask->execute($mnick, $cn, $mnick, $mident, $mhost);
2781 while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2_mask->fetchrow_array) {
2782 push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
2783 push @reply, ' '.($time ? 'Date/time added: '. gmtime2
($time).' ' : '').
2784 ($last_used ? 'Last used '.time_ago
($last_used).' ago' : '') if ($time or $last_used);
2786 $get_acc_list2_mask->finish();
2788 $get_acc_list2->execute($cn);
2789 while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2->fetchrow_array) {
2790 push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
2791 push @reply, ' '.($time ? 'Date/time added: '. gmtime2
($time).' ' : '').
2792 ($last_used ? 'Last used '.time_ago
($last_used).' ago' : '') if ($time or $last_used);
2794 $get_acc_list2->finish();
2797 notice
($user, "Access list for \002$cn\002:", @reply);
2802 sub cs_banlist
($$) {
2803 my ($user, $chan) = @_;
2804 my $cn = $chan->{CHAN
};
2805 can_do
($chan, 'UnbanSelf', $user, { NOREPLY
=> 1 }) or can_do
($chan, 'BAN', $user) or return;
2807 my $i = 0; my @data;
2808 $list_bans->execute($cn, 0);
2809 while(my ($mask, $setter, $time) = $list_bans->fetchrow_array()) {
2810 push @data, ["\002".++$i."\002", sql2glob
($mask), $setter, ($time ? gmtime2
($time) : '')];
2813 notice
($user, columnar
{TITLE
=> "Ban list of \002$cn\002:", DOUBLE
=>1,
2814 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2818 my ($user, $chan, @parms) = @_;
2819 my $cn = $chan->{CHAN
};
2822 $self = 1 if ( (scalar(@parms) == 1) and ( lc($parms[0]) eq lc(get_user_nick
($user)) ) );
2823 if ($parms[0] eq '*') {
2824 cs_clear_bans
($user, $chan);
2828 can_do
($chan, ($self ? 'UnbanSelf' : 'UNBAN'), $user) or return;
2831 my (@userlist, @masklist);
2832 foreach my $parm (@parms) {
2833 if(valid_nick
($parm)) {
2834 my $tuser = ($self ? $user : { NICK
=> $parm });
2835 unless(get_user_id
($tuser)) {
2836 notice
($user, "No such user: \002$parm\002");
2839 push @userlist, $tuser;
2840 } elsif($parm =~ /^[0-9\.,-]+$/) {
2841 foreach my $num (makeSeqList
($parm)) {
2842 push @masklist, get_ban_num
($chan, $num);
2845 push @masklist, $parm;
2849 if(scalar(@userlist)) {
2850 unban_user
($chan, @userlist);
2851 notice
($user, "All bans affecting " .
2852 ( $self ? 'you' : enum
( 'and', map(get_user_nick
($_), @userlist) ) ) .
2853 " on \002$cn\002 have been removed.");
2855 if(scalar(@masklist)) {
2856 ircd
::ban_list
(agent
($chan), $cn, -1, 'b', @masklist);
2857 notice
($user, "The following bans have been removed: ".join(' ', @masklist))
2858 if scalar(@masklist);
2862 sub cs_updown
($$@) {
2863 my ($user, $cmd, @chans) = @_;
2864 return cs_updown2
($user, $cmd, { CHAN
=> shift @chans }, @chans)
2865 if (defined($chans[1]) and $chans[1] !~ "^\#" and $chans[0] =~ "^\#");
2867 @chans = get_user_chans
($user)
2870 if (uc($cmd) eq 'UP') {
2871 foreach my $cn (@chans) {
2872 next unless ($cn =~ /^\#/);
2873 my $chan = { CHAN
=> $cn };
2874 next if cr_chk_flag
($chan, (CRF_DRONE
| CRF_CLOSE
| CRF_FREEZE
), 1);
2875 chanserv
::set_modes
($user, $chan, chanserv
::get_best_acc
($user, $chan));
2878 elsif (uc($cmd) eq 'DOWN') {
2879 foreach my $cn (@chans) {
2880 next unless ($cn =~ /^\#/);
2881 chanserv
::unset_modes
($user, { CHAN
=> $cn });
2886 sub cs_updown2
($$$@) {
2887 my ($user, $cmd, $chan, @targets) = @_;
2889 my $agent = $user->{AGENT
} or $csnick;
2890 my $cn = $chan->{CHAN
};
2892 return unless chk_registered
($user, $chan);
2893 if (cr_chk_flag
($chan, CRF_FREEZE
())) {
2894 notice
($user, "\002$cn\002 is frozen and access suspended.");
2898 my $acc = get_best_acc
($user, $chan);
2899 return unless(can_do
($chan, 'UPDOWN', $user, { ACC
=> $acc }));
2901 my $updown = ((uc($cmd) eq 'UP') ? 1 : 0);
2903 my ($override, $check_override);
2905 foreach my $target (@targets) {
2907 my $tuser = { NICK
=> $target };
2909 unless(is_in_chan
($tuser, $chan)) {
2910 notice
($user, "\002$target\002 is not in \002$cn\002.");
2915 push @list, $target;
2916 chanserv
::set_modes
($tuser, $chan, chanserv
::get_best_acc
($tuser, $chan));
2919 my $top = get_op
($tuser, $chan);
2921 notice
($user, "\002$target\002 is already deopped in \002$cn\002.");
2925 if(!$override and get_best_acc
($tuser, $chan) > $acc) {
2926 unless($check_override) {
2927 $override = adminserv
::can_do
($user, 'SUPER');
2928 $check_override = 1;
2930 if($check_override and !$override) {
2931 notice
($user, "\002$target\002 outranks you in \002$cn\002.");
2935 push @list, $target;
2936 chanserv
::unset_modes
($tuser, { CHAN
=> $cn });
2941 my $src = get_user_nick
($user);
2942 ircd
::notice
(agent
($chan), '%'.$cn, "$src used $cmd ".join(' ', @list))
2943 if (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
2947 my ($user, $chan) = @_;
2948 my $cn = $chan->{CHAN
};
2950 can_do
($chan, 'GETKEY', $user) or return;
2952 $get_chanmodes->execute($cn);
2953 my $modes = $get_chanmodes->fetchrow_array; $get_chanmodes->finish();
2955 if(my $key = modes
::get_key
($modes)) {
2956 notice
($user, "Channel key for \002$cn\002: $key");
2959 notice
($user, "\002$cn\002 has no channel key.");
2964 my ($user, $chan, $cmd, @args) = @_;
2965 my $cn = $chan->{CHAN
};
2968 return unless chk_registered
($user, $chan);
2969 return unless can_do
($chan, 'AccChange', $user);
2970 my $userlevel = get_best_acc
($user, $chan);
2971 if($cmd eq 'list') {
2973 $list_auth_chan->execute($cn);
2974 while(my ($nick, $data) = $list_auth_chan->fetchrow_array()) {
2975 my ($adder, $old, $level, $time) = split(/:/, $data);
2976 push @data, ["\002$nick\002", $levels[$level], $adder, gmtime2
($time)];
2978 if ($list_auth_chan->rows()) {
2979 notice
($user, columnar
{TITLE
=> "Pending authorizations for \002$cn\002:",
2980 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
2983 notice
($user, "There are no pending authorizations for \002$cn\002");
2985 $list_auth_chan->finish();
2987 elsif($cmd eq 'remove' or $cmd eq 'delete' or $cmd eq 'del') {
2988 my ($nick, $adder, $old, $level, $time);
2989 my $parm = shift @args;
2990 if(misc
::isint
($parm) and ($nick, $adder, $old, $level, $time) = get_auth_num
($cn, $parm))
2993 elsif (($adder, $old, $level, $time) = get_auth_nick
($cn, $parm))
2998 # This should normally be an 'else' as the elsif above should prove false
2999 # For some reason, it doesn't work. the unless ($nick) fixes it.
3000 # It only doesn't work for numbered entries
3001 notice
($user, "There is no entry for \002$parm\002 in \002$cn\002's AUTH list");
3004 $nickserv::del_auth-
>execute($nick, $cn); $nickserv::del_auth-
>finish();
3005 my $log_str = "deleted AUTH entry $cn $nick $levels[$level]";
3006 my $src = get_user_nick
($user);
3007 notice
($user, "You have $log_str");
3008 ircd
::notice
(agent
($chan), '%'.$cn, "has \002$src\002 has $log_str")
3009 if cr_chk_flag
($chan, CRF_VERBOSE
);
3010 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "has $log_str", $user, $chan);
3013 notice
($user, "Unknown AUTH command \002$cmd\002");
3018 my ($user, $chan, $modes_in, @parms_in) = @_;
3019 can_do
($chan, 'MODE', $user) or return undef;
3020 ($modes_in, @parms_in) = validate_chmodes
($modes_in, @parms_in);
3029 my $sign = '+'; my $cn = $chan->{CHAN
};
3030 my ($modes_out, @parms_out, @bans);
3031 foreach my $mode (split(//, $modes_in)) {
3032 $sign = $mode if $mode =~ /[+-]/;
3033 if ($permhash{$mode}) {
3034 my $parm = shift @parms_in;
3035 cs_setmodes
($user, ($sign eq '-' ? 'de' : '').$permhash{$mode}, $chan, $parm);
3037 elsif ($mode eq 'b') {
3038 my $parm = shift @parms_in;
3044 elsif($mode =~ /[eIlLkjf]/) {
3045 $modes_out .= $mode;
3046 push @parms_out, shift @parms_in;
3048 $modes_out .= $mode;
3053 cs_ban
($user, $chan, undef, @bans);
3055 return if $modes_out =~ /^[+-]*$/;
3056 ircd
::setmode
(agent
($chan), $chan->{CHAN
}, $modes_out, join(' ', @parms_out));
3057 do_modelock
($chan, $modes_out.' '.join(' ', @parms_out));
3059 $modes_out =~ s/^[+-]*([+-].*)$/$1/;
3060 ircd
::notice
(agent
($chan), '%'.$cn, get_user_nick
($user).' used MODE '.join(' ', $modes_out, @parms_out))
3061 if (lc $user->{AGENT
} eq lc $csnick) and cr_chk_flag
($chan, CRF_VERBOSE
);
3065 my ($user, $chan1, @args) = @_;
3066 my $cn1 = $chan1->{CHAN
};
3069 if($args[0] =~ /^#/) {
3073 if($args[0] =~ /(?:acc(?:ess)?|akick|levels|all)/i) {
3074 $type = shift @args;
3075 $cn2 = shift @args unless $cn2;
3078 if($type =~ /^acc(?:ess)?/i) {
3080 $rank = shift @args;
3086 unless(defined $cn2 and defined $type) {
3087 notice
($user, 'Unknown COPY command', 'Syntax: COPY #chan1 [type] #chan2');
3089 my $chan2 = { CHAN
=> $cn2 };
3090 if(lc($cn1) eq lc($cn2)) {
3091 notice
($user, "You cannot copy a channel onto itself.");
3093 unless(is_registered
($chan1)) {
3094 notice
($user, "Source channel \002$cn1\002 must be registered.");
3097 can_do
($chan1, 'COPY', $user) or return undef;
3098 if(lc $type eq 'all') {
3099 if(is_registered
($chan2)) {
3100 notice
($user, "When copying all channel details, destination channel cannot be registered.");
3102 } elsif(!(get_op
($user, $chan2) & ($opmodes{o
} | $opmodes{a
} | $opmodes{q
}))) {
3103 # This would be preferred to be a 'opmode_mask' or something
3104 # However that might be misleading due to hop not being enough to register
3105 notice
($user, "You must have channel operator status to register \002$cn2\002.");
3108 cs_copy_chan_all
($user, $chan1, $chan2);
3112 unless(is_registered
($chan2)) {
3113 notice
($user, "When copying channel lists, destination channel must be registered.");
3116 can_do
($chan2, 'COPY', $user) or return undef;
3118 if(lc $type eq 'akick') {
3119 cs_copy_chan_akick
($user, $chan1, $chan2);
3120 } elsif(lc $type eq 'levels') {
3121 cs_copy_chan_levels
($user, $chan1, $chan2);
3122 } elsif($type =~ /^acc(?:ess)?/i) {
3123 cs_copy_chan_acc
($user, $chan1, $chan2, xop_byname
($rank));
3127 sub cs_copy_chan_all
($$$) {
3128 my ($user, $chan1, $chan2) = @_;
3129 cs_copy_chan_chanreg
($user, $chan1, $chan2);
3130 cs_copy_chan_levels
($user, $chan1, $chan2);
3131 cs_copy_chan_acc
($user, $chan1, $chan2);
3132 cs_copy_chan_akick
($user, $chan1, $chan2);
3136 sub cs_copy_chan_chanreg
($$$) {
3137 my ($user, $chan1, $chan2) = @_;
3138 my $cn1 = $chan1->{CHAN
};
3139 my $cn2 = $chan2->{CHAN
};
3141 copy_chan_chanreg
($cn1, $cn2);
3142 botserv
::bot_join
($chan2) unless (lc(agent
($chan2)) eq lc($csnick) );
3143 do_modelock
($chan2);
3144 notice
($user, "Registration for \002$cn1\002 copied to \002$cn2\002");
3146 my $log_str = "copied the channel registration for \002$cn1\002 to \002$cn2\002";
3147 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3149 my $src = get_user_nick
($user);
3150 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3151 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3152 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3153 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3156 sub cs_copy_chan_acc
($$$;$) {
3157 my ($user, $chan1, $chan2, $level) = @_;
3158 my $cn1 = $chan1->{CHAN
};
3159 my $cn2 = $chan2->{CHAN
};
3161 copy_chan_acc
($cn1, $cn2, $level);
3163 unless(cr_chk_flag
($chan2, CRF_NEVEROP
)) {
3164 $get_chan_users->execute($cn2); my @targets;
3165 while (my ($nick, $uid) = $get_chan_users->fetchrow_array()) {
3166 push @targets, $nick unless nr_chk_flag_user
({ NICK
=> $nick, ID
=> $uid }, NRF_NEVEROP
);
3168 cs_updown2
($user, 'UP', $chan2, @targets);
3171 notice
($user, "Access list for \002$cn1\002 ".
3172 ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
3173 "copied to \002$cn2\002");
3175 my $log_str = "copied the channel access list for \002$cn1\002 ".
3176 ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
3178 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3180 my $src = get_user_nick
($user);
3181 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3182 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3183 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3184 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3187 sub cs_copy_chan_levels
($$$) {
3188 my ($user, $chan1, $chan2) = @_;
3189 my $cn1 = $chan1->{CHAN
};
3190 my $cn2 = $chan2->{CHAN
};
3192 copy_chan_levels
($cn1, $cn2);
3193 notice
($user, "LEVELS for \002$cn1\002 copied to \002$cn2\002");
3195 my $log_str = "copied the LEVELS list for \002$cn1\002 to \002$cn2\002";
3196 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3198 my $src = get_user_nick
($user);
3199 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3200 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3201 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3202 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3205 sub cs_copy_chan_akick
($$$) {
3206 my ($user, $chan1, $chan2) = @_;
3207 my $cn1 = $chan1->{CHAN
};
3208 my $cn2 = $chan2->{CHAN
};
3210 copy_chan_akick
($cn1, $cn2);
3211 notice
($user, "Channel AKick list for \002$cn1\002 copied to \002$cn2\002");
3213 my $log_str = "copied the AKick list for \002$cn1\002 to \002$cn2\002";
3214 services
::ulog
($chanserv::csnick
, LOG_INFO
(), "$log_str", $user, $chan1);
3216 my $src = get_user_nick
($user);
3217 ircd
::notice
(agent
($chan1), '%'.$cn1, "\002$src\002 $log_str")
3218 if cr_chk_flag
($chan1, CRF_VERBOSE
);
3219 ircd
::notice
(agent
($chan2), '%'.$cn2, "\002$src\002 $log_str")
3220 if cr_chk_flag
($chan2, CRF_VERBOSE
);
3223 sub cs_mlock
($$$@) {
3224 my ($user, $chan, $cmd, @args) = @_;
3225 my $cn = $chan->{CHAN
};
3226 # does this need its own privilege now?
3227 can_do
($chan, 'SET', $user) or return;
3230 my ($modes_in, @parms_in) = validate_chmodes
(shift @args, @args);
3231 $modes = $modes_in.' '.join(' ', @parms_in);
3235 my $cur_modelock = get_modelock
($chan);
3236 if(lc $cmd eq 'add') {
3237 $modes = modes
::merge
($cur_modelock, $modes, 1);
3238 $modes = sanitize_mlockable
($modes);
3239 $set_modelock->execute($modes, $cn);
3241 elsif(lc $cmd eq 'del') {
3242 $modes =~ s/[+-]//g;
3243 $modes = modes
::add
($cur_modelock, "-$modes", 1);
3244 $set_modelock->execute($modes, $cn);
3246 elsif(lc $cmd eq 'set') {
3247 $modes = modes
::merge
($modes, "+r", 1);
3248 $set_modelock->execute($modes, $cn);
3250 elsif(lc $cmd eq 'reset') {
3251 $set_modelock->execute(services_conf_default_channel_mlock
, $cn);
3253 notice
($user, "Unknown MLOCK command \"$cmd\"");
3257 notice
($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
3261 notice
($user, columnar
{TITLE
=> "Ban list of \002$cn\002:", DOUBLE
=>1,
3262 NOHIGHLIGHT
=> nr_chk_flag_user
($user, NRF_NOHIGHLIGHT
)}, @data);
3266 use SrSv
::MySQL
::Stub
{
3267 getChanUsers
=> ['COLUMN', "SELECT user.nick FROM chanuser JOIN user ON (user.id=chanuser.nickid)
3268 WHERE chanuser.chan=? AND chanuser.joined=1"]
3272 my ($user, @cns) = @_;
3273 foreach my $cn (@cns) {
3274 my $chan = { CHAN
=> $cn };
3275 next unless cs_clear_ops
($user, $chan, 'Resync');
3276 cs_updown2
($user, 'up', $chan, getChanUsers
($cn));
3277 if(can_do
($chan, 'AKickEnforce', $user, { OVERRIDE_MSG
=> "AKICK $cn ENFORCE", NOREPLY
=> 1 })) {
3278 cs_akick_enforce
($user, $chan);
3284 my ($user, @cns) = @_;
3287 foreach my $cn (@cns) {
3289 push @cns, split(',', $cn);
3294 my $chan = { CHAN
=> $cn };
3295 my $cando_opts = { NOREPLY
=> 1 };
3296 if(check_akick
($user, $chan, 1)) {
3297 push @reply, "You are banned from $cn";
3299 } elsif(!can_do
($chan, 'JOIN', $user, $cando_opts)) {
3300 push @reply, "$cn is a private channel.";
3303 if(is_in_chan
($user, $chan)) {
3306 if(can_do
($chan, 'InviteSelf', $user, $cando_opts)) {
3307 cs_invite
($user, $chan, $user);
3312 ircd
::svsjoin
(get_user_agent
($user), $user, @out_cns) if scalar @out_cns;
3313 notice
($user, @reply) if scalar @reply;
3317 my ($user, $cn, @args) = @_;
3318 my ($chan, $msg) = ($cn->{CHAN
}, join(" ", @args));
3319 can_do
($cn, 'SETTOPIC', $user) or return undef;
3320 ircd
::settopic
(agent
($cn), $chan, get_user_nick
($user), time, ($msg =~ /^none/i ? "" : $msg));
3325 # these are helpers and do NOT check if $cn1 or $cn2 is reg'd
3326 sub copy_chan_acc
($$;$) {
3327 my ($cn1, $cn2, $level) = @_;
3329 $copy_acc_rank->execute($cn2, $cn1, $level);
3330 $copy_acc_rank->finish();
3332 $get_founder->execute($cn2);
3333 my ($founder) = $get_founder->fetchrow_array;
3334 $get_founder->finish();
3336 $copy_acc->execute($cn2, $cn1, $founder);
3337 $copy_acc->finish();
3341 sub copy_chan_akick
($$;$) {
3342 my ($cn1, $cn2) = @_;
3343 $copy_akick->execute($cn2, $cn1);
3344 $copy_akick->finish();
3345 copy_chan_acc
($cn1, $cn2, -1);
3348 sub copy_chan_levels
($$) {
3349 my ($cn1, $cn2) = @_;
3350 $copy_levels->execute($cn2, $cn1);
3351 $copy_levels->finish();
3354 sub copy_chan_chanreg
($$) {
3355 my ($cn1, $cn2) = @_;
3356 $get_founder->execute($cn1);
3357 my ($founder) = $get_founder->fetchrow_array;
3358 $get_founder->finish();
3359 set_acc
($founder, undef, { CHAN
=> $cn2 }, FOUNDER
);
3360 $copy_chanreg->execute($cn2, $cn1);
3361 $copy_chanreg->finish();
3364 sub do_welcome
($$) {
3365 my ($user, $chan) = @_;
3366 my $cn = $chan->{CHAN
};
3368 $get_welcomes->execute($cn);
3369 if($get_welcomes->rows) {
3371 while(my ($msg) = $get_welcomes->fetchrow_array) {
3372 push @welcomes, (cr_chk_flag
($chan, CRF_WELCOMEINCHAN
) ? '' : "[$cn] " ).$msg;
3374 if(cr_chk_flag
($chan, CRF_WELCOMEINCHAN
)) {
3375 ircd
::privmsg
(agent
($chan), $cn, @welcomes);
3377 notice
($user, @welcomes);
3380 $get_welcomes->finish();
3384 my ($user, $chan) = @_;
3385 my $cn = $chan->{CHAN
};
3387 if(can_do
($chan, 'GREET', $user)) {
3388 my $src = get_user_nick
($user);
3389 $nickserv::get_greet-
>execute(get_user_id
($user));
3390 my ($greet) = $nickserv::get_greet-
>fetchrow_array();
3391 $nickserv::get_greet-
>finish();
3392 ircd
::privmsg
(agent
($chan), $cn, "[\002$src\002] $greet") if $greet;
3396 sub chk_registered
($$) {
3397 my ($user, $chan) = @_;
3399 unless(is_registered
($chan)) {
3400 my $cn = $chan->{CHAN
};
3402 notice
($user, "The channel \002$cn\002 is not registered.");
3409 sub make_banmask
($$;$) {
3410 my ($chan, $tuser, $type) = @_;
3411 my $nick = get_user_nick
($tuser);
3413 my ($ident, $vhost) = get_vhost
($tuser);
3415 my ($nick, $ident, $vhost) = make_hostmask
(get_bantype
($chan), $nick, $ident, $vhost);
3418 } elsif($type eq 'n') {
3423 return $type."$nick!$ident\@$vhost";
3426 sub kickban
($$$$;$) {
3427 my ($chan, $user, $mask, $reason, $noflush) = @_;
3428 my $cn = $chan->{CHAN
};
3429 my $nick = get_user_nick
($user);
3431 return 0 if adminserv
::is_service
($user);
3433 my $agent = agent
($chan);
3436 $mask = make_banmask
($chan, $user);
3439 enforcer_join
($chan) if (get_user_count
($chan) <= 1);
3440 ircd
::setmode
($agent, $cn, '+b', $mask);
3441 ircd
::flushmodes
() unless $noflush;
3442 ircd
::kick
($agent, $cn, $user, $reason);
3446 sub kickban_multi
($$$) {
3447 my ($chan, $users, $reason) = @_;
3448 my $cn = $chan->{CHAN
};
3449 my $agent = agent
($chan);
3451 enforcer_join
($chan);
3452 ircd
::setmode
($agent, $cn, '+b', '*!*@*');
3455 foreach my $user (@$users) {
3456 next if adminserv
::is_ircop
($user) or adminserv
::is_svsop
($user, adminserv
::S_HELP
());
3457 ircd
::kick
($agent, $cn, $user, $reason);
3461 sub clear_users
($$) {
3462 my ($chan, $reason) = @_;
3463 my $cn = $chan->{CHAN
};
3464 my $agent = agent
($chan);
3467 enforcer_join
($chan);
3468 ircd
::setmode
($agent, $cn, '+b', '*!*@*');
3470 $get_chan_users->execute($cn);
3471 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3472 my $user = { NICK
=> $nick, ID
=> $uid };
3473 ircd
::kick
($agent, $cn, $user, $reason)
3474 unless adminserv
::is_ircop
($user) or adminserv
::is_svsop
($user, adminserv
::S_HELP
());
3481 sub kickmask
($$$$) {
3482 my ($chan, $mask, $reason, $ban) = @_;
3483 my $cn = $chan->{CHAN
};
3484 my $agent = agent
($chan);
3486 my ($nick, $ident, $host) = glob2sql
(parse_mask
($mask));
3487 $nick = '%' if ($nick eq '');
3488 $ident = '%' if ($ident eq '');
3489 $host = '%' if ($host eq '');
3492 my $banmask = $nick.'!'.$ident.'@'.$host;
3493 $banmask =~ tr/%_/*?/;
3494 ircd
::setmode
($agent, $cn, '+b', $banmask);
3499 $get_chan_users_mask->execute($cn, $nick, $ident, $host, $host, $host);
3500 while(my ($nick, $uid) = $get_chan_users_mask->fetchrow_array) {
3501 my $user = { NICK
=> $nick, ID
=> $uid };
3502 ircd
::kick
($agent, $cn, $user, $reason)
3503 unless adminserv
::is_service
($user);
3506 $get_chan_users_mask->finish();
3511 sub kickmask_noacc
($$$$) {
3512 my ($chan, $mask, $reason, $ban) = @_;
3513 my $cn = $chan->{CHAN
};
3514 my $agent = agent
($chan);
3516 my ($nick, $ident, $host) = glob2sql
(parse_mask
($mask));
3517 $nick = '%' if ($nick eq '');
3518 $ident = '%' if ($ident eq '');
3519 $host = '%' if ($host eq '');
3522 my $banmask = $nick.'!'.$ident.'@'.$host;
3523 $banmask =~ tr/%_/*?/;
3524 ircd
::setmode
($agent, $cn, '+b', $banmask);
3529 $get_chan_users_mask_noacc->execute($cn, $nick, $ident, $host, $host, $host);
3530 while(my ($nick, $uid) = $get_chan_users_mask_noacc->fetchrow_array) {
3531 my $user = { NICK
=> $nick, ID
=> $uid };
3532 ircd
::kick
($agent, $cn, $user, $reason)
3533 unless adminserv
::is_service
($user);
3536 $get_chan_users_mask_noacc->finish();
3543 my $cn = $chan->{CHAN
};
3545 my $agent = agent
($chan);
3547 $get_chan_users->execute($cn);
3548 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3549 my $user = { NICK
=> $nick };
3550 get_user_id
($user);
3551 my $opmodes = get_op
($user, $chan);
3552 print "OPMODES $opmodes\n";
3553 for(my $i; $i < 5; $i++) {
3554 if($opmodes & 2**$i) {
3555 push @modelist, ['-'.$opmodes[$i], $user];
3560 ircd
::setmode2
($agent, $cn, @modelist);
3563 sub clear_bans
($;$) {
3564 my ($chan, $type) = @_;
3565 my $cn = $chan->{CHAN
};
3567 my $agent = agent
($chan);
3568 $type = 0 unless defined $type;
3569 my $mode = ($type == 128 ? 'e' : 'b');
3572 $get_all_bans->execute($cn, $type);
3573 while(my ($mask) = $get_all_bans->fetchrow_array) {
3574 $mask =~ tr/\%\_/\*\?/;
3575 push @banlist, $mask;
3578 ircd
::ban_list
($agent, $cn, -1, $mode, @banlist);
3582 sub unban_user
($@) {
3583 my ($chan, @userlist) = @_;
3584 my $cn = $chan->{CHAN
};
3586 if (defined(&ircd
::unban_users
)) {
3587 ircd
::unban_users
(@userlist);
3590 foreach my $tuser (@userlist) {
3592 unless($tuid = get_user_id
($tuser)) {
3596 # We don't handle extended bans. Yet.
3597 $find_bans_chan_user->execute($cn, $tuid, 0);
3598 while (my ($mask) = $find_bans_chan_user->fetchrow_array) {
3599 $mask =~ tr/\%\_/\*\?/;
3602 $find_bans_chan_user->finish();
3604 ircd
::ban_list
(agent
($chan), $cn, -1, 'b', @bans) if scalar(@bans);
3605 $delete_bans_chan_user->execute($cn, $tuid, 0); $delete_bans_chan_user->finish();
3611 sub chan_kill
($$;$) {
3612 my ($chan, $reason, $tusers) = @_;
3613 my $cn = $chan->{CHAN
};
3614 my $agent = agent
($chan);
3617 enforcer_join
($chan);
3619 foreach my $tuser (@$tusers) {
3620 $tuser->{ID
} = $tuser->{__ID
} if defined($tuser->{__ID
}); # user_join_multi does this.
3621 nickserv
::kline_user
($tuser, services_conf_chankilltime
, $reason)
3622 unless adminserv
::is_ircop
($tuser) or adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3627 $get_chan_users->execute($cn);
3628 while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
3629 my $tuser = { NICK
=> $nick, ID
=> $uid, AGENT
=> $agent };
3630 nickserv
::kline_user
($tuser, services_conf_chankilltime
, $reason)
3631 unless adminserv
::is_ircop
($tuser) or adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3639 sub do_nick_akick
($$;$) {
3640 my ($tuser, $chan, $root) = @_;
3641 my $cn = $chan->{CHAN
};
3642 unless(defined($root)) {
3643 (undef, $root) = get_best_acc
($tuser, $chan, 2);
3645 $get_nick_akick->execute($cn, $root);
3646 my ($reason) = $get_nick_akick->fetchrow_array(); $get_nick_akick->finish();
3647 return 0 if adminserv
::is_svsop
($tuser, adminserv
::S_HELP
());
3648 if(defined($reason) && $reason =~ /\|/) {
3649 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3651 kickban
($chan, $tuser, undef, "User has been banned from ".$cn.($reason?": $reason":''));
3654 sub check_akick
($$;$) {
3655 my ($user, $chan, $check_only) = @_;
3656 if(adminserv
::is_svsop
($user, adminserv
::S_HELP
())) {
3659 my ($acc, $root) = get_best_acc
($user, $chan, 2);
3661 do_nick_akick
($user, $chan, $root) unless $check_only;
3664 my $cn = $chan->{CHAN
};
3665 my $uid = get_user_id
($user);
3667 $get_akick->execute($uid, $cn);
3668 if(my @akick = $get_akick->fetchrow_array) {
3669 akickban
($cn, @akick) unless $check_only;
3676 sub do_status
($$;$) {
3677 my ($user, $chan, $check_only) = @_;
3679 return 0 if cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
));
3681 my $nick = get_user_nick
($user);
3683 if(check_akick
($user, $chan, $check_only)) {
3686 my ($acc, $root) = get_best_acc
($user, $chan, 2);
3687 if(!can_do
($chan, 'JOIN', $user, { ACC
=> $acc, NOREPLY
=> 1 })) {
3688 if (!is_agent
($user->{NICK
})) {
3689 kickban
($chan, $user, undef, 'This is a private channel.')
3695 if( !$check_only && is_registered
($chan) &&
3696 !cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
)) )
3698 my $neverop = (is_neverop_user
($user) || cr_chk_flag
($chan, CRF_NEVEROP
, 1));
3699 my $no_deop = cr_chk_flag
($chan, CRF_SPLITOPS
, 0);
3701 if($neverop && cr_chk_flag
($chan, CRF_AUTOVOICE
, 1) && $acc > 2) {
3706 set_modes
($user, $chan, $acc,
3708 # this probably needs to be configurable for ports
3709 # also Unreal may [optionally] set +q on join.
3711 !$neverop || $op_anyway,
3718 sub akick_alluser
($) {
3720 my $uid = get_user_id
($user);
3722 $get_akick_alluser->execute($uid);
3723 while(my @akick = $get_akick_alluser->fetchrow_array) {
3728 sub akick_allchan
($) {
3730 my $cn = $chan->{CHAN
};
3732 $get_akick_allchan->execute($cn);
3733 while(my @akick = $get_akick_allchan->fetchrow_array) {
3734 akickban
($cn, @akick);
3739 my ($cn, $knick, $bnick, $ident, $host, $reason, $bident) = @_;
3740 my $target = { NICK
=> $knick };
3741 my $chan = { CHAN
=> $cn };
3742 return 0 if adminserv
::is_svsop
($target, adminserv
::S_HELP
());
3745 ($bnick, $ident, $host) = make_hostmask
(get_bantype
($chan), $knick, $bident, $host);
3746 } elsif($host =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
3747 ($bnick, $ident, $host) = make_hostmask
(4, $knick, $bident, $host);
3749 $bnick =~ tr/\%\_/\*\?/;
3750 $ident =~ tr/\%\_/\*\?/;
3751 $host =~ tr/\%\_/\*\?/;
3753 if(defined($reason) && $reason =~ /\|/) {
3754 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3757 if(defined($reason) && $reason =~ /\|/) {
3758 ($reason, undef) = split(/ ?\| ?/, $reason, 2);
3761 return kickban
($chan, $target, "$bnick!$ident\@$host", "User has been banned from ".$cn.($reason?": $reason":''));
3764 sub notice_all_nicks
($$$) {
3765 my ($user, $nick, $msg) = @_;
3766 my $src = get_user_nick
($user);
3768 notice
($user, $msg);
3769 foreach my $u (get_nick_user_nicks
$nick) {
3770 notice
({ NICK
=> $u, AGENT
=> $csUser }, $msg) unless lc $src eq lc $u;
3778 if($name =~ /^uop$/i) { $level=1; }
3779 elsif($name =~ /^vop$/i) { $level=2; }
3780 elsif($name =~ /^hop$/i) { $level=3; }
3781 elsif($name =~ /^aop$/i) { $level=4; }
3782 elsif($name =~ /^sop$/i) { $level=5; }
3783 elsif($name =~ /^co?f(ounder)?$/i) { $level=6; }
3784 elsif($name =~ /^founder$/i) { $level=7; }
3785 elsif($name =~ /^(any|all|user)/i) { $level=0; }
3786 elsif($name =~ /^akick$/i) { $level=-1; }
3787 elsif($name =~ /^(none|disabled?|nobody)$/i) { $level=8; }
3793 return if services_conf_noexpire
;
3795 $get_expired->execute(time() - (86400 * services_conf_chanexpire
));
3796 while(my ($cn, $founder) = $get_expired->fetchrow_array) {
3797 drop
({ CHAN
=> $cn });
3798 wlog
($csnick, LOG_INFO
(), "\002$cn\002 has expired. Founder: $founder");
3802 sub enforcer_join
($) {
3804 my $cn = $chan->{CHAN
};
3805 my $bot = agent
($chan);
3807 return if $enforcers{lc $cn};
3808 $enforcers{lc $cn} = lc $bot;
3810 botserv
::bot_join
($chan);
3812 add_timer
("CSEnforce $bot $cn", 60, __PACKAGE__
, 'chanserv::enforcer_part');
3815 sub enforcer_part
($) {
3817 my ($junk, $bot, $cn) = split(/ /, $cookie);
3819 return unless $enforcers{lc $cn};
3820 undef($enforcers{lc $cn});
3822 botserv
::bot_part_if_needed
($bot, {CHAN
=> $cn}, 'Enforcer Leaving');
3825 sub fix_private_join_before_id
($) {
3828 my @cns = get_recent_private_chans
(get_user_id
($user));
3829 foreach my $cn (@cns) {
3830 my $chan = { CHAN
=> $cn };
3831 unban_user
($chan, $user);
3834 ircd
::svsjoin
($csUser, $user, @cns) if @cns;
3837 ### DATABASE UTILITY FUNCTIONS ###
3839 sub get_user_count
($) {
3841 my $cn = $chan->{CHAN
};
3843 $get_user_count->execute($cn);
3845 return $get_user_count->fetchrow_array;
3856 if($cur_lock ne $chan) {
3857 really_release_lock
($chan);
3859 die("Tried to get two locks at the same time: $cur_lock, $chan")
3864 $get_lock->execute(sql_conf_mysql_db
.".chan.$chan");
3869 sub release_lock
($) {
3876 if($cur_lock and $cur_lock ne $chan) {
3877 really_release_lock
($cur_lock);
3879 die("Tried to release the wrong lock");
3885 really_release_lock
($chan);
3889 sub really_release_lock
($) {
3893 $release_lock->execute(sql_conf_mysql_db
.".chan.$chan");
3894 $release_lock->finish;
3898 #sub is_free_lock($) {
3899 # $is_free_lock->execute($_[0]);
3900 # return $is_free_lock->fetchrow_array;
3903 sub get_modelock
($) {
3912 $get_modelock->execute($cn);
3913 my ($ml) = $get_modelock->fetchrow_array;
3914 $get_modelock->finish();
3918 sub do_modelock
($;$) {
3919 my ($chan, $modes) = @_;
3920 my $cn = $chan->{CHAN
};
3924 $get_modelock_lock->execute; $get_modelock_lock->finish;
3926 $get_chanmodes->execute($cn);
3927 my ($omodes) = $get_chanmodes->fetchrow_array;
3928 my $ml = get_modelock
($chan);
3930 $ml = do_modelock_fast
($cn, $modes, $omodes, $ml);
3932 $unlock_tables->execute; $unlock_tables->finish;
3934 ircd
::setmode
(agent
($chan), $cn, $ml) if($ml);
3937 sub do_modelock_fast
($$$$) {
3938 my ($cn, $modes, $omodes, $ml) = @_;
3939 my $nmodes = modes
::add
($omodes, $modes, 1);
3940 $ml = modes
::diff
($nmodes, $ml, 1);
3941 $set_chanmodes->execute(modes
::add
($nmodes, $ml, 1), $cn);
3946 sub update_modes
($$) {
3947 my ($cn, $modes) = @_;
3949 $get_update_modes_lock->execute; $get_update_modes_lock->finish;
3950 $get_chanmodes->execute($cn);
3951 my ($omodes) = $get_chanmodes->fetchrow_array;
3953 $set_chanmodes->execute(modes
::add
($omodes, $modes, 1), $cn);
3954 $unlock_tables->execute; $unlock_tables->finish;
3960 $is_level->execute($perm);
3962 return $is_level->fetchrow_array;
3966 return nr_chk_flag
($_[0], NRF_NEVEROP
(), 1);
3969 sub is_neverop_user
($) {
3970 return nr_chk_flag_user
($_[0], NRF_NEVEROP
(), 1);
3973 sub is_in_chan
($$) {
3974 my ($user, $chan) = @_;
3975 my $cn = $chan->{CHAN
};
3976 my $uid = get_user_id
($user);
3978 $is_in_chan->execute($uid, $cn);
3979 if($is_in_chan->fetchrow_array) {
3986 sub is_registered
($) {
3988 my $cn = $chan->{CHAN
};
3990 $is_registered->execute($cn);
3991 if($is_registered->fetchrow_array) {
3998 sub get_user_chans
($) {
4000 my $uid = get_user_id
($user);
4003 $get_user_chans->execute($uid, $ircline, $ircline+1000);
4004 while(my ($chan) = $get_user_chans->fetchrow_array) {
4011 sub get_user_chans_recent
($) {
4013 my $uid = get_user_id
($user);
4014 my (@curchans, @oldchans);
4016 $get_user_chans_recent->execute($uid);
4017 while(my ($cn, $joined, $op) = $get_user_chans_recent->fetchrow_array) {
4019 push @curchans, make_op_prefix
($op).$cn;
4022 push @oldchans, $cn;
4026 return (\
@curchans, \
@oldchans);
4029 my ($prefixes, $modes);
4030 sub make_op_prefix
($) {
4034 unless(defined($prefixes) and defined($modes)) {
4035 $IRCd_capabilities{PREFIX
} =~ /^\((\S+)\)(\S+)$/;
4036 ($modes, $prefixes) = ($1, $2);
4037 $modes = reverse $modes;
4038 $prefixes = reverse $prefixes;
4042 for(my $i = 0; $i < length($prefixes); $i++) {
4043 $op_prefix = substr($prefixes, $i, 1).$op_prefix if ($op & (2**$i));
4049 my ($user, $chan) = @_;
4050 my $cn = $chan->{CHAN
};
4051 my $uid = get_user_id
($user);
4053 $get_op->execute($uid, $cn);
4054 my ($op) = $get_op->fetchrow_array;
4059 sub get_best_acc
($$;$) {
4060 my ($user, $chan, $retnick) = @_;
4061 my $uid = get_user_id
($user);
4062 my $cn = $chan->{CHAN
};
4064 $get_best_acc->execute($uid, $cn);
4065 my ($bnick, $best) = $get_best_acc->fetchrow_array;
4066 $get_best_acc->finish();
4069 return ($best, $bnick);
4070 } elsif($retnick == 1) {
4078 my ($nick, $chan) = @_;
4079 my $cn = $chan->{CHAN
};
4082 if cr_chk_flag
($chan, (CRF_DRONE
| CRF_CLOSE
| CRF_FREEZE
), 1);
4084 $get_acc->execute($cn, $nick);
4085 my ($acc) = $get_acc->fetchrow_array;
4091 my ($nick, $user, $chan, $level) = @_;
4092 my $cn = $chan->{CHAN
};
4094 $adder = get_best_acc
($user, $chan, 1) if $user;
4096 $set_acc1->execute($cn, $level, $nick);
4097 $set_acc2->execute($level, $adder, $cn, $nick);
4099 if ( ( $level > 0 and !is_neverop
($nick) and !cr_chk_flag
($chan, CRF_NEVEROP
) )
4102 set_modes_allnick
($nick, $chan, $level);
4107 my ($nick, $chan) = @_;
4108 my $cn = $chan->{CHAN
};
4110 $del_acc->execute($cn, $nick);
4112 foreach my $user (get_nick_users
$nick) {
4113 set_modes
($user, $chan, 0, 1) if is_in_chan
($user, $chan);
4117 sub get_auth_nick
($$) {
4118 my ($cn, $nick) = @_;
4120 $get_auth_nick->execute($cn, $nick);
4121 my ($data) = $get_auth_nick->fetchrow_array();
4122 $get_auth_nick->finish();
4124 return split(/:/, $data);
4126 sub get_auth_num
($$) {
4127 my ($cn, $num) = @_;
4129 $get_auth_num->execute($cn, $num - 1);
4130 my ($nick, $data) = $get_auth_num->fetchrow_array();
4131 $get_auth_num->finish();
4133 return ($nick, split(/:/, $data));
4136 my ($cn, $nick) = @_;
4138 $find_auth->execute($cn, $nick);
4139 my ($ret) = $find_auth->fetchrow_array();
4140 $find_auth->finish();
4145 # Only call this if you've checked the user for NEVEROP already.
4146 sub set_modes_allchan
($;$) {
4147 my ($user, $neverop) = @_;
4148 my $uid = get_user_id
($user);
4150 $get_user_chans->execute($uid, $ircline, $ircline+1000);
4151 while(my ($cn) = $get_user_chans->fetchrow_array) {
4152 my $chan = { CHAN
=> $cn };
4153 my $acc = get_best_acc
($user, $chan);
4155 set_modes
($user, $chan, $acc) unless ($neverop or cr_chk_flag
($chan, CRF_NEVEROP
));
4157 do_nick_akick
($user, $chan);
4162 # Only call this if you've checked for NEVEROP already.
4163 sub set_modes_allnick
($$$) {
4164 my ($nick, $chan, $level) = @_;
4165 my $cn = $chan->{CHAN
};
4167 $get_using_nick_chans->execute($nick, $cn);
4168 while(my ($n) = $get_using_nick_chans->fetchrow_array) {
4169 my $user = { NICK
=> $n };
4170 my $l = get_best_acc
($user, $chan);
4172 set_modes
($user, $chan, $level, 1) if($level == $l);
4174 do_nick_akick
($user, $chan);
4179 # If channel has OPGUARD, $doneg is true.
4180 sub set_modes
($$$;$$) {
4181 my ($user, $chan, $acc, $doneg, $dopos) = @_;
4183 $dopos = 1 unless defined($dopos);
4184 $doneg = 0 unless defined($doneg);
4185 my $cn = $chan->{CHAN
};
4188 # Do akick stuff here.
4191 my $dst = ( $acc > 0 ? $ops[$acc] : 0 );
4192 my $cur = get_op
($user, $chan);
4195 if (cr_chk_flag
($chan, CRF_FREEZE
)) {
4196 set_mode_mask
($user, $chan, $cur, undef);
4199 if (($acc == 0) and cr_chk_flag
($chan, CRF_AUTOVOICE
)) {
4200 set_mode_mask
($user, $chan, $cur, 1);
4204 $pos = $dst ^ ($dst & $cur);
4205 $neg = ($dst ^ $cur) & $cur if $doneg;
4208 set_mode_mask
($user, $chan, ($doneg ? $neg : '-'), ($dopos ? $pos : '+'));
4213 set_lastused
($cn, get_user_id
($user));
4217 sub unset_modes
($$) {
4218 my ($user, $chan) = @_;
4220 my $mask = get_op
($user, $chan);
4222 set_mode_mask
($user, $chan, $mask, 0);
4225 sub set_mode_mask
($$$$) {
4226 my ($user, $chan, @masks) = @_;
4227 my $nick = get_user_nick
($user);
4228 my $cn = $chan->{CHAN
};
4231 for(my $sign; $sign < 2; $sign++) {
4232 next if($masks[$sign] == 0);
4234 $out .= '-' if $sign == 0;
4235 $out .= '+' if $sign == 1;
4237 for(my $i; $i < 5; $i++) {
4238 my @l = ('v', 'h', 'o', 'a', 'q');
4239 if ($IRCd_capabilities{"FOUNDER"} eq "") {
4242 if ($IRCd_capabilities{"ADMIN"} eq "") {
4245 if($masks[$sign] & 2**$i) {
4247 my $user_ = { NICK
=> $nick, AGENT
=> $csnick};
4248 get_user_id
($user_);
4255 ircd
::setmode_many
(agent
($chan), $cn, $out, @args);
4260 my ($chan, $perm) = @_;
4261 my $cn = $chan->{CHAN
};
4263 $get_level->execute($cn, $perm);
4264 my ($level, $isnotnull) = $get_level->fetchrow_array;
4265 $get_level->finish();
4268 return ($level, $isnotnull);
4275 sub check_override
($$;$) {
4276 my ($user, $perm, $logMsg) = @_;
4279 #{OVERRIDE::$perm} produces funny package problems, so wrap it in double-quotes.
4280 if(exists($user->{"OVERRIDE::$perm"}) && (my $nick = $user->{"OVERRIDE::$perm"})) {
4281 if(defined($nick)) {
4282 if(services_conf_log_overrides
&& $logMsg) {
4283 my $src = get_user_nick
($user);
4284 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override $logMsg");
4286 return (wantarray ? ($nick, 1) : $nick);
4291 foreach my $o (@override) {
4292 my ($operRank, $permHashRef) = @$o;
4293 if($permHashRef->{$perm} and my $nick = adminserv
::can_do
($user, $operRank)) {
4294 $user->{"OVERRIDE::$perm"} = $nick;
4295 if(services_conf_log_overrides
&& $logMsg) {
4296 my $src = get_user_nick
($user);
4297 wlog
($csnick, LOG_INFO
(), "\002$src\002 used override $logMsg");
4299 return (wantarray ? ($nick, 1) : $nick);
4302 $user->{"OVERRIDE::$perm"} = undef;
4306 my ($chan, $perm, $user, $data) = @_;
4307 $data = {} unless defined $data;
4308 # $data is a hashref/struct
4309 my $noreply = $data->{NOREPLY
};
4310 my $acc = $data->{ACC
};
4311 my $overrideMsg = $data->{OVERRIDE_MSG
};
4313 if(my $nick = __can_do
($chan, $perm, $user, $acc)) {
4314 # This is becoming increasingly complicated
4315 # and checking if an override was used is becoming tricky.
4316 # We had a case in cs_kick where an oper should be able to override +Q/$peace
4317 # but cannot b/c they have regular access in that channel.
4319 if(defined($user)) {
4320 (undef, $override) = check_override
($user, $perm);
4322 return (wantarray ? ($nick, $override) : $nick);
4323 } elsif ( $user and adminserv
::is_svsop
($user, adminserv
::S_HELP
()) ) {
4324 #set_lastused($cn, get_user_id($user));
4325 my ($nick, $override) = check_override
($user, $perm, $overrideMsg);
4326 return (wantarray ? ($nick, $override) : $nick) if $override;
4328 if($user and !$noreply) {
4329 my $cn = $chan->{CHAN
};
4330 if (cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
4331 notice
($user, "\002$cn\002 is closed and cannot be used".
4332 ((uc $perm eq 'INFO') ? ': '.get_close
($chan) : '.'));
4334 elsif(cr_chk_flag
($chan, CRF_FREEZE
)) {
4335 notice
($user, "\002$cn\002 is frozen and access suspended.");
4338 notice
($user, "$cn: $err_deny");
4344 sub __can_do
($$$;$) {
4345 my ($chan, $perm, $user, $acc) = @_;
4347 my $cn = $chan->{CHAN
};
4351 unless(exists($chan->{"PERM::$perm"})) {
4352 $level = $chan->{"PERM::$perm"} = get_level
($chan, $perm);
4354 $level = $chan->{"PERM::$perm"};
4357 unless(defined($acc)) {
4358 unless (defined $user && ref($user) eq 'HASH') {
4359 die "invalid __can_do call";
4361 my $chanuser = $user->{lc $cn};
4362 unless (defined($chanuser) && exists($chanuser->{ACC
})) {
4363 ($acc, $nick) = get_best_acc
($user, $chan, 2);
4364 ($chanuser->{ACC
}, $chanuser->{ACCNICK
}) = ($acc, $nick);
4366 ($acc, $nick) = ($chanuser->{ACC
}, $chanuser->{ACCNICK
});
4369 $nick = 1 unless $nick;
4371 if($acc >= $level and !cr_chk_flag
($chan, (CRF_CLOSE
| CRF_FREEZE
| CRF_DRONE
))) {
4372 set_lastused
($cn, get_user_id
($user)) if $user;
4373 return (wantarray ? ($nick, 0) : $nick);
4376 if(cr_chk_flag
($chan, CRF_FREEZE
) and ($perm eq 'JOIN')) {
4377 return (wantarray ? ($nick, 0) : $nick);
4383 sub can_keep_op
($$$$) {
4384 # This is a naïve implemenation using a loop.
4385 # If we ever do a more flexible version that further restricts how
4386 # LEVELS affect opguard, the loop will have to be unrolled.
4388 # Only call this if you've already checked opguard, as we do not check it here.
4390 # Remember, this isn't a permission check if someone is allowed to op someone [else],
4391 # rather this checks if the person being opped is allowed to keep/have it.
4392 my ($user, $chan, $tuser, $opmode) = @_;
4393 return 1 if $opmode eq 'v'; # why remove a voice?
4395 'q' => ['OWNER', 4],
4396 'a' => ['ADMIN', 3],
4398 'h' => ['HALFOP', 1],
4402 my $self = (lc(get_user_nick
($user)) eq lc(get_user_nick
($tuser)));
4404 #my ($level, $isnotnull) = get_level($chan, $permhash{$opmode}[1]);
4405 my $level = get_level
($chan, $permhash{$opmode}[0]);
4407 foreach my $luser ($tuser, $user) {
4408 # We check target first, as there seems no reason that
4409 # someone who has access can't be opped by someone
4410 # who technically doesn't.
4411 return 1 if (adminserv
::is_svsop
($luser, adminserv
::S_HELP
()) and
4412 check_override
($luser, $permhash{$opmode}[0]));
4414 my $acc = get_best_acc
($luser, $chan);
4415 return 1 if ($self and ($permhash{opmode
}[2] + 2) <= $acc);
4428 if($chan->{AGENT
}) {
4429 my $a = $chan->{AGENT
};
4430 $a->{ID
} = ircd
::getAgentUuid
($a->{NICK
});
4434 unless(initial_synced
()) {
4437 $botserv::get_chan_bot-
>execute($chan->{CHAN
});
4438 my $agentnick = $botserv::get_chan_bot-
>fetchrow_array;
4439 my ($agent) = { NICK
=> $agentnick, ID
=> ircd
::getAgentUuid
($agentnick)};
4440 $agent = $csUser unless $agentnick;
4441 return $chan->{AGENT
} = $agent;
4446 my $cn = $chan->{CHAN
};
4448 undef($enforcers{lc $cn});
4449 my $agent = agent
($chan);
4450 agent_part
($agent, $cn, 'Channel dropped') unless (lc($agent) eq lc($csnick));
4451 if (module
::is_loaded
('logserv')) {
4452 eval { logserv
::delchan
(undef, $cn); }
4455 $drop_acc->execute($cn);
4456 $drop_lvl->execute($cn);
4457 $del_close->execute($cn);
4458 $drop_akick->execute($cn);
4459 $drop_welcome->execute($cn);
4460 $drop_chantext->execute($cn);
4461 $drop_nicktext->execute($cn); # Leftover channel auths
4462 $drop->execute($cn);
4463 ircd
::setmode
($csUser, $cn, '-r');
4466 sub drop_nick_chans
($) {
4469 $delete_successors->execute($nick);
4471 $get_nick_own_chans->execute($nick);
4472 while(my ($cn) = $get_nick_own_chans->fetchrow_array) {
4473 succeed_chan
($cn, $nick);
4477 sub succeed_chan
($$) {
4478 my ($cn, $nick) = @_;
4480 $get_successor->execute($cn);
4481 my ($suc) = $get_successor->fetchrow_array;
4484 $set_founder->execute($suc, $cn);
4485 set_acc
($suc, undef, {CHAN
=> $cn}, FOUNDER
);
4486 $del_successor->execute($cn);
4488 drop
({CHAN
=> $cn});
4489 wlog
($csnick, LOG_INFO
(), "\002$cn\002 has been dropped due to expiry/drop of \002$nick\002");
4495 my $cn = $chan->{CHAN
};
4496 return undef unless cr_chk_flag
($chan, CRF_CLOSE
| CRF_DRONE
);
4498 $get_close->execute($cn);
4499 my ($reason, $opnick, $time) = $get_close->fetchrow_array();
4500 $get_close->finish();
4502 $reason = "[$opnick ".gmtime2
($time)."] - $reason";
4504 return (wantarray ? ($reason, $opnick, $time) : $reason);
4507 sub get_users_nochans
(;$) {
4512 $get_users_nochans_noid->execute();
4513 while (my ($usernick, $userid) = $get_users_nochans_noid->fetchrow_array()) {
4514 push @users, { NICK
=> $usernick, ID
=> $userid };
4516 $get_users_nochans_noid->finish();
4519 $get_users_nochans->execute();
4520 while (my ($usernick, $userid) = $get_users_nochans->fetchrow_array()) {
4521 push @users, { NICK
=> $usernick, ID
=> $userid };
4523 $get_users_nochans->finish();
4529 sub get_bantype
($) {
4531 my $cn = $chan->{CHAN
};
4533 unless (exists($chan->{BANTYPE
})) {
4534 $get_bantype->execute($cn);
4535 ($chan->{BANTYPE
}) = $get_bantype->fetchrow_array();
4536 $get_bantype->finish();
4539 return $chan->{BANTYPE
};
4543 my ($chan, $log) = @_;
4545 my $level = get_level
($chan, "MemoAccChange");
4546 return if $level == 8; # 8 is 'disable'
4547 $level = 1 if $level == 0;
4548 memoserv
::send_chan_memo
($csnick, $chan, $log, $level);
4551 sub get_ban_num
($$) {
4552 my ($chan, $num) = @_;
4553 $get_ban_num->execute($chan->{CHAN
}, 0, $num-1);
4554 my ($mask) = $get_ban_num->fetchrow_array();
4555 $get_ban_num->finish();
4556 return sql2glob
($mask);
4562 # Due to special casing of '0' this wrapper should be used
4563 # by anyone handling a JOIN (not SJOIN, it's a JOIN) event.
4564 # This is an RFC1459 requirement.
4565 my ($user, $cn) = @_;
4566 my $chan = { CHAN
=> $cn };
4569 # This should be treated as a number
4570 # Just in case we ever got passed '000', not that Unreal does.
4571 # In C, you could check that chan[0] != '#' && chan[0] == '0'
4572 user_part_multi
($user, [ get_user_chans
($user) ], 'Left all channels');
4575 user_join_multi
($chan, [$user]);
4579 sub handle_sjoin
($$$$$$$) {
4580 my ($server, $cn, $ts, $chmodes, $chmodeparms, $userarray, $banarray, $exceptarray) = @_;
4581 my $chan = { CHAN
=> $cn };
4584 chan_mode
($server, $cn, $chmodes, $chmodeparms) if $chmodes;
4586 update_modes
($cn, "$chmodes $chmodeparms") if $chmodes;
4588 user_join_multi
($chan, $userarray) if scalar @$userarray;
4590 foreach my $ban (@$banarray) {
4591 process_ban
($cn, $ban, $server, 0, 1);
4593 foreach my $except (@$exceptarray) {
4594 process_ban
($cn, $except, $server, 128, 1);
4598 sub user_join_multi
($$) {
4599 my ($chan, $users) = @_;
4600 my $cn = $chan->{CHAN
};
4602 my $multi_tradeoff = 2; # could use some synthetic-benchmark tuning
4603 foreach my $user (@$users) {
4604 if ($user->{ID
} && !$user->{NICK
}) {
4605 get_user_nick
($user); # INSP
4607 $user->{__ID
} = get_user_id
($user);
4608 unless (defined($user->{__ID
})) {
4609 # This does happen occasionally. it's a BUG.
4610 # At least we have a diagnostic for it now.
4611 # Normally we'd just get a [useless] warning from the SQL server
4612 ircd
::debug
($user->{NICK
}.' has a NULL user->{__ID} in user_join_multi('.$cn.', ...');
4616 $get_joinpart_lock->execute; $get_joinpart_lock->finish;
4618 $chan_create->execute($seq, $cn);
4620 $get_user_count->execute($cn);
4621 my ($count) = $get_user_count->fetchrow_array;
4623 if(scalar(@$users) < $multi_tradeoff) {
4624 foreach my $user (@$users) {
4625 # see note above in get_user_id loop
4626 if (defined($user->{__ID
})) {
4627 $chanjoin->execute($seq, $user->{__ID
}, $cn, $user->{__OP
});
4632 my $query = "REPLACE INTO chanuser (seq, nickid, chan, op, joined) VALUES ";
4633 foreach my $user (@$users) {
4634 # a join(',', list) would be nice but would involve preparing the list first.
4635 # I think this will be faster.
4636 if (defined($user->{__ID
})) {
4637 # see note above in get_user_id loop
4638 $query .= '('.$dbh->quote($seq).','.
4639 $dbh->quote($user->{__ID
}).','.
4640 $dbh->quote($cn).','.
4641 $dbh->quote($user->{__OP
}).', 1),';
4648 $unlock_tables->execute; $unlock_tables->finish;
4650 my $bot = agent
($chan);
4651 foreach my $user (@$users) {
4652 $user->{AGENT
} = $bot;
4655 if(initial_synced
() and cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
))) {
4656 my ($reason, $opnick, $time) = get_close
($chan);
4657 my $cmsg = "$cn is closed: $reason";
4658 my $preenforce = $enforcers{lc $chan};
4659 if (cr_chk_flag
($chan, CRF_CLOSE
)) {
4660 kickban_multi
($chan, $users, $cmsg);
4662 elsif (cr_chk_flag
($chan, CRF_DRONE
)) {
4663 chan_kill
($chan, $cmsg, $users);
4666 unless($preenforce) {
4667 ircd
::settopic
($bot, $cn, $opnick, $time, $cmsg);
4669 my $ml = get_modelock
($chan);
4670 ircd
::setmode
($bot, $cn, $ml) if($ml);
4674 if(($count == 0 or !is_agent_in_chan
($bot, $cn)) and initial_synced
()) {
4675 unless ($bot eq $csUser) {
4676 unless(is_agent_in_chan
($bot, $cn)) {
4677 botserv
::bot_join
($chan);
4682 #return unless synced() and not cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
4683 return unless not cr_chk_flag
($chan, (CRF_CLOSE
| CRF_DRONE
));
4684 #commands aren't sent before synced() anyway
4686 foreach my $user (@$users) {
4687 if(do_status
($user, $chan)) {
4689 $user->{__DO_WELCOME
} = 1;
4693 if($count == 0 and $n) {
4694 my ($ml) = get_modelock
($chan);
4695 ircd
::setmode
($bot, $cn, $ml) if($ml);
4697 $get_topic->execute($cn);
4698 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4699 ircd
::settopic
($bot, $cn, $nsetter, $ntime, $ntopic) if $ntopic;
4705 foreach my $user (@$users) {
4706 if ($user->{__DO_WELCOME
} and chk_user_flag
($user, UF_FINISHED
())) {
4707 do_welcome
($user, $chan);
4708 do_greet
($user, $chan)
4709 if can_do
($chan, 'GREET', $user, { NOREPLY
=> 1 });
4715 sub user_part
($$$) {
4716 my ($nick, $cn, $reason) = @_;
4717 my $user = ( ref $nick eq 'HASH' ? $nick : { NICK
=> $nick });
4718 user_part_multi
($user, [ $cn ], $reason);
4721 sub user_part_multi
($$$) {
4722 # user_join_multi takes a channel and multiple users
4723 # user_part_multi takes a user and multiple channels
4724 # There should probably be a user_join_* that takes one user, multiple channels
4725 # However, it seems that so far, Unreal splits both PART and JOIN (non-SJOIN)
4726 # into multiple events/cmds. The reason is unclear.
4727 # Other ircds may not do so.
4728 # There is also KICK. some IRCds allow KICK #chan user1,user2,...
4729 # Unreal it's _supposed_ to work, but it does not.
4730 my ($user, $chanlist, $reason) = @_;
4732 foreach my $cn (@$chanlist) {
4733 push @chans, { CHAN
=> $cn };
4736 my $uid = get_user_id
($user);
4739 $get_joinpart_lock->execute; $get_joinpart_lock->finish;
4741 foreach my $chan (@chans) {
4742 my $cn = $chan->{CHAN
};
4743 $chanpart->execute($seq, $uid, $cn, $seq, $seq+1000);
4744 $get_user_count->execute($cn);
4745 $chan->{COUNT
} = $get_user_count->fetchrow_array;
4748 $unlock_tables->execute; $unlock_tables->finish;
4750 foreach my $chan (@chans) {
4751 channel_emptied
($chan) if $chan->{COUNT
} == 0;
4755 sub channel_emptied
($) {
4758 botserv
::bot_part_if_needed
(undef, $chan, 'Nobody\'s here', 1);
4759 $chan_delete->execute($chan->{CHAN
});
4760 $wipe_bans->execute($chan->{CHAN
});
4763 sub process_kick
($$$$) {
4764 my ($src, $cn, $target, $reason) = @_;
4765 my $tuser = { NICK
=> $target };
4766 get_user_id
($tuser);
4767 user_part
($tuser, $cn, 'Kicked by '.$src.' ('.$reason.')');
4769 my $chan = { CHAN
=> $cn };
4770 if ( !(is_agent
($src) or $src =~ /\./ or adminserv
::is_ircop
({ NICK
=> $src })) and
4771 ({modes
::splitmodes
(get_modelock
($chan))}->{Q
}->[0] eq '+') )
4773 my $srcUser = { NICK
=> $src };
4774 get_user_id
($srcUser);
4775 #ircd::irckill(agent($chan), $src, "War script detected (kicked $target past +Q in $cn)");
4776 nickserv
::kline_user
($srcUser, 300, "War script detected (kicked $target past +Q in $cn)");
4777 # SVSJOIN won't work while they're banned, unless you invite.
4778 ircd
::invite
(agent
($chan), $cn, $tuser);
4779 ircd
::svsjoin
(undef, $tuser, $cn);
4780 unban_user
($chan, $tuser);
4784 sub chan_mode
($$$$@) {
4785 my ($user, $cn, $modes, $args, @userargs) = @_;
4786 print "CHAN_MODE $user $cn $modes $args\n";
4788 if (ref($user) eq "HASH") {
4789 $src = $user->{NICK
};
4794 my $chan = { CHAN
=> $cn };
4797 # XXX This is not quite right, but maybe it's good enough.
4798 my $mysync = ($src =~ /\./ ? 0 : 1);
4800 if($modes !~ /^[beIvhoaq+-]+$/ and (!synced
() or $mysync)) {
4801 do_modelock
($chan, "$modes $args");
4804 my $opguard = (!current_message-
>{SYNC
} and cr_chk_flag
($chan, CRF_OPGUARD
, 1));
4806 my @perms = ('VOICE', 'HALFOP', 'OP', 'PROTECT');
4810 my @modes = split(//, $modes);
4811 my @args = split(/ /, $args);
4813 foreach my $mode (@modes) {
4814 if($mode eq '+') { $sign = 1; next; }
4815 if($mode eq '-') { $sign = 0; next; }
4817 my $arg = shift(@args) if($mode =~ $scm or $mode =~ $ocm);
4818 if($mode =~ /^[vhoaq]$/) {
4820 next if is_agent
($arg);
4821 my $auser = shift (@userargs);
4822 if (!defined($auser)) {
4823 if ($IRCd_capabilities{INSP
}) {
4824 $auser = {ID
=>$arg};
4825 get_user_nick
($auser);
4828 $auser = { NICK
=> $arg };
4829 get_user_id
($auser);
4832 $num = 0 if $mode eq 'v';
4833 $num = 1 if $mode eq 'h';
4834 $num = 2 if $mode eq 'o';
4835 $num = 3 if $mode eq 'a';
4836 $num = 4 if $mode eq 'q';
4837 if($opguard and $sign == 1 and
4838 !can_keep_op
($user, $chan, $auser, $mode)
4840 push @unargs, ["-" . $mode, $auser];
4843 $nid = get_user_id
($auser) or next;
4847 $r = $chop->execute((2**$num), (2**$num), $nid, $cn);
4849 $r = $chdeop->execute((2**$num), (2**$num), $nid, $cn);
4852 } while($r==0 and $i<10);
4857 process_ban
($cn, $arg, $src, 0, $sign);
4861 process_ban
($cn, $arg, $src, 128, $sign);
4864 next;# if $arg eq '';
4865 #process_ban($cn, $arg, $src, 128, $sign);
4868 ircd
::setmode2
(agent
($chan), $cn, @unargs) if($opguard and @unargs);
4871 sub process_ban
($$$$) {
4872 my ($cn, $arg, $src, $type, $sign) = @_;
4874 $arg =~ tr/\*\?/\%\_/;
4877 $add_ban->execute($cn, $arg, $src, $type);
4879 $delete_ban->execute($cn, $arg, $type);
4884 my ($src, $cn, $suser, $time, $topic) = @_;
4885 my $chan = { CHAN
=> $cn };
4886 $suser -> {AGENT
} = agent
($chan);
4887 my $setter = $suser -> {NICK
};
4888 return if cr_chk_flag
($chan, CRF_CLOSE
, 1);
4890 if(current_message-
>{SYNC
}) { # We don't need to undo our own topic changes.
4891 print "Line @{[__LINE__]}\n";
4892 $set_topic1->execute($setter, $time, $cn);
4893 $set_topic2->execute($cn, $topic);
4897 print "Line @{[__LINE__]}\n";
4898 $get_topic->execute($cn);
4899 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4900 if($topic ne '' and $time == $ntime or can_do
($chan, 'SETTOPIC', undef, { ACC
=> 0 })) {
4901 print "Line @{[__LINE__]}\n";
4902 $set_topic1->execute($setter, $time, $cn);
4903 $set_topic2->execute($cn, $topic);
4905 print "Line @{[__LINE__]}\n";
4906 ircd
::settopic
(agent
($chan), $cn, $nsetter, $ntime, $ntopic);
4909 #lc($src) ne lc($setter) or - removed from previous line because
4910 #i think it was breaking it for insp & idk what it did
4913 # as explained on IRC, the intent is to determine whether it's being set due to a
4914 # net.unsplit, or b/c a user is doing it. You can probably do something more useful
4915 # by paying attn to timestamp
4917 elsif(can_do
($chan, 'SETTOPIC', $suser)) {
4918 print "Line @{[__LINE__]}\n",
4919 "condition : ", can_do
($chan, 'SETTOPIC', $suser),$/;
4920 $set_topic1->execute($setter, $time, $cn);
4921 $set_topic2->execute($cn, $topic);
4923 print "Line @{[__LINE__]}\n";
4924 $get_topic->execute($cn);
4925 my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
4926 ircd
::settopic
(agent
($chan), $cn, $nsetter, $ntime, $ntopic);
4934 $get_all_closed_chans->execute(CRF_DRONE
|CRF_CLOSE
);
4935 while(my ($cn, $type, $reason, $opnick, $time) = $get_all_closed_chans->fetchrow_array) {
4936 my $chan = { CHAN
=> $cn };
4938 my $cmsg = " is closed [$opnick ".gmtime2
($time)."]: $reason";
4939 if($type == CRF_DRONE
) {
4940 chan_kill
($chan, $cn.$cmsg);
4942 my $user = { NICK
=> $opnick};
4943 get_user_id
($user);
4944 ircd
::settopic
(agent
($chan), $cn, $opnick, $time, "Channel".$cmsg);
4945 clear_users
($chan, "Channel".$cmsg);
4949 while($chanuser_table > 0) { }
4951 $get_eos_lock->execute(); $get_eos_lock->finish;
4952 $get_akick_all->execute();
4954 $get_status_all_server->execute($server);
4955 $gsa = $get_status_all_server;
4957 $get_status_all->execute();
4958 $gsa = $get_status_all;
4960 #$unlock_tables->execute(); $unlock_tables->finish;
4962 while(my @akick = $get_akick_all->fetchrow_array) {
4966 $get_modelock_all->execute();
4967 while(my ($cn, $modes, $ml) = $get_modelock_all->fetchrow_array) {
4968 $ml = do_modelock_fast
($cn, '', $modes, $ml);
4969 ircd
::setmode
(agent
({CHAN
=>$cn}), $cn, $ml) if $ml;
4972 while(my ($cn, $cflags, $agent, $nick, $uid, $uflags, $level, $op, $neverop) = $gsa->fetchrow_array) {
4973 my $user = { NICK
=> $nick, ID
=> $uid };
4974 #next if chk_user_flag($user, UF_FINISHED);
4975 $agent = $csnick unless $agent;
4976 my $chan = { CHAN
=> $cn, FLAGS
=> $cflags, AGENT
=> $agent };
4978 set_modes
($user, $chan, $level, ($cflags & CRF_OPGUARD
)) if not $neverop and $ops[$level] != $op and not $cflags & (CRF_FREEZE
| CRF_CLOSE
| CRF_DRONE
);
4979 do_welcome
($user, $chan);
4982 set_user_flag_all
(UF_FINISHED
());
4983 $unlock_tables->execute(); $unlock_tables->finish;