Advertisement
Guest User

Untitled

a guest
May 30th, 2017
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 300.52 KB | None | 0 0
  1. 1,4883c1,4894
  2. < # This file is part of SurrealServices.
  3. < #
  4. < # SurrealServices is free software; you can redistribute it and/or modify
  5. < # it under the terms of the GNU General Public License as published by
  6. < # the Free Software Foundation; either version 2 of the License, or
  7. < # (at your option) any later version.
  8. < #
  9. < # SurrealServices is distributed in the hope that it will be useful,
  10. < # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. < # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. < # GNU General Public License for more details.
  13. < #
  14. < # You should have received a copy of the GNU General Public License
  15. < # along with SurrealServices; if not, write to the Free Software
  16. < # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. < package chanserv;
  18. <
  19. < use strict;
  20. <
  21. < use SrSv::Timer qw(add_timer);
  22. <
  23. < use SrSv::Message qw(current_message);
  24. < use SrSv::IRCd::State qw($ircline synced initial_synced %IRCd_capabilities);
  25. < use SrSv::Message qw(message current_message);
  26. < use SrSv::HostMask qw(normalize_hostmask make_hostmask parse_mask);
  27. <
  28. < #FIXME: This needs to be abstracted into a proper SrSv::IRCd module
  29. < use SrSv::Unreal::Modes qw(@opmodes %opmodes $scm $ocm $acm sanitize_mlockable);
  30. < use SrSv::IRCd::Validate qw( valid_nick validate_chmodes validate_ban );
  31. < use SrSv::Agent;
  32. <
  33. < use SrSv::Shared qw(%enforcers $chanuser_table);
  34. <
  35. < #use SrSv::Conf qw(services);
  36. < use SrSv::Conf2Consts qw( services sql );
  37. <
  38. < use SrSv::Time;
  39. < use SrSv::Text::Format qw( columnar enum );
  40. < use SrSv::Errors;
  41. <
  42. < use SrSv::Log;
  43. <
  44. < use SrSv::User qw(
  45. < get_user_nick get_user_agent get_user_id
  46. < is_online :user_flags get_host get_vhost
  47. < :flags :flood
  48. < );
  49. < use SrSv::User::Notice;
  50. < use SrSv::Help qw( sendhelp );
  51. <
  52. < use SrSv::ChanReg::Flags;
  53. <
  54. < use SrSv::NickReg::Flags;
  55. < use SrSv::NickReg::NickText;
  56. < use SrSv::NickReg::User qw(is_identified get_nick_users get_nick_user_nicks);
  57. <
  58. < use SrSv::MySQL qw( $dbh :sql_types );
  59. < use SrSv::MySQL::Glob;
  60. <
  61. < use SrSv::Util qw( makeSeqList );
  62. <
  63. < use constant {
  64. < UOP => 1,
  65. < VOP => 2,
  66. < HOP => 3,
  67. < AOP => 4,
  68. < SOP => 5,
  69. < COFOUNDER => 6,
  70. < FOUNDER => 7,
  71. <
  72. < # Maybe this should be a config option
  73. < DEFAULT_BANTYPE => 10,
  74. <
  75. < CRT_TOPIC => 1,
  76. < CRT_AKICK => 2,
  77. < };
  78. <
  79. < *get_root_nick = \&nickserv::get_root_nick;
  80. <
  81. < our @levels = ("no", "UOp", "VOp", "HOp", "AOp", "SOp", "co-founder", "founder");
  82. < our @ops;
  83. < if(!ircd::PREFIXAQ_DISABLE()) {
  84. < @ops = (0, 0, 1, 2, 4, 8, 16, 16); # PREFIX_AQ
  85. < } else { # lame IRC scripts and admins who don't enable PREFIX_AQ
  86. < @ops = (0, 0, 1, 2, 4, 12, 20, 20); # normal
  87. < }
  88. < our @plevels = ('AKICK', 'anyone', 'UOp', 'VOp', 'HOp', 'AOp', 'SOp', 'co-founder', 'founder', 'disabled');
  89. < our $plzero = 1;
  90. <
  91. < our @override = (
  92. < ['SERVOP',
  93. < {
  94. < ACCCHANGE => 1,
  95. < SET => 1,
  96. < MEMO => 1,
  97. < SETTOPIC => 1,
  98. < AKICK => 1,
  99. < LEVELS => 1,
  100. < COPY => 1,
  101. < }
  102. < ],
  103. < ['SUPER',
  104. < {
  105. < BAN => 1,
  106. < UNBANSELF => 1,
  107. < UNBAN => 1,
  108. < KICK => 1,
  109. < VOICE => 1,
  110. < HALFOP => 1,
  111. < OP => 1,
  112. < ADMIN => 1,
  113. < OWNER => 1,
  114. < SETTOPIC => 1,
  115. < INVITE => 1,
  116. < INVITESELF => 1,
  117. < CLEAR => 1,
  118. < AKICKENFORCE => 1,
  119. < UPDOWN => 1,
  120. < MODE => 1,
  121. < }
  122. < ],
  123. < ['HELP',
  124. < {
  125. < JOIN => 1,
  126. < ACCLIST => 1,
  127. < LEVELSLIST => 1,
  128. < AKICKLIST => 1,
  129. < INFO => 1,
  130. < GETKEY => 1
  131. < }
  132. < ],
  133. < ['BOT',
  134. < {
  135. < BOTSAY => 1,
  136. < BOTASSIGN => 1
  137. < }
  138. < ]
  139. < );
  140. <
  141. < $chanuser_table = 0;
  142. <
  143. < our $csnick_default = 'ChanServ';
  144. < our $csnick = $csnick_default;
  145. <
  146. < our ($cur_lock, $cnt_lock);
  147. <
  148. < our (
  149. < $get_joinpart_lock, $get_modelock_lock, $get_update_modes_lock,
  150. <
  151. < $chanjoin, $chanpart, $chop, $chdeop, $get_op, $get_user_chans, $get_user_chans_recent,
  152. < $get_all_closed_chans, $get_user_count,
  153. <
  154. < $is_in_chan,
  155. <
  156. < #$lock_chanuser, $get_all_chan_users,
  157. < $unlock_tables,
  158. < $get_chan_users, $get_chan_users_noacc, $get_chan_users_mask, $get_chan_users_mask_noacc,
  159. <
  160. < $get_users_nochans, $get_users_nochans_noid,
  161. <
  162. < $get_using_nick_chans,
  163. <
  164. < $get_lock, $release_lock, $is_free_lock,
  165. <
  166. < $chan_create, $chan_delete, $get_chanmodes, $set_chanmodes,
  167. <
  168. < $is_registered, $get_modelock, $set_modelock, $set_descrip,
  169. <
  170. < $get_topic, $set_topic1, $set_topic2,
  171. <
  172. < $get_acc, $set_acc1, $set_acc2, $del_acc, $get_acc_list, $get_acc_list2, $get_acc_list_mask, $get_acc_list2_mask,
  173. < $wipe_acc_list,
  174. < $get_best_acc, $get_all_acc, $get_highrank, $get_acc_count,
  175. < $copy_acc, $copy_acc_rank,
  176. <
  177. < $get_eos_lock, $get_status_all, $get_status_all_server, $get_modelock_all,
  178. <
  179. < $get_akick, $get_akick_allchan, $get_akick_alluser, $get_akick_all, $add_akick, $del_akick,
  180. < $get_akick_list, $get_akick_by_num,
  181. <
  182. < $add_nick_akick, $del_nick_akick, $get_nick_akick, $drop_nick_akick,
  183. < $copy_akick,
  184. <
  185. < $is_level, $get_level, $get_levels, $add_level, $set_level, $reset_level, $clear_levels, $get_level_max,
  186. < $copy_levels,
  187. <
  188. < $get_founder, $get_successor,
  189. < $set_founder, $set_successor, $del_successor,
  190. <
  191. < $get_nick_own_chans, $delete_successors,
  192. <
  193. < $get_info,
  194. <
  195. < $register, $drop_acc, $drop_lvl, $drop_akick, $drop,
  196. < $copy_chanreg,
  197. <
  198. < $get_expired,
  199. <
  200. < $get_close, $set_close, $del_close,
  201. <
  202. < $add_welcome, $del_welcome, $list_welcome, $get_welcomes, $drop_welcome,
  203. < $count_welcome, $consolidate_welcome,
  204. <
  205. < $add_ban, $delete_bans, $delete_ban,
  206. < $get_all_bans, $get_ban_num,
  207. < $find_bans, $list_bans, $wipe_bans,
  208. < $find_bans_chan_user, $delete_bans_chan_user,
  209. <
  210. < $add_auth, $list_auth_chan, $get_auth_nick, $get_auth_num, $find_auth,
  211. <
  212. < $set_bantype, $get_bantype,
  213. <
  214. < $drop_chantext, $drop_nicktext,
  215. < );
  216. <
  217. < sub init() {
  218. < #$chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET id=(RAND()*294967293)+1, chan=?");
  219. < $get_joinpart_lock = $dbh->prepare("LOCK TABLES chan WRITE, chanuser WRITE");
  220. < $get_modelock_lock = $dbh->prepare("LOCK TABLES chanreg READ LOCAL, chan WRITE");
  221. < $get_update_modes_lock = $dbh->prepare("LOCK TABLES chan WRITE");
  222. <
  223. < $chanjoin = $dbh->prepare("REPLACE INTO chanuser (seq,nickid,chan,op,joined) VALUES (?, ?, ?, ?, 1)");
  224. < $chanpart = $dbh->prepare("UPDATE chanuser SET joined=0, seq=?
  225. < WHERE nickid=? AND chan=? AND (seq <= ? OR seq > ?)");
  226. < #$chop = $dbh->prepare("UPDATE chanuser SET op=op+? WHERE nickid=? AND chan=?");
  227. < $chop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op, op ^ ?) WHERE nickid=? AND chan=?");
  228. < $chdeop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op ^ ?, op) WHERE nickid=? AND chan=?");
  229. < $get_op = $dbh->prepare("SELECT op FROM chanuser WHERE nickid=? AND chan=?");
  230. < $get_user_chans = $dbh->prepare("SELECT chan, op FROM chanuser WHERE nickid=? AND joined=1 AND (seq <= ? OR seq > ?)");
  231. < $get_user_chans_recent = $dbh->prepare("SELECT chan, joined, op FROM chanuser WHERE nickid=?");
  232. <
  233. < $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");
  234. < $get_user_count = $dbh->prepare("SELECT COUNT(*) FROM chanuser WHERE chan=? AND joined=1");
  235. <
  236. < $is_in_chan = $dbh->prepare("SELECT 1 FROM chanuser WHERE nickid=? AND chan=? AND joined=1");
  237. <
  238. < #$lock_chanuser = $dbh->prepare("LOCK TABLES chanuser READ, user READ");
  239. < #$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");
  240. < $unlock_tables = $dbh->prepare("UNLOCK TABLES");
  241. <
  242. < $get_chan_users = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
  243. < WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1");
  244. < my $chan_users_noacc_tables = 'user '.
  245. < 'JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1 AND user.online=1) '.
  246. < 'LEFT JOIN nickid ON (chanuser.nickid=nickid.id) '.
  247. < 'LEFT JOIN chanacc ON (nickid.nrid=chanacc.nrid AND chanuser.chan=chanacc.chan)';
  248. < $get_chan_users_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
  249. < WHERE chanuser.chan=?
  250. < GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  251. < ORDER BY NULL");
  252. < my $check_mask = "((user.nick LIKE ?) AND (user.ident LIKE ?)
  253. < AND ((user.vhost LIKE ?) OR (user.host LIKE ?) OR (user.cloakhost LIKE ?)))";
  254. < $get_chan_users_mask = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
  255. < WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1 AND $check_mask");
  256. < $get_chan_users_mask_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
  257. < WHERE chanuser.chan=? AND $check_mask
  258. < GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  259. < ORDER BY NULL");
  260. <
  261. < $get_users_nochans = $dbh->prepare("SELECT user.nick, user.id
  262. < FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
  263. < WHERE chanuser.chan IS NULL AND user.online=1");
  264. < $get_users_nochans_noid = $dbh->prepare("SELECT user.nick, user.id
  265. < FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
  266. < LEFT JOIN nickid ON (nickid.id=user.id)
  267. < WHERE chanuser.chan IS NULL AND nickid.id IS NULL
  268. < AND user.online=1");
  269. <
  270. < $get_using_nick_chans = $dbh->prepare("SELECT user.nick FROM user, nickid, nickreg, chanuser
  271. < WHERE user.id=nickid.id AND user.id=chanuser.nickid AND nickid.nrid=nickreg.id AND chanuser.joined=1
  272. < AND nickreg.nick=? AND chanuser.chan=?");
  273. <
  274. < $get_lock = $dbh->prepare("SELECT GET_LOCK(?, 3)");
  275. < $release_lock = $dbh->prepare("DO RELEASE_LOCK(?)");
  276. < $is_free_lock = $dbh->prepare("SELECT IS_FREE_LOCK(?)");
  277. <
  278. < $chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET seq=?, chan=?");
  279. < $chan_delete = $dbh->prepare("DELETE FROM chan WHERE chan=?");
  280. < $get_chanmodes = $dbh->prepare("SELECT modes FROM chan WHERE chan=?");
  281. < $set_chanmodes = $dbh->prepare("REPLACE INTO chan SET modes=?, chan=?");
  282. <
  283. < $is_registered = $dbh->prepare("SELECT 1 FROM chanreg WHERE chan=?");
  284. < $get_modelock = $dbh->prepare("SELECT modelock FROM chanreg WHERE chan=?");
  285. < $set_modelock = $dbh->prepare("UPDATE chanreg SET modelock=? WHERE chan=?");
  286. <
  287. < $set_descrip = $dbh->prepare("UPDATE chanreg SET descrip=? WHERE chan=?");
  288. <
  289. < $get_topic = $dbh->prepare("SELECT chantext.data, topicer, topicd FROM chanreg, chantext
  290. < WHERE chanreg.chan=chantext.chan AND chantext.chan=?");
  291. < $set_topic1 = $dbh->prepare("UPDATE chanreg SET chanreg.topicer=?, chanreg.topicd=?
  292. < WHERE chanreg.chan=?");
  293. < $set_topic2 = $dbh->prepare("REPLACE INTO chantext SET chan=?, type=".CRT_TOPIC().", data=?");
  294. <
  295. < $get_acc = $dbh->prepare("SELECT chanacc.level FROM chanacc, nickalias
  296. < WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
  297. < $set_acc1 = $dbh->prepare("INSERT IGNORE INTO chanacc SELECT ?, nrid, ?, NULL, UNIX_TIMESTAMP(), 0
  298. < FROM nickalias WHERE alias=?");
  299. < $set_acc2 = $dbh->prepare("UPDATE chanacc, nickalias
  300. < SET chanacc.level=?, chanacc.adder=?, chanacc.time=UNIX_TIMESTAMP()
  301. < WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
  302. < $del_acc = $dbh->prepare("DELETE FROM chanacc USING chanacc, nickalias
  303. < WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
  304. < $wipe_acc_list = $dbh->prepare("DELETE FROM chanacc WHERE chan=? AND level=?");
  305. < $get_acc_list = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.time,
  306. < chanacc.last, nickreg.ident, nickreg.vhost
  307. < FROM chanacc, nickreg
  308. < WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
  309. < $get_acc_list2 = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.level, chanacc.time,
  310. < chanacc.last, nickreg.ident, nickreg.vhost
  311. < FROM chanacc, nickreg
  312. < WHERE chanacc.chan=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
  313. < $get_acc_list_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias), chanacc.adder, chanacc.time,
  314. < chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
  315. < FROM chanacc, nickalias, nickreg
  316. < WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
  317. < AND chanacc.level > 0
  318. < AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
  319. < GROUP BY nickreg.id
  320. < ORDER BY nickalias.alias");
  321. < $get_acc_list2_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias),
  322. < chanacc.adder, chanacc.level, chanacc.time,
  323. < chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
  324. < FROM chanacc, nickalias, nickreg
  325. < WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
  326. < AND chanacc.level > 0
  327. < AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
  328. < GROUP BY nickreg.id
  329. < ORDER BY nickalias.alias");
  330. <
  331. < $get_best_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
  332. < FROM nickid, nickalias, nickreg, chanacc
  333. < WHERE nickid.nrid=nickreg.id AND nickalias.nrid=nickreg.id AND nickid.id=?
  334. < AND chanacc.nrid=nickreg.id AND chanacc.chan=? ORDER BY chanacc.level DESC LIMIT 1");
  335. < $get_all_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
  336. < FROM nickid, nickreg, chanacc
  337. < WHERE nickid.nrid=nickreg.id AND nickid.id=? AND chanacc.nrid=nickreg.id
  338. < AND chanacc.chan=? ORDER BY chanacc.level");
  339. < $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");
  340. < $get_acc_count = $dbh->prepare("SELECT COUNT(*) FROM chanacc WHERE chan=? AND level=?");
  341. < $copy_acc = $dbh->prepare("REPLACE INTO chanacc
  342. < ( chan, nrid, level, adder, time)
  343. < SELECT ?, nrid, level, adder, time FROM chanacc JOIN nickreg ON (chanacc.nrid=nickreg.id)
  344. < WHERE chan=? AND nickreg.nick!=? AND chanacc.level!=7");
  345. < $copy_acc_rank = $dbh->prepare("REPLACE INTO chanacc
  346. < ( chan, nrid, level, adder, time)
  347. < SELECT ?, nrid, level, adder, time FROM chanacc
  348. < WHERE chan=? AND chanacc.level=?");
  349. <
  350. < $get_eos_lock = $dbh->prepare("LOCK TABLES akick READ LOCAL, welcome READ LOCAL, chanuser WRITE, user WRITE,
  351. < user AS u1 READ, user AS u2 READ, chan WRITE, chanreg WRITE, nickid READ LOCAL, nickreg READ LOCAL,
  352. < nickalias READ LOCAL, chanacc READ LOCAL, chanban WRITE, svsop READ");
  353. < 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().")
  354. < FROM user, chanreg, chanuser
  355. < LEFT JOIN nickid ON(nickid.id=chanuser.nickid)
  356. < LEFT JOIN nickreg ON(nickid.nrid=nickreg.id)
  357. < LEFT JOIN chanacc ON(chanacc.chan=chanuser.chan AND chanacc.nrid=nickid.nrid AND (nickreg.flags & ".NRF_NEVEROP().")=0)
  358. < WHERE";
  359. < 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)
  360. < GROUP BY chanuser.chan, chanuser.nickid ORDER BY NULL";
  361. < $get_status_all = $dbh->prepare("$get_status_all_1 $get_status_all_2");
  362. < $get_status_all_server = $dbh->prepare("$get_status_all_1 user.server=? AND $get_status_all_2");
  363. <
  364. < $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");
  365. <
  366. < my $akick_rows = "user.nick, akick.nick, akick.ident, akick.host, akick.reason";
  367. < my $akick_no_zerolen = "(akick.ident != '' AND akick.host != '')";
  368. < my $akick_single_cond = "$akick_no_zerolen AND user.nick LIKE akick.nick AND user.ident LIKE akick.ident ".
  369. < "AND ( (user.host LIKE akick.host) OR (user.vhost LIKE akick.host) OR ".
  370. < "(IF((user.ip IS NOT NULL) AND (user.ip != 0), INET_NTOA(user.ip) LIKE akick.host, 0)) OR ".
  371. < "(IF(user.cloakhost IS NOT NULL, user.cloakhost LIKE akick.host, 0)) )";
  372. < my $akick_multi_cond = "chanuser.chan=akick.chan AND $akick_single_cond";
  373. <
  374. < $get_akick = $dbh->prepare("SELECT $akick_rows FROM akick, user ".
  375. < "WHERE user.id=? AND akick.chan=? AND $akick_single_cond LIMIT 1");
  376. < $get_akick_allchan = $dbh->prepare("SELECT $akick_rows FROM $chan_users_noacc_tables
  377. < JOIN akick ON($akick_multi_cond)
  378. < WHERE akick.chan=?
  379. < GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  380. < ORDER BY NULL");
  381. < $get_akick_alluser = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
  382. < JOIN akick ON($akick_multi_cond)
  383. < WHERE chanuser.nickid=?
  384. < GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  385. < ORDER BY NULL");
  386. < $get_akick_all = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
  387. < JOIN akick ON($akick_multi_cond)
  388. < GROUP BY akick.chan, user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  389. < ORDER BY NULL");
  390. <
  391. < $add_akick = $dbh->prepare("INSERT INTO akick SET chan=?, nick=?, ident=?, host=?, adder=?, reason=?, time=UNIX_TIMESTAMP()");
  392. < $add_akick->{PrintError} = 0;
  393. < $del_akick = $dbh->prepare("DELETE FROM akick WHERE chan=? AND nick=? AND ident=? AND host=?");
  394. < $get_akick_list = $dbh->prepare("SELECT nick, ident, host, adder, reason, time FROM akick WHERE chan=? ORDER BY time");
  395. <
  396. < $add_nick_akick = $dbh->prepare("INSERT INTO akick SELECT ?, nickalias.nrid, '', '', ?, ?, UNIX_TIMESTAMP()
  397. < FROM nickalias WHERE alias=?");
  398. < $del_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickalias
  399. < WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
  400. < $get_nick_akick = $dbh->prepare("SELECT reason FROM akick, nickalias
  401. < WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
  402. < $drop_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickreg
  403. < WHERE akick.nick=nickreg.id AND akick.ident='' AND akick.host='' AND nickreg.nick=?");
  404. < $copy_akick = $dbh->prepare("REPLACE INTO akick
  405. < ( chan, nick, ident, host, adder, reason, time)
  406. < SELECT ?, nick, ident, host, adder, reason, time FROM akick WHERE chan=?");
  407. < $get_akick_by_num = $dbh->prepare("SELECT akick.nick, akick.ident, akick.host FROM akick WHERE chan=?
  408. < ORDER BY time LIMIT 1 OFFSET ?");
  409. < $get_akick_by_num->bind_param(2, 0, SQL_INTEGER);
  410. <
  411. < $is_level = $dbh->prepare("SELECT 1 FROM chanperm WHERE chanperm.name=?");
  412. < $get_level = $dbh->prepare("SELECT IF(chanlvl.level IS NULL, chanperm.level, chanlvl.level), chanlvl.level
  413. < FROM chanperm LEFT JOIN chanlvl ON chanlvl.perm=chanperm.id AND chanlvl.chan=?
  414. < WHERE chanperm.name=?");
  415. < $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");
  416. < $add_level = $dbh->prepare("INSERT IGNORE INTO chanlvl SELECT ?, chanperm.id, chanperm.level FROM chanperm WHERE chanperm.name=?");
  417. < $set_level = $dbh->prepare("UPDATE chanlvl, chanperm SET chanlvl.level=? WHERE chanlvl.chan=? AND chanperm.id=chanlvl.perm AND chanperm.name=?");
  418. < $reset_level = $dbh->prepare("DELETE FROM chanlvl USING chanlvl, chanperm WHERE chanperm.name=? AND chanlvl.perm=chanperm.id AND chanlvl.chan=?");
  419. < $clear_levels = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
  420. < $get_level_max = $dbh->prepare("SELECT max FROM chanperm WHERE name=?");
  421. < $copy_levels = $dbh->prepare("REPLACE INTO chanlvl
  422. < ( chan, perm, level)
  423. < SELECT ?, perm, level FROM chanlvl WHERE chan=?");
  424. <
  425. < $get_founder = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.founderid=nickreg.id");
  426. < $get_successor = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.successorid=nickreg.id");
  427. < $set_founder = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.founderid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
  428. < $set_successor = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
  429. < $del_successor = $dbh->prepare("UPDATE chanreg SET chanreg.successorid=NULL WHERE chanreg.chan=?");
  430. <
  431. < $get_nick_own_chans = $dbh->prepare("SELECT chanreg.chan FROM chanreg, nickreg WHERE nickreg.nick=? AND chanreg.founderid=nickreg.id");
  432. < $delete_successors = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=NULL WHERE nickreg.nick=? AND chanreg.successorid=nickreg.id");
  433. <
  434. <
  435. < $get_info = $dbh->prepare("SELECT chanreg.descrip, chanreg.regd, chanreg.last, chantext.data,
  436. < chanreg.topicer, chanreg.modelock, foundernick.nick, successornick.nick, chanreg.bot, chanreg.bantype
  437. < FROM nickreg AS foundernick, chanreg
  438. < LEFT JOIN nickreg AS successornick ON(successornick.id=chanreg.successorid)
  439. < LEFT JOIN chantext ON (chanreg.chan=chantext.chan AND chantext.type=".CRT_TOPIC().")
  440. < WHERE chanreg.chan=? AND foundernick.id=chanreg.founderid");
  441. <
  442. < $register = $dbh->prepare("INSERT INTO chanreg
  443. < SELECT ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), NULL, NULL,
  444. < NULL, id, NULL, NULL, NULL, ".DEFAULT_BANTYPE()." FROM nickreg WHERE nick=?");
  445. < $register->{PrintError} = 0;
  446. < $copy_chanreg = $dbh->prepare("INSERT INTO chanreg
  447. < ( chan, descrip, regd, last, modelock, founderid, successorid, bot, flags, bantype)
  448. < SELECT ?, descrip, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), modelock, founderid, successorid, bot, flags, bantype
  449. < FROM chanreg WHERE chan=?");
  450. <
  451. < $drop_acc = $dbh->prepare("DELETE FROM chanacc WHERE chan=?");
  452. < $drop_lvl = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
  453. < $drop_akick = $dbh->prepare("DELETE FROM akick WHERE chan=?");
  454. < $drop = $dbh->prepare("DELETE FROM chanreg WHERE chan=?");
  455. <
  456. < $get_expired = $dbh->prepare("SELECT chanreg.chan, nickreg.nick FROM nickreg, chanreg
  457. < LEFT JOIN chanuser ON(chanreg.chan=chanuser.chan AND chanuser.op!=0)
  458. < WHERE chanreg.founderid=nickreg.id AND chanuser.chan IS NULL AND chanreg.last<? AND
  459. < !(chanreg.flags & " . CRF_HOLD . ")");
  460. <
  461. < $get_close = $dbh->prepare("SELECT reason, nick, time FROM chanclose WHERE chan=?");
  462. < $set_close = $dbh->prepare("REPLACE INTO chanclose SET chan=?, reason=?, nick=?, time=UNIX_TIMESTAMP(), type=?");
  463. < $del_close = $dbh->prepare("DELETE FROM chanclose WHERE chan=?");
  464. <
  465. < $add_welcome = $dbh->prepare("REPLACE INTO welcome SET chan=?, id=?, adder=?, time=UNIX_TIMESTAMP(), msg=?");
  466. < $del_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=? AND id=?");
  467. < $list_welcome = $dbh->prepare("SELECT id, time, adder, msg FROM welcome WHERE chan=? ORDER BY id");
  468. < $get_welcomes = $dbh->prepare("SELECT msg FROM welcome WHERE chan=? ORDER BY id");
  469. < $drop_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=?");
  470. < $count_welcome = $dbh->prepare("SELECT COUNT(*) FROM welcome WHERE chan=?");
  471. < $consolidate_welcome = $dbh->prepare("UPDATE welcome SET id=id-1 WHERE chan=? AND id>?");
  472. <
  473. < $add_ban = $dbh->prepare("INSERT IGNORE INTO chanban SET chan=?, mask=?, setter=?, type=?, time=UNIX_TIMESTAMP()");
  474. < $delete_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
  475. < # likely need a better name for this or for the above.
  476. < $delete_ban = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND mask=? AND type=?");
  477. < $find_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
  478. < $get_all_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=?");
  479. < $get_ban_num = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=? ORDER BY time, mask LIMIT 1 OFFSET ?");
  480. < $get_ban_num->bind_param(3, 0, SQL_INTEGER);
  481. < $list_bans = $dbh->prepare("SELECT mask, setter, time FROM chanban WHERE chan=? AND type=? ORDER BY time, mask");
  482. < $wipe_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=?");
  483. <
  484. < my $chanban_mask = "((CONCAT(user.nick, '!', user.ident, '\@', user.host) LIKE chanban.mask) ".
  485. < "OR (CONCAT(user.nick , '!' , user.ident , '\@' , user.vhost) LIKE chanban.mask) ".
  486. < "OR IF(user.cloakhost IS NOT NULL, ".
  487. < "(CONCAT(user.nick , '!' , user.ident , '\@' , user.cloakhost) LIKE chanban.mask), 0))";
  488. < $find_bans_chan_user = $dbh->prepare("SELECT mask FROM chanban,user
  489. < WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
  490. < $delete_bans_chan_user = $dbh->prepare("DELETE FROM chanban USING chanban,user
  491. < WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
  492. <
  493. < $add_auth = $dbh->prepare("REPLACE INTO nicktext
  494. < SELECT nickalias.nrid, (".NTF_AUTH()."), 1, ?, ? FROM nickalias WHERE nickalias.alias=?");
  495. < $list_auth_chan = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nicktext
  496. < WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".NTF_AUTH().") AND nicktext.chan=?");
  497. < $get_auth_nick = $dbh->prepare("SELECT nicktext.data FROM nickreg, nickalias, nicktext
  498. < WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH().")
  499. < AND nicktext.chan=? AND nickalias.alias=?");
  500. < $get_auth_num = $dbh->prepare("SELECT nickreg.nick, 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=? LIMIT 1 OFFSET ?");
  503. < $get_auth_num->bind_param(2, 0, SQL_INTEGER);
  504. < $find_auth = $dbh->prepare("SELECT 1 FROM nickalias, nicktext
  505. < WHERE nickalias.nrid=nicktext.nrid AND nicktext.type=(".NTF_AUTH().")
  506. < AND nicktext.chan=? AND nickalias.alias=?");
  507. <
  508. < $set_bantype = $dbh->prepare("UPDATE chanreg SET bantype=? WHERE chan=?");
  509. < $get_bantype = $dbh->prepare("SELECT bantype FROM chanreg WHERE chan=?");
  510. <
  511. < $drop_chantext = $dbh->prepare("DELETE FROM chantext WHERE chan=?");
  512. < $drop_nicktext = $dbh->prepare("DELETE nicktext.* FROM nicktext WHERE nicktext.chan=?");
  513. < }
  514. <
  515. < use SrSv::MySQL::Stub {
  516. < set_lastop => ['NULL', "UPDATE chanreg SET last=UNIX_TIMESTAMP() WHERE chan=?"],
  517. < set_lastused => ['NULL', "UPDATE chanacc, nickid SET chanacc.last=UNIX_TIMESTAMP() WHERE
  518. < chanacc.chan=? AND nickid.id=? AND chanacc.nrid=nickid.nrid AND chanacc.level > 0"],
  519. < get_recent_private_chans => ['COLUMN', "SELECT DISTINCT chanuser.chan FROM chanuser
  520. < JOIN chanacc ON (chanuser.chan=chanacc.chan AND chanuser.joined=0)
  521. < JOIN chanlvl ON (chanlvl.level <= chanacc.level AND chanlvl.level > 0 AND chanuser.chan=chanlvl.chan)
  522. < JOIN chanperm ON (chanlvl.perm=chanperm.id)
  523. < JOIN nickid ON (chanuser.nickid=nickid.id AND chanacc.nrid=nickid.nrid)
  524. < WHERE chanperm.name='Join'
  525. < AND nickid.id=?"],
  526. < };
  527. <
  528. < ### CHANSERV COMMANDS ###
  529. <
  530. < sub dispatch($$$) {
  531. < my ($src, $dst, $msg) = @_;
  532. <
  533. < $msg =~ s/^\s+//;
  534. < my @args = split(/\s+/, $msg);
  535. < my $cmd = shift @args;
  536. <
  537. < my $user = { NICK => $src, AGENT => $dst };
  538. <
  539. < return if flood_check($user);
  540. <
  541. < if($cmd =~ /^register$/i) {
  542. < if(@args >= 1) {
  543. < my @args = split(/\s+/, $msg, 4);
  544. < cs_register($user, { CHAN => $args[1] }, $args[2], $args[3]);
  545. < } else {
  546. < notice($user, 'Syntax: REGISTER <#channel> [password] [description]');
  547. < }
  548. < }
  549. < elsif($cmd =~ /^(?:[uvhas]op|co?f(ounder)?)$/i) {
  550. < my ($cn, $cmd2) = splice(@args, 0, 2);
  551. < my $chan = { CHAN => $cn };
  552. <
  553. < if($cmd2 =~ /^add$/i) {
  554. < if(@args == 1) {
  555. < cs_xop_add($user, $chan, $cmd, $args[0]);
  556. < } else {
  557. < notice($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
  558. < }
  559. < }
  560. < elsif($cmd2 =~ /^del(ete)?$/i) {
  561. < if(@args == 1) {
  562. < cs_xop_del($user, $chan, $cmd, $args[0]);
  563. < } else {
  564. < notice($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
  565. < }
  566. < }
  567. < elsif($cmd2 =~ /^list$/i) {
  568. < if(@args >= 0) {
  569. < cs_xop_list($user, $chan, $cmd, $args[0]);
  570. < } else {
  571. < notice($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
  572. < }
  573. < }
  574. < elsif($cmd2 =~ /^(wipe|clear)$/i) {
  575. < if(@args == 0) {
  576. < cs_xop_wipe($user, $chan, $cmd);
  577. < } else {
  578. < notice($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
  579. < }
  580. < }
  581. < else {
  582. < notice($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
  583. < }
  584. < }
  585. < elsif($cmd =~ /^levels$/i) {
  586. < if(@args < 2) {
  587. < notice($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
  588. < return;
  589. < }
  590. <
  591. < my $cmd2 = lc(splice(@args, 1, 1));
  592. <
  593. < if($cmd2 eq 'set') {
  594. < if(@args == 3) {
  595. < cs_levels_set($user, { CHAN => $args[0] }, $args[1], $args[2]);
  596. < } else {
  597. < notice($user, 'Syntax: LEVELS <#channel> SET <permission> <level>');
  598. < }
  599. < }
  600. < elsif($cmd2 eq 'reset') {
  601. < if(@args == 2) {
  602. < cs_levels_set($user, { CHAN => $args[0] }, $args[1]);
  603. < } else {
  604. < notice($user, 'Syntax: LEVELS <#channel> RESET <permission>');
  605. < }
  606. < }
  607. < elsif($cmd2 eq 'list') {
  608. < if(@args == 1) {
  609. < cs_levels_list($user, { CHAN => $args[0] });
  610. < } else {
  611. < notice($user, 'Syntax: LEVELS <#channel> LIST');
  612. < }
  613. < }
  614. < elsif($cmd2 eq 'clear') {
  615. < if(@args == 1) {
  616. < cs_levels_clear($user, { CHAN => $args[0] });
  617. < } else {
  618. < notice($user, 'Syntax: LEVELS <#channel> CLEAR');
  619. < }
  620. < }
  621. < else {
  622. < notice($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
  623. < }
  624. < }
  625. < elsif($cmd =~ /^akick$/i) {
  626. < if(@args < 2) {
  627. < notice($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
  628. < return;
  629. < }
  630. <
  631. < #my $cmd2 = lc($args[1]);
  632. < my $cmd2 = lc(splice(@args, 1, 1));
  633. <
  634. < if($cmd2 eq 'add') {
  635. < if(@args >= 2) {
  636. < my @args = split(/\s+/, $msg, 5);
  637. < cs_akick_add($user, { CHAN => $args[1] }, $args[3], $args[4]);
  638. < } else {
  639. < notice($user, 'Syntax: AKICK <#channel> ADD <nick|mask> <reason>');
  640. < }
  641. < }
  642. < elsif($cmd2 eq 'del') {
  643. < if(@args >= 2) {
  644. < cs_akick_del($user, { CHAN => $args[0] }, $args[1]);
  645. < } else {
  646. < notice($user, 'Syntax: AKICK <#channel> DEL <nick|mask|num|seq>');
  647. < }
  648. < }
  649. < elsif($cmd2 eq 'list') {
  650. < if(@args == 1) {
  651. < cs_akick_list($user, { CHAN => $args[0] });
  652. < } else {
  653. < notice($user, 'Syntax: AKICK <#channel> LIST');
  654. < }
  655. < }
  656. < elsif($cmd2 =~ /^(wipe|clear)$/i) {
  657. < if(@args == 1) {
  658. < cs_akick_wipe($user, { CHAN => $args[0] });
  659. < } else {
  660. < notice($user, 'Syntax: AKICK <#channel> WIPE');
  661. < }
  662. < }
  663. < elsif($cmd2 =~ /^enforce$/i) {
  664. < if(@args == 1) {
  665. < cs_akick_enforce($user, { CHAN => $args[0] });
  666. < } else {
  667. < notice($user, 'Syntax: AKICK <#channel> ENFORCE');
  668. < }
  669. < }
  670. < else {
  671. < notice($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
  672. < }
  673. < }
  674. < elsif($cmd =~ /^info$/i) {
  675. < if(@args == 1) {
  676. < cs_info($user, { CHAN => $args[0] });
  677. < } else {
  678. < notice($user, 'Syntax: INFO <channel>');
  679. < }
  680. < }
  681. < elsif($cmd =~ /^set$/i) {
  682. < if(@args == 2 and lc($args[1]) eq 'unsuccessor') {
  683. < cs_set($user, { CHAN => $args[0] }, $args[1]);
  684. < }
  685. < elsif(@args >= 3 and (
  686. < $args[1] =~ /m(?:ode)?lock/i or
  687. < lc($args[1]) eq 'desc'
  688. < )) {
  689. < my @args = split(/\s+/, $msg, 4);
  690. < cs_set($user, { CHAN => $args[1] }, $args[2], $args[3]);
  691. < }
  692. < elsif(@args == 3) {
  693. < cs_set($user, { CHAN => $args[0] }, $args[1], $args[2]);
  694. < }
  695. < else {
  696. < notice($user, 'Syntax: SET <channel> <option> <value>');
  697. < }
  698. < }
  699. < elsif($cmd =~ /^why$/i) {
  700. < if(@args == 1) {
  701. < cs_why($user, { CHAN => shift @args }, $src);
  702. < }
  703. < elsif(@args >= 2) {
  704. < cs_why($user, { CHAN => shift @args }, @args);
  705. < } else {
  706. < notice($user, 'Syntax: WHY <channel> <nick> [nick [nick ...]]');
  707. < return;
  708. < }
  709. < }
  710. < elsif($cmd =~ /^(de)?(voice|h(alf)?op|op|protect|admin|owner)$/i) {
  711. < if(@args >= 1) {
  712. < cs_setmodes($user, $cmd, { CHAN => shift(@args) }, @args);
  713. < } else {
  714. < notice($user, 'Syntax: '.uc($cmd).' <channel> [nick [nick ...]]');
  715. < }
  716. < }
  717. < elsif($cmd =~ /^(up|down)$/i) {
  718. < cs_updown($user, $cmd, @args);
  719. < }
  720. < elsif($cmd =~ /^drop$/i) {
  721. < if(@args == 1) {
  722. < cs_drop($user, { CHAN => $args[0] });
  723. < } else {
  724. < notice($user, 'Syntax: DROP <channel>');
  725. < }
  726. < }
  727. < elsif($cmd =~ /^help$/i) {
  728. < sendhelp($user, 'chanserv', @args)
  729. < }
  730. < elsif($cmd =~ /^count$/i) {
  731. < if(@args == 1) {
  732. < cs_count($user, { CHAN => $args[0] });
  733. < } else {
  734. < notice($user, 'Syntax: COUNT <channel>');
  735. < }
  736. < }
  737. < elsif($cmd =~ /^kick$/i) {
  738. < my @args = split(/\s+/, $msg, 4); shift @args;
  739. < if(@args >= 2) {
  740. < cs_kick($user, { CHAN => $args[0] }, $args[1], 0, $args[2])
  741. < }
  742. < else {
  743. < notice($user, 'Syntax: KICK <channel> <nick> [reason]');
  744. < }
  745. < }
  746. < elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)$/i) {
  747. < my @args = split(/\s+/, $msg, 4); shift @args;
  748. < if(@args >= 2) {
  749. < cs_kick($user, { CHAN => $args[0] }, $args[1], 1, $args[2]);
  750. < } else {
  751. < notice($user, 'Syntax: KICKBAN <channel> <nick> [reason]');
  752. < }
  753. < }
  754. < elsif($cmd =~ /^k(ick)?m(ask)?$/i) {
  755. < my @args = split(/\s+/, $msg, 4); shift @args;
  756. < if(@args >= 2) {
  757. < cs_kickmask($user, { CHAN => $args[0] }, $args[1], 0, $args[2])
  758. < }
  759. < else {
  760. < notice($user, 'Syntax: KICKMASK <channel> <mask> [reason]');
  761. < }
  762. < }
  763. < elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)m(ask)?$/i) {
  764. < my @args = split(/\s+/, $msg, 4); shift @args;
  765. < if(@args >= 2) {
  766. < cs_kickmask($user, { CHAN => $args[0] }, $args[1], 1, $args[2]);
  767. < } else {
  768. < notice($user, 'Syntax: KICKBANMASK <channel> <mask> [reason]');
  769. < }
  770. < }
  771. < elsif($cmd =~ /^invite$/i) {
  772. < my $chan = shift @args;
  773. < if(@args == 0) {
  774. < cs_invite($user, { CHAN => $chan }, $src)
  775. < }
  776. < elsif(@args >= 1) {
  777. < cs_invite($user, { CHAN => $chan }, @args)
  778. < }
  779. < else {
  780. < notice($user, 'Syntax: INVITE <channel> <nick>');
  781. < }
  782. < }
  783. < elsif($cmd =~ /^(close|forbid)$/i) {
  784. < if(@args > 1) {
  785. < my @args = split(/\s+/, $msg, 3);
  786. < cs_close($user, { CHAN => $args[1] }, $args[2], CRF_CLOSE);
  787. < }
  788. < else {
  789. < notice($user, 'Syntax: CLOSE <chan> <reason>');
  790. < }
  791. < }
  792. < elsif($cmd =~ /^drone$/i) {
  793. < if(@args > 1) {
  794. < my @args = split(/\s+/, $msg, 3);
  795. < cs_close($user, { CHAN => $args[1] }, $args[2], CRF_DRONE);
  796. < }
  797. < else {
  798. < notice($user, 'Syntax: DRONE <chan> <reason>');
  799. < }
  800. < }
  801. < elsif($cmd =~ /^clear$/i) {
  802. < my ($cmd, $chan, $clearcmd, $reason) = split(/\s+/, $msg, 4);
  803. < unless ($chan and $clearcmd) {
  804. < notice($user, 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
  805. < return;
  806. < }
  807. < if($clearcmd =~ /^modes$/i) {
  808. < cs_clear_modes($user, { CHAN => $chan }, $reason);
  809. < }
  810. < elsif($clearcmd =~ /^ops$/i) {
  811. < cs_clear_ops($user, { CHAN => $chan }, $reason);
  812. < }
  813. < elsif($clearcmd =~ /^users$/i) {
  814. < cs_clear_users($user, { CHAN => $chan }, $reason);
  815. < }
  816. < elsif($clearcmd =~ /^bans?$/i) {
  817. < cs_clear_bans($user, { CHAN => $chan }, 0, $reason);
  818. < }
  819. < elsif($clearcmd =~ /^excepts?$/i) {
  820. < cs_clear_bans($user, { CHAN => $chan }, 128, $reason);
  821. < }
  822. < else {
  823. < notice($user, "Unknown CLEAR command \002$clearcmd\002",
  824. < 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
  825. < }
  826. < }
  827. < elsif($cmd =~ /^mkick$/i) {
  828. < my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
  829. < if($chan) {
  830. < cs_clear_users($user, { CHAN => $chan }, $reason);
  831. < }
  832. < else {
  833. < notice($user, 'Syntax: MKICK <chan> [reason]');
  834. < }
  835. < }
  836. < elsif($cmd =~ /^mdeop$/i) {
  837. < my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
  838. < if($chan) {
  839. < cs_clear_ops($user, { CHAN => $chan }, $reason);
  840. < }
  841. < else {
  842. < notice($user, 'Syntax: MDEOP <chan> [reason]');
  843. < }
  844. < }
  845. < elsif($cmd =~ /^welcome$/i) {
  846. < my $wcmd = splice(@args, 1, 1);
  847. < if(lc($wcmd) eq 'add') {
  848. < my ($chan, $wmsg) = (splice(@args, 0, 1), join(' ', @args));
  849. < unless ($chan and $wmsg) {
  850. < notice($user, 'Syntax: WELCOME <channel> ADD <message>');
  851. < return;
  852. < }
  853. < cs_welcome_add($user, { CHAN => $chan }, $wmsg);
  854. < }
  855. < elsif(lc($wcmd) eq 'del') {
  856. < if (@args != 2 or !misc::isint($args[1])) {
  857. < notice($user, 'Syntax: WELCOME <channnel> DEL <number>');
  858. < return;
  859. < }
  860. < cs_welcome_del($user, { CHAN => $args[0] }, $args[1]);
  861. < }
  862. < elsif(lc($wcmd) eq 'list') {
  863. < if (@args != 1) {
  864. < notice($user, 'Syntax: WELCOME <channel> LIST');
  865. < return;
  866. < }
  867. < cs_welcome_list($user, { CHAN => $args[0] });
  868. < }
  869. < else {
  870. < notice($user, 'Syntax: WELCOME <channel> <ADD|DEL|LIST>');
  871. < }
  872. < }
  873. < elsif($cmd =~ /^alist$/i) {
  874. < if(@args >= 1) {
  875. < cs_alist($user, { CHAN => shift @args }, shift @args);
  876. < } else {
  877. < notice($user, 'Syntax: ALIST <channel> [mask]');
  878. < }
  879. < }
  880. < elsif($cmd =~ /^unban$/i) {
  881. < if(@args == 1) {
  882. < cs_unban($user, { CHAN => shift @args }, $src);
  883. < }
  884. < elsif(@args >= 2) {
  885. < cs_unban($user, { CHAN => shift @args }, @args);
  886. < } else {
  887. < notice($user, 'Syntax: UNBAN <channel> [nick]');
  888. < }
  889. < }
  890. < elsif($cmd =~ /^getkey$/i) {
  891. < if(@args == 1) {
  892. < cs_getkey($user, { CHAN => $args[0] });
  893. < } else {
  894. < notice($user, 'Syntax: GETKEY <channel>');
  895. < }
  896. < }
  897. < elsif($cmd =~ /^auth$/i) {
  898. < if (@args == 0) {
  899. < notice($user, 'Syntax: AUTH <channel> <LIST|DELETE> [param]');
  900. < } else {
  901. < cs_auth($user, { CHAN => shift @args }, shift @args, @args);
  902. < }
  903. < }
  904. < elsif($cmd =~ /^dice$/i) {
  905. < notice($user, botserv::get_dice($args[0]));
  906. < }
  907. < elsif($cmd =~ /^(q|n)?ban$/i) {
  908. < my $type = $1;
  909. < my $chan = shift @args;
  910. < if(@args >= 1) {
  911. < cs_ban($user, { CHAN => $chan }, $type, @args)
  912. < }
  913. < else {
  914. < notice($user, 'Syntax: BAN <channel> <nick|mask>');
  915. < }
  916. < }
  917. < elsif($cmd =~ /^banlist$/i) {
  918. < my $chan = shift @args;
  919. < if(@args == 0) {
  920. < cs_banlist($user, { CHAN => $chan });
  921. < }
  922. < else {
  923. < notice($user, 'Syntax: BANLIST <channel>');
  924. < }
  925. < }
  926. < elsif($cmd =~ /^assign$/i) {
  927. < my $chan = shift @args;
  928. < notice($user, "$csnick ASSIGN is deprecated. Please use $botserv::bsnick ASSIGN");
  929. < if(@args == 2) {
  930. < botserv::bs_assign($user, { CHAN => shift @args }, shift @args);
  931. < }
  932. < else {
  933. < notice($user, 'Syntax: ASSIGN <#channel> <bot>');
  934. < }
  935. < }
  936. < elsif($cmd =~ /^mode$/i) {
  937. < my $chan = shift @args;
  938. < if(@args >= 1) {
  939. < cs_mode($user, { CHAN => $chan }, @args)
  940. < }
  941. < else {
  942. < notice($user, 'Syntax: MODE <channel> <modes> [parms]');
  943. < }
  944. < }
  945. < elsif($cmd =~ /^copy$/i) {
  946. < my $chan = shift @args;
  947. < if(@args >= 1) {
  948. < cs_copy($user, { CHAN => $chan }, @args)
  949. < }
  950. < else {
  951. < notice($user, 'Syntax: COPY #chan1 [type] #chan2');
  952. < }
  953. < }
  954. < elsif($cmd =~ /^m(?:ode)?lock$/i) {
  955. < my $chan = shift @args;
  956. < if(@args >= 1) {
  957. < cs_mlock($user, { CHAN => $chan }, @args)
  958. < }
  959. < else {
  960. < notice($user, 'Syntax: MLOCK <channel> <ADD|DEL|SET|RESET> <modes> [parms]');
  961. < }
  962. < }
  963. < elsif($cmd =~ /^resync$/i) {
  964. < if (@args == 0) {
  965. < notice($user, 'Syntax: RESYNC <chan1> [chan2 [chan3 [..]]]');
  966. < } else {
  967. < cs_resync($user, @args);
  968. < }
  969. < }
  970. < elsif($cmd =~ /^JOIN$/i) {
  971. < if (@args == 0) {
  972. < notice($user, 'Syntax: JOIN <chan1> [chan2 [chan3 [..]]]');
  973. < } else {
  974. < cs_join($user, @args);
  975. < }
  976. < }
  977. < else {
  978. < notice($user, "Unrecognized command \002$cmd\002.", "For help, type: \002/msg chanserv help\002");
  979. < wlog($csnick, LOG_DEBUG(), "$src tried to use $csnick $msg");
  980. < }
  981. < }
  982. <
  983. < sub cs_register($$;$$) {
  984. < my ($user, $chan, $pass, $desc) = @_;
  985. < # $pass is still passed in, but never used!
  986. < my $src = get_user_nick($user);
  987. < my $cn = $chan->{CHAN};
  988. <
  989. < unless(is_identified($user, $src)) {
  990. < notice($user, 'You must register your nickname first.', "Type \002/msg NickServ HELP\002 for information on registering nicknames.");
  991. < return;
  992. < }
  993. <
  994. < unless(is_in_chan($user, $chan)) {
  995. < notice($user, "You are not in \002$cn\002.");
  996. < return;
  997. < }
  998. <
  999. < if(services_conf_chanreg_needs_oper && !adminserv::is_svsop($user)) {
  1000. < notice($user, "You must be network staff to register a channel\n");
  1001. < return;
  1002. < }
  1003. < unless(get_op($user, $chan) & ($opmodes{o} | $opmodes{a} | $opmodes{q})) {
  1004. < # This would be preferred to be a 'opmode_mask' or something
  1005. < # However that might be misleading due to hop not being enough to register
  1006. < notice($user, "You must have channel operator status to register \002$cn\002.");
  1007. < return;
  1008. < }
  1009. <
  1010. < my $root = get_root_nick($src);
  1011. <
  1012. < if($desc) {
  1013. < my $dlength = length($desc);
  1014. < if($dlength >= 350) {
  1015. < notice($user, 'Channel description is too long by '. $dlength-350 .' character(s). Maximum length is 350 characters.');
  1016. < return;
  1017. < }
  1018. < }
  1019. <
  1020. < if($register->execute($cn, $desc, $root)) {
  1021. < notice($user, "\002Your channel is now registered. Thank you.\002");
  1022. < notice($user, ' ', "\002NOTICE:\002 Channel passwords are not used, as a security precaution.")
  1023. < if $pass;
  1024. < set_acc($root, $user, $chan, FOUNDER);
  1025. < $set_modelock->execute(services_conf_default_channel_mlock, $cn);
  1026. < do_modelock($chan);
  1027. < services::ulog($csnick, LOG_INFO(), "registered $cn", $user, $chan);
  1028. < botserv::bs_assign($user, $chan, services_conf_default_chanbot) if services_conf_default_chanbot;
  1029. < } else {
  1030. < notice($user, 'That channel has already been registered.');
  1031. < }
  1032. < }
  1033. <
  1034. < =cut
  1035. < cs_command new SrSv::AgentUI::Simple {
  1036. < COMMAND => [qw(uop vop hop aop sop cf cofounder cof cfounder)],
  1037. < SYNTAX => '#chan add/del/list/wipe/clear [nick/mask]',
  1038. < CALL => \&cs_xop_dispatch,
  1039. < CMD_TOO => 1,
  1040. < };
  1041. < =cut
  1042. < sub cs_xop_dispatch {
  1043. < my ($user, $cmd, $chan, $cmd2, @args) = @_;
  1044. < $cmd = uc $cmd;
  1045. <
  1046. < if($cmd2 =~ /^add$/i) {
  1047. < if(@args == 1) {
  1048. < cs_xop_add($user, $chan, $cmd, $args[0]);
  1049. < } else {
  1050. < notice($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
  1051. < }
  1052. < }
  1053. < elsif($cmd2 =~ /^del(ete)?$/i) {
  1054. < if(@args == 1) {
  1055. < cs_xop_del($user, $chan, $cmd, $args[0]);
  1056. < } else {
  1057. < notice($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
  1058. < }
  1059. < }
  1060. < elsif($cmd2 =~ /^list$/i) {
  1061. < if(@args >= 0) {
  1062. < cs_xop_list($user, $chan, $cmd, $args[0]);
  1063. < } else {
  1064. < notice($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
  1065. < }
  1066. < }
  1067. < elsif($cmd2 =~ /^(wipe|clear)$/i) {
  1068. < if(@args == 0) {
  1069. < cs_xop_wipe($user, $chan, $cmd);
  1070. < } else {
  1071. < notice($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
  1072. < }
  1073. < }
  1074. < else {
  1075. < notice($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
  1076. < }
  1077. < }
  1078. <
  1079. < sub cs_xop_ad_pre($$$$$) {
  1080. < my ($user, $chan, $nick, $level, $del) = @_;
  1081. <
  1082. < my $old = get_acc($nick, $chan); $old = 0 unless $old;
  1083. < my $slevel = get_best_acc($user, $chan);
  1084. <
  1085. < unless(($del and is_identified($user, $nick)) or adminserv::can_do($user, 'SERVOP')) {
  1086. < unless($level < $slevel and $old < $slevel) {
  1087. < notice($user, $err_deny);
  1088. < return undef;
  1089. < }
  1090. < my $cn = $chan->{CHAN};
  1091. < my $overrideMsg = "$levels[$level] $cn ".($del ? 'DEL' : 'ADD')." $nick";
  1092. < can_do($chan, 'ACCCHANGE', $user, { OVERRIDE_MSG => $overrideMsg }) or return undef;
  1093. < }
  1094. <
  1095. < nickserv::chk_registered($user, $nick) or return undef;
  1096. < if (nr_chk_flag($nick, NRF_NOACC()) and !adminserv::can_do($user, 'SERVOP') and !$del) {
  1097. < notice($user, "\002$nick\002 is not able to be added to access lists.");
  1098. < return undef;
  1099. < }
  1100. <
  1101. < return $old;
  1102. < }
  1103. <
  1104. < sub cs_xop_list($$$;$) {
  1105. < my ($user, $chan, $cmd, $mask) = @_;
  1106. < chk_registered($user, $chan) or return;
  1107. < my $cn = $chan->{CHAN};
  1108. < my $level = xop_byname($cmd);
  1109. <
  1110. < my $overrideMsg = "$cmd $cn LIST";
  1111. < can_do($chan, 'ACCLIST', $user, { OVERRIDE_MSG => $overrideMsg }) or return;
  1112. <
  1113. < my @reply;
  1114. < if($mask) {
  1115. < my ($mnick, $mident, $mhost) = glob2sql(parse_mask($mask));
  1116. < $mnick = '%' if($mnick eq '');
  1117. < $mident = '%' if($mident eq '');
  1118. < $mhost = '%' if($mhost eq '');
  1119. <
  1120. < $get_acc_list_mask->execute($mnick, $cn, $level, $mnick, $mident, $mhost);
  1121. < while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list_mask->fetchrow_array) {
  1122. < push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
  1123. < push @reply, ' '.($t ? 'Date/time added: '. gmtime2($t).' ' : '').
  1124. < ($lu ? 'Last used '.time_ago($lu).' ago' : '') if ($t or $lu);
  1125. < }
  1126. < $get_acc_list_mask->finish();
  1127. < } else {
  1128. < $get_acc_list->execute($cn, $level);
  1129. < while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list->fetchrow_array) {
  1130. < push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
  1131. < push @reply, ' '.($t ? 'Date/time added: '. gmtime2($t).' ' : '').
  1132. < ($lu ? 'Last used '.time_ago($lu).' ago' : '') if ($t or $lu);
  1133. < }
  1134. < $get_acc_list->finish();
  1135. < }
  1136. <
  1137. < notice($user, "$levels[$level] list for \002$cn\002:", @reply);
  1138. <
  1139. < return;
  1140. < }
  1141. <
  1142. < sub cs_xop_wipe($$$) {
  1143. < my ($user, $chan, $cmd, $nick) = @_;
  1144. < chk_registered($user, $chan) or return;
  1145. <
  1146. < my $slevel = get_best_acc($user, $chan);
  1147. < my $level = xop_byname($cmd);
  1148. <
  1149. < unless($level < $slevel) {
  1150. < notice($user, $err_deny);
  1151. < return;
  1152. < }
  1153. < my $cn = $chan->{CHAN};
  1154. < my $overrideMsg = "$cmd $cn WIPE";
  1155. < my $srcnick = can_do($chan, 'ACCCHANGE', $user, { ACC => $slevel, OVERRIDE_MSG => $overrideMsg }) or return;
  1156. <
  1157. < $wipe_acc_list->execute($cn, $level);
  1158. <
  1159. < my $log_str = "wiped the $cmd list of \002$cn\002.";
  1160. < my $src = get_user_nick($user);
  1161. < notice($user, "You have $log_str");
  1162. < ircd::notice(agent($chan), '%'.$cn, "\002$src\002 has $log_str")
  1163. < if cr_chk_flag($chan, CRF_VERBOSE);
  1164. < services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  1165. <
  1166. < memolog($chan, "\002$srcnick\002 $log_str");
  1167. < }
  1168. <
  1169. < sub cs_xop_add($$$$) {
  1170. < my ($user, $chan, $cmd, $nick) = @_;
  1171. <
  1172. < chk_registered($user, $chan) or return;
  1173. < my $level = xop_byname($cmd);
  1174. < my $old = cs_xop_ad_pre($user, $chan, $nick, $level, 0);
  1175. < return unless defined($old);
  1176. <
  1177. < my $cn = $chan->{CHAN};
  1178. <
  1179. < if($old == $level) {
  1180. < notice($user, "\002$nick\002 already has $levels[$level] access to \002$cn\002.");
  1181. < return;
  1182. < }
  1183. <
  1184. < if($old == FOUNDER) {
  1185. < notice($user, "\002$nick\002 is the founder of \002$cn\002 and cannot be added to access lists.",
  1186. < "For more information, type: \002/msg chanserv help set founder\002");
  1187. < return;
  1188. < }
  1189. <
  1190. < my $root = get_root_nick($nick);
  1191. < my $auth = nr_chk_flag($root, NRF_AUTH());
  1192. < my $src = get_user_nick($user);
  1193. <
  1194. < if($auth) {
  1195. < $add_auth->execute($cn, "$src:".($old ? $old : 0 ).":$level:".time(), $root);
  1196. < del_acc($root, $chan) if $level < $old;
  1197. < }
  1198. < else {
  1199. < set_acc($root, $user, $chan, $level);
  1200. < }
  1201. <
  1202. < if($old < 0) {
  1203. < $del_nick_akick->execute($cn, $root);
  1204. < my $log_str = "moved $root from the AKICK list to the ${levels[$level]} list of \002$cn\002".
  1205. < ($auth ? ' (requires authorization)' : '');
  1206. <
  1207. < my $src = get_user_nick($user);
  1208. < notice_all_nicks($user, $root, "\002$src\002 $log_str");
  1209. < ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  1210. < if cr_chk_flag($chan, CRF_VERBOSE);
  1211. < services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  1212. < my $srcnick = can_do($chan, 'ACCLIST', $user);
  1213. < memolog($chan, "\002$srcnick\002 $log_str");
  1214. < } else {
  1215. < my $log_str = ($old?'moved':'added')." \002$root\002"
  1216. < . ($old ? " from the ${levels[$old]}" : '') .
  1217. < " to the ${levels[$level]} list of \002$cn\002" .
  1218. < ($auth ? ' (requires authorization)' : '');
  1219. < my $src = get_user_nick($user);
  1220. < notice_all_nicks($user, $root, "\002$src\002 $log_str");
  1221. < ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  1222. < if cr_chk_flag($chan, CRF_VERBOSE);
  1223. < services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  1224. < my $srcnick = can_do($chan, 'ACCLIST', $user);
  1225. < memolog($chan, "\002$srcnick\002 $log_str");
  1226. < }
  1227. < }
  1228. <
  1229. < sub cs_xop_del($$$) {
  1230. < my ($user, $chan, $cmd, $nick) = @_;
  1231. <
  1232. < chk_registered($user, $chan) or return;
  1233. < my $level = xop_byname($cmd);
  1234. < my $old = cs_xop_ad_pre($user, $chan, $nick, $level, 1);
  1235. < return unless defined($old);
  1236. <
  1237. < my $cn = $chan->{CHAN};
  1238. <
  1239. < unless($old == $level) {
  1240. < notice($user, "\002$nick\002 is not on the ${levels[$level]} list of \002$cn\002.");
  1241. < return;
  1242. < }
  1243. <
  1244. < my $root = get_root_nick($nick);
  1245. < my $srcnick = can_do($chan, 'ACCLIST', $user);
  1246. <
  1247. < del_acc($root, $chan);
  1248. <
  1249. < my $src = get_user_nick($user);
  1250. < my $log_str = "removed \002$root\002 ($nick) from the ${levels[$level]} list of \002$cn\002";
  1251. < notice_all_nicks($user, $root, "\002$src\002 $log_str");
  1252. < ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  1253. < if cr_chk_flag($chan, CRF_VERBOSE);
  1254. < services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  1255. < memolog($chan, "\002$srcnick\002 $log_str");
  1256. < }
  1257. <
  1258. < sub cs_count($$) {
  1259. < my ($user, $chan) = @_;
  1260. <
  1261. < chk_registered($user, $chan) or return;
  1262. <
  1263. < my $cn = $chan->{CHAN};
  1264. < my $overrideMsg = "COUNT $cn";
  1265. < if(can_do($chan, 'ACCLIST', $user, { OVERRIDE_MSG => $overrideMsg })) {
  1266. < } else {
  1267. < return;
  1268. < }
  1269. <
  1270. < my $reply = '';
  1271. < for (my $level = $plzero + 1; $level < COFOUNDER + 2; $level++) {
  1272. < $get_acc_count->execute($cn, $level - 1);
  1273. < my ($num_recs) = $get_acc_count->fetchrow_array;
  1274. < $reply = $reply." $plevels[$level]: ".$num_recs;
  1275. < }
  1276. < notice($user, "\002$cn Count:\002 ".$reply);
  1277. < }
  1278. <
  1279. < sub cs_levels_pre($$$;$) {
  1280. < my($user, $chan, $cmd, $listonly) = @_;
  1281. <
  1282. < chk_registered($user, $chan) or return 0;
  1283. < my $cn = $chan->{CHAN};
  1284. < my $overrideMsg = "LEVELS $cn $cmd";
  1285. < return can_do($chan, ($listonly ? 'LEVELSLIST' : 'LEVELS'), $user, { OVERRIDE_MSG => $overrideMsg });
  1286. < }
  1287. <
  1288. < sub cs_levels_set($$$;$) {
  1289. < my ($user, $chan, $perm, $level) = @_;
  1290. <
  1291. < cs_levels_pre($user, $chan, "$perm $level") or return;
  1292. < my $cn = $chan->{CHAN};
  1293. <
  1294. < unless(is_level($perm)) {
  1295. < notice($user, "$perm is not a valid permission.");
  1296. < return;
  1297. < }
  1298. <
  1299. < if(defined($level)) {
  1300. < $level = xop_byname($level);
  1301. < unless(defined($level) and $level >= 0) {
  1302. < notice($user, 'You must specify one of the following levels: '.
  1303. < 'any, uop, vop, hop, aop, sop, cofounder, founder, nobody');
  1304. < return;
  1305. < }
  1306. <
  1307. < $get_level_max->execute($perm);
  1308. < my ($max) = $get_level_max->fetchrow_array;
  1309. < $get_level_max->finish();
  1310. <
  1311. < if($max and $level > $max) {
  1312. < notice($user, "\002$perm\002 cannot be set to " . $plevels[$level+$plzero] . '.');
  1313. < return;
  1314. < }
  1315. <
  1316. < $add_level->execute($cn, $perm);
  1317. < $set_level->execute($level, $cn, $perm);
  1318. <
  1319. < if($level == 8) {
  1320. < notice($user, "\002$perm\002 is now disabled in \002$cn\002.");
  1321. < } else {
  1322. < notice($user, "\002$perm\002 now requires " . $levels[$level] . " access in \002$cn\002.");
  1323. < }
  1324. < } else {
  1325. < $reset_level->execute($perm, $cn);
  1326. <
  1327. < notice($user, "\002$perm\002 has been reset to default.");
  1328. < }
  1329. < }
  1330. <
  1331. < sub cs_levels_list($$) {
  1332. < my ($user, $chan) = @_;
  1333. <
  1334. < cs_levels_pre($user, $chan, 'LIST', 1) or return;
  1335. < my $cn = $chan->{CHAN};
  1336. <
  1337. < $get_levels->execute($cn);
  1338. < my @data;
  1339. < while(my ($name, $def, $lvl) = $get_levels->fetchrow_array) {
  1340. < push @data, [$name,
  1341. < (defined($lvl) ? $plevels[$lvl+$plzero] : $plevels[$def+$plzero]),
  1342. < (defined($lvl) ? '' : '(default)')];
  1343. < }
  1344. <
  1345. < notice($user, columnar { TITLE => "Permission levels for \002$cn\002:",
  1346. < NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT) }, @data);
  1347. < }
  1348. <
  1349. < sub cs_levels_clear($$) {
  1350. < my ($user, $chan) = @_;
  1351. <
  1352. < cs_levels_pre($user, $chan, 'CLEAR') or return;
  1353. < my $cn = $chan->{CHAN};
  1354. <
  1355. < $clear_levels->execute($cn);
  1356. <
  1357. < notice($user, "All permissions have been reset to default.");
  1358. < }
  1359. <
  1360. < sub cs_akick_pre($$$;$) {
  1361. < my ($user, $chan, $overrideMsg, $list) = @_;
  1362. <
  1363. < chk_registered($user, $chan) or return 0;
  1364. <
  1365. < return can_do($chan, ($list ? 'AKICKLIST' : 'AKICK'), $user, { OVERRIDE_MSG => $overrideMsg });
  1366. < }
  1367. <
  1368. < sub cs_akick_add($$$$) {
  1369. < my ($user, $chan, $mask, $reason) = @_;
  1370. < my $cn = $chan->{CHAN};
  1371. <
  1372. < my $adder = cs_akick_pre($user, $chan, "ADD $mask $reason") or return;
  1373. <
  1374. < my ($nick, $ident, $host) = parse_mask($mask);
  1375. <
  1376. < if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
  1377. < notice($user, 'Invalid hostmask.');
  1378. < return;
  1379. < }
  1380. <
  1381. < if($ident eq '') {
  1382. < $nick = $mask;
  1383. <
  1384. < unless(valid_nick($nick)) {
  1385. < $mask = normalize_hostmask($mask);
  1386. < ($nick, $ident, $host) = parse_mask($mask);
  1387. < }
  1388. < }
  1389. <
  1390. < if ($ident eq '' and $host eq '' and !nickserv::is_registered($nick)) {
  1391. < notice($user, "\002$nick\002 is not registered");
  1392. < return;
  1393. < }
  1394. <
  1395. < my $rlength = length($reason);
  1396. < if($rlength >= 350) {
  1397. < notice($user, 'AKick reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  1398. < return;
  1399. < }
  1400. <
  1401. < my $log_str;
  1402. < my $src = get_user_nick($user);
  1403. < if($ident eq '' and $host eq '' and my $old = get_acc($nick, $chan)) {
  1404. < if ($old == -1) {
  1405. < notice($user, "\002$nick\002 is already on the AKick list in \002$cn\002");
  1406. < return;
  1407. < }
  1408. < if($old < get_best_acc($user, $chan) or adminserv::can_do($user, 'SERVOP')) {
  1409. < if ($old == FOUNDER()) {
  1410. < # This is a fallthrough for the override case.
  1411. < # It shouldn't happen otherwise.
  1412. < # I didn't make it part of the previous conditional
  1413. < # b/c just $err_deny is a bit undescriptive in the override case.
  1414. < notice($user, "You can't akick the founder!", $err_deny);
  1415. < return;
  1416. < }
  1417. <
  1418. < my $root = get_root_nick($nick);
  1419. < $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
  1420. < set_acc($nick, $user, $chan, -1);
  1421. < $log_str = "moved \002$nick\002 (root: \002$root\002) from the $levels[$old] list".
  1422. < " to the AKick list of \002$cn\002";
  1423. < notice_all_nicks($user, $root, "\002$src\002 $log_str");
  1424. < } else {
  1425. < notice($user, $err_deny);
  1426. < return;
  1427. < }
  1428. < } else {
  1429. < if($ident eq '' and $host eq '') {
  1430. < $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
  1431. < if (find_auth($cn, $nick)) {
  1432. < # Don't allow a pending AUTH entry to potentially override an AKick entry
  1433. < # Believe it or not, it almost happened with #animechat on SCnet.
  1434. < # This would also end up leaving an orphan entry in the akick table.
  1435. < $nickserv::del_auth->execute($nick, $cn);
  1436. < $nickserv::del_auth->finish();
  1437. < }
  1438. < set_acc($nick, $user, $chan, -1);
  1439. < my $root = get_root_nick($nick);
  1440. < $log_str = "added \002$nick\002 (root: \002$root\002) to the AKick list of \002$cn\002.";
  1441. < } else {
  1442. < ($nick, $ident, $host) = glob2sql($nick, $ident, $host);
  1443. < unless($add_akick->execute($cn, $nick, $ident, $host, $adder, $reason)) {
  1444. < notice($user, "\002$mask\002 is already on the AKick list of \002$cn\002.");
  1445. < return;
  1446. < }
  1447. < $log_str = "added \002$mask\002 to the AKick list of \002$cn\002.";
  1448. < }
  1449. <
  1450. < }
  1451. < notice($user, "You have $log_str");
  1452. < ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  1453. < if cr_chk_flag($chan, CRF_VERBOSE);
  1454. < services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  1455. < memolog($chan, "\002$adder\002 $log_str");
  1456. <
  1457. < akick_allchan($chan);
  1458. < }
  1459. <
  1460. < sub get_akick_by_num($$) {
  1461. < my ($chan, $num) = @_;
  1462. < my $cn = $chan->{CHAN};
  1463. <
  1464. < $get_akick_by_num->execute($cn, $num);
  1465. < my ($nick, $ident, $host) = $get_akick_by_num->fetchrow_array();
  1466. < ($nick, $ident, $host) = sql2glob($nick, $ident, $host);
  1467. < $get_akick_by_num->finish();
  1468. < if(!$nick) {
  1469. < return undef;
  1470. < } elsif($ident eq '' and $host eq '') {
  1471. < # nick based akicks don't use nicks but nickreg.id
  1472. < # so we have to get the nickreg.nick back
  1473. < $nick = nickserv::get_id_nick($nick);
  1474. < }
  1475. < return ($nick, $ident, $host);
  1476. < }
  1477. <
  1478. < sub cs_akick_del($$$) {
  1479. < my ($user, $chan, $mask) = @_;
  1480. < my $cn = $chan->{CHAN};
  1481. <
  1482. < my $adder = cs_akick_pre($user, $chan, "DEL $mask") or return;
  1483. <
  1484. < my @masks;
  1485. < if ($mask =~ /^[0-9\.,-]+$/) {
  1486. < foreach my $num (makeSeqList($mask)) {
  1487. < my ($nick, $ident, $host) = get_akick_by_num($chan, $num - 1) or next;
  1488. < if($ident eq '' and $host eq '') {
  1489. < push @masks, $nick;
  1490. < } else {
  1491. < push @masks, "$nick!$ident\@$host";
  1492. < }
  1493. < }
  1494. < } else {
  1495. < @masks = ($mask);
  1496. < }
  1497. < foreach my $mask (@masks) {
  1498. < my ($nick, $ident, $host) = parse_mask($mask);
  1499. <
  1500. < if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
  1501. < notice($user, 'Invalid hostmask.');
  1502. < return;
  1503. < }
  1504. <
  1505. < if($ident eq '') {
  1506. < $nick = $mask;
  1507. <
  1508. < unless(valid_nick($nick)) {
  1509. < $mask = normalize_hostmask($mask);
  1510. < ($nick, $ident, $host) = parse_mask($mask);
  1511. < }
  1512. < }
  1513. <
  1514. < if ($ident eq '' and $host eq '' and !nickserv::is_registered($nick)) {
  1515. < notice($user, "\002$nick\002 is not registered");
  1516. < return;
  1517. < }
  1518. <
  1519. < my ($success, $log_str) = do_akick_del($chan, $mask, $nick, $ident, $host);
  1520. < my $src = get_user_nick($user);
  1521. < if($success) {
  1522. < notice($user, "\002$src\002 $log_str");
  1523. < services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  1524. < ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag($chan, CRF_VERBOSE);
  1525. < memolog($chan, "\002$adder\002 $log_str");
  1526. < } else {
  1527. < notice($user, $log_str);
  1528. < }
  1529. < }
  1530. < }
  1531. <
  1532. < sub do_akick_del($$$$$) {
  1533. < my ($chan, $mask, $nick, $ident, $host) = @_;
  1534. < my $cn = $chan->{CHAN};
  1535. <
  1536. < my $log_str;
  1537. < if($ident eq '' and $host eq '') {
  1538. < if(get_acc($nick, $chan) == -1) {
  1539. < del_acc($nick, $chan);
  1540. < $del_nick_akick->execute($cn, $nick); $del_nick_akick->finish();
  1541. < my $root = get_root_nick($nick);
  1542. < return (1, "deleted \002$nick\002 (root: \002$root\002) from the AKick list of \002$cn\002.")
  1543. < } else {
  1544. < return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
  1545. < }
  1546. < } else {
  1547. < ($nick, $ident, $host) = glob2sql($nick, $ident, $host);
  1548. < if($del_akick->execute($cn, $nick, $ident, $host) != 0) {
  1549. < return (1, "deleted \002$mask\002 from the AKick list of \002$cn\002.");
  1550. < } else {
  1551. < return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
  1552. < }
  1553. < }
  1554. < }
  1555. <
  1556. < sub cs_akick_list($$) {
  1557. < my ($user, $chan) = @_;
  1558. < my $cn = $chan->{CHAN};
  1559. <
  1560. < cs_akick_pre($user, $chan, 'LIST', 1) or return;
  1561. <
  1562. < my @data;
  1563. <
  1564. < $get_akick_list->execute($cn);
  1565. < my $i = 0;
  1566. < while(my ($nick, $ident, $host, $adder, $reason, $time) = $get_akick_list->fetchrow_array) {
  1567. < if($ident ne '') {
  1568. < ($nick, $ident, $host) = sql2glob($nick, $ident, $host);
  1569. < }
  1570. <
  1571. < if($ident eq '' and $host eq '') {
  1572. < $nick = nickserv::get_id_nick($nick);
  1573. < } else {
  1574. < $nick = "$nick!$ident\@$host";
  1575. < }
  1576. <
  1577. < push @data, ["\002".++$i."\002", $nick, $adder, ($time ? gmtime2($time) : ''), $reason];
  1578. < }
  1579. <
  1580. < notice($user, columnar {TITLE => "AKICK list of \002$cn\002:", DOUBLE=>1,
  1581. < NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  1582. < }
  1583. <
  1584. < sub cs_akick_wipe($$$) {
  1585. < my ($user, $chan) = @_;
  1586. < my $cn = $chan->{CHAN};
  1587. <
  1588. < my $adder = cs_akick_pre($user, $chan, 'WIPE') or return;
  1589. <
  1590. < $drop_akick->execute($cn);
  1591. < $wipe_acc_list->execute($cn, -1);
  1592. < my $log_str = "wiped the AKICK list of \002$cn\002.";
  1593. < my $src = get_user_nick($user);
  1594. < notice($user, "You have $log_str");
  1595. < ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag($chan, CRF_VERBOSE);
  1596. < services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  1597. < memolog($chan, "\002$adder\002 $log_str");
  1598. < }
  1599. <
  1600. < sub cs_akick_enforce($$) {
  1601. < my ($user, $chan) = @_;
  1602. < my $cn = $chan->{CHAN};
  1603. <
  1604. < chk_registered($user, $chan) or return;
  1605. <
  1606. < can_do($chan, 'AKickEnforce', $user, { OVERRIDE_MSG => "AKICK $cn ENFORCE" }) or return;
  1607. <
  1608. < akick_allchan($chan);
  1609. < }
  1610. <
  1611. < =cut
  1612. < cs_command new SrSv::AgentUI::Simple {
  1613. < COMMAND => [qw(info)],
  1614. < SYNTAX => 'LIST:#chan',
  1615. < CALL => \&cs_info,
  1616. < NO_WRAPPER => 1,
  1617. < };
  1618. < =cut
  1619. < sub cs_info($@) {
  1620. < my ($user, @chanList) = @_;
  1621. <
  1622. < my @reply;
  1623. < foreach my $cn (@chanList) {
  1624. < if(ref($cn) eq 'HASH') {
  1625. < $cn = $cn->{CHAN};
  1626. < }
  1627. < elsif($cn =~ /,/) {
  1628. < push @chanList, split(',', $cn);
  1629. < next;
  1630. < }
  1631. < my $chan = { CHAN => $cn };
  1632. < unless(__can_do($chan, 'INFO', undef, 0)) {
  1633. < can_do($chan, 'INFO', $user, { OVERRIDE_MSG => "INFO $cn" })
  1634. < or next;
  1635. < }
  1636. <
  1637. < $get_info->execute($cn);
  1638. < my @result = $get_info->fetchrow_array;
  1639. < unless(@result) {
  1640. < push @reply, "The channel \002$cn\002 is not registered.";
  1641. < next;
  1642. < }
  1643. <
  1644. < my ($descrip, $regd, $last, $topic, $topicer, $modelock, $founder, $successor, $bot, $bantype) = @result;
  1645. <
  1646. < $modelock = modes::sanitize($modelock) unless can_do($chan, 'GETKEY', $user, { NOREPLY => 1 });
  1647. <
  1648. < my @opts;
  1649. <
  1650. < my $topiclock = get_level($chan, 'SETTOPIC');
  1651. < push @opts, "Topic Lock ($levels[$topiclock])" if $topiclock;
  1652. <
  1653. < if(cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE))) {
  1654. < push @reply, "\002$cn\002 is closed and cannot be used: ". get_close($chan);
  1655. < next;
  1656. < }
  1657. <
  1658. < my @extra;
  1659. < push @extra, 'Will not expire' if cr_chk_flag($chan, CRF_HOLD);
  1660. < push @extra, 'Channel is frozen and access suspended' if cr_chk_flag($chan, CRF_FREEZE);
  1661. <
  1662. < push @opts, 'OpGuard' if cr_chk_flag($chan, CRF_OPGUARD);
  1663. < push @opts, 'BotStay' if cr_chk_flag($chan, CRF_BOTSTAY);
  1664. < push @opts, 'SplitOps' if cr_chk_flag($chan, CRF_SPLITOPS);
  1665. < push @opts, 'Verbose' if cr_chk_flag($chan, CRF_VERBOSE);
  1666. < push @opts, 'NeverOp' if cr_chk_flag($chan, CRF_NEVEROP);
  1667. < push @opts, 'Ban type '.$bantype if $bantype;
  1668. < my $opts = join(', ', @opts);
  1669. <
  1670. < my @data;
  1671. <
  1672. < push @data, ['Founder:', $founder];
  1673. < push @data, ['Successor:', $successor] if $successor;
  1674. < push @data, ['Description:', $descrip] if $descrip;
  1675. < push @data, ['Mode lock:', $modelock];
  1676. < push @data, ['Settings:', $opts] if $opts;
  1677. < push @data, ['ChanBot:', $bot] if $bot and $bot ne '';
  1678. < #FIXME: memo level
  1679. < push @data, ['Registered:', gmtime2($regd)],
  1680. < ['Last opping:', gmtime2($last)],
  1681. < ['Time now:', gmtime2(time)];
  1682. <
  1683. < push @reply, columnar {TITLE => "ChanServ info for \002$cn\002:", NOHIGHLIGHT => 1}, @data,
  1684. < {COLLAPSE => \@extra, BULLET => 1};
  1685. < }
  1686. < notice($user, @reply);
  1687. < }
  1688. <
  1689. < sub cs_set_pre($$$$) {
  1690. < my ($user, $chan, $set, $parm) = @_;
  1691. < my $cn = $chan->{CHAN};
  1692. < my $override = 0;
  1693. <
  1694. < my %valid_set = (
  1695. < 'founder' => 1, 'successor' => 1, 'unsuccessor' => 1,
  1696. < #'mlock' => 1, 'modelock' => 1,
  1697. < 'desc' => 1,
  1698. < 'topiclock' => 1, 'greet' => 1, 'opguard' => 1,
  1699. < 'freeze' => 1, 'botstay' => 1, 'verbose' => 1,
  1700. < 'splitops' => 1, 'bantype' => 1, 'dice' => 1,
  1701. < 'welcomeinchan' => 1, 'log' => 1,
  1702. <
  1703. < 'hold' => 1, 'noexpire' => 1, 'no-expire' => 1,
  1704. <
  1705. < 'autovoice' => 1, 'avoice' => 1,
  1706. < 'neverop' => 1, 'noop' => 1,
  1707. < );
  1708. < my %override_set = (
  1709. < 'hold' => 'SERVOP', 'noexpire' => 'SERVOP', 'no-expire' => 'SERVOP',
  1710. < 'freeze' => 'FREEZE', 'botstay' => 'BOT',
  1711. < );
  1712. <
  1713. < chk_registered($user, $chan) or return 0;
  1714. < if($set =~ /m(?:ode)?lock/) {
  1715. < notice($user, "CS SET MLOCK is deprecated and replaced with CS MLOCK",
  1716. < "For more information, please /CS HELP MLOCK");
  1717. < return 0;
  1718. < }
  1719. < unless($valid_set{lc $set}) {
  1720. < notice($user, "$set is not a valid ChanServ setting.");
  1721. < return 0;
  1722. < }
  1723. <
  1724. < if($override_set{lc($set)}) {
  1725. < if(adminserv::can_do($user, $override_set{lc($set)}) ) {
  1726. < if(services_conf_log_overrides) {
  1727. < my $src = get_user_nick($user);
  1728. < wlog($csnick, LOG_INFO(), "\002$src\002 used override CS SET $cn $set $parm");
  1729. < }
  1730. < $override = 1;
  1731. < } else {
  1732. < notice($user, $err_deny);
  1733. < return 0;
  1734. < }
  1735. < }
  1736. < else {
  1737. < can_do($chan, 'SET', $user) or return 0;
  1738. < }
  1739. <
  1740. < return 1;
  1741. < }
  1742. <
  1743. < sub cs_set($$$;$) {
  1744. < my ($user, $chan, $set, $parm) = @_;
  1745. < my $cn = $chan->{CHAN};
  1746. < $set = lc $set;
  1747. <
  1748. < cs_set_pre($user, $chan, $set, $parm) or return;
  1749. <
  1750. < if($set =~ /^founder$/i) {
  1751. < my $override;
  1752. < unless(get_best_acc($user, $chan) == FOUNDER) {
  1753. < if(adminserv::can_do($user, 'SERVOP')) {
  1754. < $override = 1;
  1755. < } else {
  1756. < notice($user, $err_deny);
  1757. < return;
  1758. < }
  1759. < }
  1760. <
  1761. < my $root;
  1762. < unless($root = get_root_nick($parm)) {
  1763. < notice($user, "The nick \002$parm\002 is not registered.");
  1764. < return;
  1765. < }
  1766. <
  1767. < $get_founder->execute($cn);
  1768. < my ($prev) = $get_founder->fetchrow_array;
  1769. < $get_founder->finish();
  1770. <
  1771. < if(lc($root) eq lc($prev)) {
  1772. < notice($user, "\002$parm\002 is already the founder of \002$cn\002.");
  1773. < return;
  1774. < }
  1775. <
  1776. < set_acc($prev, $user, $chan, COFOUNDER);
  1777. <
  1778. < $set_founder->execute($root, $cn); $set_founder->finish();
  1779. < set_acc($root, $user, $chan, FOUNDER);
  1780. <
  1781. < notice($user, ($override ? "The previous founder, \002$prev\002, has" : "You have") . " been moved to the co-founder list of \002$cn\002.");
  1782. < notice_all_nicks($user, $root, "\002$root\002 has been set as the founder of \002$cn\002.");
  1783. < services::ulog($csnick, LOG_INFO(), "set founder of \002$cn\002 to \002$root\002", $user, $chan);
  1784. <
  1785. < $get_successor->execute($cn);
  1786. < my $suc = $get_successor->fetchrow_array; $get_successor->finish();
  1787. < if(lc($suc) eq lc($root)) {
  1788. < $del_successor->execute($cn); $del_successor->finish();
  1789. < notice($user, "Successor has been removed from \002$cn\002.");
  1790. < }
  1791. <
  1792. < return;
  1793. < }
  1794. <
  1795. < if($set eq 'successor') {
  1796. < unless(get_best_acc($user, $chan) == FOUNDER or adminserv::can_do($user, 'SERVOP')) {
  1797. < notice($user, $err_deny);
  1798. < return;
  1799. < }
  1800. <
  1801. < if(get_acc($parm, $chan) == 7) {
  1802. < notice($user, "The channel founder may not be the successor.");
  1803. < return;
  1804. < }
  1805. <
  1806. < my $root;
  1807. < unless($root = get_root_nick($parm)) {
  1808. < notice($user, "The nick \002$parm\002 is not registered.");
  1809. < return;
  1810. < }
  1811. <
  1812. < $set_successor->execute($root, $cn); $set_successor->finish();
  1813. <
  1814. < notice($user, "\002$parm\002 is now the successor of \002$cn\002");
  1815. < services::ulog($csnick, LOG_INFO(), "set successor of \002$cn\002 to \002$root\002", $user, $chan);
  1816. < return;
  1817. < }
  1818. <
  1819. < if($set eq 'unsuccessor') {
  1820. < unless(get_best_acc($user, $chan) == FOUNDER or adminserv::can_do($user, 'SERVOP')) {
  1821. < notice($user, $err_deny);
  1822. < return;
  1823. < }
  1824. <
  1825. < $del_successor->execute($cn); $del_successor->finish();
  1826. <
  1827. < notice($user, "Successor has been removed from \002$cn\002.");
  1828. < services::ulog($csnick, LOG_INFO(), "removed successor from \002$cn\002", $user, $chan);
  1829. < return;
  1830. < }
  1831. <
  1832. < if($set =~ /m(?:ode)?lock/) {
  1833. < my $modes = modes::merge($parm, '+r', 1);
  1834. < $modes = sanitize_mlockable($modes);
  1835. < $set_modelock->execute($modes, $cn);
  1836. <
  1837. < notice($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
  1838. < do_modelock($chan);
  1839. < return;
  1840. < }
  1841. <
  1842. < if($set eq 'desc') {
  1843. < $set_descrip->execute($parm, $cn);
  1844. <
  1845. < notice($user, "Description of \002$cn\002 has been changed.");
  1846. < return;
  1847. < }
  1848. <
  1849. < if($set eq 'topiclock') {
  1850. < my $perm = xop_byname($parm);
  1851. < if($parm =~ /^(?:no|off|false|0)$/i) {
  1852. < cs_levels_set($user, $chan, 'SETTOPIC');
  1853. < cs_levels_set($user, $chan, 'TOPIC');
  1854. < } elsif($perm >= 0 and defined($perm)) {
  1855. < cs_levels_set($user, $chan, 'SETTOPIC', $parm);
  1856. < cs_levels_set($user, $chan, 'TOPIC', $parm);
  1857. < } else {
  1858. < notice($user, 'Syntax: SET <#chan> TOPICLOCK <off|any|uop|vop|hop|aop|sop|cf|founder>');
  1859. < }
  1860. < return;
  1861. < }
  1862. <
  1863. < if($set =~ /^bantype$/i) {
  1864. < unless (misc::isint($parm) and ($parm >= 0 and $parm <= 10)) {
  1865. < notice($user, 'Invalid bantype');
  1866. < return;
  1867. < }
  1868. <
  1869. < $set_bantype->execute($parm, $cn);
  1870. <
  1871. < notice($user, "Ban-Type for \002$cn\002 now set to \002$parm\002.");
  1872. <
  1873. < return;
  1874. < }
  1875. <
  1876. < my $val;
  1877. < if($parm =~ /^(?:no|off|false|0)$/i) { $val = 0; }
  1878. < elsif($parm =~ /^(?:yes|on|true|1)$/i) { $val = 1; }
  1879. < else {
  1880. < notice($user, "Please say \002on\002 or \002off\002.");
  1881. < return;
  1882. < }
  1883. <
  1884. < if($set =~ /^(?:opguard|secureops)$/i) {
  1885. < cr_set_flag($chan, CRF_OPGUARD, $val);
  1886. <
  1887. < if($val) {
  1888. < notice($user,
  1889. < "OpGuard is now \002ON\002.",
  1890. < "Channel status may not be granted by unauthorized users in \002$cn\002."#,
  1891. < #"Note that you must change the $csnick LEVELS settings for VOICE, HALFOP, OP, and/or ADMIN for this setting to have any effect."
  1892. < );
  1893. < } else {
  1894. < notice($user,
  1895. < "OpGuard is now \002OFF\002.",
  1896. < "Channel status may be given freely in \002$cn\002."
  1897. < );
  1898. < }
  1899. <
  1900. < return;
  1901. < }
  1902. <
  1903. < if($set =~ /^(?:splitops)$/i) {
  1904. < cr_set_flag($chan, CRF_SPLITOPS, $val);
  1905. <
  1906. < if($val) {
  1907. < notice($user, "SplitOps is now \002ON\002.");
  1908. < } else {
  1909. < notice($user, "SplitOps is now \002OFF\002.");
  1910. < }
  1911. <
  1912. < return;
  1913. < }
  1914. <
  1915. < if($set =~ /^(hold|no-?expire)$/i) {
  1916. < cr_set_flag($chan, CRF_HOLD, $val);
  1917. <
  1918. < if($val) {
  1919. < notice($user, "\002$cn\002 will not expire");
  1920. < services::ulog($csnick, LOG_INFO(), "has held \002$cn\002", $user, $chan);
  1921. < } else {
  1922. < notice($user, "\002$cn\002 is no longer held from expiration");
  1923. < services::ulog($csnick, LOG_INFO(), "has removed \002$cn\002 from hold", $user, $chan);
  1924. < }
  1925. <
  1926. < return;
  1927. < }
  1928. <
  1929. < if($set =~ /^freeze$/i) {
  1930. < cr_set_flag($chan, CRF_FREEZE, $val);
  1931. <
  1932. < if($val) {
  1933. < notice($user, "\002$cn\002 is now frozen and access suspended");
  1934. < services::ulog($csnick, LOG_INFO(), "has frozen \002$cn\002", $user, $chan);
  1935. < } else {
  1936. < notice($user, "\002$cn\002 is now unfrozen and access restored");
  1937. < services::ulog($csnick, LOG_INFO(), "has unfrozen \002$cn\002", $user, $chan);
  1938. < }
  1939. <
  1940. < return;
  1941. < }
  1942. <
  1943. < if($set =~ /^botstay$/i) {
  1944. < cr_set_flag($chan, CRF_BOTSTAY, $val);
  1945. <
  1946. < if($val) {
  1947. < notice($user, "Bot will now always stay in \002$cn");
  1948. < botserv::bot_join($chan, undef);
  1949. < } else {
  1950. < notice($user, "Bot will now part if less than one user is in \002$cn");
  1951. < botserv::bot_part_if_needed(undef, $chan, "Botstay turned off");
  1952. < }
  1953. <
  1954. < return;
  1955. < }
  1956. < if($set =~ /^verbose$/i) {
  1957. < cr_set_flag($chan, CRF_VERBOSE, $val);
  1958. <
  1959. < if($val) {
  1960. < notice($user, "Verbose mode enabled on \002$cn");
  1961. < }
  1962. < else {
  1963. < notice($user, "Verbose mode disabled on \002$cn");
  1964. < }
  1965. < return;
  1966. < }
  1967. <
  1968. < if($set =~ /^greet$/i) {
  1969. < if($val) {
  1970. < notice($user, "$csnick SET $cn GREET ON is deprecated.",
  1971. < "Please use $csnick LEVELS $cn SET GREET <rank>");
  1972. < } else {
  1973. < cs_levels_set($user, $chan, 'GREET', 'nobody');
  1974. < }
  1975. <
  1976. < return;
  1977. < }
  1978. <
  1979. < if($set =~ /^dice$/i) {
  1980. < if($val) {
  1981. < notice($user, "$csnick SET $cn DICE ON is deprecated.",
  1982. < "Please use $csnick LEVELS $cn SET DICE <rank>");
  1983. < } else {
  1984. < cs_levels_set($user, $chan, 'DICE', 'nobody');
  1985. < }
  1986. <
  1987. < return;
  1988. < }
  1989. <
  1990. < if($set =~ /^welcomeinchan$/i) {
  1991. < cr_set_flag($chan, CRF_WELCOMEINCHAN(), $val);
  1992. <
  1993. < if($val) {
  1994. < notice($user, "WELCOME messages will be put in the channel.");
  1995. < } else {
  1996. < notice($user, "WELCOME messages will be sent privately.");
  1997. < }
  1998. <
  1999. < return;
  2000. < }
  2001. <
  2002. < if($set =~ /^log$/i) {
  2003. < unless(module::is_loaded('logserv')) {
  2004. < notice($user, "module logserv is not loaded, logging is not available.");
  2005. < return;
  2006. < }
  2007. <
  2008. < if($val) {
  2009. < logserv::addchan($user, $cn) and cr_set_flag($chan, CRF_LOG, $val);
  2010. < }
  2011. < else {
  2012. < logserv::delchan($user, $cn) and cr_set_flag($chan, CRF_LOG, $val);
  2013. < }
  2014. < return;
  2015. < }
  2016. <
  2017. < if($set =~ /^a(?:uto)?voice$/i) {
  2018. < cr_set_flag($chan, CRF_AUTOVOICE(), $val);
  2019. <
  2020. < if($val) {
  2021. < notice($user, "All users w/o access will be autovoiced on join.");
  2022. < } else {
  2023. < notice($user, "AUTOVOICE disabled.");
  2024. < }
  2025. <
  2026. < return;
  2027. < }
  2028. <
  2029. < if($set =~ /^(?:never|no)op$/i) {
  2030. < cr_set_flag($chan, CRF_NEVEROP(), $val);
  2031. <
  2032. < if($val) {
  2033. < notice($user, "Users will not be automatically opped on join.");
  2034. < } else {
  2035. < notice($user, "Users with access will now be automatically opped on join.");
  2036. < }
  2037. <
  2038. < return;
  2039. < }
  2040. < }
  2041. <
  2042. < sub cs_why($$@) {
  2043. < my ($user, $chan, @tnicks) = @_;
  2044. <
  2045. < chk_registered($user, $chan) or return;
  2046. <
  2047. < my ($candoNick, $override) = can_do($chan, 'ACCLIST', $user, undef);
  2048. < return unless $candoNick;
  2049. <
  2050. < my $cn = $chan->{CHAN};
  2051. < my @reply;
  2052. < foreach my $tnick (@tnicks) {
  2053. < my $tuser = { NICK => $tnick };
  2054. < unless(get_user_id($tuser)) {
  2055. < notice($user, "\002$tnick\002: No such user.");
  2056. < return;
  2057. < }
  2058. <
  2059. < my $has;
  2060. < if(is_online($tnick)) {
  2061. < $has = 'has';
  2062. < } else {
  2063. < $has = 'had';
  2064. < }
  2065. <
  2066. < my $n;
  2067. < $get_all_acc->execute(get_user_id($tuser), $cn);
  2068. < while(my ($rnick, $acc) = $get_all_acc->fetchrow_array) {
  2069. < $n++;
  2070. < push @reply, "\002$tnick\002 $has $plevels[$acc+$plzero] access to \002$cn\002 due to identification to the nick \002$rnick\002.";
  2071. < }
  2072. < $get_all_acc->finish();
  2073. <
  2074. < unless($n) {
  2075. < push @reply, "\002$tnick\002 has no access to \002$cn\002.";
  2076. < }
  2077. < if(services_conf_log_overrides && $override) {
  2078. < my $src = get_user_nick($user);
  2079. < wlog($csnick, LOG_INFO(), "\002$src\002 used override CS WHY $cn $tnick");
  2080. < }
  2081. < }
  2082. < notice($user, @reply);
  2083. < }
  2084. <
  2085. < sub cs_setmodes($$$@) {
  2086. < my ($user, $cmd, $chan, @args) = @_;
  2087. < no warnings 'void';
  2088. < my $agent = $user->{AGENT} or $csnick;
  2089. < my $src = get_user_nick($user);
  2090. < my $cn = $chan->{CHAN};
  2091. < my $self;
  2092. <
  2093. < if (cr_chk_flag($chan, CRF_FREEZE())) {
  2094. < notice($user, "\002$cn\002 is frozen and access suspended.");
  2095. < return;
  2096. < }
  2097. <
  2098. < if(scalar(@args) == 0) {
  2099. < @args = ($src);
  2100. < $self = 1;
  2101. < } elsif($args[0] =~ /^#/) {
  2102. < foreach my $chn ($cn, @args) {
  2103. < next unless $chn =~ /^#/;
  2104. < no warnings 'prototype'; # we call ourselves
  2105. < cs_setmodes($user, $cmd, { CHAN => $chn });
  2106. < }
  2107. < return;
  2108. < } elsif((scalar(@args) == 1) and (lc($args[0]) eq lc($src))) {
  2109. < $self = 1;
  2110. < }
  2111. <
  2112. < # PROTECT is deprecated. remove it in a couple versions.
  2113. < # It should be called ADMIN under PREFIX_AQ
  2114. < my @mperms = ('VOICE', 'HALFOP', 'OP', 'ADMIN', 'OWNER');
  2115. < my @l = ('v', 'h', 'o', 'a', 'q');
  2116. < my ($level, @modes, $count);
  2117. <
  2118. < if($cmd =~ /voice$/i) { $level = 0 }
  2119. < elsif($cmd =~ /h(alf)?op$/i) { $level = 1 }
  2120. < elsif($cmd =~ /op$/i) { $level = 2 }
  2121. < elsif($cmd =~ /(protect|admin)$/i) { $level = 3 }
  2122. < elsif($cmd =~ /owner$/i) { $level = 4 }
  2123. < my $de = 1 if($cmd =~ s/^de//i);
  2124. < #$cmd =~ s/^de//i;
  2125. <
  2126. < my $acc = get_best_acc($user, $chan);
  2127. <
  2128. < # XXX I'm not sure this is the best way to do it.
  2129. < unless(
  2130. < ($de and $self) or ($self and ($level + 2) <= $acc) or
  2131. < can_do($chan, $mperms[$level], $user, { ACC => $acc, NOREPLY => 1, OVERRIDE_MSG => "$cmd $cn @args" }) )
  2132. < {
  2133. < notice($user, "$cn: $err_deny");
  2134. < return;
  2135. < }
  2136. <
  2137. < my ($override, $check_override);
  2138. <
  2139. < foreach my $target (@args) {
  2140. < my ($tuser);
  2141. <
  2142. < $tuser = ($self ? $user : { NICK => $target } );
  2143. <
  2144. < unless(is_in_chan($tuser, $chan)) {
  2145. < notice($user, "\002$target\002 is not in \002$cn\002.");
  2146. < next;
  2147. < }
  2148. <
  2149. < my $top = get_op($tuser, $chan);
  2150. <
  2151. < if($de) {
  2152. < unless($top & (2**$level)) {
  2153. < notice($user, "\002$target\002 has no $cmd in \002$cn\002.");
  2154. < next;
  2155. < }
  2156. <
  2157. < if(!$override and get_best_acc($tuser, $chan) > $acc) {
  2158. < unless($check_override) {
  2159. < $override = adminserv::can_do($user, 'SUPER');
  2160. < $check_override = 1;
  2161. < }
  2162. < if($check_override and !$override) {
  2163. < notice($user, "\002$target\002 outranks you in \002$cn\002.");
  2164. < next;
  2165. < }
  2166. < }
  2167. < } else {
  2168. < if($top & (2**$level)) {
  2169. < if($self) {
  2170. < notice($user, "You already have $cmd in \002$cn\002.");
  2171. < } else {
  2172. < notice($user, "\002$target\002 already has $cmd in \002$cn\002.");
  2173. < }
  2174. < next;
  2175. < }
  2176. < if (cr_chk_flag($chan, CRF_OPGUARD()) and
  2177. < !can_keep_op($user, $chan, $tuser, $l[$level]))
  2178. < {
  2179. < notice($user, "$target may not hold ops in $cn because OpGuard is enabled. ".
  2180. < "Please respect the founders wishes.");
  2181. < next;
  2182. < }
  2183. < }
  2184. <
  2185. < push @modes, [($de ? '-' : '+').$l[$level], $target];
  2186. < $count++;
  2187. <
  2188. < }
  2189. <
  2190. < ircd::setmode2(agent($chan), $cn, @modes) if scalar @modes;
  2191. < ircd::notice(agent($chan), '%'.$cn, "$src used ".($de ? "de$cmd" : $cmd).' '.join(' ', @args))
  2192. < if !$self and (lc $user->{AGENT} eq lc $csnick) and cr_chk_flag($chan, CRF_VERBOSE);
  2193. < }
  2194. <
  2195. < sub cs_drop($$) {
  2196. < my ($user, $chan) = @_;
  2197. < my $cn = $chan->{CHAN};
  2198. <
  2199. < chk_registered($user, $chan) or return;
  2200. <
  2201. < unless(get_best_acc($user, $chan) == FOUNDER or adminserv::can_do($user, 'SERVOP')) {
  2202. < notice($user, $err_deny);
  2203. < return;
  2204. < }
  2205. <
  2206. < drop($chan);
  2207. < notice($user, $cn.' has been dropped.');
  2208. < services::ulog($csnick, LOG_INFO(), "dropped $cn", $user, $chan);
  2209. <
  2210. < undef($enforcers{lc $cn});
  2211. < botserv::bot_part_if_needed(undef(), $chan, "Channel dropped.");
  2212. < }
  2213. <
  2214. < sub cs_kick($$$;$$) {
  2215. < my ($user, $chan, $target, $ban, $reason) = @_;
  2216. <
  2217. < my $cmd = ($ban ? 'KICKBAN' : 'KICK');
  2218. < my $perm = ($ban ? 'BAN' : 'KICK');
  2219. < if(ref($chan) ne 'HASH' || !defined($chan->{CHAN})) {
  2220. < notice($user, "Invalid $cmd command, no channel specified");
  2221. < return;
  2222. < }
  2223. <
  2224. < my $srclevel = get_best_acc($user, $chan);
  2225. <
  2226. < my ($nick, $override) = can_do($chan, ($ban ? 'BAN' : 'KICK'), $user, { ACC => $srclevel });
  2227. < return unless $nick;
  2228. <
  2229. < my $src = get_user_nick($user);
  2230. < my $cn = $chan->{CHAN};
  2231. <
  2232. < $reason = "Requested by $src".($reason?": $reason":'');
  2233. <
  2234. < my @errors = (
  2235. < ["I'm sorry, $src, I'm afraid I can't do that."],
  2236. < ["They are not in \002$cn\002."],
  2237. < [$err_deny],
  2238. < ["User not found"],
  2239. < );
  2240. < my @notinchan = ();
  2241. < my $peace = ({modes::splitmodes(get_modelock($chan))}->{Q}->[0] eq '+');
  2242. <
  2243. < my @targets = split(/\,/, $target);
  2244. < foreach $target (@targets) {
  2245. < my $tuser = { NICK => $target };
  2246. < my $targetlevel = get_best_acc($tuser, $chan);
  2247. <
  2248. < if(lc $target eq lc agent($chan) or adminserv::is_service($tuser)) {
  2249. < push @{$errors[0]}, $target;
  2250. < next;
  2251. < }
  2252. <
  2253. < if(get_user_id($tuser)) {
  2254. < unless(is_in_chan($tuser, $chan)) {
  2255. < if ($ban) {
  2256. < push @notinchan, $tuser;
  2257. < } else {
  2258. < push @{$errors[1]}, $target;
  2259. < }
  2260. < next;
  2261. < }
  2262. < } else {
  2263. < push @{$errors[3]}, $target;
  2264. < next;
  2265. < }
  2266. <
  2267. < if( ( ($peace and $targetlevel > 0) or ($srclevel <= $targetlevel) )
  2268. < and not ($override && check_override($user, ($ban ? 'BAN' : 'KICK'), "$cmd $cn $target")) )
  2269. < {
  2270. < push @{$errors[2]}, $target;
  2271. < next;
  2272. < }
  2273. <
  2274. < if($ban) {
  2275. < kickban($chan, $tuser, undef, $reason);
  2276. < } else {
  2277. < ircd::kick(agent($chan), $cn, $target, $reason) unless adminserv::is_service($user);
  2278. < }
  2279. < }
  2280. <
  2281. < foreach my $errlist (@errors) {
  2282. < if(@$errlist > 1) {
  2283. < my $msg = shift @$errlist;
  2284. <
  2285. < foreach my $e (@$errlist) { $e = "\002$e\002" }
  2286. <
  2287. < notice($user,
  2288. < "Cannot $cmd ".
  2289. < enum("or", @$errlist).
  2290. < ": $msg"
  2291. < );
  2292. < }
  2293. < }
  2294. < cs_ban($user, $chan, '', @notinchan) if ($ban and scalar (@notinchan));
  2295. < }
  2296. <
  2297. < sub cs_kickmask($$$;$$) {
  2298. < my ($user, $chan, $mask, $ban, $reason) = @_;
  2299. <
  2300. < my $srclevel = get_best_acc($user, $chan);
  2301. < my $src = get_user_nick($user);
  2302. < my $cn = $chan->{CHAN};
  2303. <
  2304. < my $candoOpts = { ACC => $srclevel, OVERRIDE_MSG => 'KICK'.($ban ? 'BAN' : '')."MASK $cn $mask $reason" };
  2305. < my ($nick, $override) = can_do($chan, ($ban ? 'BAN' : 'KICK'), $user, $candoOpts);
  2306. < return unless $nick;
  2307. <
  2308. <
  2309. < $reason = "Requested by $src".($reason?": $reason":'');
  2310. <
  2311. < my $count = kickmask_noacc($chan, $mask, $reason, $ban);
  2312. < notice($user, ($count ? "Users kicked from \002$cn\002: $count." : "No users in \002$cn\002 matched $mask."))
  2313. < }
  2314. <
  2315. < sub cs_ban($$$@) {
  2316. < my ($user, $chan, $type, @targets) = @_;
  2317. < my $cn = $chan->{CHAN};
  2318. < my $src = get_user_nick($user);
  2319. <
  2320. < my $srclevel = get_best_acc($user, $chan);
  2321. < my ($nick, $override) = can_do($chan, 'BAN', $user, { ACC => $srclevel });
  2322. < return unless $nick;
  2323. <
  2324. < my @errors = (
  2325. < ["I'm sorry, $src, I'm afraid I can't do that."],
  2326. < ["User not found"],
  2327. < [$err_deny]
  2328. < );
  2329. <
  2330. < my (@bans, @unbans);
  2331. < foreach my $target (@targets) {
  2332. < my $tuser;
  2333. <
  2334. < if(ref($target)) {
  2335. < $tuser = $target;
  2336. < }
  2337. < elsif($target =~ /\,/) {
  2338. < push @targets, split(',', $target);
  2339. < next;
  2340. < }
  2341. < elsif($target eq '') {
  2342. < # Should never happen
  2343. < # but it could, given the split above
  2344. < next;
  2345. < }
  2346. < elsif($target =~ /^-/) {
  2347. < $target =~ s/^\-//;
  2348. < push @unbans, $target;
  2349. < next;
  2350. < }
  2351. < =cut
  2352. < elsif($target =~ /[!@]+/) {
  2353. < ircd::debug("normalizing hostmask $target");
  2354. < #$target = normalize_hostmask($target);
  2355. < #=cut
  2356. < my ($nick, $ident, $host) = parse_mask($target);
  2357. < $nick = '*' unless length($nick);
  2358. < $ident = '*' unless length($ident);
  2359. < $host = '*' unless length($host);
  2360. < $target = "$nick\!$ident\@$host";
  2361. < #=cut
  2362. < ircd::debug("normalized hostmask: $target");
  2363. <
  2364. < push @bans, $target;
  2365. < next;
  2366. < }
  2367. < =cut
  2368. < elsif(valid_nick($target)) {
  2369. < $tuser = { NICK => $target };
  2370. < }
  2371. < elsif($target = validate_ban($target)) {
  2372. < push @bans, $target;
  2373. < next;
  2374. < } else {
  2375. < notice($user, "Not a valid ban target: $target");
  2376. < next;
  2377. < }
  2378. < my $targetlevel = get_best_acc($tuser, $chan);
  2379. <
  2380. < if(lc $target eq lc agent($chan) or adminserv::is_service($tuser)) {
  2381. < push @{$errors[0]}, get_user_nick($tuser);
  2382. < next;
  2383. < }
  2384. <
  2385. < unless(get_user_id($tuser)) {
  2386. < push @{$errors[1]}, get_user_nick($tuser);
  2387. < next;
  2388. < }
  2389. < if( $srclevel <= $targetlevel and not ($override && check_override($user, 'BAN', "BAN $cn $target")) ) {
  2390. < push @{$errors[2]}, $target;
  2391. < next;
  2392. < }
  2393. <
  2394. < push @bans, make_banmask($chan, $tuser, $type);
  2395. < }
  2396. <
  2397. < foreach my $errlist (@errors) {
  2398. < if(@$errlist > 1) {
  2399. < my $msg = shift @$errlist;
  2400. <
  2401. < foreach my $e (@$errlist) { $e = "\002$e\002" }
  2402. <
  2403. < notice($user,
  2404. < "Cannot ban ".
  2405. < enum("or", @$errlist).
  2406. < ": $msg"
  2407. < );
  2408. < }
  2409. < }
  2410. <
  2411. < ircd::ban_list(agent($chan), $cn, +1, 'b', @bans) if (scalar(@bans));
  2412. < ircd::notice(agent($chan), $cn, "$src used BAN ".join(' ', @bans))
  2413. < if (lc $user->{AGENT} eq lc $csnick) and (cr_chk_flag($chan, CRF_VERBOSE) and scalar(@bans));
  2414. < cs_unban($user, $chan, @unbans) if scalar(@unbans);
  2415. < }
  2416. <
  2417. < sub cs_invite($$@) {
  2418. < my ($user, $chan, @targets) = @_;
  2419. < my $src = get_user_nick($user);
  2420. < my $cn = $chan->{CHAN};
  2421. < my $srclevel = get_best_acc($user, $chan);
  2422. <
  2423. < my @errors = (
  2424. < ["They are not online."],
  2425. < ["They are already in \002$cn\002."],
  2426. < [$err_deny]
  2427. < );
  2428. <
  2429. < my @invited;
  2430. < foreach my $target (@targets) {
  2431. < my $tuser;
  2432. < my $tnick;
  2433. < if(ref($target)) {
  2434. < $tuser = $target;
  2435. < $tnick = get_user_nick($tuser);
  2436. < } elsif(lc($src) eq lc($target)) {
  2437. < $tuser = $user;
  2438. < $tnick = $src;
  2439. < } elsif($target =~ /\,/) {
  2440. < push @targets, split(',', $target);
  2441. < next;
  2442. < } elsif($target eq '') {
  2443. < # Should never happen
  2444. < # but it could, given the split above
  2445. < next;
  2446. < } else {
  2447. < $tuser = { NICK => $target };
  2448. < $tnick = $target;
  2449. < }
  2450. <
  2451. < my $candoOpts = { ACC => $srclevel, NOREPLY => 1, OVERRIDE_MSG => "INVITE $cn $target" };
  2452. < if(lc($src) eq lc($tnick)) {
  2453. < unless(can_do($chan, 'InviteSelf', $user, $candoOpts)) {
  2454. < push @{$errors[2]}, $tnick;
  2455. < next;
  2456. < }
  2457. < }
  2458. < else {
  2459. < unless(can_do($chan, 'INVITE', $user, $candoOpts)) {
  2460. < push @{$errors[2]}, $tnick;
  2461. < next;
  2462. < }
  2463. <
  2464. < unless(nickserv::is_online($tnick)) {
  2465. < push @{$errors[0]}, $tnick;
  2466. < next;
  2467. < }
  2468. <
  2469. < # invite is annoying, so punish them mercilessly
  2470. < return if flood_check($user, 2);
  2471. < }
  2472. <
  2473. < if(is_in_chan($tuser, $chan)) {
  2474. < push @{$errors[1]}, $tnick;
  2475. < next;
  2476. < }
  2477. <
  2478. < ircd::invite(agent($chan), $cn, $tnick); push @invited, $tnick;
  2479. < ircd::notice(agent($chan), $tnick, "\002$src\002 has invited you to \002$cn\002.")
  2480. < unless(lc($src) eq lc($tnick));
  2481. < }
  2482. <
  2483. < foreach my $errlist (@errors) {
  2484. < if(@$errlist > 1) {
  2485. < my $msg = shift @$errlist;
  2486. <
  2487. < foreach my $e (@$errlist) { $e = "\002$e\002" }
  2488. <
  2489. < notice($user,
  2490. < "Cannot invite ".
  2491. < enum("or", @$errlist).
  2492. < ": $msg"
  2493. < );
  2494. < }
  2495. < }
  2496. <
  2497. < ircd::notice(agent($chan), $cn, "$src used INVITE ".join(' ', @invited))
  2498. < if (lc $user->{AGENT} eq lc $csnick)and cr_chk_flag($chan, CRF_VERBOSE) and scalar(@invited);
  2499. < }
  2500. <
  2501. < sub cs_close($$$) {
  2502. < my ($user, $chan, $reason, $type) = @_;
  2503. < # $type is a flag, either CRF_CLOSE or CRF_DRONE
  2504. < my $cn = $chan->{CHAN};
  2505. < my $oper;
  2506. <
  2507. < unless($oper = adminserv::can_do($user, 'SERVOP')) {
  2508. < notice($user, $err_deny);
  2509. < return;
  2510. < }
  2511. <
  2512. < my $rlength = length($reason);
  2513. < if($rlength >= 350) {
  2514. < notice($user, 'Close reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  2515. < return;
  2516. < }
  2517. <
  2518. < if(is_registered($chan)) {
  2519. < $drop_acc->execute($cn);
  2520. < $drop_lvl->execute($cn);
  2521. < $del_close->execute($cn);
  2522. < $drop_akick->execute($cn);
  2523. < $drop_welcome->execute($cn);
  2524. < $drop_chantext->execute($cn);
  2525. < $drop_nicktext->execute($cn); # Leftover channel auths
  2526. <
  2527. < $set_founder->execute($oper, $cn);
  2528. < }
  2529. < else {
  2530. < $register->execute($cn, $reason, $oper);
  2531. < }
  2532. < $set_modelock->execute('+rsnt', $cn);
  2533. < do_modelock($chan);
  2534. < set_acc($oper, undef, $chan, FOUNDER);
  2535. <
  2536. < $set_close->execute($cn, $reason, $oper, $type);
  2537. < cr_set_flag($chan, (CRF_FREEZE | CRF_CLOSE | CRF_DRONE), 0); #unset flags
  2538. < cr_set_flag($chan, CRF_HOLD, 1); #set flags
  2539. <
  2540. < my $src = get_user_nick($user);
  2541. < my $time = gmtime2(time);
  2542. < my $cmsg = "is closed [$src $time]: $reason";
  2543. <
  2544. < if ($type == CRF_CLOSE) {
  2545. < cr_set_flag($chan, CRF_CLOSE, 1); #set flags
  2546. < clear_users($chan, "Channel $cmsg");
  2547. < ircd::settopic(agent($chan), $cn, $src, time(), "Channel $cmsg")
  2548. < }
  2549. < elsif ($type == CRF_DRONE) {
  2550. < cr_set_flag($chan, CRF_DRONE, 1); #set flags
  2551. < chan_kill($chan, "$cn $cmsg");
  2552. < }
  2553. <
  2554. < notice($user, "The channel \002$cn\002 is now closed.");
  2555. < services::ulog($csnick, LOG_INFO(), "closed $cn with reason: $reason", $user, $chan);
  2556. < }
  2557. <
  2558. < sub cs_clear_pre($$) {
  2559. < my ($user, $chan) = @_;
  2560. < my $cn = $chan->{CHAN};
  2561. <
  2562. < my $srclevel = get_best_acc($user, $chan);
  2563. <
  2564. < my ($cando, $override) = can_do($chan, 'CLEAR', $user, { ACC => $srclevel });
  2565. < return 0 unless($cando);
  2566. <
  2567. < $get_highrank->execute($cn);
  2568. < my ($highrank_nick, $highrank_level) = $get_highrank->fetchrow_array();
  2569. < $get_highrank->finish();
  2570. <
  2571. < if($highrank_level > $srclevel && !$override) {
  2572. < notice($user, "$highrank_nick outranks you in $cn (level: $levels[$highrank_level])");
  2573. < return 0;
  2574. < }
  2575. <
  2576. < return 1;
  2577. < }
  2578. <
  2579. < sub cs_clear_users($$;$) {
  2580. < my ($user, $chan, $reason) = @_;
  2581. < my $src = get_user_nick($user);
  2582. <
  2583. < cs_clear_pre($user, $chan) or return;
  2584. <
  2585. < my $rlength = length($reason);
  2586. < if($rlength >= 350) {
  2587. < notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  2588. < return;
  2589. < }
  2590. <
  2591. < clear_users($chan, "CLEAR USERS by \002$src\002".($reason?" reason: $reason":''));
  2592. < }
  2593. <
  2594. < sub cs_clear_modes($$;$) {
  2595. < my ($user, $chan, $reason) = @_;
  2596. < my $cn = $chan->{CHAN};
  2597. < my $src = get_user_nick($user);
  2598. <
  2599. < cs_clear_pre($user, $chan) or return;
  2600. <
  2601. < my $rlength = length($reason);
  2602. < if($rlength >= 350) {
  2603. < notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  2604. < return;
  2605. < }
  2606. <
  2607. < my $agent = agent($chan);
  2608. < ircd::notice($agent, $cn, "CLEAR MODES by \002$src\002".($reason?" reason: $reason":''));
  2609. <
  2610. < $get_chanmodes->execute($cn);
  2611. < my ($curmodes) = $get_chanmodes->fetchrow_array;
  2612. < my $ml = get_modelock($chan);
  2613. <
  2614. < # This method may exceed the 12-mode limit
  2615. < # But it seems to succeed anyway, even with more than 12.
  2616. < my ($modes, $parms) = split(/ /, modes::merge(modes::invert($curmodes), $ml, 1). ' * *', 2);
  2617. < # we split this separately,
  2618. < # as otherwise it insists on taking the result of the split as a scalar quantity
  2619. < ircd::setmode($agent, $cn, $modes, $parms);
  2620. < do_modelock($chan);
  2621. < }
  2622. <
  2623. < sub cs_clear_ops($$;$) {
  2624. < my ($user, $chan, $reason) = @_;
  2625. < my $cn = $chan->{CHAN};
  2626. < my $src = get_user_nick($user);
  2627. <
  2628. < cs_clear_pre($user, $chan) or return;
  2629. <
  2630. < my $rlength = length($reason);
  2631. < if($rlength >= 350) {
  2632. < notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  2633. < return;
  2634. < }
  2635. <
  2636. < clear_ops($chan);
  2637. <
  2638. < ircd::notice(agent($chan), $cn, "CLEAR OPS by \002$src\002".($reason?" reason: $reason":''));
  2639. < return 1;
  2640. < }
  2641. <
  2642. < sub cs_clear_bans($$;$$) {
  2643. < my ($user, $chan, $type, $reason) = @_;
  2644. < my $cn = $chan->{CHAN};
  2645. < my $src = get_user_nick($user);
  2646. < $type = 0 unless defined $type;
  2647. <
  2648. < cs_clear_pre($user, $chan) or return;
  2649. <
  2650. < my $rlength = length($reason);
  2651. < if($rlength >= 350) {
  2652. < notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  2653. < return;
  2654. < }
  2655. <
  2656. < clear_bans($chan, $type);
  2657. <
  2658. < ircd::notice(agent($chan), $cn, "CLEAR BANS by \002$src\002".($reason?" reason: $reason":''));
  2659. < }
  2660. <
  2661. < sub cs_welcome_pre($$) {
  2662. < my ($user, $chan) = @_;
  2663. <
  2664. < return can_do($chan, 'WELCOME', $user);
  2665. < }
  2666. <
  2667. < sub cs_welcome_add($$$) {
  2668. < my ($user, $chan, $msg) = @_;
  2669. < my $src = get_best_acc($user, $chan, 1);
  2670. < my $cn = $chan->{CHAN};
  2671. <
  2672. < cs_welcome_pre($user, $chan) or return;
  2673. <
  2674. < my $mlength = length($msg);
  2675. < if($mlength >= 350) {
  2676. < notice($user, 'Welcome Message is too long by '. $mlength-350 .' character(s). Maximum length is 350 characters.');
  2677. < return;
  2678. < }
  2679. <
  2680. < $count_welcome->execute($cn);
  2681. < my $count = $count_welcome->fetchrow_array;
  2682. < if ($count >= 5) {
  2683. < notice($user, 'There is a maximum of five (5) Channel Welcome Messages.');
  2684. < return;
  2685. < }
  2686. <
  2687. < $add_welcome->execute($cn, ++$count, $src, $msg);
  2688. <
  2689. < notice($user, "Welcome message number $count for \002$cn\002 set to:", " $msg");
  2690. < }
  2691. <
  2692. < sub cs_welcome_list($$) {
  2693. < my ($user, $chan) = @_;
  2694. < my $cn = $chan->{CHAN};
  2695. <
  2696. < cs_welcome_pre($user, $chan) or return;
  2697. <
  2698. < $list_welcome->execute($cn);
  2699. <
  2700. < my @data;
  2701. <
  2702. < while(my ($id, $time, $adder, $msg) = $list_welcome->fetchrow_array) {
  2703. < push @data, ["$id.", $adder, gmtime2($time), $msg];
  2704. < }
  2705. < $list_welcome->finish();
  2706. <
  2707. < notice($user, columnar {TITLE => "Welcome message list for \002$cn\002:", DOUBLE=>1,
  2708. < NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  2709. < }
  2710. <
  2711. < sub cs_welcome_del($$$) {
  2712. < my ($user, $chan, $id) = @_;
  2713. < my $cn = $chan->{CHAN};
  2714. <
  2715. < cs_welcome_pre($user, $chan) or return;
  2716. <
  2717. < if ($del_welcome->execute($cn, $id) == 1) {
  2718. < notice($user, "Welcome Message \002$id\002 deleted from \002$cn\002");
  2719. < $consolidate_welcome->execute($cn, $id);
  2720. < }
  2721. < else {
  2722. < notice($user,
  2723. < "Welcome Message number $id for \002$cn\002 does not exist.");
  2724. < }
  2725. < }
  2726. <
  2727. < sub cs_alist($$;$) {
  2728. < my ($user, $chan, $mask) = @_;
  2729. < my $cn = $chan->{CHAN};
  2730. <
  2731. < chk_registered($user, $chan) or return;
  2732. <
  2733. < my $slevel = get_best_acc($user, $chan);
  2734. <
  2735. < can_do($chan, 'ACCLIST', $user, { ACC => $slevel }) or return;
  2736. <
  2737. < my @reply;
  2738. <
  2739. < if($mask) {
  2740. < my ($mnick, $mident, $mhost) = glob2sql(parse_mask($mask));
  2741. < $mnick = '%' if($mnick eq '');
  2742. < $mident = '%' if($mident eq '');
  2743. < $mhost = '%' if($mhost eq '');
  2744. <
  2745. < $get_acc_list2_mask->execute($mnick, $cn, $mnick, $mident, $mhost);
  2746. < while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2_mask->fetchrow_array) {
  2747. < push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
  2748. < push @reply, ' '.($time ? 'Date/time added: '. gmtime2($time).' ' : '').
  2749. < ($last_used ? 'Last used '.time_ago($last_used).' ago' : '') if ($time or $last_used);
  2750. < }
  2751. < $get_acc_list2_mask->finish();
  2752. < } else {
  2753. < $get_acc_list2->execute($cn);
  2754. < while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2->fetchrow_array) {
  2755. < push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
  2756. < push @reply, ' '.($time ? 'Date/time added: '. gmtime2($time).' ' : '').
  2757. < ($last_used ? 'Last used '.time_ago($last_used).' ago' : '') if ($time or $last_used);
  2758. < }
  2759. < $get_acc_list2->finish();
  2760. < }
  2761. <
  2762. < notice($user, "Access list for \002$cn\002:", @reply);
  2763. <
  2764. < return;
  2765. < }
  2766. <
  2767. < sub cs_banlist($$) {
  2768. < my ($user, $chan) = @_;
  2769. < my $cn = $chan->{CHAN};
  2770. < can_do($chan, 'UnbanSelf', $user, { NOREPLY => 1 }) or can_do($chan, 'BAN', $user) or return;
  2771. <
  2772. < my $i = 0; my @data;
  2773. < $list_bans->execute($cn, 0);
  2774. < while(my ($mask, $setter, $time) = $list_bans->fetchrow_array()) {
  2775. < push @data, ["\002".++$i."\002", sql2glob($mask), $setter, ($time ? gmtime2($time) : '')];
  2776. < }
  2777. <
  2778. < notice($user, columnar {TITLE => "Ban list of \002$cn\002:", DOUBLE=>1,
  2779. < NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  2780. < }
  2781. <
  2782. < sub cs_unban($$@) {
  2783. < my ($user, $chan, @parms) = @_;
  2784. < my $cn = $chan->{CHAN};
  2785. <
  2786. < my $self = 1 if ( (scalar(@parms) == 1) and ( lc($parms[0]) eq lc(get_user_nick($user)) ) );
  2787. < if ($parms[0] eq '*') {
  2788. < cs_clear_bans($user, $chan);
  2789. < return;
  2790. < }
  2791. < else {
  2792. < can_do($chan, ($self ? 'UnbanSelf' : 'UNBAN'), $user) or return;
  2793. < }
  2794. <
  2795. < my (@userlist, @masklist);
  2796. < foreach my $parm (@parms) {
  2797. < if(valid_nick($parm)) {
  2798. < my $tuser = ($self ? $user : { NICK => $parm });
  2799. < unless(get_user_id($tuser)) {
  2800. < notice($user, "No such user: \002$parm\002");
  2801. < next;
  2802. < }
  2803. < push @userlist, $tuser;
  2804. < } elsif($parm =~ /^[0-9\.,-]+$/) {
  2805. < foreach my $num (makeSeqList($parm)) {
  2806. < push @masklist, get_ban_num($chan, $num);
  2807. < }
  2808. < } else {
  2809. < push @masklist, $parm;
  2810. < }
  2811. < }
  2812. <
  2813. < if(scalar(@userlist)) {
  2814. < unban_user($chan, @userlist);
  2815. < notice($user, "All bans affecting " .
  2816. < ( $self ? 'you' : enum( 'and', map(get_user_nick($_), @userlist) ) ) .
  2817. < " on \002$cn\002 have been removed.");
  2818. < }
  2819. < if(scalar(@masklist)) {
  2820. < ircd::ban_list(agent($chan), $cn, -1, 'b', @masklist);
  2821. < notice($user, "The following bans have been removed: ".join(' ', @masklist))
  2822. < if scalar(@masklist);
  2823. < }
  2824. < }
  2825. <
  2826. < sub cs_updown($$@) {
  2827. < my ($user, $cmd, @chans) = @_;
  2828. < return cs_updown2($user, $cmd, { CHAN => shift @chans }, @chans)
  2829. < if (defined($chans[1]) and $chans[1] !~ "^\#" and $chans[0] =~ "^\#");
  2830. <
  2831. < @chans = get_user_chans($user)
  2832. < unless (@chans);
  2833. <
  2834. < if (uc($cmd) eq 'UP') {
  2835. < foreach my $cn (@chans) {
  2836. < next unless ($cn =~ /^\#/);
  2837. < my $chan = { CHAN => $cn };
  2838. < next if cr_chk_flag($chan, (CRF_DRONE | CRF_CLOSE | CRF_FREEZE), 1);
  2839. < chanserv::set_modes($user, $chan, chanserv::get_best_acc($user, $chan));
  2840. < }
  2841. < }
  2842. < elsif (uc($cmd) eq 'DOWN') {
  2843. < foreach my $cn (@chans) {
  2844. < next unless ($cn =~ /^\#/);
  2845. < chanserv::unset_modes($user, { CHAN => $cn });
  2846. < }
  2847. < }
  2848. < }
  2849. <
  2850. < sub cs_updown2($$$@) {
  2851. < my ($user, $cmd, $chan, @targets) = @_;
  2852. < no warnings 'void';
  2853. < my $agent = $user->{AGENT} or $csnick;
  2854. < my $cn = $chan->{CHAN};
  2855. <
  2856. < return unless chk_registered($user, $chan);
  2857. < if (cr_chk_flag($chan, CRF_FREEZE())) {
  2858. < notice($user, "\002$cn\002 is frozen and access suspended.");
  2859. < return;
  2860. < }
  2861. <
  2862. < my $acc = get_best_acc($user, $chan);
  2863. < return unless(can_do($chan, 'UPDOWN', $user, { ACC => $acc }));
  2864. <
  2865. < my $updown = ((uc($cmd) eq 'UP') ? 1 : 0);
  2866. <
  2867. < my ($override, $check_override);
  2868. < my (@list, $count);
  2869. < foreach my $target (@targets) {
  2870. <
  2871. < my $tuser = { NICK => $target };
  2872. <
  2873. < unless(is_in_chan($tuser, $chan)) {
  2874. < notice($user, "\002$target\002 is not in \002$cn\002.");
  2875. < next;
  2876. < }
  2877. <
  2878. < if($updown) {
  2879. < push @list, $target;
  2880. < chanserv::set_modes($tuser, $chan, chanserv::get_best_acc($tuser, $chan));
  2881. < }
  2882. < else {
  2883. < my $top = get_op($tuser, $chan);
  2884. < unless($top) {
  2885. < notice($user, "\002$target\002 is already deopped in \002$cn\002.");
  2886. < next;
  2887. < }
  2888. <
  2889. < if(!$override and get_best_acc($tuser, $chan) > $acc) {
  2890. < unless($check_override) {
  2891. < $override = adminserv::can_do($user, 'SUPER');
  2892. < $check_override = 1;
  2893. < }
  2894. < if($check_override and !$override) {
  2895. < notice($user, "\002$target\002 outranks you in \002$cn\002.");
  2896. < next;
  2897. < }
  2898. < }
  2899. < push @list, $target;
  2900. < chanserv::unset_modes($tuser, { CHAN => $cn });
  2901. < }
  2902. < $count++;
  2903. < }
  2904. <
  2905. < my $src = get_user_nick($user);
  2906. < ircd::notice(agent($chan), '%'.$cn, "$src used $cmd ".join(' ', @list))
  2907. < if (lc $user->{AGENT} eq lc $csnick) and cr_chk_flag($chan, CRF_VERBOSE);
  2908. < }
  2909. <
  2910. < sub cs_getkey($$) {
  2911. < my ($user, $chan) = @_;
  2912. < my $cn = $chan->{CHAN};
  2913. <
  2914. < can_do($chan, 'GETKEY', $user) or return;
  2915. <
  2916. < $get_chanmodes->execute($cn);
  2917. < my $modes = $get_chanmodes->fetchrow_array; $get_chanmodes->finish();
  2918. <
  2919. < if(my $key = modes::get_key($modes)) {
  2920. < notice($user, "Channel key for \002$cn\002: $key");
  2921. < }
  2922. < else {
  2923. < notice($user, "\002$cn\002 has no channel key.");
  2924. < }
  2925. < }
  2926. <
  2927. < sub cs_auth($$$@) {
  2928. < my ($user, $chan, $cmd, @args) = @_;
  2929. < my $cn = $chan->{CHAN};
  2930. < $cmd = lc $cmd;
  2931. <
  2932. < return unless chk_registered($user, $chan);
  2933. < return unless can_do($chan, 'AccChange', $user);
  2934. < my $userlevel = get_best_acc($user, $chan);
  2935. < if($cmd eq 'list') {
  2936. < my @data;
  2937. < $list_auth_chan->execute($cn);
  2938. < while(my ($nick, $data) = $list_auth_chan->fetchrow_array()) {
  2939. < my ($adder, $old, $level, $time) = split(/:/, $data);
  2940. < push @data, ["\002$nick\002", $levels[$level], $adder, gmtime2($time)];
  2941. < }
  2942. < if ($list_auth_chan->rows()) {
  2943. < notice($user, columnar {TITLE => "Pending authorizations for \002$cn\002:",
  2944. < NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  2945. < }
  2946. < else {
  2947. < notice($user, "There are no pending authorizations for \002$cn\002");
  2948. < }
  2949. < $list_auth_chan->finish();
  2950. < }
  2951. < elsif($cmd eq 'remove' or $cmd eq 'delete' or $cmd eq 'del') {
  2952. < my ($nick, $adder, $old, $level, $time);
  2953. < my $parm = shift @args;
  2954. < if(misc::isint($parm) and ($nick, $adder, $old, $level, $time) = get_auth_num($cn, $parm))
  2955. < {
  2956. < }
  2957. < elsif (($adder, $old, $level, $time) = get_auth_nick($cn, $parm))
  2958. < {
  2959. < $nick = $parm;
  2960. < }
  2961. < unless ($nick) {
  2962. < # This should normally be an 'else' as the elsif above should prove false
  2963. < # For some reason, it doesn't work. the unless ($nick) fixes it.
  2964. < # It only doesn't work for numbered entries
  2965. < notice($user, "There is no entry for \002$parm\002 in \002$cn\002's AUTH list");
  2966. < return;
  2967. < }
  2968. < $nickserv::del_auth->execute($nick, $cn); $nickserv::del_auth->finish();
  2969. < my $log_str = "deleted AUTH entry $cn $nick $levels[$level]";
  2970. < my $src = get_user_nick($user);
  2971. < notice($user, "You have $log_str");
  2972. < ircd::notice(agent($chan), '%'.$cn, "has \002$src\002 has $log_str")
  2973. < if cr_chk_flag($chan, CRF_VERBOSE);
  2974. < services::ulog($chanserv::csnick, LOG_INFO(), "has $log_str", $user, $chan);
  2975. < }
  2976. < else {
  2977. < notice($user, "Unknown AUTH command \002$cmd\002");
  2978. < }
  2979. < }
  2980. <
  2981. < sub cs_mode($$$@) {
  2982. < my ($user, $chan, $modes_in, @parms_in) = @_;
  2983. < can_do($chan, 'MODE', $user) or return undef;
  2984. < ($modes_in, @parms_in) = validate_chmodes($modes_in, @parms_in);
  2985. <
  2986. < my %permhash = (
  2987. < 'q' => 'OWNER',
  2988. < 'a' => 'ADMIN',
  2989. < 'o' => 'OP',
  2990. < 'h' => 'HALFOP',
  2991. < 'v' => 'VOICE',
  2992. < );
  2993. < my $sign = '+'; my $cn = $chan->{CHAN};
  2994. < my ($modes_out, @parms_out, @bans);
  2995. < foreach my $mode (split(//, $modes_in)) {
  2996. < $sign = $mode if $mode =~ /[+-]/;
  2997. < if ($permhash{$mode}) {
  2998. < my $parm = shift @parms_in;
  2999. < cs_setmodes($user, ($sign eq '-' ? 'de' : '').$permhash{$mode}, $chan, $parm);
  3000. < }
  3001. < elsif ($mode eq 'b') {
  3002. < my $parm = shift @parms_in;
  3003. < if($sign eq '-') {
  3004. < $parm = '-'.$parm;
  3005. < }
  3006. < push @bans, $parm;
  3007. < }
  3008. < elsif($mode =~ /[eIlLkjf]/) {
  3009. < $modes_out .= $mode;
  3010. < push @parms_out, shift @parms_in;
  3011. < } else {
  3012. < $modes_out .= $mode;
  3013. < }
  3014. < }
  3015. <
  3016. < if(scalar(@bans)) {
  3017. < cs_ban($user, $chan, undef, @bans);
  3018. < }
  3019. < return if $modes_out =~ /^[+-]*$/;
  3020. < ircd::setmode(agent($chan), $chan->{CHAN}, $modes_out, join(' ', @parms_out));
  3021. < do_modelock($chan, $modes_out.' '.join(' ', @parms_out));
  3022. <
  3023. < $modes_out =~ s/^[+-]*([+-].*)$/$1/;
  3024. < ircd::notice(agent($chan), '%'.$cn, get_user_nick($user).' used MODE '.join(' ', $modes_out, @parms_out))
  3025. < if (lc $user->{AGENT} eq lc $csnick) and cr_chk_flag($chan, CRF_VERBOSE);
  3026. < }
  3027. <
  3028. < sub cs_copy($$@) {
  3029. < my ($user, $chan1, @args) = @_;
  3030. < my $cn1 = $chan1->{CHAN};
  3031. < my $cn2;
  3032. < my $type;
  3033. < if($args[0] =~ /^#/) {
  3034. < $cn2 = shift @args;
  3035. < $type = 'all';
  3036. < }
  3037. < if($args[0] =~ /(?:acc(?:ess)?|akick|levels|all)/i) {
  3038. < $type = shift @args;
  3039. < $cn2 = shift @args unless $cn2;
  3040. < }
  3041. < my $rank;
  3042. < if($type =~ /^acc(?:ess)?/i) {
  3043. < if($cn2 =~ /^#/) {
  3044. < $rank = shift @args;
  3045. < } else {
  3046. < $rank = $cn2;
  3047. < $cn2 = shift @args;
  3048. < }
  3049. < }
  3050. < unless(defined $cn2 and defined $type) {
  3051. < notice($user, 'Unknown COPY command', 'Syntax: COPY #chan1 [type] #chan2');
  3052. < }
  3053. < my $chan2 = { CHAN => $cn2 };
  3054. < if(lc($cn1) eq lc($cn2)) {
  3055. < notice($user, "You cannot copy a channel onto itself.");
  3056. < }
  3057. < unless(is_registered($chan1)) {
  3058. < notice($user, "Source channel \002$cn1\002 must be registered.");
  3059. < return;
  3060. < }
  3061. < can_do($chan1, 'COPY', $user) or return undef;
  3062. < if(lc $type eq 'all') {
  3063. < if(is_registered($chan2)) {
  3064. < notice($user, "When copying all channel details, destination channel cannot be registered.");
  3065. < return;
  3066. < } elsif(!(get_op($user, $chan2) & ($opmodes{o} | $opmodes{a} | $opmodes{q}))) {
  3067. < # This would be preferred to be a 'opmode_mask' or something
  3068. < # However that might be misleading due to hop not being enough to register
  3069. < notice($user, "You must have channel operator status to register \002$cn2\002.");
  3070. < return;
  3071. < } else {
  3072. < cs_copy_chan_all($user, $chan1, $chan2);
  3073. < return;
  3074. < }
  3075. < } else {
  3076. < unless(is_registered($chan2)) {
  3077. < notice($user, "When copying channel lists, destination channel must be registered.");
  3078. < return;
  3079. < }
  3080. < can_do($chan2, 'COPY', $user) or return undef;
  3081. < }
  3082. < if(lc $type eq 'akick') {
  3083. < cs_copy_chan_akick($user, $chan1, $chan2);
  3084. < } elsif(lc $type eq 'levels') {
  3085. < cs_copy_chan_levels($user, $chan1, $chan2);
  3086. < } elsif($type =~ /^acc(?:ess)?/i) {
  3087. < cs_copy_chan_acc($user, $chan1, $chan2, xop_byname($rank));
  3088. < }
  3089. < }
  3090. <
  3091. < sub cs_copy_chan_all($$$) {
  3092. < my ($user, $chan1, $chan2) = @_;
  3093. < cs_copy_chan_chanreg($user, $chan1, $chan2);
  3094. < cs_copy_chan_levels($user, $chan1, $chan2);
  3095. < cs_copy_chan_acc($user, $chan1, $chan2);
  3096. < cs_copy_chan_akick($user, $chan1, $chan2);
  3097. < return;
  3098. < }
  3099. <
  3100. < sub cs_copy_chan_chanreg($$$) {
  3101. < my ($user, $chan1, $chan2) = @_;
  3102. < my $cn1 = $chan1->{CHAN};
  3103. < my $cn2 = $chan2->{CHAN};
  3104. <
  3105. < copy_chan_chanreg($cn1, $cn2);
  3106. < botserv::bot_join($chan2) unless (lc(agent($chan2)) eq lc($csnick) );
  3107. < do_modelock($chan2);
  3108. < notice($user, "Registration for \002$cn1\002 copied to \002$cn2\002");
  3109. <
  3110. < my $log_str = "copied the channel registration for \002$cn1\002 to \002$cn2\002";
  3111. < services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  3112. <
  3113. < my $src = get_user_nick($user);
  3114. < ircd::notice(agent($chan1), '%'.$cn1, "\002$src\002 $log_str")
  3115. < if cr_chk_flag($chan1, CRF_VERBOSE);
  3116. < ircd::notice(agent($chan2), '%'.$cn2, "\002$src\002 $log_str")
  3117. < if cr_chk_flag($chan2, CRF_VERBOSE);
  3118. < }
  3119. <
  3120. < sub cs_copy_chan_acc($$$;$) {
  3121. < my ($user, $chan1, $chan2, $level) = @_;
  3122. < my $cn1 = $chan1->{CHAN};
  3123. < my $cn2 = $chan2->{CHAN};
  3124. <
  3125. < copy_chan_acc($cn1, $cn2, $level);
  3126. <
  3127. < unless(cr_chk_flag($chan2, CRF_NEVEROP)) {
  3128. < $get_chan_users->execute($cn2); my @targets;
  3129. < while (my ($nick, $uid) = $get_chan_users->fetchrow_array()) {
  3130. < push @targets, $nick unless nr_chk_flag_user({ NICK => $nick, ID => $uid }, NRF_NEVEROP);
  3131. < }
  3132. < cs_updown2($user, 'UP', $chan2, @targets);
  3133. < }
  3134. <
  3135. < notice($user, "Access list for \002$cn1\002 ".
  3136. < ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
  3137. < "copied to \002$cn2\002");
  3138. <
  3139. < my $log_str = "copied the channel access list for \002$cn1\002 ".
  3140. < ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
  3141. < "to \002$cn2\002";
  3142. < services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  3143. <
  3144. < my $src = get_user_nick($user);
  3145. < ircd::notice(agent($chan1), '%'.$cn1, "\002$src\002 $log_str")
  3146. < if cr_chk_flag($chan1, CRF_VERBOSE);
  3147. < ircd::notice(agent($chan2), '%'.$cn2, "\002$src\002 $log_str")
  3148. < if cr_chk_flag($chan2, CRF_VERBOSE);
  3149. < }
  3150. <
  3151. < sub cs_copy_chan_levels($$$) {
  3152. < my ($user, $chan1, $chan2) = @_;
  3153. < my $cn1 = $chan1->{CHAN};
  3154. < my $cn2 = $chan2->{CHAN};
  3155. <
  3156. < copy_chan_levels($cn1, $cn2);
  3157. < notice($user, "LEVELS for \002$cn1\002 copied to \002$cn2\002");
  3158. <
  3159. < my $log_str = "copied the LEVELS list for \002$cn1\002 to \002$cn2\002";
  3160. < services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  3161. <
  3162. < my $src = get_user_nick($user);
  3163. < ircd::notice(agent($chan1), '%'.$cn1, "\002$src\002 $log_str")
  3164. < if cr_chk_flag($chan1, CRF_VERBOSE);
  3165. < ircd::notice(agent($chan2), '%'.$cn2, "\002$src\002 $log_str")
  3166. < if cr_chk_flag($chan2, CRF_VERBOSE);
  3167. < }
  3168. <
  3169. < sub cs_copy_chan_akick($$$) {
  3170. < my ($user, $chan1, $chan2) = @_;
  3171. < my $cn1 = $chan1->{CHAN};
  3172. < my $cn2 = $chan2->{CHAN};
  3173. <
  3174. < copy_chan_akick($cn1, $cn2);
  3175. < notice($user, "Channel AKick list for \002$cn1\002 copied to \002$cn2\002");
  3176. <
  3177. < my $log_str = "copied the AKick list for \002$cn1\002 to \002$cn2\002";
  3178. < services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  3179. <
  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);
  3185. < }
  3186. <
  3187. < sub cs_mlock($$$@) {
  3188. < my ($user, $chan, $cmd, @args) = @_;
  3189. < my $cn = $chan->{CHAN};
  3190. < # does this need its own privilege now?
  3191. < can_do($chan, 'SET', $user) or return;
  3192. < my $modes;
  3193. < if(scalar(@args)) {
  3194. < my ($modes_in, @parms_in) = validate_chmodes(shift @args, @args);
  3195. < $modes = $modes_in.' '.join(' ', @parms_in);
  3196. < @args = undef;
  3197. < }
  3198. <
  3199. < my $cur_modelock = get_modelock($chan);
  3200. < if(lc $cmd eq 'add') {
  3201. < $modes = modes::merge($cur_modelock, $modes, 1);
  3202. < $modes = sanitize_mlockable($modes);
  3203. < $set_modelock->execute($modes, $cn);
  3204. < }
  3205. < elsif(lc $cmd eq 'del') {
  3206. < $modes =~ s/[+-]//g;
  3207. < $modes = modes::add($cur_modelock, "-$modes", 1);
  3208. < $set_modelock->execute($modes, $cn);
  3209. < }
  3210. < elsif(lc $cmd eq 'set') {
  3211. < $modes = modes::merge($modes, "+r", 1);
  3212. < $set_modelock->execute($modes, $cn);
  3213. < }
  3214. < elsif(lc $cmd eq 'reset') {
  3215. < $set_modelock->execute(services_conf_default_channel_mlock, $cn);
  3216. < } else {
  3217. < notice($user, "Unknown MLOCK command \"$cmd\"");
  3218. < return;
  3219. < }
  3220. <
  3221. < notice($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
  3222. < do_modelock($chan);
  3223. <
  3224. < =cut
  3225. < notice($user, columnar {TITLE => "Ban list of \002$cn\002:", DOUBLE=>1,
  3226. < NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  3227. < =cut
  3228. < }
  3229. <
  3230. < use SrSv::MySQL::Stub {
  3231. < getChanUsers => ['COLUMN', "SELECT user.nick FROM chanuser JOIN user ON (user.id=chanuser.nickid)
  3232. < WHERE chanuser.chan=? AND chanuser.joined=1"]
  3233. < };
  3234. <
  3235. < sub cs_resync($@) {
  3236. < my ($user, @cns) = @_;
  3237. < foreach my $cn (@cns) {
  3238. < my $chan = { CHAN => $cn };
  3239. < next unless cs_clear_ops($user, $chan, 'Resync');
  3240. < cs_updown2($user, 'up', $chan, getChanUsers($cn));
  3241. < if(can_do($chan, 'AKickEnforce', $user, { OVERRIDE_MSG => "AKICK $cn ENFORCE", NOREPLY => 1 })) {
  3242. < cs_akick_enforce($user, $chan);
  3243. < }
  3244. < }
  3245. < }
  3246. <
  3247. < sub cs_join($@) {
  3248. < my ($user, @cns) = @_;
  3249. < my @reply;
  3250. < my @out_cns;
  3251. < foreach my $cn (@cns) {
  3252. < if($cn =~ /,/) {
  3253. < push @cns, split(',', $cn);
  3254. < }
  3255. < elsif($cn eq '') {
  3256. < next;
  3257. < }
  3258. < my $chan = { CHAN => $cn };
  3259. < my $cando_opts = { NOREPLY => 1 };
  3260. < if(check_akick($user, $chan, 1)) {
  3261. < push @reply, "You are banned from $cn";
  3262. < next;
  3263. < } elsif(!can_do($chan, 'JOIN', $user, $cando_opts)) {
  3264. < push @reply, "$cn is a private channel.";
  3265. < next;
  3266. < }
  3267. < if(is_in_chan($user, $chan)) {
  3268. < next;
  3269. < }
  3270. < if(can_do($chan, 'InviteSelf', $user, $cando_opts)) {
  3271. < cs_invite($user, $chan, $user);
  3272. < }
  3273. < push @out_cns, $cn;
  3274. <
  3275. < }
  3276. < ircd::svsjoin(get_user_agent($user), get_user_nick($user), @out_cns) if scalar @out_cns;
  3277. < notice($user, @reply) if scalar @reply;
  3278. < }
  3279. <
  3280. < ### MISCELLANEA ###
  3281. <
  3282. < # these are helpers and do NOT check if $cn1 or $cn2 is reg'd
  3283. < sub copy_chan_acc($$;$) {
  3284. < my ($cn1, $cn2, $level) = @_;
  3285. < if($level) {
  3286. < $copy_acc_rank->execute($cn2, $cn1, $level);
  3287. < $copy_acc_rank->finish();
  3288. < } else {
  3289. < $get_founder->execute($cn2);
  3290. < my ($founder) = $get_founder->fetchrow_array;
  3291. < $get_founder->finish();
  3292. <
  3293. < $copy_acc->execute($cn2, $cn1, $founder);
  3294. < $copy_acc->finish();
  3295. < }
  3296. < }
  3297. <
  3298. < sub copy_chan_akick($$;$) {
  3299. < my ($cn1, $cn2) = @_;
  3300. < $copy_akick->execute($cn2, $cn1);
  3301. < $copy_akick->finish();
  3302. < copy_chan_acc($cn1, $cn2, -1);
  3303. < }
  3304. <
  3305. < sub copy_chan_levels($$) {
  3306. < my ($cn1, $cn2) = @_;
  3307. < $copy_levels->execute($cn2, $cn1);
  3308. < $copy_levels->finish();
  3309. < }
  3310. <
  3311. < sub copy_chan_chanreg($$) {
  3312. < my ($cn1, $cn2) = @_;
  3313. < $get_founder->execute($cn1);
  3314. < my ($founder) = $get_founder->fetchrow_array;
  3315. < $get_founder->finish();
  3316. < set_acc($founder, undef, { CHAN => $cn2 }, FOUNDER);
  3317. < $copy_chanreg->execute($cn2, $cn1);
  3318. < $copy_chanreg->finish();
  3319. < }
  3320. <
  3321. < sub do_welcome($$) {
  3322. < my ($user, $chan) = @_;
  3323. < my $cn = $chan->{CHAN};
  3324. <
  3325. < $get_welcomes->execute($cn);
  3326. < if($get_welcomes->rows) {
  3327. < my @welcomes;
  3328. < while(my ($msg) = $get_welcomes->fetchrow_array) {
  3329. < push @welcomes, (cr_chk_flag($chan, CRF_WELCOMEINCHAN) ? '' : "[$cn] " ).$msg;
  3330. < }
  3331. < if(cr_chk_flag($chan, CRF_WELCOMEINCHAN)) {
  3332. < ircd::privmsg(agent($chan), $cn, @welcomes);
  3333. < } else {
  3334. < notice($user, @welcomes);
  3335. < }
  3336. < }
  3337. < $get_welcomes->finish();
  3338. < }
  3339. <
  3340. < sub do_greet($$) {
  3341. < my ($user, $chan) = @_;
  3342. < my $cn = $chan->{CHAN};
  3343. <
  3344. < if(can_do($chan, 'GREET', $user)) {
  3345. < my $src = get_user_nick($user);
  3346. < $nickserv::get_greet->execute(get_user_id($user));
  3347. < my ($greet) = $nickserv::get_greet->fetchrow_array();
  3348. < $nickserv::get_greet->finish();
  3349. < ircd::privmsg(agent($chan), $cn, "[\002$src\002] $greet") if $greet;
  3350. < }
  3351. < }
  3352. <
  3353. < sub chk_registered($$) {
  3354. < my ($user, $chan) = @_;
  3355. <
  3356. < unless(is_registered($chan)) {
  3357. < my $cn = $chan->{CHAN};
  3358. <
  3359. < notice($user, "The channel \002$cn\002 is not registered.");
  3360. < return 0;
  3361. < }
  3362. <
  3363. < return 1;
  3364. < }
  3365. <
  3366. < sub make_banmask($$;$) {
  3367. < my ($chan, $tuser, $type) = @_;
  3368. < my $nick = get_user_nick($tuser);
  3369. <
  3370. < my ($ident, $vhost) = get_vhost($tuser);
  3371. < no warnings 'misc';
  3372. < my ($nick, $ident, $vhost) = make_hostmask(get_bantype($chan), $nick, $ident, $vhost);
  3373. < if($type eq 'q') {
  3374. < $type = '~q:';
  3375. < } elsif($type eq 'n') {
  3376. < $type = '~n:';
  3377. < } else {
  3378. < $type = '';
  3379. < }
  3380. < return $type."$nick!$ident\@$vhost";
  3381. < }
  3382. <
  3383. < sub kickban($$$$) {
  3384. < my ($chan, $user, $mask, $reason) = @_;
  3385. < my $cn = $chan->{CHAN};
  3386. < my $nick = get_user_nick($user);
  3387. <
  3388. < return 0 if adminserv::is_service($user);
  3389. <
  3390. < my $agent = agent($chan);
  3391. <
  3392. < unless($mask) {
  3393. < $mask = make_banmask($chan, $user);
  3394. < }
  3395. <
  3396. < enforcer_join($chan) if (get_user_count($chan) <= 1);
  3397. < ircd::setmode($agent, $cn, '+b', $mask);
  3398. < ircd::flushmodes();
  3399. < ircd::kick($agent, $cn, $nick, $reason);
  3400. < return 1;
  3401. < }
  3402. <
  3403. < sub kickban_multi($$$) {
  3404. < my ($chan, $users, $reason) = @_;
  3405. < my $cn = $chan->{CHAN};
  3406. < my $agent = agent($chan);
  3407. <
  3408. < enforcer_join($chan);
  3409. < ircd::setmode($agent, $cn, '+b', '*!*@*');
  3410. < ircd::flushmodes();
  3411. <
  3412. < foreach my $user (@$users) {
  3413. < next if adminserv::is_ircop($user) or adminserv::is_svsop($user, adminserv::S_HELP());
  3414. < ircd::kick($agent, $cn, get_user_nick($user), $reason);
  3415. < }
  3416. < }
  3417. <
  3418. < sub clear_users($$) {
  3419. < my ($chan, $reason) = @_;
  3420. < my $cn = $chan->{CHAN};
  3421. < my $agent = agent($chan);
  3422. < my $i;
  3423. <
  3424. < enforcer_join($chan);
  3425. < ircd::setmode($agent, $cn, '+b', '*!*@*');
  3426. < ircd::flushmodes();
  3427. < $get_chan_users->execute($cn);
  3428. < while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
  3429. < my $user = { NICK => $nick, ID => $uid };
  3430. < ircd::kick($agent, $cn, $nick, $reason)
  3431. < unless adminserv::is_ircop($user) or adminserv::is_svsop($user, adminserv::S_HELP());
  3432. < $i++;
  3433. < }
  3434. <
  3435. < return $i;
  3436. < }
  3437. <
  3438. < sub kickmask($$$$) {
  3439. < my ($chan, $mask, $reason, $ban) = @_;
  3440. < my $cn = $chan->{CHAN};
  3441. < my $agent = agent($chan);
  3442. <
  3443. < my ($nick, $ident, $host) = glob2sql(parse_mask($mask));
  3444. < $nick = '%' if ($nick eq '');
  3445. < $ident = '%' if ($ident eq '');
  3446. < $host = '%' if ($host eq '');
  3447. <
  3448. < if ($ban) {
  3449. < my $banmask = $nick.'!'.$ident.'@'.$host;
  3450. < $banmask =~ tr/%_/*?/;
  3451. < ircd::setmode($agent, $cn, '+b', $banmask);
  3452. < ircd::flushmodes();
  3453. < }
  3454. <
  3455. < my $i;
  3456. < $get_chan_users_mask->execute($cn, $nick, $ident, $host, $host, $host);
  3457. < while(my ($nick, $uid) = $get_chan_users_mask->fetchrow_array) {
  3458. < my $user = { NICK => $nick, ID => $uid };
  3459. < ircd::kick($agent, $cn, $nick, $reason)
  3460. < unless adminserv::is_service($user);
  3461. < $i++;
  3462. < }
  3463. < $get_chan_users_mask->finish();
  3464. <
  3465. < return $i;
  3466. < }
  3467. <
  3468. < sub kickmask_noacc($$$$) {
  3469. < my ($chan, $mask, $reason, $ban) = @_;
  3470. < my $cn = $chan->{CHAN};
  3471. < my $agent = agent($chan);
  3472. <
  3473. < my ($nick, $ident, $host) = glob2sql(parse_mask($mask));
  3474. < $nick = '%' if ($nick eq '');
  3475. < $ident = '%' if ($ident eq '');
  3476. < $host = '%' if ($host eq '');
  3477. <
  3478. < if ($ban) {
  3479. < my $banmask = $nick.'!'.$ident.'@'.$host;
  3480. < $banmask =~ tr/%_/*?/;
  3481. < ircd::setmode($agent, $cn, '+b', $banmask);
  3482. < ircd::flushmodes();
  3483. < }
  3484. <
  3485. < my $i;
  3486. < $get_chan_users_mask_noacc->execute($cn, $nick, $ident, $host, $host, $host);
  3487. < while(my ($nick, $uid) = $get_chan_users_mask_noacc->fetchrow_array) {
  3488. < my $user = { NICK => $nick, ID => $uid };
  3489. < ircd::kick($agent, $cn, $nick, $reason)
  3490. < unless adminserv::is_service($user);
  3491. < $i++;
  3492. < }
  3493. < $get_chan_users_mask_noacc->finish();
  3494. <
  3495. < return $i;
  3496. < }
  3497. <
  3498. < sub clear_ops($) {
  3499. < my ($chan) = @_;
  3500. < my $cn = $chan->{CHAN};
  3501. < my @modelist;
  3502. < my $agent = agent($chan);
  3503. <
  3504. < $get_chan_users->execute($cn);
  3505. < while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
  3506. < my $user = { NICK => $nick, ID => $uid };
  3507. < my $opmodes = get_op($user, $chan);
  3508. < for(my $i; $i < 5; $i++) {
  3509. < if($opmodes & 2**$i) {
  3510. < push @modelist, ['-'.$opmodes[$i], $nick];
  3511. < }
  3512. < }
  3513. < }
  3514. <
  3515. < ircd::setmode2($agent, $cn, @modelist);
  3516. < }
  3517. <
  3518. < sub clear_bans($;$) {
  3519. < my ($chan, $type) = @_;
  3520. < my $cn = $chan->{CHAN};
  3521. < my @args = ();
  3522. < my $agent = agent($chan);
  3523. < $type = 0 unless defined $type;
  3524. < my $mode = ($type == 128 ? 'e' : 'b');
  3525. <
  3526. < my @banlist = ();
  3527. < $get_all_bans->execute($cn, $type);
  3528. < while(my ($mask) = $get_all_bans->fetchrow_array) {
  3529. < $mask =~ tr/\%\_/\*\?/;
  3530. < push @banlist, $mask;
  3531. < }
  3532. <
  3533. < ircd::ban_list($agent, $cn, -1, $mode, @banlist);
  3534. < ircd::flushmodes();
  3535. < }
  3536. <
  3537. < sub unban_user($@) {
  3538. < my ($chan, @userlist) = @_;
  3539. < my $cn = $chan->{CHAN};
  3540. < my $count;
  3541. < if (defined(&ircd::unban_nick)) {
  3542. < my @nicklist;
  3543. < foreach my $tuser (@userlist) {
  3544. < push @nicklist, get_user_nick($tuser);
  3545. < }
  3546. < ircd::unban_nick(agent($chan), $cn, @nicklist);
  3547. < return scalar(@nicklist);
  3548. < }
  3549. <
  3550. < foreach my $tuser (@userlist) {
  3551. < my $tuid;
  3552. < unless($tuid = get_user_id($tuser)) {
  3553. < next;
  3554. < }
  3555. <
  3556. < my (@bans);
  3557. < # We don't handle extended bans. Yet.
  3558. < $find_bans_chan_user->execute($cn, $tuid, 0);
  3559. < while (my ($mask) = $find_bans_chan_user->fetchrow_array) {
  3560. < $mask =~ tr/\%\_/\*\?/;
  3561. < push @bans, $mask;
  3562. < }
  3563. < $find_bans_chan_user->finish();
  3564. <
  3565. < ircd::ban_list(agent($chan), $cn, -1, 'b', @bans) if scalar(@bans);
  3566. < $delete_bans_chan_user->execute($cn, $tuid, 0); $delete_bans_chan_user->finish();
  3567. < $count++;
  3568. < }
  3569. < return $count;
  3570. < }
  3571. <
  3572. < sub chan_kill($$;$) {
  3573. < my ($chan, $reason, $tusers) = @_;
  3574. < my $cn = $chan->{CHAN};
  3575. < my $agent = agent($chan);
  3576. < my $i;
  3577. <
  3578. < enforcer_join($chan);
  3579. < if ($tusers) {
  3580. < foreach my $tuser (@$tusers) {
  3581. < $tuser->{ID} = $tuser->{__ID} if defined($tuser->{__ID}); # user_join_multi does this.
  3582. < nickserv::kline_user($tuser, services_conf_chankilltime, $reason)
  3583. < unless adminserv::is_ircop($tuser) or adminserv::is_svsop($tuser, adminserv::S_HELP());
  3584. < $i++;
  3585. < }
  3586. < }
  3587. < else {
  3588. < $get_chan_users->execute($cn);
  3589. < while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
  3590. < my $tuser = { NICK => $nick, ID => $uid, AGENT => $agent };
  3591. < nickserv::kline_user($tuser, services_conf_chankilltime, $reason)
  3592. < unless adminserv::is_ircop($tuser) or adminserv::is_svsop($tuser, adminserv::S_HELP());
  3593. < $i++;
  3594. < }
  3595. < }
  3596. <
  3597. < return $i;
  3598. < }
  3599. <
  3600. < sub do_nick_akick($$;$) {
  3601. < my ($tuser, $chan, $root) = @_;
  3602. < my $cn = $chan->{CHAN};
  3603. < unless(defined($root)) {
  3604. < (undef, $root) = get_best_acc($tuser, $chan, 2);
  3605. < }
  3606. <
  3607. < $get_nick_akick->execute($cn, $root);
  3608. < my ($reason) = $get_nick_akick->fetchrow_array(); $get_nick_akick->finish();
  3609. <
  3610. < return 0 if adminserv::is_svsop($tuser, adminserv::S_HELP());
  3611. < kickban($chan, $tuser, undef, "User has been banned from ".$cn.($reason?": $reason":''));
  3612. < }
  3613. <
  3614. < sub check_akick($$;$) {
  3615. < my ($user, $chan, $check_only) = @_;
  3616. <
  3617. < if(adminserv::is_svsop($user, adminserv::S_HELP())) {
  3618. < return 0;
  3619. < }
  3620. < my ($acc, $root) = get_best_acc($user, $chan, 2);
  3621. < if ($acc == -1) {
  3622. < do_nick_akick($user, $chan, $root) unless $check_only;
  3623. < return 1;
  3624. < }
  3625. < my $cn = $chan->{CHAN};
  3626. < my $uid = get_user_id($user);
  3627. < unless($acc) {
  3628. < $get_akick->execute($uid, $cn);
  3629. < if(my @akick = $get_akick->fetchrow_array) {
  3630. < akickban($cn, @akick) unless $check_only;
  3631. < return 1;
  3632. < }
  3633. < }
  3634. < return 0;
  3635. < }
  3636. <
  3637. < sub do_status($$;$) {
  3638. < my ($user, $chan, $check_only) = @_;
  3639. <
  3640. < return 0 if cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
  3641. <
  3642. < my $nick = get_user_nick($user);
  3643. <
  3644. < if(check_akick($user, $chan, $check_only)) {
  3645. < return 0;
  3646. < }
  3647. < my ($acc, $root) = get_best_acc($user, $chan, 2);
  3648. < if(!can_do($chan, 'JOIN', $user, { ACC => $acc, NOREPLY => 1 })) {
  3649. < kickban($chan, $user, undef, 'This is a private channel.')
  3650. < unless $check_only;
  3651. < return 0;
  3652. < }
  3653. <
  3654. < if( !$check_only && is_registered($chan) && !is_neverop_user($user) &&
  3655. < !cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE | CRF_NEVEROP)) )
  3656. < {
  3657. < set_modes($user, $chan, $acc, cr_chk_flag($chan, CRF_SPLITOPS, 0));
  3658. < }
  3659. <
  3660. < return 1;
  3661. < }
  3662. <
  3663. < sub akick_alluser($) {
  3664. < my ($user) = @_;
  3665. < my $uid = get_user_id($user);
  3666. <
  3667. < $get_akick_alluser->execute($uid);
  3668. < while(my @akick = $get_akick_alluser->fetchrow_array) {
  3669. < akickban(@akick);
  3670. < }
  3671. < }
  3672. <
  3673. < sub akick_allchan($) {
  3674. < my ($chan) = @_;
  3675. < my $cn = $chan->{CHAN};
  3676. <
  3677. < $get_akick_allchan->execute($cn);
  3678. < while(my @akick = $get_akick_allchan->fetchrow_array) {
  3679. < akickban($cn, @akick);
  3680. < }
  3681. < }
  3682. <
  3683. < sub akickban(@) {
  3684. < my ($cn, $knick, $bnick, $ident, $host, $reason, $bident) = @_;
  3685. <
  3686. < my $target = { NICK => $knick };
  3687. < my $chan = { CHAN => $cn };
  3688. < return 0 if adminserv::is_svsop($target, adminserv::S_HELP());
  3689. <
  3690. < if($bident) {
  3691. < ($bnick, $ident, $host) = make_hostmask(get_bantype($chan), $knick, $bident, $host);
  3692. < } elsif($host =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
  3693. < ($bnick, $ident, $host) = make_hostmask(4, $knick, $bident, $host);
  3694. < } else {
  3695. < $bnick =~ tr/\%\_/\*\?/;
  3696. < $ident =~ tr/\%\_/\*\?/;
  3697. < $host =~ tr/\%\_/\*\?/;
  3698. < }
  3699. <
  3700. < return kickban($chan, $target, "$bnick!$ident\@$host", "User has been banned from ".$cn.($reason?": $reason":''));
  3701. < }
  3702. <
  3703. < sub notice_all_nicks($$$) {
  3704. < my ($user, $nick, $msg) = @_;
  3705. < my $src = get_user_nick($user);
  3706. <
  3707. < notice($user, $msg);
  3708. < foreach my $u (get_nick_user_nicks $nick) {
  3709. < notice({ NICK => $u, AGENT => $csnick }, $msg) unless lc $src eq lc $u;
  3710. < }
  3711. < }
  3712. <
  3713. < sub xop_byname($) {
  3714. < my ($name) = @_;
  3715. < my $level;
  3716. <
  3717. < if($name =~ /^uop$/i) { $level=1; }
  3718. < elsif($name =~ /^vop$/i) { $level=2; }
  3719. < elsif($name =~ /^hop$/i) { $level=3; }
  3720. < elsif($name =~ /^aop$/i) { $level=4; }
  3721. < elsif($name =~ /^sop$/i) { $level=5; }
  3722. < elsif($name =~ /^co?f(ounder)?$/i) { $level=6; }
  3723. < elsif($name =~ /^founder$/i) { $level=7; }
  3724. < elsif($name =~ /^(any|all|user)/i) { $level=0; }
  3725. < elsif($name =~ /^akick$/i) { $level=-1; }
  3726. < elsif($name =~ /^(none|disabled?|nobody)$/i) { $level=8; }
  3727. <
  3728. < return $level;
  3729. < }
  3730. <
  3731. < sub expire {
  3732. < return if services_conf_noexpire;
  3733. <
  3734. < $get_expired->execute(time() - (86400 * services_conf_chanexpire));
  3735. < while(my ($cn, $founder) = $get_expired->fetchrow_array) {
  3736. < drop({ CHAN => $cn });
  3737. < wlog($csnick, LOG_INFO(), "\002$cn\002 has expired. Founder: $founder");
  3738. < }
  3739. < }
  3740. <
  3741. < sub enforcer_join($) {
  3742. < my ($chan) = @_;
  3743. < my $cn = $chan->{CHAN};
  3744. < my $bot = agent($chan);
  3745. <
  3746. < return if $enforcers{lc $cn};
  3747. < $enforcers{lc $cn} = lc $bot;
  3748. <
  3749. < botserv::bot_join($chan);
  3750. <
  3751. < add_timer("CSEnforce $bot $cn", 60, __PACKAGE__, 'chanserv::enforcer_part');
  3752. < }
  3753. <
  3754. < sub enforcer_part($) {
  3755. < my ($cookie) = @_;
  3756. < my ($junk, $bot, $cn) = split(/ /, $cookie);
  3757. <
  3758. < return unless $enforcers{lc $cn};
  3759. < undef($enforcers{lc $cn});
  3760. <
  3761. < botserv::bot_part_if_needed($bot, {CHAN => $cn}, 'Enforcer Leaving');
  3762. < }
  3763. <
  3764. < sub fix_private_join_before_id($) {
  3765. < my ($user) = @_;
  3766. <
  3767. < my @cns = get_recent_private_chans(get_user_id($user));
  3768. < foreach my $cn (@cns) {
  3769. < my $chan = { CHAN => $cn };
  3770. < unban_user($chan, $user);
  3771. < }
  3772. <
  3773. < ircd::svsjoin($csnick, get_user_nick($user), @cns) if @cns;
  3774. < }
  3775. <
  3776. < ### DATABASE UTILITY FUNCTIONS ###
  3777. <
  3778. < sub get_user_count($) {
  3779. < my ($chan) = @_;
  3780. < my $cn = $chan->{CHAN};
  3781. <
  3782. < $get_user_count->execute($cn);
  3783. <
  3784. < return $get_user_count->fetchrow_array;
  3785. < }
  3786. <
  3787. < sub get_lock($) {
  3788. < my ($chan) = @_;
  3789. <
  3790. < $chan = lc $chan;
  3791. <
  3792. < $chanuser_table++;
  3793. <
  3794. < if($cur_lock) {
  3795. < if($cur_lock ne $chan) {
  3796. < really_release_lock($chan);
  3797. < $chanuser_table--;
  3798. < die("Tried to get two locks at the same time: $cur_lock, $chan")
  3799. < }
  3800. < $cnt_lock++;
  3801. < } else {
  3802. < $cur_lock = $chan;
  3803. < $get_lock->execute(sql_conf_mysql_db.".chan.$chan");
  3804. < $get_lock->finish;
  3805. < }
  3806. < }
  3807. <
  3808. < sub release_lock($) {
  3809. < my ($chan) = @_;
  3810. <
  3811. < $chan = lc $chan;
  3812. <
  3813. < $chanuser_table--;
  3814. <
  3815. < if($cur_lock and $cur_lock ne $chan) {
  3816. < really_release_lock($cur_lock);
  3817. <
  3818. < die("Tried to release the wrong lock");
  3819. < }
  3820. <
  3821. < if($cnt_lock) {
  3822. < $cnt_lock--;
  3823. < } else {
  3824. < really_release_lock($chan);
  3825. < }
  3826. < }
  3827. <
  3828. < sub really_release_lock($) {
  3829. < my ($chan) = @_;
  3830. <
  3831. < $cnt_lock = 0;
  3832. < $release_lock->execute(sql_conf_mysql_db.".chan.$chan");
  3833. < $release_lock->finish;
  3834. < undef $cur_lock;
  3835. < }
  3836. <
  3837. < #sub is_free_lock($) {
  3838. < # $is_free_lock->execute($_[0]);
  3839. < # return $is_free_lock->fetchrow_array;
  3840. < #}
  3841. <
  3842. < sub get_modelock($) {
  3843. < my ($chan) = @_;
  3844. < my $cn;
  3845. < if(ref($chan)) {
  3846. < $cn = $chan->{CHAN}
  3847. < } else {
  3848. < $cn = $chan;
  3849. < }
  3850. <
  3851. < $get_modelock->execute($cn);
  3852. < my ($ml) = $get_modelock->fetchrow_array;
  3853. < $get_modelock->finish();
  3854. < return $ml;
  3855. < }
  3856. <
  3857. < sub do_modelock($;$) {
  3858. < my ($chan, $modes) = @_;
  3859. < my $cn = $chan->{CHAN};
  3860. <
  3861. < my $seq = $ircline;
  3862. <
  3863. < $get_modelock_lock->execute; $get_modelock_lock->finish;
  3864. <
  3865. < $get_chanmodes->execute($cn);
  3866. < my ($omodes) = $get_chanmodes->fetchrow_array;
  3867. < my $ml = get_modelock($chan);
  3868. <
  3869. < $ml = do_modelock_fast($cn, $modes, $omodes, $ml);
  3870. <
  3871. < $unlock_tables->execute; $unlock_tables->finish;
  3872. <
  3873. < ircd::setmode(agent($chan), $cn, $ml) if($ml);
  3874. < }
  3875. <
  3876. < sub do_modelock_fast($$$$) {
  3877. < my ($cn, $modes, $omodes, $ml) = @_;
  3878. < my $nmodes = modes::add($omodes, $modes, 1);
  3879. < $ml = modes::diff($nmodes, $ml, 1);
  3880. < $set_chanmodes->execute(modes::add($nmodes, $ml, 1), $cn);
  3881. <
  3882. < return $ml;
  3883. < }
  3884. <
  3885. < sub update_modes($$) {
  3886. < my ($cn, $modes) = @_;
  3887. <
  3888. < $get_update_modes_lock->execute; $get_update_modes_lock->finish;
  3889. < $get_chanmodes->execute($cn);
  3890. < my ($omodes) = $get_chanmodes->fetchrow_array;
  3891. <
  3892. < $set_chanmodes->execute(modes::add($omodes, $modes, 1), $cn);
  3893. < $unlock_tables->execute; $unlock_tables->finish;
  3894. < }
  3895. <
  3896. < sub is_level($) {
  3897. < my ($perm) = @_;
  3898. <
  3899. < $is_level->execute($perm);
  3900. <
  3901. < return $is_level->fetchrow_array;
  3902. < }
  3903. <
  3904. < sub is_neverop($) {
  3905. < return nr_chk_flag($_[0], NRF_NEVEROP(), 1);
  3906. < }
  3907. <
  3908. < sub is_neverop_user($) {
  3909. < return nr_chk_flag_user($_[0], NRF_NEVEROP(), 1);
  3910. < }
  3911. <
  3912. < sub is_in_chan($$) {
  3913. < my ($user, $chan) = @_;
  3914. < my $cn = $chan->{CHAN};
  3915. < my $uid = get_user_id($user);
  3916. <
  3917. < $is_in_chan->execute($uid, $cn);
  3918. < if($is_in_chan->fetchrow_array) {
  3919. < return 1;
  3920. < }
  3921. <
  3922. < return 0;
  3923. < }
  3924. <
  3925. < sub is_registered($) {
  3926. < my ($chan) = @_;
  3927. < my $cn = $chan->{CHAN};
  3928. <
  3929. < $is_registered->execute($cn);
  3930. < if($is_registered->fetchrow_array) {
  3931. < return 1;
  3932. < } else {
  3933. < return 0;
  3934. < }
  3935. < }
  3936. <
  3937. < sub get_user_chans($) {
  3938. < my ($user) = @_;
  3939. < my $uid = get_user_id($user);
  3940. < my @chans;
  3941. <
  3942. < $get_user_chans->execute($uid, $ircline, $ircline+1000);
  3943. < while(my ($chan) = $get_user_chans->fetchrow_array) {
  3944. < push @chans, $chan;
  3945. < }
  3946. <
  3947. < return (@chans);
  3948. < }
  3949. <
  3950. < sub get_user_chans_recent($) {
  3951. < my ($user) = @_;
  3952. < my $uid = get_user_id($user);
  3953. < my (@curchans, @oldchans);
  3954. <
  3955. < $get_user_chans_recent->execute($uid);
  3956. < while(my ($cn, $joined, $op) = $get_user_chans_recent->fetchrow_array) {
  3957. < if ($joined) {
  3958. < push @curchans, make_op_prefix($op).$cn;
  3959. < }
  3960. < else {
  3961. < push @oldchans, $cn;
  3962. < }
  3963. < }
  3964. <
  3965. < return (\@curchans, \@oldchans);
  3966. < }
  3967. <
  3968. < my ($prefixes, $modes);
  3969. < sub make_op_prefix($) {
  3970. < my ($op) = @_;
  3971. < return unless $op;
  3972. <
  3973. < unless(defined($prefixes) and defined($modes)) {
  3974. < $IRCd_capabilities{PREFIX} =~ /^\((\S+)\)(\S+)$/;
  3975. < ($modes, $prefixes) = ($1, $2);
  3976. < $modes = reverse $modes;
  3977. < $prefixes = reverse $prefixes;
  3978. < }
  3979. <
  3980. < my $op_prefix = '';
  3981. < for(my $i = 0; $i < length($prefixes); $i++) {
  3982. < $op_prefix = substr($prefixes, $i, 1).$op_prefix if ($op & (2**$i));
  3983. < }
  3984. < return $op_prefix;
  3985. < }
  3986. <
  3987. < sub get_op($$) {
  3988. < my ($user, $chan) = @_;
  3989. < my $cn = $chan->{CHAN};
  3990. < my $uid = get_user_id($user);
  3991. <
  3992. < $get_op->execute($uid, $cn);
  3993. < my ($op) = $get_op->fetchrow_array;
  3994. <
  3995. < return $op;
  3996. < }
  3997. <
  3998. < sub get_best_acc($$;$) {
  3999. < my ($user, $chan, $retnick) = @_;
  4000. < my $uid = get_user_id($user);
  4001. < my $cn = $chan->{CHAN};
  4002. <
  4003. < $get_best_acc->execute($uid, $cn);
  4004. < my ($bnick, $best) = $get_best_acc->fetchrow_array;
  4005. < $get_best_acc->finish();
  4006. <
  4007. < if($retnick == 2) {
  4008. < return ($best, $bnick);
  4009. < } elsif($retnick == 1) {
  4010. < return $bnick;
  4011. < } else {
  4012. < return $best;
  4013. < }
  4014. < }
  4015. <
  4016. < sub get_acc($$) {
  4017. < my ($nick, $chan) = @_;
  4018. < my $cn = $chan->{CHAN};
  4019. <
  4020. < return undef
  4021. < if cr_chk_flag($chan, (CRF_DRONE | CRF_CLOSE | CRF_FREEZE), 1);
  4022. <
  4023. < $get_acc->execute($cn, $nick);
  4024. < my ($acc) = $get_acc->fetchrow_array;
  4025. <
  4026. < return $acc;
  4027. < }
  4028. <
  4029. < sub set_acc($$$$) {
  4030. < my ($nick, $user, $chan, $level) = @_;
  4031. < my $cn = $chan->{CHAN};
  4032. < my $adder = get_best_acc($user, $chan, 1) if $user;
  4033. <
  4034. < $set_acc1->execute($cn, $level, $nick);
  4035. < $set_acc2->execute($level, $adder, $cn, $nick);
  4036. <
  4037. < if ( ( $level > 0 and !is_neverop($nick) and !cr_chk_flag($chan, CRF_NEVEROP) )
  4038. < or $level < 0)
  4039. < {
  4040. < set_modes_allnick($nick, $chan, $level);
  4041. < }
  4042. < }
  4043. <
  4044. < sub del_acc($$) {
  4045. < my ($nick, $chan) = @_;
  4046. < my $cn = $chan->{CHAN};
  4047. <
  4048. < $del_acc->execute($cn, $nick);
  4049. <
  4050. < foreach my $user (get_nick_users $nick) {
  4051. < set_modes($user, $chan, 0, 1) if is_in_chan($user, $chan);
  4052. < }
  4053. < }
  4054. <
  4055. < sub get_auth_nick($$) {
  4056. < my ($cn, $nick) = @_;
  4057. <
  4058. < $get_auth_nick->execute($cn, $nick);
  4059. < my ($data) = $get_auth_nick->fetchrow_array();
  4060. < $get_auth_nick->finish();
  4061. <
  4062. < return split(/:/, $data);
  4063. < }
  4064. < sub get_auth_num($$) {
  4065. < my ($cn, $num) = @_;
  4066. <
  4067. < $get_auth_num->execute($cn, $num - 1);
  4068. < my ($nick, $data) = $get_auth_num->fetchrow_array();
  4069. < $get_auth_num->finish();
  4070. <
  4071. < return ($nick, split(/:/, $data));
  4072. < }
  4073. < sub find_auth($$) {
  4074. < my ($cn, $nick) = @_;
  4075. <
  4076. < $find_auth->execute($cn, $nick);
  4077. < my ($ret) = $find_auth->fetchrow_array();
  4078. < $find_auth->finish();
  4079. <
  4080. < return $ret;
  4081. < }
  4082. <
  4083. < # Only call this if you've checked the user for NEVEROP already.
  4084. < sub set_modes_allchan($;$) {
  4085. < my ($user, $neverop) = @_;
  4086. < my $uid = get_user_id($user);
  4087. <
  4088. < $get_user_chans->execute($uid, $ircline, $ircline+1000);
  4089. < while(my ($cn) = $get_user_chans->fetchrow_array) {
  4090. < my $chan = { CHAN => $cn };
  4091. < my $acc = get_best_acc($user, $chan);
  4092. < if($acc > 0) {
  4093. < set_modes($user, $chan, $acc) unless ($neverop or cr_chk_flag($chan, CRF_NEVEROP));
  4094. < } elsif($acc < 0) {
  4095. < do_nick_akick($user, $chan);
  4096. < }
  4097. < }
  4098. < }
  4099. <
  4100. < # Only call this if you've checked for NEVEROP already.
  4101. < sub set_modes_allnick($$$) {
  4102. < my ($nick, $chan, $level) = @_;
  4103. < my $cn = $chan->{CHAN};
  4104. <
  4105. < $get_using_nick_chans->execute($nick, $cn);
  4106. < while(my ($n) = $get_using_nick_chans->fetchrow_array) {
  4107. < my $user = { NICK => $n };
  4108. < my $l = get_best_acc($user, $chan);
  4109. < if($l > 0) {
  4110. < set_modes($user, $chan, $level, 1) if($level == $l);
  4111. < } elsif($l < 0) {
  4112. < do_nick_akick($user, $chan);
  4113. < }
  4114. < }
  4115. < }
  4116. <
  4117. < # If channel has OPGUARD, $doneg is true.
  4118. < sub set_modes($$$;$) {
  4119. < my ($user, $chan, $acc, $doneg) = @_;
  4120. < my $cn = $chan->{CHAN};
  4121. <
  4122. <
  4123. < if ($acc < 0) {
  4124. < # Do akick stuff here.
  4125. < }
  4126. <
  4127. < my $dst = ( $acc > 0 ? $ops[$acc] : 0 );
  4128. < my $cur = get_op($user, $chan);
  4129. < my ($pos, $neg);
  4130. <
  4131. < if (cr_chk_flag($chan, CRF_FREEZE)) {
  4132. < set_mode_mask($user, $chan, $cur, undef);
  4133. < return;
  4134. < }
  4135. < if (($acc == 0) and cr_chk_flag($chan, CRF_AUTOVOICE)) {
  4136. < set_mode_mask($user, $chan, $cur, 1);
  4137. < return;
  4138. < }
  4139. <
  4140. < $pos = $dst ^ ($dst & $cur);
  4141. < $neg = ($dst ^ $cur) & $cur if $doneg;
  4142. <
  4143. < if($pos or $neg) {
  4144. < set_mode_mask($user, $chan, $neg, $pos);
  4145. < }
  4146. <
  4147. < if($pos) {
  4148. < set_lastop($cn);
  4149. < set_lastused($cn, get_user_id($user));
  4150. < }
  4151. < }
  4152. <
  4153. < sub unset_modes($$) {
  4154. < my ($user, $chan) = @_;
  4155. <
  4156. < my $mask = get_op($user, $chan);
  4157. <
  4158. < set_mode_mask($user, $chan, $mask, 0);
  4159. < }
  4160. <
  4161. < sub set_mode_mask($$$$) {
  4162. < my ($user, $chan, @masks) = @_;
  4163. < my $nick = get_user_nick($user);
  4164. < my $cn = $chan->{CHAN};
  4165. < my (@args, $out);
  4166. <
  4167. < for(my $sign; $sign < 2; $sign++) {
  4168. < next if($masks[$sign] == 0);
  4169. <
  4170. < $out .= '-' if $sign == 0;
  4171. < $out .= '+' if $sign == 1;
  4172. <
  4173. < for(my $i; $i < 5; $i++) {
  4174. < my @l = ('v', 'h', 'o', 'a', 'q');
  4175. <
  4176. < if($masks[$sign] & 2**$i) {
  4177. < $out .= $l[$i];
  4178. < push @args, $nick;
  4179. < }
  4180. < }
  4181. < }
  4182. <
  4183. < if(@args) {
  4184. < ircd::setmode(agent($chan), $cn, $out, join(' ', @args));
  4185. < }
  4186. < }
  4187. <
  4188. < sub get_level($$) {
  4189. < my ($chan, $perm) = @_;
  4190. < my $cn = $chan->{CHAN};
  4191. <
  4192. < $get_level->execute($cn, $perm);
  4193. < my ($level, $isnotnull) = $get_level->fetchrow_array;
  4194. < $get_level->finish();
  4195. <
  4196. < if (wantarray()) {
  4197. < return ($level, $isnotnull);
  4198. < }
  4199. < else {
  4200. < return $level;
  4201. < }
  4202. < }
  4203. <
  4204. < sub check_override($$;$) {
  4205. < my ($user, $perm, $logMsg) = @_;
  4206. < $perm = uc $perm;
  4207. <
  4208. < #{OVERRIDE::$perm} produces funny package problems, so wrap it in double-quotes.
  4209. < if(exists($user->{"OVERRIDE::$perm"}) && (my $nick = $user->{"OVERRIDE::$perm"})) {
  4210. < if(defined($nick)) {
  4211. < if(services_conf_log_overrides && $logMsg) {
  4212. < my $src = get_user_nick($user);
  4213. < wlog($csnick, LOG_INFO(), "\002$src\002 used override $logMsg");
  4214. < }
  4215. < return (wantarray ? ($nick, 1) : $nick);
  4216. < } else {
  4217. < return;
  4218. < }
  4219. < }
  4220. < foreach my $o (@override) {
  4221. < my ($operRank, $permHashRef) = @$o;
  4222. < if($permHashRef->{$perm} and my $nick = adminserv::can_do($user, $operRank)) {
  4223. < $user->{"OVERRIDE::$perm"} = $nick;
  4224. < if(services_conf_log_overrides && $logMsg) {
  4225. < my $src = get_user_nick($user);
  4226. < wlog($csnick, LOG_INFO(), "\002$src\002 used override $logMsg");
  4227. < }
  4228. < return (wantarray ? ($nick, 1) : $nick);
  4229. < }
  4230. < }
  4231. < $user->{"OVERRIDE::$perm"} = undef;
  4232. < }
  4233. <
  4234. < sub can_do($$$;$) {
  4235. < my ($chan, $perm, $user, $data) = @_;
  4236. < $data = {} unless defined $data;
  4237. < # $data is a hashref/struct
  4238. < my $noreply = $data->{NOREPLY};
  4239. < my $acc = $data->{ACC};
  4240. < my $overrideMsg = $data->{OVERRIDE_MSG};
  4241. <
  4242. < if(my $nick = __can_do($chan, $perm, $user, $acc)) {
  4243. < # This is becoming increasingly complicated
  4244. < # and checking if an override was used is becoming tricky.
  4245. < # We had a case in cs_kick where an oper should be able to override +Q/$peace
  4246. < # but cannot b/c they have regular access in that channel.
  4247. < my $override;
  4248. < if(defined($user)) {
  4249. < (undef, $override) = check_override($user, $perm);
  4250. < }
  4251. < return (wantarray ? ($nick, $override) : $nick);
  4252. < } elsif ( $user and adminserv::is_svsop($user, adminserv::S_HELP()) ) {
  4253. < #set_lastused($cn, get_user_id($user));
  4254. < my ($nick, $override) = check_override($user, $perm, $overrideMsg);
  4255. < return (wantarray ? ($nick, $override) : $nick) if $override;
  4256. < }
  4257. < if($user and !$noreply) {
  4258. < my $cn = $chan->{CHAN};
  4259. < if (cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE))) {
  4260. < notice($user, "\002$cn\002 is closed and cannot be used".
  4261. < ((uc $perm eq 'INFO') ? ': '.get_close($chan) : '.'));
  4262. < }
  4263. < elsif(cr_chk_flag($chan, CRF_FREEZE)) {
  4264. < notice($user, "\002$cn\002 is frozen and access suspended.");
  4265. < }
  4266. < else {
  4267. < notice($user, "$cn: $err_deny");
  4268. < }
  4269. < }
  4270. < return 0;
  4271. < }
  4272. <
  4273. < sub __can_do($$$;$) {
  4274. < my ($chan, $perm, $user, $acc) = @_;
  4275. < my $nick;
  4276. < my $cn = $chan->{CHAN};
  4277. < $perm = uc $perm;
  4278. <
  4279. < my $level;
  4280. < unless(exists($chan->{"PERM::$perm"})) {
  4281. < $level = $chan->{"PERM::$perm"} = get_level($chan, $perm);
  4282. < } else {
  4283. < $level = $chan->{"PERM::$perm"};
  4284. < }
  4285. <
  4286. < unless(defined($acc)) {
  4287. < unless (defined $user && ref($user) eq 'HASH') {
  4288. < die "invalid __can_do call";
  4289. < }
  4290. < my $chanuser = $user->{lc $cn};
  4291. < unless (defined($chanuser) && exists($chanuser->{ACC})) {
  4292. < ($acc, $nick) = get_best_acc($user, $chan, 2);
  4293. < ($chanuser->{ACC}, $chanuser->{ACCNICK}) = ($acc, $nick);
  4294. < } else {
  4295. < ($acc, $nick) = ($chanuser->{ACC}, $chanuser->{ACCNICK});
  4296. < }
  4297. < }
  4298. < $nick = 1 unless $nick;
  4299. <
  4300. < if($acc >= $level and !cr_chk_flag($chan, (CRF_CLOSE | CRF_FREEZE | CRF_DRONE))) {
  4301. < set_lastused($cn, get_user_id($user)) if $user;
  4302. < return (wantarray ? ($nick, 0) : $nick);
  4303. < }
  4304. <
  4305. < if(cr_chk_flag($chan, CRF_FREEZE) and ($perm eq 'JOIN')) {
  4306. < return (wantarray ? ($nick, 0) : $nick);
  4307. < }
  4308. <
  4309. < return 0;
  4310. < }
  4311. <
  4312. < sub can_keep_op($$$$) {
  4313. < # This is a naοve implemenation using a loop.
  4314. < # If we ever do a more flexible version that further restricts how
  4315. < # LEVELS affect opguard, the loop will have to be unrolled.
  4316. < # --
  4317. < # Only call this if you've already checked opguard, as we do not check it here.
  4318. < # --
  4319. < # Remember, this isn't a permission check if someone is allowed to op someone [else],
  4320. < # rather this checks if the person being opped is allowed to keep/have it.
  4321. < my ($user, $chan, $tuser, $opmode) = @_;
  4322. < return 1 if $opmode eq 'v'; # why remove a voice?
  4323. < my %permhash = (
  4324. < 'q' => ['OWNER', 4],
  4325. < 'a' => ['ADMIN', 3],
  4326. < 'o' => ['OP', 2],
  4327. < 'h' => ['HALFOP', 1],
  4328. < 'v' => ['VOICE', 0]
  4329. < );
  4330. <
  4331. < my $self = (lc(get_user_nick($user)) eq lc(get_user_nick($tuser)));
  4332. <
  4333. < #my ($level, $isnotnull) = get_level($chan, $permhash{$opmode}[1]);
  4334. < my $level = get_level($chan, $permhash{$opmode}[0]);
  4335. <
  4336. < foreach my $luser ($tuser, $user) {
  4337. < # We check target first, as there seems no reason that
  4338. < # someone who has access can't be opped by someone
  4339. < # who technically doesn't.
  4340. < return 1 if (adminserv::is_svsop($luser, adminserv::S_HELP()) and
  4341. < check_override($luser, $permhash{$opmode}[0]));
  4342. <
  4343. < my $acc = get_best_acc($luser, $chan);
  4344. < return 1 if ($self and ($permhash{opmode}[2] + 2) <= $acc);
  4345. <
  4346. < if($acc < $level) {
  4347. < return 0;
  4348. < }
  4349. < }
  4350. <
  4351. < return 1;
  4352. < }
  4353. <
  4354. < sub agent($) {
  4355. < my ($chan) = @_;
  4356. <
  4357. < return $chan->{AGENT} if($chan->{AGENT});
  4358. <
  4359. < unless(initial_synced()) {
  4360. < return $csnick;
  4361. < }
  4362. <
  4363. < $botserv::get_chan_bot->execute($chan->{CHAN});
  4364. < my ($agent) = $botserv::get_chan_bot->fetchrow_array;
  4365. <
  4366. < $agent = $csnick unless $agent;
  4367. <
  4368. < return $chan->{AGENT} = $agent;
  4369. < }
  4370. <
  4371. < sub drop($) {
  4372. < my ($chan) = @_;
  4373. < my $cn = $chan->{CHAN};
  4374. <
  4375. < undef($enforcers{lc $cn});
  4376. < my $agent = agent($chan);
  4377. < agent_part($agent, $cn, 'Channel dropped') unless (lc($agent) eq lc($csnick));
  4378. < if (module::is_loaded('logserv')) {
  4379. < eval { logserv::delchan(undef, $cn); }
  4380. < }
  4381. <
  4382. < $drop_acc->execute($cn);
  4383. < $drop_lvl->execute($cn);
  4384. < $del_close->execute($cn);
  4385. < $drop_akick->execute($cn);
  4386. < $drop_welcome->execute($cn);
  4387. < $drop_chantext->execute($cn);
  4388. < $drop_nicktext->execute($cn); # Leftover channel auths
  4389. < $drop->execute($cn);
  4390. < ircd::setmode($csnick, $cn, '-r');
  4391. < }
  4392. <
  4393. < sub drop_nick_chans($) {
  4394. < my ($nick) = @_;
  4395. <
  4396. < $delete_successors->execute($nick);
  4397. <
  4398. < $get_nick_own_chans->execute($nick);
  4399. < while(my ($cn) = $get_nick_own_chans->fetchrow_array) {
  4400. < succeed_chan($cn, $nick);
  4401. < }
  4402. < }
  4403. <
  4404. < sub succeed_chan($$) {
  4405. < my ($cn, $nick) = @_;
  4406. <
  4407. < $get_successor->execute($cn);
  4408. < my ($suc) = $get_successor->fetchrow_array;
  4409. <
  4410. < if($suc) {
  4411. < $set_founder->execute($suc, $cn);
  4412. < set_acc($suc, undef, {CHAN => $cn}, FOUNDER);
  4413. < $del_successor->execute($cn);
  4414. < } else {
  4415. < drop({CHAN => $cn});
  4416. < wlog($csnick, LOG_INFO(), "\002$cn\002 has been dropped due to expiry/drop of \002$nick\002");
  4417. < }
  4418. < }
  4419. <
  4420. < sub get_close($) {
  4421. < my ($chan) = @_;
  4422. < my $cn = $chan->{CHAN};
  4423. < return undef unless cr_chk_flag($chan, CRF_CLOSE | CRF_DRONE);
  4424. <
  4425. < $get_close->execute($cn);
  4426. < my ($reason, $opnick, $time) = $get_close->fetchrow_array();
  4427. < $get_close->finish();
  4428. <
  4429. < $reason = "[$opnick ".gmtime2($time)."] - $reason";
  4430. <
  4431. < return (wantarray ? ($reason, $opnick, $time) : $reason);
  4432. < }
  4433. <
  4434. < sub get_users_nochans(;$) {
  4435. < my ($noid) = @_;
  4436. < my @users;
  4437. <
  4438. < if($noid) {
  4439. < $get_users_nochans_noid->execute();
  4440. < while (my ($usernick, $userid) = $get_users_nochans_noid->fetchrow_array()) {
  4441. < push @users, { NICK => $usernick, ID => $userid };
  4442. < }
  4443. < $get_users_nochans_noid->finish();
  4444. < }
  4445. < else {
  4446. < $get_users_nochans->execute();
  4447. < while (my ($usernick, $userid) = $get_users_nochans->fetchrow_array()) {
  4448. < push @users, { NICK => $usernick, ID => $userid };
  4449. < }
  4450. < $get_users_nochans->finish();
  4451. < }
  4452. <
  4453. < return @users;
  4454. < }
  4455. <
  4456. < sub get_bantype($) {
  4457. < my ($chan) = @_;
  4458. < my $cn = $chan->{CHAN};
  4459. <
  4460. < unless (exists($chan->{BANTYPE})) {
  4461. < $get_bantype->execute($cn);
  4462. < ($chan->{BANTYPE}) = $get_bantype->fetchrow_array();
  4463. < $get_bantype->finish();
  4464. < }
  4465. <
  4466. < return $chan->{BANTYPE};
  4467. < }
  4468. <
  4469. < sub memolog($$) {
  4470. < my ($chan, $log) = @_;
  4471. <
  4472. < my $level = get_level($chan, "MemoAccChange");
  4473. < return if $level == 8; # 8 is 'disable'
  4474. < $level = 1 if $level == 0;
  4475. < memoserv::send_chan_memo($csnick, $chan, $log, $level);
  4476. < }
  4477. <
  4478. < sub get_ban_num($$) {
  4479. < my ($chan, $num) = @_;
  4480. < $get_ban_num->execute($chan->{CHAN}, 0, $num-1);
  4481. < my ($mask) = $get_ban_num->fetchrow_array();
  4482. < $get_ban_num->finish();
  4483. < return sql2glob($mask);
  4484. < }
  4485. <
  4486. < ### IRC EVENTS ###
  4487. <
  4488. < sub user_join($$) {
  4489. < # Due to special casing of '0' this wrapper should be used
  4490. < # by anyone handling a JOIN (not SJOIN, it's a JOIN) event.
  4491. < # This is an RFC1459 requirement.
  4492. < my ($nick, $cn) = @_;
  4493. < my $user = { NICK => $nick };
  4494. < my $chan = { CHAN => $cn };
  4495. <
  4496. < if ($cn == 0) {
  4497. < # This should be treated as a number
  4498. < # Just in case we ever got passed '000', not that Unreal does.
  4499. < # In C, you could check that chan[0] != '#' && chan[0] == '0'
  4500. < user_part_multi($user, [ get_user_chans($user) ], 'Left all channels');
  4501. < }
  4502. < else {
  4503. < user_join_multi($chan, [$user]);
  4504. < }
  4505. < }
  4506. <
  4507. < sub handle_sjoin($$$$$$$) {
  4508. < my ($server, $cn, $ts, $chmodes, $chmodeparms, $userarray, $banarray, $exceptarray) = @_;
  4509. < my $chan = { CHAN => $cn };
  4510. <
  4511. < if(synced()) {
  4512. < chan_mode($server, $cn, $chmodes, $chmodeparms) if $chmodes;
  4513. < } else {
  4514. < update_modes($cn, "$chmodes $chmodeparms") if $chmodes;
  4515. < }
  4516. < user_join_multi($chan, $userarray) if scalar @$userarray;
  4517. <
  4518. < foreach my $ban (@$banarray) {
  4519. < process_ban($cn, $ban, $server, 0, 1);
  4520. < }
  4521. < foreach my $except (@$exceptarray) {
  4522. < process_ban($cn, $except, $server, 128, 1);
  4523. < }
  4524. < }
  4525. <
  4526. < sub user_join_multi($$) {
  4527. < my ($chan, $users) = @_;
  4528. < my $cn = $chan->{CHAN};
  4529. < my $seq = $ircline;
  4530. < my $multi_tradeoff = 2; # could use some synthetic-benchmark tuning
  4531. <
  4532. < foreach my $user (@$users) {
  4533. < $user->{__ID} = get_user_id($user);
  4534. < unless (defined($user->{__ID})) {
  4535. < # This does happen occasionally. it's a BUG.
  4536. < # At least we have a diagnostic for it now.
  4537. < # Normally we'd just get a [useless] warning from the SQL server
  4538. < ircd::debug($user->{NICK}.' has a NULL user->{__ID} in user_join_multi('.$cn.', ...');
  4539. < }
  4540. < }
  4541. <
  4542. < $get_joinpart_lock->execute; $get_joinpart_lock->finish;
  4543. <
  4544. < $chan_create->execute($seq, $cn);
  4545. <
  4546. < $get_user_count->execute($cn);
  4547. < my ($count) = $get_user_count->fetchrow_array;
  4548. <
  4549. < if(scalar(@$users) < $multi_tradeoff) {
  4550. < foreach my $user (@$users) {
  4551. < # see note above in get_user_id loop
  4552. < if (defined($user->{__ID})) {
  4553. < $chanjoin->execute($seq, $user->{__ID}, $cn, $user->{__OP});
  4554. < }
  4555. < }
  4556. < }
  4557. < else {
  4558. < my $query = "REPLACE INTO chanuser (seq, nickid, chan, op, joined) VALUES ";
  4559. < foreach my $user (@$users) {
  4560. < # a join(',', list) would be nice but would involve preparing the list first.
  4561. < # I think this will be faster.
  4562. < if (defined($user->{__ID})) {
  4563. < # see note above in get_user_id loop
  4564. < $query .= '('.$dbh->quote($seq).','.
  4565. < $dbh->quote($user->{__ID}).','.
  4566. < $dbh->quote($cn).','.
  4567. < $dbh->quote($user->{__OP}).', 1),';
  4568. < }
  4569. < }
  4570. < $query =~ s/\,$//;
  4571. < $dbh->do($query);
  4572. < }
  4573. <
  4574. < $unlock_tables->execute; $unlock_tables->finish;
  4575. <
  4576. < my $bot = agent($chan);
  4577. < foreach my $user (@$users) {
  4578. < $user->{AGENT} = $bot;
  4579. < }
  4580. <
  4581. < if(initial_synced() and cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE))) {
  4582. < my ($reason, $opnick, $time) = get_close($chan);
  4583. < my $cmsg = "$cn is closed: $reason";
  4584. < my $preenforce = $enforcers{lc $chan};
  4585. <
  4586. < if (cr_chk_flag($chan, CRF_CLOSE)) {
  4587. < kickban_multi($chan, $users, $cmsg);
  4588. < }
  4589. < elsif (cr_chk_flag($chan, CRF_DRONE)) {
  4590. < chan_kill($chan, $cmsg, $users);
  4591. < }
  4592. <
  4593. < unless($preenforce) {
  4594. < ircd::settopic($bot, $cn, $opnick, $time, $cmsg);
  4595. <
  4596. < my $ml = get_modelock($chan);
  4597. < ircd::setmode($bot, $cn, $ml) if($ml);
  4598. < }
  4599. < }
  4600. <
  4601. < if(($count == 0 or !is_agent_in_chan($bot, $cn)) and initial_synced()) {
  4602. < unless (lc($bot) eq lc($csnick)) {
  4603. < unless(is_agent_in_chan($bot, $cn)) {
  4604. < botserv::bot_join($chan);
  4605. < }
  4606. < }
  4607. < }
  4608. <
  4609. < return unless synced() and not cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
  4610. <
  4611. < my $n;
  4612. < foreach my $user (@$users) {
  4613. < if(do_status($user, $chan)) {
  4614. < $n++;
  4615. < $user->{__DO_WELCOME} = 1;
  4616. < }
  4617. < }
  4618. <
  4619. < if($count == 0 and $n) {
  4620. < my ($ml) = get_modelock($chan);
  4621. < ircd::setmode($bot, $cn, $ml) if($ml);
  4622. <
  4623. < $get_topic->execute($cn);
  4624. < my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
  4625. < ircd::settopic($bot, $cn, $nsetter, $ntime, $ntopic) if $ntopic;
  4626. < }
  4627. <
  4628. < ircd::flushmodes();
  4629. <
  4630. < if($n) {
  4631. < foreach my $user (@$users) {
  4632. < if ($user->{__DO_WELCOME} and chk_user_flag($user, UF_FINISHED())) {
  4633. < do_welcome($user, $chan);
  4634. < do_greet($user, $chan)
  4635. < if can_do($chan, 'GREET', $user, { NOREPLY => 1 });
  4636. < }
  4637. < }
  4638. < }
  4639. < }
  4640. <
  4641. < sub user_part($$$) {
  4642. < my ($nick, $cn, $reason) = @_;
  4643. <
  4644. < my $user = ( ref $nick eq 'HASH' ? $nick : { NICK => $nick });
  4645. <
  4646. < user_part_multi($user, [ $cn ], $reason);
  4647. < }
  4648. <
  4649. < sub user_part_multi($$$) {
  4650. < # user_join_multi takes a channel and multiple users
  4651. < # user_part_multi takes a user and multiple channels
  4652. < # There should probably be a user_join_* that takes one user, multiple channels
  4653. < # However, it seems that so far, Unreal splits both PART and JOIN (non-SJOIN)
  4654. < # into multiple events/cmds. The reason is unclear.
  4655. < # Other ircds may not do so.
  4656. < # There is also KICK. some IRCds allow KICK #chan user1,user2,...
  4657. < # Unreal it's _supposed_ to work, but it does not.
  4658. <
  4659. < my ($user, $chanlist, $reason) = @_;
  4660. < my @chans;
  4661. < foreach my $cn (@$chanlist) {
  4662. < push @chans, { CHAN => $cn };
  4663. <
  4664. < }
  4665. <
  4666. < my $uid = get_user_id($user);
  4667. < my $seq = $ircline;
  4668. <
  4669. < $get_joinpart_lock->execute; $get_joinpart_lock->finish;
  4670. <
  4671. < foreach my $chan (@chans) {
  4672. < my $cn = $chan->{CHAN};
  4673. < $chanpart->execute($seq, $uid, $cn, $seq, $seq+1000);
  4674. < $get_user_count->execute($cn);
  4675. < $chan->{COUNT} = $get_user_count->fetchrow_array;
  4676. < }
  4677. <
  4678. < $unlock_tables->execute; $unlock_tables->finish;
  4679. <
  4680. < foreach my $chan (@chans) {
  4681. < channel_emptied($chan) if $chan->{COUNT} == 0;
  4682. < }
  4683. < }
  4684. <
  4685. < sub channel_emptied($) {
  4686. < my ($chan) = @_;
  4687. <
  4688. < botserv::bot_part_if_needed(undef, $chan, 'Nobody\'s here', 1);
  4689. < $chan_delete->execute($chan->{CHAN});
  4690. < $wipe_bans->execute($chan->{CHAN});
  4691. < }
  4692. <
  4693. < sub process_kick($$$$) {
  4694. < my ($src, $cn, $target, $reason) = @_;
  4695. < my $tuser = { NICK => $target };
  4696. < user_part($tuser, $cn, 'Kicked by '.$src.' ('.$reason.')');
  4697. <
  4698. < my $chan = { CHAN => $cn };
  4699. < if ( !(is_agent($src) or $src =~ /\./ or adminserv::is_ircop({ NICK => $src })) and
  4700. < ({modes::splitmodes(get_modelock($chan))}->{Q}->[0] eq '+') )
  4701. < {
  4702. < my $srcUser = { NICK => $src };
  4703. < #ircd::irckill(agent($chan), $src, "War script detected (kicked $target past +Q in $cn)");
  4704. < nickserv::kline_user($srcUser, 300, "War script detected (kicked $target past +Q in $cn)");
  4705. < # SVSJOIN won't work while they're banned, unless you invite.
  4706. < ircd::invite(agent($chan), $cn, $target);
  4707. < ircd::svsjoin(undef, $target, $cn);
  4708. < unban_user($chan, $tuser);
  4709. < }
  4710. < }
  4711. <
  4712. < sub chan_mode($$$$) {
  4713. < my ($src, $cn, $modes, $args) = @_;
  4714. < my $user = { NICK => $src };
  4715. < my $chan = { CHAN => $cn };
  4716. < my ($sign, $num);
  4717. <
  4718. < # XXX This is not quite right, but maybe it's good enough.
  4719. < my $mysync = ($src =~ /\./ ? 0 : 1);
  4720. <
  4721. < if($modes !~ /^[beIvhoaq+-]+$/ and (!synced() or $mysync)) {
  4722. < do_modelock($chan, "$modes $args");
  4723. < }
  4724. <
  4725. < my $opguard = (!current_message->{SYNC} and cr_chk_flag($chan, CRF_OPGUARD, 1));
  4726. <
  4727. < my @perms = ('VOICE', 'HALFOP', 'OP', 'PROTECT');
  4728. < my $unmodes = '-';
  4729. < my @unargs;
  4730. <
  4731. < my @modes = split(//, $modes);
  4732. < my @args = split(/ /, $args);
  4733. <
  4734. < foreach my $mode (@modes) {
  4735. < if($mode eq '+') { $sign = 1; next; }
  4736. < if($mode eq '-') { $sign = 0; next; }
  4737. <
  4738. < my $arg = shift(@args) if($mode =~ $scm or $mode =~ $ocm);
  4739. < my $auser = { NICK => $arg };
  4740. <
  4741. < if($mode =~ /^[vhoaq]$/) {
  4742. < next if $arg eq '';
  4743. < next if is_agent($arg);
  4744. < $num = 0 if $mode eq 'v';
  4745. < $num = 1 if $mode eq 'h';
  4746. < $num = 2 if $mode eq 'o';
  4747. < $num = 3 if $mode eq 'a';
  4748. < $num = 4 if $mode eq 'q';
  4749. <
  4750. < if($opguard and $sign == 1 and
  4751. < !can_keep_op($user, $chan, $auser, $mode)
  4752. < ) {
  4753. < $unmodes .= $mode;
  4754. < push @unargs, $arg;
  4755. < } else {
  4756. < my $nid = get_user_id($auser) or next;
  4757. < my ($r, $i);
  4758. < do {
  4759. < if($sign) {
  4760. < $r = $chop->execute((2**$num), (2**$num), $nid, $cn);
  4761. < } else {
  4762. < $r = $chdeop->execute((2**$num), (2**$num), $nid, $cn);
  4763. < }
  4764. < $i++;
  4765. < } while($r==0 and $i<10);
  4766. < }
  4767. < }
  4768. < if ($mode eq 'b') {
  4769. < next if $arg eq '';
  4770. < process_ban($cn, $arg, $src, 0, $sign);
  4771. < }
  4772. < if ($mode eq 'e') {
  4773. < next if $arg eq '';
  4774. < process_ban($cn, $arg, $src, 128, $sign);
  4775. < }
  4776. < if ($mode eq 'I') {
  4777. < next;# if $arg eq '';
  4778. < #process_ban($cn, $arg, $src, 128, $sign);
  4779. < }
  4780. < }
  4781. < ircd::setmode(agent($chan), $cn, $unmodes, join(' ', @unargs)) if($opguard and @unargs);
  4782. < }
  4783. <
  4784. < sub process_ban($$$$) {
  4785. < my ($cn, $arg, $src, $type, $sign) = @_;
  4786. <
  4787. < $arg =~ tr/\*\?/\%\_/;
  4788. <
  4789. < if ($sign > 0) {
  4790. < $add_ban->execute($cn, $arg, $src, $type);
  4791. < } else {
  4792. < $delete_ban->execute($cn, $arg, $type);
  4793. < }
  4794. < }
  4795. <
  4796. < sub chan_topic {
  4797. < my ($src, $cn, $setter, $time, $topic) = @_;
  4798. < my $chan = { CHAN => $cn };
  4799. < my $suser = { NICK => $setter, AGENT => agent($chan) };
  4800. <
  4801. < return if cr_chk_flag($chan, CRF_CLOSE, 1);
  4802. <
  4803. < if(current_message->{SYNC}) { # We don't need to undo our own topic changes.
  4804. < $set_topic1->execute($setter, $time, $cn);
  4805. < $set_topic2->execute($cn, $topic);
  4806. < return;
  4807. < }
  4808. <
  4809. < if(!synced()) {
  4810. < $get_topic->execute($cn);
  4811. < my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
  4812. < if($topic ne '' and $time == $ntime or can_do($chan, 'SETTOPIC', undef, { ACC => 0 })) {
  4813. < $set_topic1->execute($setter, $time, $cn);
  4814. < $set_topic2->execute($cn, $topic);
  4815. < } else {
  4816. < ircd::settopic(agent($chan), $cn, $nsetter, $ntime, $ntopic);
  4817. < }
  4818. < }
  4819. <
  4820. < elsif(lc($src) ne lc($setter) or can_do($chan, 'SETTOPIC', $suser)) {
  4821. < $set_topic1->execute($setter, $time, $cn);
  4822. < $set_topic2->execute($cn, $topic);
  4823. < } else {
  4824. < $get_topic->execute($cn);
  4825. < my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
  4826. < ircd::settopic(agent($chan), $cn, $nsetter, $ntime, $ntopic);
  4827. < }
  4828. < }
  4829. <
  4830. < sub eos(;$) {
  4831. < my ($server) = @_;
  4832. < my $gsa;
  4833. <
  4834. < $get_all_closed_chans->execute(CRF_DRONE|CRF_CLOSE);
  4835. < while(my ($cn, $type, $reason, $opnick, $time) = $get_all_closed_chans->fetchrow_array) {
  4836. < my $chan = { CHAN => $cn };
  4837. <
  4838. < my $cmsg = " is closed [$opnick ".gmtime2($time)."]: $reason";
  4839. < if($type == CRF_DRONE) {
  4840. < chan_kill($chan, $cn.$cmsg);
  4841. < } else {
  4842. < ircd::settopic(agent($chan), $cn, $opnick, $time, "Channel".$cmsg);
  4843. < clear_users($chan, "Channel".$cmsg);
  4844. < }
  4845. < }
  4846. <
  4847. < while($chanuser_table > 0) { }
  4848. <
  4849. < $get_eos_lock->execute(); $get_eos_lock->finish;
  4850. < $get_akick_all->execute();
  4851. < if($server) {
  4852. < $get_status_all_server->execute($server);
  4853. < $gsa = $get_status_all_server;
  4854. < } else {
  4855. < $get_status_all->execute();
  4856. < $gsa = $get_status_all;
  4857. < }
  4858. < #$unlock_tables->execute(); $unlock_tables->finish;
  4859. <
  4860. < while(my @akick = $get_akick_all->fetchrow_array) {
  4861. < akickban(@akick);
  4862. < }
  4863. <
  4864. < $get_modelock_all->execute();
  4865. < while(my ($cn, $modes, $ml) = $get_modelock_all->fetchrow_array) {
  4866. < $ml = do_modelock_fast($cn, '', $modes, $ml);
  4867. < ircd::setmode(agent({CHAN=>$cn}), $cn, $ml) if $ml;
  4868. < }
  4869. <
  4870. < while(my ($cn, $cflags, $agent, $nick, $uid, $uflags, $level, $op, $neverop) = $gsa->fetchrow_array) {
  4871. < my $user = { NICK => $nick, ID => $uid };
  4872. < #next if chk_user_flag($user, UF_FINISHED);
  4873. < $agent = $csnick unless $agent;
  4874. < my $chan = { CHAN => $cn, FLAGS => $cflags, AGENT => $agent };
  4875. <
  4876. < set_modes($user, $chan, $level, ($cflags & CRF_OPGUARD)) if not $neverop and $ops[$level] != $op and not $cflags & (CRF_FREEZE | CRF_CLOSE | CRF_DRONE);
  4877. < do_welcome($user, $chan);
  4878. < }
  4879. <
  4880. < set_user_flag_all(UF_FINISHED());
  4881. < $unlock_tables->execute(); $unlock_tables->finish;
  4882. < }
  4883. <
  4884. < 1;
  4885. \ No newline at end of file
  4886. ---
  4887. > # This file is part of SurrealServices.
  4888. > #
  4889. > # SurrealServices is free software; you can redistribute it and/or modify
  4890. > # it under the terms of the GNU General Public License as published by
  4891. > # the Free Software Foundation; either version 2 of the License, or
  4892. > # (at your option) any later version.
  4893. > #
  4894. > # SurrealServices is distributed in the hope that it will be useful,
  4895. > # but WITHOUT ANY WARRANTY; without even the implied warranty of
  4896. > # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  4897. > # GNU General Public License for more details.
  4898. > #
  4899. > # You should have received a copy of the GNU General Public License
  4900. > # along with SurrealServices; if not, write to the Free Software
  4901. > # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  4902. > package chanserv;
  4903. >
  4904. > use strict;
  4905. >
  4906. > use SrSv::Timer qw(add_timer);
  4907. >
  4908. > use SrSv::Message qw(current_message);
  4909. > use SrSv::IRCd::State qw($ircline synced initial_synced %IRCd_capabilities);
  4910. > use SrSv::Message qw(message current_message);
  4911. > use SrSv::HostMask qw(normalize_hostmask make_hostmask parse_mask);
  4912. >
  4913. > #FIXME: This needs to be abstracted into a proper SrSv::IRCd module
  4914. > use SrSv::Unreal::Modes qw(@opmodes %opmodes $scm $ocm $acm sanitize_mlockable);
  4915. > use SrSv::IRCd::Validate qw( valid_nick validate_chmodes validate_ban );
  4916. > use SrSv::Agent;
  4917. >
  4918. > use SrSv::Shared qw(%enforcers $chanuser_table);
  4919. >
  4920. > #use SrSv::Conf qw(services);
  4921. > use SrSv::Conf2Consts qw( services sql );
  4922. >
  4923. > use SrSv::Time;
  4924. > use SrSv::Text::Format qw( columnar enum );
  4925. > use SrSv::Errors;
  4926. >
  4927. > use SrSv::Log;
  4928. >
  4929. > use SrSv::User qw(
  4930. > get_user_nick get_user_agent get_user_id
  4931. > is_online :user_flags get_host get_vhost
  4932. > :flags :flood
  4933. > );
  4934. > use SrSv::User::Notice;
  4935. > use SrSv::Help qw( sendhelp );
  4936. >
  4937. > use SrSv::ChanReg::Flags;
  4938. >
  4939. > use SrSv::NickReg::Flags;
  4940. > use SrSv::NickReg::NickText;
  4941. > use SrSv::NickReg::User qw(is_identified get_nick_users get_nick_user_nicks);
  4942. >
  4943. > use SrSv::MySQL qw( $dbh :sql_types );
  4944. > use SrSv::MySQL::Glob;
  4945. >
  4946. > use SrSv::Util qw( makeSeqList );
  4947. >
  4948. > use constant {
  4949. > UOP => 1,
  4950. > VOP => 2,
  4951. > HOP => 3,
  4952. > AOP => 4,
  4953. > SOP => 5,
  4954. > COFOUNDER => 6,
  4955. > FOUNDER => 7,
  4956. >
  4957. > # Maybe this should be a config option
  4958. > DEFAULT_BANTYPE => 10,
  4959. >
  4960. > CRT_TOPIC => 1,
  4961. > CRT_AKICK => 2,
  4962. > };
  4963. >
  4964. > *get_root_nick = \&nickserv::get_root_nick;
  4965. >
  4966. > our @levels = ("no", "UOp", "VOp", "HOp", "AOp", "SOp", "co-founder", "founder");
  4967. > our @ops;
  4968. > if(!ircd::PREFIXAQ_DISABLE()) {
  4969. > @ops = (0, 0, 1, 2, 4, 8, 16, 16); # PREFIX_AQ
  4970. > } else { # lame IRC scripts and admins who don't enable PREFIX_AQ
  4971. > @ops = (0, 0, 1, 2, 4, 12, 20, 20); # normal
  4972. > }
  4973. > our @plevels = ('AKICK', 'anyone', 'UOp', 'VOp', 'HOp', 'AOp', 'SOp', 'co-founder', 'founder', 'disabled');
  4974. > our $plzero = 1;
  4975. >
  4976. > our @override = (
  4977. > ['SERVOP',
  4978. > {
  4979. > ACCCHANGE => 1,
  4980. > SET => 1,
  4981. > MEMO => 1,
  4982. > SETTOPIC => 1,
  4983. > AKICK => 1,
  4984. > LEVELS => 1,
  4985. > COPY => 1,
  4986. > }
  4987. > ],
  4988. > ['SUPER',
  4989. > {
  4990. > BAN => 1,
  4991. > UNBANSELF => 1,
  4992. > UNBAN => 1,
  4993. > KICK => 1,
  4994. > VOICE => 1,
  4995. > HALFOP => 1,
  4996. > OP => 1,
  4997. > ADMIN => 1,
  4998. > OWNER => 1,
  4999. > SETTOPIC => 1,
  5000. > INVITE => 1,
  5001. > INVITESELF => 1,
  5002. > CLEAR => 1,
  5003. > AKICKENFORCE => 1,
  5004. > UPDOWN => 1,
  5005. > MODE => 1,
  5006. > }
  5007. > ],
  5008. > ['HELP',
  5009. > {
  5010. > JOIN => 1,
  5011. > ACCLIST => 1,
  5012. > LEVELSLIST => 1,
  5013. > AKICKLIST => 1,
  5014. > INFO => 1,
  5015. > GETKEY => 1
  5016. > }
  5017. > ],
  5018. > ['BOT',
  5019. > {
  5020. > BOTSAY => 1,
  5021. > BOTASSIGN => 1
  5022. > }
  5023. > ]
  5024. > );
  5025. >
  5026. > $chanuser_table = 0;
  5027. >
  5028. > our $csnick_default = 'ChanServ';
  5029. > our $csnick = $csnick_default;
  5030. >
  5031. > our ($cur_lock, $cnt_lock);
  5032. >
  5033. > our (
  5034. > $get_joinpart_lock, $get_modelock_lock, $get_update_modes_lock,
  5035. >
  5036. > $chanjoin, $chanpart, $chop, $chdeop, $get_op, $get_user_chans, $get_user_chans_recent,
  5037. > $get_all_closed_chans, $get_user_count,
  5038. >
  5039. > $is_in_chan,
  5040. >
  5041. > #$lock_chanuser, $get_all_chan_users,
  5042. > $unlock_tables,
  5043. > $get_chan_users, $get_chan_users_noacc, $get_chan_users_mask, $get_chan_users_mask_noacc,
  5044. >
  5045. > $get_users_nochans, $get_users_nochans_noid,
  5046. >
  5047. > $get_using_nick_chans,
  5048. >
  5049. > $get_lock, $release_lock, $is_free_lock,
  5050. >
  5051. > $chan_create, $chan_delete, $get_chanmodes, $set_chanmodes,
  5052. >
  5053. > $is_registered, $get_modelock, $set_modelock, $set_descrip,
  5054. >
  5055. > $get_topic, $set_topic1, $set_topic2,
  5056. >
  5057. > $get_acc, $set_acc1, $set_acc2, $del_acc, $get_acc_list, $get_acc_list2, $get_acc_list_mask, $get_acc_list2_mask,
  5058. > $wipe_acc_list,
  5059. > $get_best_acc, $get_all_acc, $get_highrank, $get_acc_count,
  5060. > $copy_acc, $copy_acc_rank,
  5061. >
  5062. > $get_eos_lock, $get_status_all, $get_status_all_server, $get_modelock_all,
  5063. >
  5064. > $get_akick, $get_akick_allchan, $get_akick_alluser, $get_akick_all, $add_akick, $del_akick,
  5065. > $get_akick_list, $get_akick_by_num,
  5066. >
  5067. > $add_nick_akick, $del_nick_akick, $get_nick_akick, $drop_nick_akick,
  5068. > $copy_akick,
  5069. >
  5070. > $is_level, $get_level, $get_levels, $add_level, $set_level, $reset_level, $clear_levels, $get_level_max,
  5071. > $copy_levels,
  5072. >
  5073. > $get_founder, $get_successor,
  5074. > $set_founder, $set_successor, $del_successor,
  5075. >
  5076. > $get_nick_own_chans, $delete_successors,
  5077. >
  5078. > $get_info,
  5079. >
  5080. > $register, $drop_acc, $drop_lvl, $drop_akick, $drop,
  5081. > $copy_chanreg,
  5082. >
  5083. > $get_expired,
  5084. >
  5085. > $get_close, $set_close, $del_close,
  5086. >
  5087. > $add_welcome, $del_welcome, $list_welcome, $get_welcomes, $drop_welcome,
  5088. > $count_welcome, $consolidate_welcome,
  5089. >
  5090. > $add_ban, $delete_bans, $delete_ban,
  5091. > $get_all_bans, $get_ban_num,
  5092. > $find_bans, $list_bans, $wipe_bans,
  5093. > $find_bans_chan_user, $delete_bans_chan_user,
  5094. >
  5095. > $add_auth, $list_auth_chan, $get_auth_nick, $get_auth_num, $find_auth,
  5096. >
  5097. > $set_bantype, $get_bantype,
  5098. >
  5099. > $drop_chantext, $drop_nicktext,
  5100. > );
  5101. >
  5102. > sub init() {
  5103. > #$chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET id=(RAND()*294967293)+1, chan=?");
  5104. > $get_joinpart_lock = $dbh->prepare("LOCK TABLES chan WRITE, chanuser WRITE");
  5105. > $get_modelock_lock = $dbh->prepare("LOCK TABLES chanreg READ LOCAL, chan WRITE");
  5106. > $get_update_modes_lock = $dbh->prepare("LOCK TABLES chan WRITE");
  5107. >
  5108. > $chanjoin = $dbh->prepare("REPLACE INTO chanuser (seq,nickid,chan,op,joined) VALUES (?, ?, ?, ?, 1)");
  5109. > $chanpart = $dbh->prepare("UPDATE chanuser SET joined=0, seq=?
  5110. > WHERE nickid=? AND chan=? AND (seq <= ? OR seq > ?)");
  5111. > #$chop = $dbh->prepare("UPDATE chanuser SET op=op+? WHERE nickid=? AND chan=?");
  5112. > $chop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op, op ^ ?) WHERE nickid=? AND chan=?");
  5113. > $chdeop = $dbh->prepare("UPDATE chanuser SET op=IF(op & ?, op ^ ?, op) WHERE nickid=? AND chan=?");
  5114. > $get_op = $dbh->prepare("SELECT op FROM chanuser WHERE nickid=? AND chan=?");
  5115. > $get_user_chans = $dbh->prepare("SELECT chan, op FROM chanuser WHERE nickid=? AND joined=1 AND (seq <= ? OR seq > ?)");
  5116. > $get_user_chans_recent = $dbh->prepare("SELECT chan, joined, op FROM chanuser WHERE nickid=?");
  5117. >
  5118. > $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");
  5119. > $get_user_count = $dbh->prepare("SELECT COUNT(*) FROM chanuser WHERE chan=? AND joined=1");
  5120. >
  5121. > $is_in_chan = $dbh->prepare("SELECT 1 FROM chanuser WHERE nickid=? AND chan=? AND joined=1");
  5122. >
  5123. > #$lock_chanuser = $dbh->prepare("LOCK TABLES chanuser READ, user READ");
  5124. > #$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");
  5125. > $unlock_tables = $dbh->prepare("UNLOCK TABLES");
  5126. >
  5127. > $get_chan_users = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
  5128. > WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1");
  5129. > my $chan_users_noacc_tables = 'user '.
  5130. > 'JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1 AND user.online=1) '.
  5131. > 'LEFT JOIN nickid ON (chanuser.nickid=nickid.id) '.
  5132. > 'LEFT JOIN chanacc ON (nickid.nrid=chanacc.nrid AND chanuser.chan=chanacc.chan)';
  5133. > $get_chan_users_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
  5134. > WHERE chanuser.chan=?
  5135. > GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  5136. > ORDER BY NULL");
  5137. > my $check_mask = "((user.nick LIKE ?) AND (user.ident LIKE ?)
  5138. > AND ((user.vhost LIKE ?) OR (user.host LIKE ?) OR (user.cloakhost LIKE ?)))";
  5139. > $get_chan_users_mask = $dbh->prepare("SELECT user.nick, user.id FROM chanuser, user
  5140. > WHERE chanuser.chan=? AND user.id=chanuser.nickid AND chanuser.joined=1 AND $check_mask");
  5141. > $get_chan_users_mask_noacc = $dbh->prepare("SELECT user.nick, user.id FROM $chan_users_noacc_tables
  5142. > WHERE chanuser.chan=? AND $check_mask
  5143. > GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  5144. > ORDER BY NULL");
  5145. >
  5146. > $get_users_nochans = $dbh->prepare("SELECT user.nick, user.id
  5147. > FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
  5148. > WHERE chanuser.chan IS NULL AND user.online=1");
  5149. > $get_users_nochans_noid = $dbh->prepare("SELECT user.nick, user.id
  5150. > FROM user LEFT JOIN chanuser ON (chanuser.nickid=user.id AND chanuser.joined=1)
  5151. > LEFT JOIN nickid ON (nickid.id=user.id)
  5152. > WHERE chanuser.chan IS NULL AND nickid.id IS NULL
  5153. > AND user.online=1");
  5154. >
  5155. > $get_using_nick_chans = $dbh->prepare("SELECT user.nick FROM user, nickid, nickreg, chanuser
  5156. > WHERE user.id=nickid.id AND user.id=chanuser.nickid AND nickid.nrid=nickreg.id AND chanuser.joined=1
  5157. > AND nickreg.nick=? AND chanuser.chan=?");
  5158. >
  5159. > $get_lock = $dbh->prepare("SELECT GET_LOCK(?, 3)");
  5160. > $release_lock = $dbh->prepare("DO RELEASE_LOCK(?)");
  5161. > $is_free_lock = $dbh->prepare("SELECT IS_FREE_LOCK(?)");
  5162. >
  5163. > $chan_create = $dbh->prepare("INSERT IGNORE INTO chan SET seq=?, chan=?");
  5164. > $chan_delete = $dbh->prepare("DELETE FROM chan WHERE chan=?");
  5165. > $get_chanmodes = $dbh->prepare("SELECT modes FROM chan WHERE chan=?");
  5166. > $set_chanmodes = $dbh->prepare("REPLACE INTO chan SET modes=?, chan=?");
  5167. >
  5168. > $is_registered = $dbh->prepare("SELECT 1 FROM chanreg WHERE chan=?");
  5169. > $get_modelock = $dbh->prepare("SELECT modelock FROM chanreg WHERE chan=?");
  5170. > $set_modelock = $dbh->prepare("UPDATE chanreg SET modelock=? WHERE chan=?");
  5171. >
  5172. > $set_descrip = $dbh->prepare("UPDATE chanreg SET descrip=? WHERE chan=?");
  5173. >
  5174. > $get_topic = $dbh->prepare("SELECT chantext.data, topicer, topicd FROM chanreg, chantext
  5175. > WHERE chanreg.chan=chantext.chan AND chantext.chan=?");
  5176. > $set_topic1 = $dbh->prepare("UPDATE chanreg SET chanreg.topicer=?, chanreg.topicd=?
  5177. > WHERE chanreg.chan=?");
  5178. > $set_topic2 = $dbh->prepare("REPLACE INTO chantext SET chan=?, type=".CRT_TOPIC().", data=?");
  5179. >
  5180. > $get_acc = $dbh->prepare("SELECT chanacc.level FROM chanacc, nickalias
  5181. > WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
  5182. > $set_acc1 = $dbh->prepare("INSERT IGNORE INTO chanacc SELECT ?, nrid, ?, NULL, UNIX_TIMESTAMP(), 0
  5183. > FROM nickalias WHERE alias=?");
  5184. > $set_acc2 = $dbh->prepare("UPDATE chanacc, nickalias
  5185. > SET chanacc.level=?, chanacc.adder=?, chanacc.time=UNIX_TIMESTAMP()
  5186. > WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
  5187. > $del_acc = $dbh->prepare("DELETE FROM chanacc USING chanacc, nickalias
  5188. > WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickalias.alias=?");
  5189. > $wipe_acc_list = $dbh->prepare("DELETE FROM chanacc WHERE chan=? AND level=?");
  5190. > $get_acc_list = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.time,
  5191. > chanacc.last, nickreg.ident, nickreg.vhost
  5192. > FROM chanacc, nickreg
  5193. > WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
  5194. > $get_acc_list2 = $dbh->prepare("SELECT nickreg.nick, chanacc.adder, chanacc.level, chanacc.time,
  5195. > chanacc.last, nickreg.ident, nickreg.vhost
  5196. > FROM chanacc, nickreg
  5197. > WHERE chanacc.chan=? AND chanacc.nrid=nickreg.id AND chanacc.level > 0 ORDER BY nickreg.nick");
  5198. > $get_acc_list_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias), chanacc.adder, chanacc.time,
  5199. > chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
  5200. > FROM chanacc, nickalias, nickreg
  5201. > WHERE chanacc.chan=? AND chanacc.level=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
  5202. > AND chanacc.level > 0
  5203. > AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
  5204. > GROUP BY nickreg.id
  5205. > ORDER BY nickalias.alias");
  5206. > $get_acc_list2_mask = $dbh->prepare("SELECT IF (nickreg.nick LIKE ?, nickreg.nick, nickalias.alias),
  5207. > chanacc.adder, chanacc.level, chanacc.time,
  5208. > chanacc.last, nickreg.ident, nickreg.vhost, COUNT(nickreg.id) as c
  5209. > FROM chanacc, nickalias, nickreg
  5210. > WHERE chanacc.chan=? AND chanacc.nrid=nickalias.nrid AND nickreg.id=nickalias.nrid
  5211. > AND chanacc.level > 0
  5212. > AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ?
  5213. > GROUP BY nickreg.id
  5214. > ORDER BY nickalias.alias");
  5215. >
  5216. > $get_best_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
  5217. > FROM nickid, nickalias, nickreg, chanacc
  5218. > WHERE nickid.nrid=nickreg.id AND nickalias.nrid=nickreg.id AND nickid.id=?
  5219. > AND chanacc.nrid=nickreg.id AND chanacc.chan=? ORDER BY chanacc.level DESC LIMIT 1");
  5220. > $get_all_acc = $dbh->prepare("SELECT nickreg.nick, chanacc.level
  5221. > FROM nickid, nickreg, chanacc
  5222. > WHERE nickid.nrid=nickreg.id AND nickid.id=? AND chanacc.nrid=nickreg.id
  5223. > AND chanacc.chan=? ORDER BY chanacc.level");
  5224. > $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");
  5225. > $get_acc_count = $dbh->prepare("SELECT COUNT(*) FROM chanacc WHERE chan=? AND level=?");
  5226. > $copy_acc = $dbh->prepare("REPLACE INTO chanacc
  5227. > ( chan, nrid, level, adder, time)
  5228. > SELECT ?, nrid, level, adder, time FROM chanacc JOIN nickreg ON (chanacc.nrid=nickreg.id)
  5229. > WHERE chan=? AND nickreg.nick!=? AND chanacc.level!=7");
  5230. > $copy_acc_rank = $dbh->prepare("REPLACE INTO chanacc
  5231. > ( chan, nrid, level, adder, time)
  5232. > SELECT ?, nrid, level, adder, time FROM chanacc
  5233. > WHERE chan=? AND chanacc.level=?");
  5234. >
  5235. > $get_eos_lock = $dbh->prepare("LOCK TABLES akick READ LOCAL, welcome READ LOCAL, chanuser WRITE, user WRITE,
  5236. > user AS u1 READ, user AS u2 READ, chan WRITE, chanreg WRITE, nickid READ LOCAL, nickreg READ LOCAL,
  5237. > nickalias READ LOCAL, chanacc READ LOCAL, chanban WRITE, svsop READ");
  5238. > 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().")
  5239. > FROM user, chanreg, chanuser
  5240. > LEFT JOIN nickid ON(nickid.id=chanuser.nickid)
  5241. > LEFT JOIN nickreg ON(nickid.nrid=nickreg.id)
  5242. > LEFT JOIN chanacc ON(chanacc.chan=chanuser.chan AND chanacc.nrid=nickid.nrid AND (nickreg.flags & ".NRF_NEVEROP().")=0)
  5243. > WHERE";
  5244. > 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)
  5245. > GROUP BY chanuser.chan, chanuser.nickid ORDER BY NULL";
  5246. > $get_status_all = $dbh->prepare("$get_status_all_1 $get_status_all_2");
  5247. > $get_status_all_server = $dbh->prepare("$get_status_all_1 user.server=? AND $get_status_all_2");
  5248. >
  5249. > $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");
  5250. >
  5251. > my $akick_rows = "user.nick, akick.nick, akick.ident, akick.host, akick.reason";
  5252. > my $akick_no_zerolen = "(akick.ident != '' AND akick.host != '')";
  5253. > my $akick_single_cond = "$akick_no_zerolen AND user.nick LIKE akick.nick AND user.ident LIKE akick.ident ".
  5254. > "AND ( (user.host LIKE akick.host) OR (user.vhost LIKE akick.host) OR ".
  5255. > "(IF((user.ip IS NOT NULL) AND (user.ip != 0), INET_NTOA(user.ip) LIKE akick.host, 0)) OR ".
  5256. > "(IF(user.cloakhost IS NOT NULL, user.cloakhost LIKE akick.host, 0)) )";
  5257. > my $akick_multi_cond = "chanuser.chan=akick.chan AND $akick_single_cond";
  5258. >
  5259. > $get_akick = $dbh->prepare("SELECT $akick_rows FROM akick, user ".
  5260. > "WHERE user.id=? AND akick.chan=? AND $akick_single_cond LIMIT 1");
  5261. > $get_akick_allchan = $dbh->prepare("SELECT $akick_rows FROM $chan_users_noacc_tables
  5262. > JOIN akick ON($akick_multi_cond)
  5263. > WHERE akick.chan=?
  5264. > GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  5265. > ORDER BY NULL");
  5266. > $get_akick_alluser = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
  5267. > JOIN akick ON($akick_multi_cond)
  5268. > WHERE chanuser.nickid=?
  5269. > GROUP BY user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  5270. > ORDER BY NULL");
  5271. > $get_akick_all = $dbh->prepare("SELECT akick.chan, $akick_rows FROM $chan_users_noacc_tables
  5272. > JOIN akick ON($akick_multi_cond)
  5273. > GROUP BY akick.chan, user.id HAVING MAX(IF(chanacc.level IS NULL, 0, chanacc.level)) <= 0
  5274. > ORDER BY NULL");
  5275. >
  5276. > $add_akick = $dbh->prepare("INSERT INTO akick SET chan=?, nick=?, ident=?, host=?, adder=?, reason=?, time=UNIX_TIMESTAMP()");
  5277. > $add_akick->{PrintError} = 0;
  5278. > $del_akick = $dbh->prepare("DELETE FROM akick WHERE chan=? AND nick=? AND ident=? AND host=?");
  5279. > $get_akick_list = $dbh->prepare("SELECT nick, ident, host, adder, reason, time FROM akick WHERE chan=? ORDER BY time");
  5280. >
  5281. > $add_nick_akick = $dbh->prepare("INSERT INTO akick SELECT ?, nickalias.nrid, '', '', ?, ?, UNIX_TIMESTAMP()
  5282. > FROM nickalias WHERE alias=?");
  5283. > $del_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickalias
  5284. > WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
  5285. > $get_nick_akick = $dbh->prepare("SELECT reason FROM akick, nickalias
  5286. > WHERE akick.chan=? AND akick.nick=nickalias.nrid AND akick.ident='' AND akick.host='' AND nickalias.alias=?");
  5287. > $drop_nick_akick = $dbh->prepare("DELETE FROM akick USING akick, nickreg
  5288. > WHERE akick.nick=nickreg.id AND akick.ident='' AND akick.host='' AND nickreg.nick=?");
  5289. > $copy_akick = $dbh->prepare("REPLACE INTO akick
  5290. > ( chan, nick, ident, host, adder, reason, time)
  5291. > SELECT ?, nick, ident, host, adder, reason, time FROM akick WHERE chan=?");
  5292. > $get_akick_by_num = $dbh->prepare("SELECT akick.nick, akick.ident, akick.host FROM akick WHERE chan=?
  5293. > ORDER BY time LIMIT 1 OFFSET ?");
  5294. > $get_akick_by_num->bind_param(2, 0, SQL_INTEGER);
  5295. >
  5296. > $is_level = $dbh->prepare("SELECT 1 FROM chanperm WHERE chanperm.name=?");
  5297. > $get_level = $dbh->prepare("SELECT IF(chanlvl.level IS NULL, chanperm.level, chanlvl.level), chanlvl.level
  5298. > FROM chanperm LEFT JOIN chanlvl ON chanlvl.perm=chanperm.id AND chanlvl.chan=?
  5299. > WHERE chanperm.name=?");
  5300. > $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");
  5301. > $add_level = $dbh->prepare("INSERT IGNORE INTO chanlvl SELECT ?, chanperm.id, chanperm.level FROM chanperm WHERE chanperm.name=?");
  5302. > $set_level = $dbh->prepare("UPDATE chanlvl, chanperm SET chanlvl.level=? WHERE chanlvl.chan=? AND chanperm.id=chanlvl.perm AND chanperm.name=?");
  5303. > $reset_level = $dbh->prepare("DELETE FROM chanlvl USING chanlvl, chanperm WHERE chanperm.name=? AND chanlvl.perm=chanperm.id AND chanlvl.chan=?");
  5304. > $clear_levels = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
  5305. > $get_level_max = $dbh->prepare("SELECT max FROM chanperm WHERE name=?");
  5306. > $copy_levels = $dbh->prepare("REPLACE INTO chanlvl
  5307. > ( chan, perm, level)
  5308. > SELECT ?, perm, level FROM chanlvl WHERE chan=?");
  5309. >
  5310. > $get_founder = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.founderid=nickreg.id");
  5311. > $get_successor = $dbh->prepare("SELECT nickreg.nick FROM chanreg, nickreg WHERE chanreg.chan=? AND chanreg.successorid=nickreg.id");
  5312. > $set_founder = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.founderid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
  5313. > $set_successor = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=nickreg.id WHERE nickreg.nick=? AND chanreg.chan=?");
  5314. > $del_successor = $dbh->prepare("UPDATE chanreg SET chanreg.successorid=NULL WHERE chanreg.chan=?");
  5315. >
  5316. > $get_nick_own_chans = $dbh->prepare("SELECT chanreg.chan FROM chanreg, nickreg WHERE nickreg.nick=? AND chanreg.founderid=nickreg.id");
  5317. > $delete_successors = $dbh->prepare("UPDATE chanreg, nickreg SET chanreg.successorid=NULL WHERE nickreg.nick=? AND chanreg.successorid=nickreg.id");
  5318. >
  5319. >
  5320. > $get_info = $dbh->prepare("SELECT chanreg.descrip, chanreg.regd, chanreg.last, chantext.data,
  5321. > chanreg.topicer, chanreg.modelock, foundernick.nick, successornick.nick, chanreg.bot, chanreg.bantype
  5322. > FROM nickreg AS foundernick, chanreg
  5323. > LEFT JOIN nickreg AS successornick ON(successornick.id=chanreg.successorid)
  5324. > LEFT JOIN chantext ON (chanreg.chan=chantext.chan AND chantext.type=".CRT_TOPIC().")
  5325. > WHERE chanreg.chan=? AND foundernick.id=chanreg.founderid");
  5326. >
  5327. > $register = $dbh->prepare("INSERT INTO chanreg
  5328. > SELECT ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), NULL, NULL,
  5329. > NULL, id, NULL, NULL, NULL, ".DEFAULT_BANTYPE()." FROM nickreg WHERE nick=?");
  5330. > $register->{PrintError} = 0;
  5331. > $copy_chanreg = $dbh->prepare("INSERT INTO chanreg
  5332. > ( chan, descrip, regd, last, modelock, founderid, successorid, bot, flags, bantype)
  5333. > SELECT ?, descrip, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), modelock, founderid, successorid, bot, flags, bantype
  5334. > FROM chanreg WHERE chan=?");
  5335. >
  5336. > $drop_acc = $dbh->prepare("DELETE FROM chanacc WHERE chan=?");
  5337. > $drop_lvl = $dbh->prepare("DELETE FROM chanlvl WHERE chan=?");
  5338. > $drop_akick = $dbh->prepare("DELETE FROM akick WHERE chan=?");
  5339. > $drop = $dbh->prepare("DELETE FROM chanreg WHERE chan=?");
  5340. >
  5341. > $get_expired = $dbh->prepare("SELECT chanreg.chan, nickreg.nick FROM nickreg, chanreg
  5342. > LEFT JOIN chanuser ON(chanreg.chan=chanuser.chan AND chanuser.op!=0)
  5343. > WHERE chanreg.founderid=nickreg.id AND chanuser.chan IS NULL AND chanreg.last<? AND
  5344. > !(chanreg.flags & " . CRF_HOLD . ")");
  5345. >
  5346. > $get_close = $dbh->prepare("SELECT reason, nick, time FROM chanclose WHERE chan=?");
  5347. > $set_close = $dbh->prepare("REPLACE INTO chanclose SET chan=?, reason=?, nick=?, time=UNIX_TIMESTAMP(), type=?");
  5348. > $del_close = $dbh->prepare("DELETE FROM chanclose WHERE chan=?");
  5349. >
  5350. > $add_welcome = $dbh->prepare("REPLACE INTO welcome SET chan=?, id=?, adder=?, time=UNIX_TIMESTAMP(), msg=?");
  5351. > $del_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=? AND id=?");
  5352. > $list_welcome = $dbh->prepare("SELECT id, time, adder, msg FROM welcome WHERE chan=? ORDER BY id");
  5353. > $get_welcomes = $dbh->prepare("SELECT msg FROM welcome WHERE chan=? ORDER BY id");
  5354. > $drop_welcome = $dbh->prepare("DELETE FROM welcome WHERE chan=?");
  5355. > $count_welcome = $dbh->prepare("SELECT COUNT(*) FROM welcome WHERE chan=?");
  5356. > $consolidate_welcome = $dbh->prepare("UPDATE welcome SET id=id-1 WHERE chan=? AND id>?");
  5357. >
  5358. > $add_ban = $dbh->prepare("INSERT IGNORE INTO chanban SET chan=?, mask=?, setter=?, type=?, time=UNIX_TIMESTAMP()");
  5359. > $delete_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
  5360. > # likely need a better name for this or for the above.
  5361. > $delete_ban = $dbh->prepare("DELETE FROM chanban WHERE chan=? AND mask=? AND type=?");
  5362. > $find_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND ? LIKE mask AND type=?");
  5363. > $get_all_bans = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=?");
  5364. > $get_ban_num = $dbh->prepare("SELECT mask FROM chanban WHERE chan=? AND type=? ORDER BY time, mask LIMIT 1 OFFSET ?");
  5365. > $get_ban_num->bind_param(3, 0, SQL_INTEGER);
  5366. > $list_bans = $dbh->prepare("SELECT mask, setter, time FROM chanban WHERE chan=? AND type=? ORDER BY time, mask");
  5367. > $wipe_bans = $dbh->prepare("DELETE FROM chanban WHERE chan=?");
  5368. >
  5369. > my $chanban_mask = "((CONCAT(user.nick, '!', user.ident, '\@', user.host) LIKE chanban.mask) ".
  5370. > "OR (CONCAT(user.nick , '!' , user.ident , '\@' , user.vhost) LIKE chanban.mask) ".
  5371. > "OR IF(user.cloakhost IS NOT NULL, ".
  5372. > "(CONCAT(user.nick , '!' , user.ident , '\@' , user.cloakhost) LIKE chanban.mask), 0))";
  5373. > $find_bans_chan_user = $dbh->prepare("SELECT mask FROM chanban,user
  5374. > WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
  5375. > $delete_bans_chan_user = $dbh->prepare("DELETE FROM chanban USING chanban,user
  5376. > WHERE chan=? AND user.id=? AND type=? AND $chanban_mask");
  5377. >
  5378. > $add_auth = $dbh->prepare("REPLACE INTO nicktext
  5379. > SELECT nickalias.nrid, (".NTF_AUTH()."), 1, ?, ? FROM nickalias WHERE nickalias.alias=?");
  5380. > $list_auth_chan = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nicktext
  5381. > WHERE nickreg.id=nicktext.nrid AND nicktext.type=(".NTF_AUTH().") AND nicktext.chan=?");
  5382. > $get_auth_nick = $dbh->prepare("SELECT nicktext.data FROM nickreg, nickalias, nicktext
  5383. > WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH().")
  5384. > AND nicktext.chan=? AND nickalias.alias=?");
  5385. > $get_auth_num = $dbh->prepare("SELECT nickreg.nick, nicktext.data FROM nickreg, nickalias, nicktext
  5386. > WHERE nickreg.id=nicktext.nrid AND nickreg.id=nickalias.nrid AND nicktext.type=(".NTF_AUTH().")
  5387. > AND nicktext.chan=? LIMIT 1 OFFSET ?");
  5388. > $get_auth_num->bind_param(2, 0, SQL_INTEGER);
  5389. > $find_auth = $dbh->prepare("SELECT 1 FROM nickalias, nicktext
  5390. > WHERE nickalias.nrid=nicktext.nrid AND nicktext.type=(".NTF_AUTH().")
  5391. > AND nicktext.chan=? AND nickalias.alias=?");
  5392. >
  5393. > $set_bantype = $dbh->prepare("UPDATE chanreg SET bantype=? WHERE chan=?");
  5394. > $get_bantype = $dbh->prepare("SELECT bantype FROM chanreg WHERE chan=?");
  5395. >
  5396. > $drop_chantext = $dbh->prepare("DELETE FROM chantext WHERE chan=?");
  5397. > $drop_nicktext = $dbh->prepare("DELETE nicktext.* FROM nicktext WHERE nicktext.chan=?");
  5398. > }
  5399. >
  5400. > use SrSv::MySQL::Stub {
  5401. > set_lastop => ['NULL', "UPDATE chanreg SET last=UNIX_TIMESTAMP() WHERE chan=?"],
  5402. > set_lastused => ['NULL', "UPDATE chanacc, nickid SET chanacc.last=UNIX_TIMESTAMP() WHERE
  5403. > chanacc.chan=? AND nickid.id=? AND chanacc.nrid=nickid.nrid AND chanacc.level > 0"],
  5404. > get_recent_private_chans => ['COLUMN', "SELECT DISTINCT chanuser.chan FROM chanuser
  5405. > JOIN chanacc ON (chanuser.chan=chanacc.chan AND chanuser.joined=0)
  5406. > JOIN chanlvl ON (chanlvl.level <= chanacc.level AND chanlvl.level > 0 AND chanuser.chan=chanlvl.chan)
  5407. > JOIN chanperm ON (chanlvl.perm=chanperm.id)
  5408. > JOIN nickid ON (chanuser.nickid=nickid.id AND chanacc.nrid=nickid.nrid)
  5409. > WHERE chanperm.name='Join'
  5410. > AND nickid.id=?"],
  5411. > };
  5412. >
  5413. > ### CHANSERV COMMANDS ###
  5414. >
  5415. > sub dispatch($$$) {
  5416. > my ($src, $dst, $msg) = @_;
  5417. >
  5418. > $msg =~ s/^\s+//;
  5419. > my @args = split(/\s+/, $msg);
  5420. > my $cmd = shift @args;
  5421. >
  5422. > my $user = { NICK => $src, AGENT => $dst };
  5423. >
  5424. > return if flood_check($user);
  5425. >
  5426. > if($cmd =~ /^register$/i) {
  5427. > if(@args >= 1) {
  5428. > my @args = split(/\s+/, $msg, 4);
  5429. > cs_register($user, { CHAN => $args[1] }, $args[2], $args[3]);
  5430. > } else {
  5431. > notice($user, 'Syntax: REGISTER <#channel> [password] [description]');
  5432. > }
  5433. > }
  5434. > elsif($cmd =~ /^(?:[uvhas]op|co?f(ounder)?)$/i) {
  5435. > my ($cn, $cmd2) = splice(@args, 0, 2);
  5436. > my $chan = { CHAN => $cn };
  5437. >
  5438. > if($cmd2 =~ /^add$/i) {
  5439. > if(@args == 1) {
  5440. > cs_xop_add($user, $chan, $cmd, $args[0]);
  5441. > } else {
  5442. > notice($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
  5443. > }
  5444. > }
  5445. > elsif($cmd2 =~ /^del(ete)?$/i) {
  5446. > if(@args == 1) {
  5447. > cs_xop_del($user, $chan, $cmd, $args[0]);
  5448. > } else {
  5449. > notice($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
  5450. > }
  5451. > }
  5452. > elsif($cmd2 =~ /^list$/i) {
  5453. > if(@args >= 0) {
  5454. > cs_xop_list($user, $chan, $cmd, $args[0]);
  5455. > } else {
  5456. > notice($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
  5457. > }
  5458. > }
  5459. > elsif($cmd2 =~ /^(wipe|clear)$/i) {
  5460. > if(@args == 0) {
  5461. > cs_xop_wipe($user, $chan, $cmd);
  5462. > } else {
  5463. > notice($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
  5464. > }
  5465. > }
  5466. > else {
  5467. > notice($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
  5468. > }
  5469. > }
  5470. > elsif($cmd =~ /^levels$/i) {
  5471. > if(@args < 2) {
  5472. > notice($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
  5473. > return;
  5474. > }
  5475. >
  5476. > my $cmd2 = lc(splice(@args, 1, 1));
  5477. >
  5478. > if($cmd2 eq 'set') {
  5479. > if(@args == 3) {
  5480. > cs_levels_set($user, { CHAN => $args[0] }, $args[1], $args[2]);
  5481. > } else {
  5482. > notice($user, 'Syntax: LEVELS <#channel> SET <permission> <level>');
  5483. > }
  5484. > }
  5485. > elsif($cmd2 eq 'reset') {
  5486. > if(@args == 2) {
  5487. > cs_levels_set($user, { CHAN => $args[0] }, $args[1]);
  5488. > } else {
  5489. > notice($user, 'Syntax: LEVELS <#channel> RESET <permission>');
  5490. > }
  5491. > }
  5492. > elsif($cmd2 eq 'list') {
  5493. > if(@args == 1) {
  5494. > cs_levels_list($user, { CHAN => $args[0] });
  5495. > } else {
  5496. > notice($user, 'Syntax: LEVELS <#channel> LIST');
  5497. > }
  5498. > }
  5499. > elsif($cmd2 eq 'clear') {
  5500. > if(@args == 1) {
  5501. > cs_levels_clear($user, { CHAN => $args[0] });
  5502. > } else {
  5503. > notice($user, 'Syntax: LEVELS <#channel> CLEAR');
  5504. > }
  5505. > }
  5506. > else {
  5507. > notice($user, 'Syntax: LEVELS <#channel> <SET|RESET|LIST|CLEAR>');
  5508. > }
  5509. > }
  5510. > elsif($cmd =~ /^akick$/i) {
  5511. > if(@args < 2) {
  5512. > notice($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
  5513. > return;
  5514. > }
  5515. >
  5516. > #my $cmd2 = lc($args[1]);
  5517. > my $cmd2 = lc(splice(@args, 1, 1));
  5518. >
  5519. > if($cmd2 eq 'add') {
  5520. > if(@args >= 2) {
  5521. > my @args = split(/\s+/, $msg, 5);
  5522. > cs_akick_add($user, { CHAN => $args[1] }, $args[3], $args[4]);
  5523. > } else {
  5524. > notice($user, 'Syntax: AKICK <#channel> ADD <nick|mask> <reason>');
  5525. > }
  5526. > }
  5527. > elsif($cmd2 eq 'del') {
  5528. > if(@args >= 2) {
  5529. > cs_akick_del($user, { CHAN => $args[0] }, $args[1]);
  5530. > } else {
  5531. > notice($user, 'Syntax: AKICK <#channel> DEL <nick|mask|num|seq>');
  5532. > }
  5533. > }
  5534. > elsif($cmd2 eq 'list') {
  5535. > if(@args == 1) {
  5536. > cs_akick_list($user, { CHAN => $args[0] });
  5537. > } else {
  5538. > notice($user, 'Syntax: AKICK <#channel> LIST');
  5539. > }
  5540. > }
  5541. > elsif($cmd2 =~ /^(wipe|clear)$/i) {
  5542. > if(@args == 1) {
  5543. > cs_akick_wipe($user, { CHAN => $args[0] });
  5544. > } else {
  5545. > notice($user, 'Syntax: AKICK <#channel> WIPE');
  5546. > }
  5547. > }
  5548. > elsif($cmd2 =~ /^enforce$/i) {
  5549. > if(@args == 1) {
  5550. > cs_akick_enforce($user, { CHAN => $args[0] });
  5551. > } else {
  5552. > notice($user, 'Syntax: AKICK <#channel> ENFORCE');
  5553. > }
  5554. > }
  5555. > else {
  5556. > notice($user, 'Syntax: AKICK <#channel> <ADD|DEL|LIST|WIPE|CLEAR>');
  5557. > }
  5558. > }
  5559. > elsif($cmd =~ /^info$/i) {
  5560. > if(@args == 1) {
  5561. > cs_info($user, { CHAN => $args[0] });
  5562. > } else {
  5563. > notice($user, 'Syntax: INFO <channel>');
  5564. > }
  5565. > }
  5566. > elsif($cmd =~ /^set$/i) {
  5567. > if(@args == 2 and lc($args[1]) eq 'unsuccessor') {
  5568. > cs_set($user, { CHAN => $args[0] }, $args[1]);
  5569. > }
  5570. > elsif(@args >= 3 and (
  5571. > $args[1] =~ /m(?:ode)?lock/i or
  5572. > lc($args[1]) eq 'desc'
  5573. > )) {
  5574. > my @args = split(/\s+/, $msg, 4);
  5575. > cs_set($user, { CHAN => $args[1] }, $args[2], $args[3]);
  5576. > }
  5577. > elsif(@args == 3) {
  5578. > cs_set($user, { CHAN => $args[0] }, $args[1], $args[2]);
  5579. > }
  5580. > else {
  5581. > notice($user, 'Syntax: SET <channel> <option> <value>');
  5582. > }
  5583. > }
  5584. > elsif($cmd =~ /^why$/i) {
  5585. > if(@args == 1) {
  5586. > cs_why($user, { CHAN => shift @args }, $src);
  5587. > }
  5588. > elsif(@args >= 2) {
  5589. > cs_why($user, { CHAN => shift @args }, @args);
  5590. > } else {
  5591. > notice($user, 'Syntax: WHY <channel> <nick> [nick [nick ...]]');
  5592. > return;
  5593. > }
  5594. > }
  5595. > elsif($cmd =~ /^(de)?(voice|h(alf)?op|op|protect|admin|owner)$/i) {
  5596. > if(@args >= 1) {
  5597. > cs_setmodes($user, $cmd, { CHAN => shift(@args) }, @args);
  5598. > } else {
  5599. > notice($user, 'Syntax: '.uc($cmd).' <channel> [nick [nick ...]]');
  5600. > }
  5601. > }
  5602. > elsif($cmd =~ /^(up|down)$/i) {
  5603. > cs_updown($user, $cmd, @args);
  5604. > }
  5605. > elsif($cmd =~ /^drop$/i) {
  5606. > if(@args == 1) {
  5607. > cs_drop($user, { CHAN => $args[0] });
  5608. > } else {
  5609. > notice($user, 'Syntax: DROP <channel>');
  5610. > }
  5611. > }
  5612. > elsif($cmd =~ /^help$/i) {
  5613. > sendhelp($user, 'chanserv', @args)
  5614. > }
  5615. > elsif($cmd =~ /^count$/i) {
  5616. > if(@args == 1) {
  5617. > cs_count($user, { CHAN => $args[0] });
  5618. > } else {
  5619. > notice($user, 'Syntax: COUNT <channel>');
  5620. > }
  5621. > }
  5622. > elsif($cmd =~ /^kick$/i) {
  5623. > my @args = split(/\s+/, $msg, 4); shift @args;
  5624. > if(@args >= 2) {
  5625. > cs_kick($user, { CHAN => $args[0] }, $args[1], 0, $args[2])
  5626. > }
  5627. > else {
  5628. > notice($user, 'Syntax: KICK <channel> <nick> [reason]');
  5629. > }
  5630. > }
  5631. > elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)$/i) {
  5632. > my @args = split(/\s+/, $msg, 4); shift @args;
  5633. > if(@args >= 2) {
  5634. > cs_kick($user, { CHAN => $args[0] }, $args[1], 1, $args[2]);
  5635. > } else {
  5636. > notice($user, 'Syntax: KICKBAN <channel> <nick> [reason]');
  5637. > }
  5638. > }
  5639. > elsif($cmd =~ /^k(ick)?m(ask)?$/i) {
  5640. > my @args = split(/\s+/, $msg, 4); shift @args;
  5641. > if(@args >= 2) {
  5642. > cs_kickmask($user, { CHAN => $args[0] }, $args[1], 0, $args[2])
  5643. > }
  5644. > else {
  5645. > notice($user, 'Syntax: KICKMASK <channel> <mask> [reason]');
  5646. > }
  5647. > }
  5648. > elsif($cmd =~ /^(k(ick)?b(an)?|b(an)?k(ick)?)m(ask)?$/i) {
  5649. > my @args = split(/\s+/, $msg, 4); shift @args;
  5650. > if(@args >= 2) {
  5651. > cs_kickmask($user, { CHAN => $args[0] }, $args[1], 1, $args[2]);
  5652. > } else {
  5653. > notice($user, 'Syntax: KICKBANMASK <channel> <mask> [reason]');
  5654. > }
  5655. > }
  5656. > elsif($cmd =~ /^invite$/i) {
  5657. > my $chan = shift @args;
  5658. > if(@args == 0) {
  5659. > cs_invite($user, { CHAN => $chan }, $src)
  5660. > }
  5661. > elsif(@args >= 1) {
  5662. > cs_invite($user, { CHAN => $chan }, @args)
  5663. > }
  5664. > else {
  5665. > notice($user, 'Syntax: INVITE <channel> <nick>');
  5666. > }
  5667. > }
  5668. > elsif($cmd =~ /^(close|forbid)$/i) {
  5669. > if(@args > 1) {
  5670. > my @args = split(/\s+/, $msg, 3);
  5671. > cs_close($user, { CHAN => $args[1] }, $args[2], CRF_CLOSE);
  5672. > }
  5673. > else {
  5674. > notice($user, 'Syntax: CLOSE <chan> <reason>');
  5675. > }
  5676. > }
  5677. > elsif($cmd =~ /^drone$/i) {
  5678. > if(@args > 1) {
  5679. > my @args = split(/\s+/, $msg, 3);
  5680. > cs_close($user, { CHAN => $args[1] }, $args[2], CRF_DRONE);
  5681. > }
  5682. > else {
  5683. > notice($user, 'Syntax: DRONE <chan> <reason>');
  5684. > }
  5685. > }
  5686. > elsif($cmd =~ /^clear$/i) {
  5687. > my ($cmd, $chan, $clearcmd, $reason) = split(/\s+/, $msg, 4);
  5688. > unless ($chan and $clearcmd) {
  5689. > notice($user, 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
  5690. > return;
  5691. > }
  5692. > if($clearcmd =~ /^modes$/i) {
  5693. > cs_clear_modes($user, { CHAN => $chan }, $reason);
  5694. > }
  5695. > elsif($clearcmd =~ /^ops$/i) {
  5696. > cs_clear_ops($user, { CHAN => $chan }, $reason);
  5697. > }
  5698. > elsif($clearcmd =~ /^users$/i) {
  5699. > cs_clear_users($user, { CHAN => $chan }, $reason);
  5700. > }
  5701. > elsif($clearcmd =~ /^bans?$/i) {
  5702. > cs_clear_bans($user, { CHAN => $chan }, 0, $reason);
  5703. > }
  5704. > elsif($clearcmd =~ /^excepts?$/i) {
  5705. > cs_clear_bans($user, { CHAN => $chan }, 128, $reason);
  5706. > }
  5707. > else {
  5708. > notice($user, "Unknown CLEAR command \002$clearcmd\002",
  5709. > 'Syntax: CLEAR <channel> <MODES|OPS|USERS|BANS> [reason]');
  5710. > }
  5711. > }
  5712. > elsif($cmd =~ /^mkick$/i) {
  5713. > my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
  5714. > if($chan) {
  5715. > cs_clear_users($user, { CHAN => $chan }, $reason);
  5716. > }
  5717. > else {
  5718. > notice($user, 'Syntax: MKICK <chan> [reason]');
  5719. > }
  5720. > }
  5721. > elsif($cmd =~ /^mdeop$/i) {
  5722. > my ($cmd, $chan, $reason) = split(/\s+/, $msg, 3);
  5723. > if($chan) {
  5724. > cs_clear_ops($user, { CHAN => $chan }, $reason);
  5725. > }
  5726. > else {
  5727. > notice($user, 'Syntax: MDEOP <chan> [reason]');
  5728. > }
  5729. > }
  5730. > elsif($cmd =~ /^welcome$/i) {
  5731. > my $wcmd = splice(@args, 1, 1);
  5732. > if(lc($wcmd) eq 'add') {
  5733. > my ($chan, $wmsg) = (splice(@args, 0, 1), join(' ', @args));
  5734. > unless ($chan and $wmsg) {
  5735. > notice($user, 'Syntax: WELCOME <channel> ADD <message>');
  5736. > return;
  5737. > }
  5738. > cs_welcome_add($user, { CHAN => $chan }, $wmsg);
  5739. > }
  5740. > elsif(lc($wcmd) eq 'del') {
  5741. > if (@args != 2 or !misc::isint($args[1])) {
  5742. > notice($user, 'Syntax: WELCOME <channnel> DEL <number>');
  5743. > return;
  5744. > }
  5745. > cs_welcome_del($user, { CHAN => $args[0] }, $args[1]);
  5746. > }
  5747. > elsif(lc($wcmd) eq 'list') {
  5748. > if (@args != 1) {
  5749. > notice($user, 'Syntax: WELCOME <channel> LIST');
  5750. > return;
  5751. > }
  5752. > cs_welcome_list($user, { CHAN => $args[0] });
  5753. > }
  5754. > else {
  5755. > notice($user, 'Syntax: WELCOME <channel> <ADD|DEL|LIST>');
  5756. > }
  5757. > }
  5758. > elsif($cmd =~ /^alist$/i) {
  5759. > if(@args >= 1) {
  5760. > cs_alist($user, { CHAN => shift @args }, shift @args);
  5761. > } else {
  5762. > notice($user, 'Syntax: ALIST <channel> [mask]');
  5763. > }
  5764. > }
  5765. > elsif($cmd =~ /^unban$/i) {
  5766. > if(@args == 1) {
  5767. > cs_unban($user, { CHAN => shift @args }, $src);
  5768. > }
  5769. > elsif(@args >= 2) {
  5770. > cs_unban($user, { CHAN => shift @args }, @args);
  5771. > } else {
  5772. > notice($user, 'Syntax: UNBAN <channel> [nick]');
  5773. > }
  5774. > }
  5775. > elsif($cmd =~ /^getkey$/i) {
  5776. > if(@args == 1) {
  5777. > cs_getkey($user, { CHAN => $args[0] });
  5778. > } else {
  5779. > notice($user, 'Syntax: GETKEY <channel>');
  5780. > }
  5781. > }
  5782. > elsif($cmd =~ /^auth$/i) {
  5783. > if (@args == 0) {
  5784. > notice($user, 'Syntax: AUTH <channel> <LIST|DELETE> [param]');
  5785. > } else {
  5786. > cs_auth($user, { CHAN => shift @args }, shift @args, @args);
  5787. > }
  5788. > }
  5789. > elsif($cmd =~ /^dice$/i) {
  5790. > notice($user, botserv::get_dice($args[0]));
  5791. > }
  5792. > elsif($cmd =~ /^(q|n)?ban$/i) {
  5793. > my $type = $1;
  5794. > my $chan = shift @args;
  5795. > if(@args >= 1) {
  5796. > cs_ban($user, { CHAN => $chan }, $type, @args)
  5797. > }
  5798. > else {
  5799. > notice($user, 'Syntax: BAN <channel> <nick|mask>');
  5800. > }
  5801. > }
  5802. > elsif($cmd =~ /^banlist$/i) {
  5803. > my $chan = shift @args;
  5804. > if(@args == 0) {
  5805. > cs_banlist($user, { CHAN => $chan });
  5806. > }
  5807. > else {
  5808. > notice($user, 'Syntax: BANLIST <channel>');
  5809. > }
  5810. > }
  5811. > elsif($cmd =~ /^assign$/i) {
  5812. > my $chan = shift @args;
  5813. > notice($user, "$csnick ASSIGN is deprecated. Please use $botserv::bsnick ASSIGN");
  5814. > if(@args == 2) {
  5815. > botserv::bs_assign($user, { CHAN => shift @args }, shift @args);
  5816. > }
  5817. > else {
  5818. > notice($user, 'Syntax: ASSIGN <#channel> <bot>');
  5819. > }
  5820. > }
  5821. > elsif($cmd =~ /^mode$/i) {
  5822. > my $chan = shift @args;
  5823. > if(@args >= 1) {
  5824. > cs_mode($user, { CHAN => $chan }, @args)
  5825. > }
  5826. > else {
  5827. > notice($user, 'Syntax: MODE <channel> <modes> [parms]');
  5828. > }
  5829. > }
  5830. > elsif($cmd =~ /^copy$/i) {
  5831. > my $chan = shift @args;
  5832. > if(@args >= 1) {
  5833. > cs_copy($user, { CHAN => $chan }, @args)
  5834. > }
  5835. > else {
  5836. > notice($user, 'Syntax: COPY #chan1 [type] #chan2');
  5837. > }
  5838. > }
  5839. > elsif($cmd =~ /^m(?:ode)?lock$/i) {
  5840. > my $chan = shift @args;
  5841. > if(@args >= 1) {
  5842. > cs_mlock($user, { CHAN => $chan }, @args)
  5843. > }
  5844. > else {
  5845. > notice($user, 'Syntax: MLOCK <channel> <ADD|DEL|SET|RESET> <modes> [parms]');
  5846. > }
  5847. > }
  5848. > elsif($cmd =~ /^resync$/i) {
  5849. > if (@args == 0) {
  5850. > notice($user, 'Syntax: RESYNC <chan1> [chan2 [chan3 [..]]]');
  5851. > } else {
  5852. > cs_resync($user, @args);
  5853. > }
  5854. > }
  5855. > elsif($cmd =~ /^JOIN$/i) {
  5856. > if (@args == 0) {
  5857. > notice($user, 'Syntax: JOIN <chan1> [chan2 [chan3 [..]]]');
  5858. > } else {
  5859. > cs_join($user, @args);
  5860. > }
  5861. > }
  5862. > else {
  5863. > notice($user, "Unrecognized command \002$cmd\002.", "For help, type: \002/msg chanserv help\002");
  5864. > wlog($csnick, LOG_DEBUG(), "$src tried to use $csnick $msg");
  5865. > }
  5866. > }
  5867. >
  5868. > sub cs_register($$;$$) {
  5869. > my ($user, $chan, $pass, $desc) = @_;
  5870. > # $pass is still passed in, but never used!
  5871. > my $src = get_user_nick($user);
  5872. > my $cn = $chan->{CHAN};
  5873. >
  5874. > unless(is_identified($user, $src)) {
  5875. > notice($user, 'You must register your nickname first.', "Type \002/msg NickServ HELP\002 for information on registering nicknames.");
  5876. > return;
  5877. > }
  5878. >
  5879. > unless(is_in_chan($user, $chan)) {
  5880. > notice($user, "You are not in \002$cn\002.");
  5881. > return;
  5882. > }
  5883. >
  5884. > if(services_conf_chanreg_needs_oper && !adminserv::is_svsop($user)) {
  5885. > notice($user, "You must be network staff to register a channel\n");
  5886. > return;
  5887. > }
  5888. > unless(get_op($user, $chan) & ($opmodes{o} | $opmodes{a} | $opmodes{q})) {
  5889. > # This would be preferred to be a 'opmode_mask' or something
  5890. > # However that might be misleading due to hop not being enough to register
  5891. > notice($user, "You must have channel operator status to register \002$cn\002.");
  5892. > return;
  5893. > }
  5894. >
  5895. > my $root = get_root_nick($src);
  5896. >
  5897. > if($desc) {
  5898. > my $dlength = length($desc);
  5899. > if($dlength >= 350) {
  5900. > notice($user, 'Channel description is too long by '. $dlength-350 .' character(s). Maximum length is 350 characters.');
  5901. > return;
  5902. > }
  5903. > }
  5904. >
  5905. > if($register->execute($cn, $desc, $root)) {
  5906. > notice($user, "\002Your channel is now registered. Thank you.\002");
  5907. > notice($user, ' ', "\002NOTICE:\002 Channel passwords are not used, as a security precaution.")
  5908. > if $pass;
  5909. > set_acc($root, $user, $chan, FOUNDER);
  5910. > $set_modelock->execute(services_conf_default_channel_mlock, $cn);
  5911. > do_modelock($chan);
  5912. > services::ulog($csnick, LOG_INFO(), "registered $cn", $user, $chan);
  5913. > botserv::bs_assign($user, $chan, services_conf_default_chanbot) if services_conf_default_chanbot;
  5914. > } else {
  5915. > notice($user, 'That channel has already been registered.');
  5916. > }
  5917. > }
  5918. >
  5919. > =cut
  5920. > cs_command new SrSv::AgentUI::Simple {
  5921. > COMMAND => [qw(uop vop hop aop sop cf cofounder cof cfounder)],
  5922. > SYNTAX => '#chan add/del/list/wipe/clear [nick/mask]',
  5923. > CALL => \&cs_xop_dispatch,
  5924. > CMD_TOO => 1,
  5925. > };
  5926. > =cut
  5927. > sub cs_xop_dispatch {
  5928. > my ($user, $cmd, $chan, $cmd2, @args) = @_;
  5929. > $cmd = uc $cmd;
  5930. >
  5931. > if($cmd2 =~ /^add$/i) {
  5932. > if(@args == 1) {
  5933. > cs_xop_add($user, $chan, $cmd, $args[0]);
  5934. > } else {
  5935. > notice($user, 'Syntax: '.uc $cmd.' <#channel> ADD <nick>');
  5936. > }
  5937. > }
  5938. > elsif($cmd2 =~ /^del(ete)?$/i) {
  5939. > if(@args == 1) {
  5940. > cs_xop_del($user, $chan, $cmd, $args[0]);
  5941. > } else {
  5942. > notice($user, 'Syntax: '.uc $cmd.' <#channel> DEL <nick>');
  5943. > }
  5944. > }
  5945. > elsif($cmd2 =~ /^list$/i) {
  5946. > if(@args >= 0) {
  5947. > cs_xop_list($user, $chan, $cmd, $args[0]);
  5948. > } else {
  5949. > notice($user, 'Syntax: '.uc $cmd.' <#channel> LIST [mask]');
  5950. > }
  5951. > }
  5952. > elsif($cmd2 =~ /^(wipe|clear)$/i) {
  5953. > if(@args == 0) {
  5954. > cs_xop_wipe($user, $chan, $cmd);
  5955. > } else {
  5956. > notice($user, 'Syntax: '.uc $cmd.' <#channel> WIPE');
  5957. > }
  5958. > }
  5959. > else {
  5960. > notice($user, 'Syntax: '.uc $cmd.' <#channel> <ADD|DEL|LIST|WIPE>');
  5961. > }
  5962. > }
  5963. >
  5964. > sub cs_xop_ad_pre($$$$$) {
  5965. > my ($user, $chan, $nick, $level, $del) = @_;
  5966. >
  5967. > my $old = get_acc($nick, $chan); $old = 0 unless $old;
  5968. > my $slevel = get_best_acc($user, $chan);
  5969. >
  5970. > unless(($del and is_identified($user, $nick)) or adminserv::can_do($user, 'SERVOP')) {
  5971. > unless($level < $slevel and $old < $slevel) {
  5972. > notice($user, $err_deny);
  5973. > return undef;
  5974. > }
  5975. > my $cn = $chan->{CHAN};
  5976. > my $overrideMsg = "$levels[$level] $cn ".($del ? 'DEL' : 'ADD')." $nick";
  5977. > can_do($chan, 'ACCCHANGE', $user, { OVERRIDE_MSG => $overrideMsg }) or return undef;
  5978. > }
  5979. >
  5980. > nickserv::chk_registered($user, $nick) or return undef;
  5981. > if (nr_chk_flag($nick, NRF_NOACC()) and !adminserv::can_do($user, 'SERVOP') and !$del) {
  5982. > notice($user, "\002$nick\002 is not able to be added to access lists.");
  5983. > return undef;
  5984. > }
  5985. >
  5986. > return $old;
  5987. > }
  5988. >
  5989. > sub cs_xop_list($$$;$) {
  5990. > my ($user, $chan, $cmd, $mask) = @_;
  5991. > chk_registered($user, $chan) or return;
  5992. > my $cn = $chan->{CHAN};
  5993. > my $level = xop_byname($cmd);
  5994. >
  5995. > my $overrideMsg = "$cmd $cn LIST";
  5996. > can_do($chan, 'ACCLIST', $user, { OVERRIDE_MSG => $overrideMsg }) or return;
  5997. >
  5998. > my @reply;
  5999. > if($mask) {
  6000. > my ($mnick, $mident, $mhost) = glob2sql(parse_mask($mask));
  6001. > $mnick = '%' if($mnick eq '');
  6002. > $mident = '%' if($mident eq '');
  6003. > $mhost = '%' if($mhost eq '');
  6004. >
  6005. > $get_acc_list_mask->execute($mnick, $cn, $level, $mnick, $mident, $mhost);
  6006. > while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list_mask->fetchrow_array) {
  6007. > push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
  6008. > push @reply, ' '.($t ? 'Date/time added: '. gmtime2($t).' ' : '').
  6009. > ($lu ? 'Last used '.time_ago($lu).' ago' : '') if ($t or $lu);
  6010. > }
  6011. > $get_acc_list_mask->finish();
  6012. > } else {
  6013. > $get_acc_list->execute($cn, $level);
  6014. > while(my ($n, $a, $t, $lu, $id, $vh) = $get_acc_list->fetchrow_array) {
  6015. > push @reply, "*) $n ($id\@$vh)" . ($a ? ' Added by: '.$a : '');
  6016. > push @reply, ' '.($t ? 'Date/time added: '. gmtime2($t).' ' : '').
  6017. > ($lu ? 'Last used '.time_ago($lu).' ago' : '') if ($t or $lu);
  6018. > }
  6019. > $get_acc_list->finish();
  6020. > }
  6021. >
  6022. > notice($user, "$levels[$level] list for \002$cn\002:", @reply);
  6023. >
  6024. > return;
  6025. > }
  6026. >
  6027. > sub cs_xop_wipe($$$) {
  6028. > my ($user, $chan, $cmd, $nick) = @_;
  6029. > chk_registered($user, $chan) or return;
  6030. >
  6031. > my $slevel = get_best_acc($user, $chan);
  6032. > my $level = xop_byname($cmd);
  6033. >
  6034. > unless($level < $slevel) {
  6035. > notice($user, $err_deny);
  6036. > return;
  6037. > }
  6038. > my $cn = $chan->{CHAN};
  6039. > my $overrideMsg = "$cmd $cn WIPE";
  6040. > my $srcnick = can_do($chan, 'ACCCHANGE', $user, { ACC => $slevel, OVERRIDE_MSG => $overrideMsg }) or return;
  6041. >
  6042. > $wipe_acc_list->execute($cn, $level);
  6043. >
  6044. > my $log_str = "wiped the $cmd list of \002$cn\002.";
  6045. > my $src = get_user_nick($user);
  6046. > notice($user, "You have $log_str");
  6047. > ircd::notice(agent($chan), '%'.$cn, "\002$src\002 has $log_str")
  6048. > if cr_chk_flag($chan, CRF_VERBOSE);
  6049. > services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  6050. >
  6051. > memolog($chan, "\002$srcnick\002 $log_str");
  6052. > }
  6053. >
  6054. > sub cs_xop_add($$$$) {
  6055. > my ($user, $chan, $cmd, $nick) = @_;
  6056. >
  6057. > chk_registered($user, $chan) or return;
  6058. > my $level = xop_byname($cmd);
  6059. > my $old = cs_xop_ad_pre($user, $chan, $nick, $level, 0);
  6060. > return unless defined($old);
  6061. >
  6062. > my $cn = $chan->{CHAN};
  6063. >
  6064. > if($old == $level) {
  6065. > notice($user, "\002$nick\002 already has $levels[$level] access to \002$cn\002.");
  6066. > return;
  6067. > }
  6068. >
  6069. > if($old == FOUNDER) {
  6070. > notice($user, "\002$nick\002 is the founder of \002$cn\002 and cannot be added to access lists.",
  6071. > "For more information, type: \002/msg chanserv help set founder\002");
  6072. > return;
  6073. > }
  6074. >
  6075. > my $root = get_root_nick($nick);
  6076. > my $auth = nr_chk_flag($root, NRF_AUTH());
  6077. > my $src = get_user_nick($user);
  6078. >
  6079. > if($auth) {
  6080. > $add_auth->execute($cn, "$src:".($old ? $old : 0 ).":$level:".time(), $root);
  6081. > del_acc($root, $chan) if $level < $old;
  6082. > }
  6083. > else {
  6084. > set_acc($root, $user, $chan, $level);
  6085. > }
  6086. >
  6087. > if($old < 0) {
  6088. > $del_nick_akick->execute($cn, $root);
  6089. > my $log_str = "moved $root from the AKICK list to the ${levels[$level]} list of \002$cn\002".
  6090. > ($auth ? ' (requires authorization)' : '');
  6091. >
  6092. > my $src = get_user_nick($user);
  6093. > notice_all_nicks($user, $root, "\002$src\002 $log_str");
  6094. > ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  6095. > if cr_chk_flag($chan, CRF_VERBOSE);
  6096. > services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  6097. > my $srcnick = can_do($chan, 'ACCLIST', $user);
  6098. > memolog($chan, "\002$srcnick\002 $log_str");
  6099. > } else {
  6100. > my $log_str = ($old?'moved':'added')." \002$root\002"
  6101. > . ($old ? " from the ${levels[$old]}" : '') .
  6102. > " to the ${levels[$level]} list of \002$cn\002" .
  6103. > ($auth ? ' (requires authorization)' : '');
  6104. > my $src = get_user_nick($user);
  6105. > notice_all_nicks($user, $root, "\002$src\002 $log_str");
  6106. > ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  6107. > if cr_chk_flag($chan, CRF_VERBOSE);
  6108. > services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  6109. > my $srcnick = can_do($chan, 'ACCLIST', $user);
  6110. > memolog($chan, "\002$srcnick\002 $log_str");
  6111. > }
  6112. > }
  6113. >
  6114. > sub cs_xop_del($$$) {
  6115. > my ($user, $chan, $cmd, $nick) = @_;
  6116. >
  6117. > chk_registered($user, $chan) or return;
  6118. > my $level = xop_byname($cmd);
  6119. > my $old = cs_xop_ad_pre($user, $chan, $nick, $level, 1);
  6120. > return unless defined($old);
  6121. >
  6122. > my $cn = $chan->{CHAN};
  6123. >
  6124. > unless($old == $level) {
  6125. > notice($user, "\002$nick\002 is not on the ${levels[$level]} list of \002$cn\002.");
  6126. > return;
  6127. > }
  6128. >
  6129. > my $root = get_root_nick($nick);
  6130. > my $srcnick = can_do($chan, 'ACCLIST', $user);
  6131. >
  6132. > del_acc($root, $chan);
  6133. >
  6134. > my $src = get_user_nick($user);
  6135. > my $log_str = "removed \002$root\002 ($nick) from the ${levels[$level]} list of \002$cn\002";
  6136. > notice_all_nicks($user, $root, "\002$src\002 $log_str");
  6137. > ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  6138. > if cr_chk_flag($chan, CRF_VERBOSE);
  6139. > services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  6140. > memolog($chan, "\002$srcnick\002 $log_str");
  6141. > }
  6142. >
  6143. > sub cs_count($$) {
  6144. > my ($user, $chan) = @_;
  6145. >
  6146. > chk_registered($user, $chan) or return;
  6147. >
  6148. > my $cn = $chan->{CHAN};
  6149. > my $overrideMsg = "COUNT $cn";
  6150. > if(can_do($chan, 'ACCLIST', $user, { OVERRIDE_MSG => $overrideMsg })) {
  6151. > } else {
  6152. > return;
  6153. > }
  6154. >
  6155. > my $reply = '';
  6156. > for (my $level = $plzero + 1; $level < COFOUNDER + 2; $level++) {
  6157. > $get_acc_count->execute($cn, $level - 1);
  6158. > my ($num_recs) = $get_acc_count->fetchrow_array;
  6159. > $reply = $reply." $plevels[$level]: ".$num_recs;
  6160. > }
  6161. > notice($user, "\002$cn Count:\002 ".$reply);
  6162. > }
  6163. >
  6164. > sub cs_levels_pre($$$;$) {
  6165. > my($user, $chan, $cmd, $listonly) = @_;
  6166. >
  6167. > chk_registered($user, $chan) or return 0;
  6168. > my $cn = $chan->{CHAN};
  6169. > my $overrideMsg = "LEVELS $cn $cmd";
  6170. > return can_do($chan, ($listonly ? 'LEVELSLIST' : 'LEVELS'), $user, { OVERRIDE_MSG => $overrideMsg });
  6171. > }
  6172. >
  6173. > sub cs_levels_set($$$;$) {
  6174. > my ($user, $chan, $perm, $level) = @_;
  6175. >
  6176. > cs_levels_pre($user, $chan, "$perm $level") or return;
  6177. > my $cn = $chan->{CHAN};
  6178. >
  6179. > unless(is_level($perm)) {
  6180. > notice($user, "$perm is not a valid permission.");
  6181. > return;
  6182. > }
  6183. >
  6184. > if(defined($level)) {
  6185. > $level = xop_byname($level);
  6186. > unless(defined($level) and $level >= 0) {
  6187. > notice($user, 'You must specify one of the following levels: '.
  6188. > 'any, uop, vop, hop, aop, sop, cofounder, founder, nobody');
  6189. > return;
  6190. > }
  6191. >
  6192. > $get_level_max->execute($perm);
  6193. > my ($max) = $get_level_max->fetchrow_array;
  6194. > $get_level_max->finish();
  6195. >
  6196. > if($max and $level > $max) {
  6197. > notice($user, "\002$perm\002 cannot be set to " . $plevels[$level+$plzero] . '.');
  6198. > return;
  6199. > }
  6200. >
  6201. > $add_level->execute($cn, $perm);
  6202. > $set_level->execute($level, $cn, $perm);
  6203. >
  6204. > if($level == 8) {
  6205. > notice($user, "\002$perm\002 is now disabled in \002$cn\002.");
  6206. > } else {
  6207. > notice($user, "\002$perm\002 now requires " . $levels[$level] . " access in \002$cn\002.");
  6208. > }
  6209. > } else {
  6210. > $reset_level->execute($perm, $cn);
  6211. >
  6212. > notice($user, "\002$perm\002 has been reset to default.");
  6213. > }
  6214. > }
  6215. >
  6216. > sub cs_levels_list($$) {
  6217. > my ($user, $chan) = @_;
  6218. >
  6219. > cs_levels_pre($user, $chan, 'LIST', 1) or return;
  6220. > my $cn = $chan->{CHAN};
  6221. >
  6222. > $get_levels->execute($cn);
  6223. > my @data;
  6224. > while(my ($name, $def, $lvl) = $get_levels->fetchrow_array) {
  6225. > push @data, [$name,
  6226. > (defined($lvl) ? $plevels[$lvl+$plzero] : $plevels[$def+$plzero]),
  6227. > (defined($lvl) ? '' : '(default)')];
  6228. > }
  6229. >
  6230. > notice($user, columnar { TITLE => "Permission levels for \002$cn\002:",
  6231. > NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT) }, @data);
  6232. > }
  6233. >
  6234. > sub cs_levels_clear($$) {
  6235. > my ($user, $chan) = @_;
  6236. >
  6237. > cs_levels_pre($user, $chan, 'CLEAR') or return;
  6238. > my $cn = $chan->{CHAN};
  6239. >
  6240. > $clear_levels->execute($cn);
  6241. >
  6242. > notice($user, "All permissions have been reset to default.");
  6243. > }
  6244. >
  6245. > sub cs_akick_pre($$$;$) {
  6246. > my ($user, $chan, $overrideMsg, $list) = @_;
  6247. >
  6248. > chk_registered($user, $chan) or return 0;
  6249. >
  6250. > return can_do($chan, ($list ? 'AKICKLIST' : 'AKICK'), $user, { OVERRIDE_MSG => $overrideMsg });
  6251. > }
  6252. >
  6253. > sub cs_akick_add($$$$) {
  6254. > my ($user, $chan, $mask, $reason) = @_;
  6255. > my $cn = $chan->{CHAN};
  6256. >
  6257. > my $adder = cs_akick_pre($user, $chan, "ADD $mask $reason") or return;
  6258. >
  6259. > my ($nick, $ident, $host) = parse_mask($mask);
  6260. >
  6261. > if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
  6262. > notice($user, 'Invalid hostmask.');
  6263. > return;
  6264. > }
  6265. >
  6266. > if($ident eq '') {
  6267. > $nick = $mask;
  6268. >
  6269. > unless(valid_nick($nick)) {
  6270. > $mask = normalize_hostmask($mask);
  6271. > ($nick, $ident, $host) = parse_mask($mask);
  6272. > }
  6273. > }
  6274. >
  6275. > if ($ident eq '' and $host eq '' and !nickserv::is_registered($nick)) {
  6276. > notice($user, "\002$nick\002 is not registered");
  6277. > return;
  6278. > }
  6279. >
  6280. > my $rlength = length($reason);
  6281. > if($rlength >= 350) {
  6282. > notice($user, 'AKick reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  6283. > return;
  6284. > }
  6285. >
  6286. > my $log_str;
  6287. > my $src = get_user_nick($user);
  6288. > if($ident eq '' and $host eq '' and my $old = get_acc($nick, $chan)) {
  6289. > if ($old == -1) {
  6290. > notice($user, "\002$nick\002 is already on the AKick list in \002$cn\002");
  6291. > return;
  6292. > }
  6293. > if($old < get_best_acc($user, $chan) or adminserv::can_do($user, 'SERVOP')) {
  6294. > if ($old == FOUNDER()) {
  6295. > # This is a fallthrough for the override case.
  6296. > # It shouldn't happen otherwise.
  6297. > # I didn't make it part of the previous conditional
  6298. > # b/c just $err_deny is a bit undescriptive in the override case.
  6299. > notice($user, "You can't akick the founder!", $err_deny);
  6300. > return;
  6301. > }
  6302. >
  6303. > my $root = get_root_nick($nick);
  6304. > $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
  6305. > set_acc($nick, $user, $chan, -1);
  6306. > $log_str = "moved \002$nick\002 (root: \002$root\002) from the $levels[$old] list".
  6307. > " to the AKick list of \002$cn\002";
  6308. > notice_all_nicks($user, $root, "\002$src\002 $log_str");
  6309. > } else {
  6310. > notice($user, $err_deny);
  6311. > return;
  6312. > }
  6313. > } else {
  6314. > if($ident eq '' and $host eq '') {
  6315. > $add_nick_akick->execute($cn, $src, $reason, $nick); $add_nick_akick->finish();
  6316. > if (find_auth($cn, $nick)) {
  6317. > # Don't allow a pending AUTH entry to potentially override an AKick entry
  6318. > # Believe it or not, it almost happened with #animechat on SCnet.
  6319. > # This would also end up leaving an orphan entry in the akick table.
  6320. > $nickserv::del_auth->execute($nick, $cn);
  6321. > $nickserv::del_auth->finish();
  6322. > }
  6323. > set_acc($nick, $user, $chan, -1);
  6324. > my $root = get_root_nick($nick);
  6325. > $log_str = "added \002$nick\002 (root: \002$root\002) to the AKick list of \002$cn\002.";
  6326. > } else {
  6327. > ($nick, $ident, $host) = glob2sql($nick, $ident, $host);
  6328. > unless($add_akick->execute($cn, $nick, $ident, $host, $adder, $reason)) {
  6329. > notice($user, "\002$mask\002 is already on the AKick list of \002$cn\002.");
  6330. > return;
  6331. > }
  6332. > $log_str = "added \002$mask\002 to the AKick list of \002$cn\002.";
  6333. > }
  6334. >
  6335. > }
  6336. > notice($user, "You have $log_str");
  6337. > ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str")
  6338. > if cr_chk_flag($chan, CRF_VERBOSE);
  6339. > services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  6340. > memolog($chan, "\002$adder\002 $log_str");
  6341. >
  6342. > akick_allchan($chan);
  6343. > }
  6344. >
  6345. > sub get_akick_by_num($$) {
  6346. > my ($chan, $num) = @_;
  6347. > my $cn = $chan->{CHAN};
  6348. >
  6349. > $get_akick_by_num->execute($cn, $num);
  6350. > my ($nick, $ident, $host) = $get_akick_by_num->fetchrow_array();
  6351. > ($nick, $ident, $host) = sql2glob($nick, $ident, $host);
  6352. > $get_akick_by_num->finish();
  6353. > if(!$nick) {
  6354. > return undef;
  6355. > } elsif($ident eq '' and $host eq '') {
  6356. > # nick based akicks don't use nicks but nickreg.id
  6357. > # so we have to get the nickreg.nick back
  6358. > $nick = nickserv::get_id_nick($nick);
  6359. > }
  6360. > return ($nick, $ident, $host);
  6361. > }
  6362. >
  6363. > sub cs_akick_del($$$) {
  6364. > my ($user, $chan, $mask) = @_;
  6365. > my $cn = $chan->{CHAN};
  6366. >
  6367. > my $adder = cs_akick_pre($user, $chan, "DEL $mask") or return;
  6368. >
  6369. > my @masks;
  6370. > if ($mask =~ /^[0-9\.,-]+$/) {
  6371. > foreach my $num (makeSeqList($mask)) {
  6372. > my ($nick, $ident, $host) = get_akick_by_num($chan, $num - 1) or next;
  6373. > if($ident eq '' and $host eq '') {
  6374. > push @masks, $nick;
  6375. > } else {
  6376. > push @masks, "$nick!$ident\@$host";
  6377. > }
  6378. > }
  6379. > } else {
  6380. > @masks = ($mask);
  6381. > }
  6382. > foreach my $mask (@masks) {
  6383. > my ($nick, $ident, $host) = parse_mask($mask);
  6384. >
  6385. > if(($ident eq '' or $host eq '') and not ($ident eq '' and $host eq '')) {
  6386. > notice($user, 'Invalid hostmask.');
  6387. > return;
  6388. > }
  6389. >
  6390. > if($ident eq '') {
  6391. > $nick = $mask;
  6392. >
  6393. > unless(valid_nick($nick)) {
  6394. > $mask = normalize_hostmask($mask);
  6395. > ($nick, $ident, $host) = parse_mask($mask);
  6396. > }
  6397. > }
  6398. >
  6399. > if ($ident eq '' and $host eq '' and !nickserv::is_registered($nick)) {
  6400. > notice($user, "\002$nick\002 is not registered");
  6401. > return;
  6402. > }
  6403. >
  6404. > my ($success, $log_str) = do_akick_del($chan, $mask, $nick, $ident, $host);
  6405. > my $src = get_user_nick($user);
  6406. > if($success) {
  6407. > notice($user, "\002$src\002 $log_str");
  6408. > services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  6409. > ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag($chan, CRF_VERBOSE);
  6410. > memolog($chan, "\002$adder\002 $log_str");
  6411. > } else {
  6412. > notice($user, $log_str);
  6413. > }
  6414. > }
  6415. > }
  6416. >
  6417. > sub do_akick_del($$$$$) {
  6418. > my ($chan, $mask, $nick, $ident, $host) = @_;
  6419. > my $cn = $chan->{CHAN};
  6420. >
  6421. > my $log_str;
  6422. > if($ident eq '' and $host eq '') {
  6423. > if(get_acc($nick, $chan) == -1) {
  6424. > del_acc($nick, $chan);
  6425. > $del_nick_akick->execute($cn, $nick); $del_nick_akick->finish();
  6426. > my $root = get_root_nick($nick);
  6427. > return (1, "deleted \002$nick\002 (root: \002$root\002) from the AKick list of \002$cn\002.")
  6428. > } else {
  6429. > return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
  6430. > }
  6431. > } else {
  6432. > ($nick, $ident, $host) = glob2sql($nick, $ident, $host);
  6433. > if($del_akick->execute($cn, $nick, $ident, $host) != 0) {
  6434. > return (1, "deleted \002$mask\002 from the AKick list of \002$cn\002.");
  6435. > } else {
  6436. > return (undef, "\002$mask\002 was not on the AKick list of \002$cn\002.");
  6437. > }
  6438. > }
  6439. > }
  6440. >
  6441. > sub cs_akick_list($$) {
  6442. > my ($user, $chan) = @_;
  6443. > my $cn = $chan->{CHAN};
  6444. >
  6445. > cs_akick_pre($user, $chan, 'LIST', 1) or return;
  6446. >
  6447. > my @data;
  6448. >
  6449. > $get_akick_list->execute($cn);
  6450. > my $i = 0;
  6451. > while(my ($nick, $ident, $host, $adder, $reason, $time) = $get_akick_list->fetchrow_array) {
  6452. > if($ident ne '') {
  6453. > ($nick, $ident, $host) = sql2glob($nick, $ident, $host);
  6454. > }
  6455. >
  6456. > if($ident eq '' and $host eq '') {
  6457. > $nick = nickserv::get_id_nick($nick);
  6458. > } else {
  6459. > $nick = "$nick!$ident\@$host";
  6460. > }
  6461. >
  6462. > push @data, ["\002".++$i."\002", $nick, $adder, ($time ? gmtime2($time) : ''), $reason];
  6463. > }
  6464. >
  6465. > notice($user, columnar {TITLE => "AKICK list of \002$cn\002:", DOUBLE=>1,
  6466. > NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  6467. > }
  6468. >
  6469. > sub cs_akick_wipe($$$) {
  6470. > my ($user, $chan) = @_;
  6471. > my $cn = $chan->{CHAN};
  6472. >
  6473. > my $adder = cs_akick_pre($user, $chan, 'WIPE') or return;
  6474. >
  6475. > $drop_akick->execute($cn);
  6476. > $wipe_acc_list->execute($cn, -1);
  6477. > my $log_str = "wiped the AKICK list of \002$cn\002.";
  6478. > my $src = get_user_nick($user);
  6479. > notice($user, "You have $log_str");
  6480. > ircd::notice(agent($chan), '%'.$cn, "\002$src\002 $log_str") if cr_chk_flag($chan, CRF_VERBOSE);
  6481. > services::ulog($csnick, LOG_INFO(), $log_str, $user, $chan);
  6482. > memolog($chan, "\002$adder\002 $log_str");
  6483. > }
  6484. >
  6485. > sub cs_akick_enforce($$) {
  6486. > my ($user, $chan) = @_;
  6487. > my $cn = $chan->{CHAN};
  6488. >
  6489. > chk_registered($user, $chan) or return;
  6490. >
  6491. > can_do($chan, 'AKickEnforce', $user, { OVERRIDE_MSG => "AKICK $cn ENFORCE" }) or return;
  6492. >
  6493. > akick_allchan($chan);
  6494. > }
  6495. >
  6496. > =cut
  6497. > cs_command new SrSv::AgentUI::Simple {
  6498. > COMMAND => [qw(info)],
  6499. > SYNTAX => 'LIST:#chan',
  6500. > CALL => \&cs_info,
  6501. > NO_WRAPPER => 1,
  6502. > };
  6503. > =cut
  6504. > sub cs_info($@) {
  6505. > my ($user, @chanList) = @_;
  6506. >
  6507. > my @reply;
  6508. > foreach my $cn (@chanList) {
  6509. > if(ref($cn) eq 'HASH') {
  6510. > $cn = $cn->{CHAN};
  6511. > }
  6512. > elsif($cn =~ /,/) {
  6513. > push @chanList, split(',', $cn);
  6514. > next;
  6515. > }
  6516. > my $chan = { CHAN => $cn };
  6517. > unless(__can_do($chan, 'INFO', undef, 0)) {
  6518. > can_do($chan, 'INFO', $user, { OVERRIDE_MSG => "INFO $cn" })
  6519. > or next;
  6520. > }
  6521. >
  6522. > $get_info->execute($cn);
  6523. > my @result = $get_info->fetchrow_array;
  6524. > unless(@result) {
  6525. > push @reply, "The channel \002$cn\002 is not registered.";
  6526. > next;
  6527. > }
  6528. >
  6529. > my ($descrip, $regd, $last, $topic, $topicer, $modelock, $founder, $successor, $bot, $bantype) = @result;
  6530. >
  6531. > $modelock = modes::sanitize($modelock) unless can_do($chan, 'GETKEY', $user, { NOREPLY => 1 });
  6532. >
  6533. > my @opts;
  6534. >
  6535. > my $topiclock = get_level($chan, 'SETTOPIC');
  6536. > push @opts, "Topic Lock ($levels[$topiclock])" if $topiclock;
  6537. >
  6538. > if(cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE))) {
  6539. > push @reply, "\002$cn\002 is closed and cannot be used: ". get_close($chan);
  6540. > next;
  6541. > }
  6542. >
  6543. > my @extra;
  6544. > push @extra, 'Will not expire' if cr_chk_flag($chan, CRF_HOLD);
  6545. > push @extra, 'Channel is frozen and access suspended' if cr_chk_flag($chan, CRF_FREEZE);
  6546. >
  6547. > push @opts, 'OpGuard' if cr_chk_flag($chan, CRF_OPGUARD);
  6548. > push @opts, 'BotStay' if cr_chk_flag($chan, CRF_BOTSTAY);
  6549. > push @opts, 'SplitOps' if cr_chk_flag($chan, CRF_SPLITOPS);
  6550. > push @opts, 'Verbose' if cr_chk_flag($chan, CRF_VERBOSE);
  6551. > push @opts, 'NeverOp' if cr_chk_flag($chan, CRF_NEVEROP);
  6552. > push @opts, 'Ban type '.$bantype if $bantype;
  6553. > my $opts = join(', ', @opts);
  6554. >
  6555. > my @data;
  6556. >
  6557. > push @data, ['Founder:', $founder];
  6558. > push @data, ['Successor:', $successor] if $successor;
  6559. > push @data, ['Description:', $descrip] if $descrip;
  6560. > push @data, ['Mode lock:', $modelock];
  6561. > push @data, ['Settings:', $opts] if $opts;
  6562. > push @data, ['ChanBot:', $bot] if $bot and $bot ne '';
  6563. > #FIXME: memo level
  6564. > push @data, ['Registered:', gmtime2($regd)],
  6565. > ['Last opping:', gmtime2($last)],
  6566. > ['Time now:', gmtime2(time)];
  6567. >
  6568. > push @reply, columnar {TITLE => "ChanServ info for \002$cn\002:", NOHIGHLIGHT => 1}, @data,
  6569. > {COLLAPSE => \@extra, BULLET => 1};
  6570. > }
  6571. > notice($user, @reply);
  6572. > }
  6573. >
  6574. > sub cs_set_pre($$$$) {
  6575. > my ($user, $chan, $set, $parm) = @_;
  6576. > my $cn = $chan->{CHAN};
  6577. > my $override = 0;
  6578. >
  6579. > my %valid_set = (
  6580. > 'founder' => 1, 'successor' => 1, 'unsuccessor' => 1,
  6581. > #'mlock' => 1, 'modelock' => 1,
  6582. > 'desc' => 1,
  6583. > 'topiclock' => 1, 'greet' => 1, 'opguard' => 1,
  6584. > 'freeze' => 1, 'botstay' => 1, 'verbose' => 1,
  6585. > 'splitops' => 1, 'bantype' => 1, 'dice' => 1,
  6586. > 'welcomeinchan' => 1, 'log' => 1,
  6587. >
  6588. > 'hold' => 1, 'noexpire' => 1, 'no-expire' => 1,
  6589. >
  6590. > 'autovoice' => 1, 'avoice' => 1,
  6591. > 'neverop' => 1, 'noop' => 1,
  6592. > );
  6593. > my %override_set = (
  6594. > 'hold' => 'SERVOP', 'noexpire' => 'SERVOP', 'no-expire' => 'SERVOP',
  6595. > 'freeze' => 'FREEZE', 'botstay' => 'BOT',
  6596. > );
  6597. >
  6598. > chk_registered($user, $chan) or return 0;
  6599. > if($set =~ /m(?:ode)?lock/) {
  6600. > notice($user, "CS SET MLOCK is deprecated and replaced with CS MLOCK",
  6601. > "For more information, please /CS HELP MLOCK");
  6602. > return 0;
  6603. > }
  6604. > unless($valid_set{lc $set}) {
  6605. > notice($user, "$set is not a valid ChanServ setting.");
  6606. > return 0;
  6607. > }
  6608. >
  6609. > if($override_set{lc($set)}) {
  6610. > if(adminserv::can_do($user, $override_set{lc($set)}) ) {
  6611. > if(services_conf_log_overrides) {
  6612. > my $src = get_user_nick($user);
  6613. > wlog($csnick, LOG_INFO(), "\002$src\002 used override CS SET $cn $set $parm");
  6614. > }
  6615. > $override = 1;
  6616. > } else {
  6617. > notice($user, $err_deny);
  6618. > return 0;
  6619. > }
  6620. > }
  6621. > else {
  6622. > can_do($chan, 'SET', $user) or return 0;
  6623. > }
  6624. >
  6625. > return 1;
  6626. > }
  6627. >
  6628. > sub cs_set($$$;$) {
  6629. > my ($user, $chan, $set, $parm) = @_;
  6630. > my $cn = $chan->{CHAN};
  6631. > $set = lc $set;
  6632. >
  6633. > cs_set_pre($user, $chan, $set, $parm) or return;
  6634. >
  6635. > if($set =~ /^founder$/i) {
  6636. > my $override;
  6637. > unless(get_best_acc($user, $chan) == FOUNDER) {
  6638. > if(adminserv::can_do($user, 'SERVOP')) {
  6639. > $override = 1;
  6640. > } else {
  6641. > notice($user, $err_deny);
  6642. > return;
  6643. > }
  6644. > }
  6645. >
  6646. > my $root;
  6647. > unless($root = get_root_nick($parm)) {
  6648. > notice($user, "The nick \002$parm\002 is not registered.");
  6649. > return;
  6650. > }
  6651. >
  6652. > $get_founder->execute($cn);
  6653. > my ($prev) = $get_founder->fetchrow_array;
  6654. > $get_founder->finish();
  6655. >
  6656. > if(lc($root) eq lc($prev)) {
  6657. > notice($user, "\002$parm\002 is already the founder of \002$cn\002.");
  6658. > return;
  6659. > }
  6660. >
  6661. > set_acc($prev, $user, $chan, COFOUNDER);
  6662. >
  6663. > $set_founder->execute($root, $cn); $set_founder->finish();
  6664. > set_acc($root, $user, $chan, FOUNDER);
  6665. >
  6666. > notice($user, ($override ? "The previous founder, \002$prev\002, has" : "You have") . " been moved to the co-founder list of \002$cn\002.");
  6667. > notice_all_nicks($user, $root, "\002$root\002 has been set as the founder of \002$cn\002.");
  6668. > services::ulog($csnick, LOG_INFO(), "set founder of \002$cn\002 to \002$root\002", $user, $chan);
  6669. >
  6670. > $get_successor->execute($cn);
  6671. > my $suc = $get_successor->fetchrow_array; $get_successor->finish();
  6672. > if(lc($suc) eq lc($root)) {
  6673. > $del_successor->execute($cn); $del_successor->finish();
  6674. > notice($user, "Successor has been removed from \002$cn\002.");
  6675. > }
  6676. >
  6677. > return;
  6678. > }
  6679. >
  6680. > if($set eq 'successor') {
  6681. > unless(get_best_acc($user, $chan) == FOUNDER or adminserv::can_do($user, 'SERVOP')) {
  6682. > notice($user, $err_deny);
  6683. > return;
  6684. > }
  6685. >
  6686. > if(get_acc($parm, $chan) == 7) {
  6687. > notice($user, "The channel founder may not be the successor.");
  6688. > return;
  6689. > }
  6690. >
  6691. > my $root;
  6692. > unless($root = get_root_nick($parm)) {
  6693. > notice($user, "The nick \002$parm\002 is not registered.");
  6694. > return;
  6695. > }
  6696. >
  6697. > $set_successor->execute($root, $cn); $set_successor->finish();
  6698. >
  6699. > notice($user, "\002$parm\002 is now the successor of \002$cn\002");
  6700. > services::ulog($csnick, LOG_INFO(), "set successor of \002$cn\002 to \002$root\002", $user, $chan);
  6701. > return;
  6702. > }
  6703. >
  6704. > if($set eq 'unsuccessor') {
  6705. > unless(get_best_acc($user, $chan) == FOUNDER or adminserv::can_do($user, 'SERVOP')) {
  6706. > notice($user, $err_deny);
  6707. > return;
  6708. > }
  6709. >
  6710. > $del_successor->execute($cn); $del_successor->finish();
  6711. >
  6712. > notice($user, "Successor has been removed from \002$cn\002.");
  6713. > services::ulog($csnick, LOG_INFO(), "removed successor from \002$cn\002", $user, $chan);
  6714. > return;
  6715. > }
  6716. >
  6717. > if($set =~ /m(?:ode)?lock/) {
  6718. > my $modes = modes::merge($parm, '+r', 1);
  6719. > $modes = sanitize_mlockable($modes);
  6720. > $set_modelock->execute($modes, $cn);
  6721. >
  6722. > notice($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
  6723. > do_modelock($chan);
  6724. > return;
  6725. > }
  6726. >
  6727. > if($set eq 'desc') {
  6728. > $set_descrip->execute($parm, $cn);
  6729. >
  6730. > notice($user, "Description of \002$cn\002 has been changed.");
  6731. > return;
  6732. > }
  6733. >
  6734. > if($set eq 'topiclock') {
  6735. > my $perm = xop_byname($parm);
  6736. > if($parm =~ /^(?:no|off|false|0)$/i) {
  6737. > cs_levels_set($user, $chan, 'SETTOPIC');
  6738. > cs_levels_set($user, $chan, 'TOPIC');
  6739. > } elsif($perm >= 0 and defined($perm)) {
  6740. > cs_levels_set($user, $chan, 'SETTOPIC', $parm);
  6741. > cs_levels_set($user, $chan, 'TOPIC', $parm);
  6742. > } else {
  6743. > notice($user, 'Syntax: SET <#chan> TOPICLOCK <off|any|uop|vop|hop|aop|sop|cf|founder>');
  6744. > }
  6745. > return;
  6746. > }
  6747. >
  6748. > if($set =~ /^bantype$/i) {
  6749. > unless (misc::isint($parm) and ($parm >= 0 and $parm <= 10)) {
  6750. > notice($user, 'Invalid bantype');
  6751. > return;
  6752. > }
  6753. >
  6754. > $set_bantype->execute($parm, $cn);
  6755. >
  6756. > notice($user, "Ban-Type for \002$cn\002 now set to \002$parm\002.");
  6757. >
  6758. > return;
  6759. > }
  6760. >
  6761. > my $val;
  6762. > if($parm =~ /^(?:no|off|false|0)$/i) { $val = 0; }
  6763. > elsif($parm =~ /^(?:yes|on|true|1)$/i) { $val = 1; }
  6764. > else {
  6765. > notice($user, "Please say \002on\002 or \002off\002.");
  6766. > return;
  6767. > }
  6768. >
  6769. > if($set =~ /^(?:opguard|secureops)$/i) {
  6770. > cr_set_flag($chan, CRF_OPGUARD, $val);
  6771. >
  6772. > if($val) {
  6773. > notice($user,
  6774. > "OpGuard is now \002ON\002.",
  6775. > "Channel status may not be granted by unauthorized users in \002$cn\002."#,
  6776. > #"Note that you must change the $csnick LEVELS settings for VOICE, HALFOP, OP, and/or ADMIN for this setting to have any effect."
  6777. > );
  6778. > } else {
  6779. > notice($user,
  6780. > "OpGuard is now \002OFF\002.",
  6781. > "Channel status may be given freely in \002$cn\002."
  6782. > );
  6783. > }
  6784. >
  6785. > return;
  6786. > }
  6787. >
  6788. > if($set =~ /^(?:splitops)$/i) {
  6789. > cr_set_flag($chan, CRF_SPLITOPS, $val);
  6790. >
  6791. > if($val) {
  6792. > notice($user, "SplitOps is now \002ON\002.");
  6793. > } else {
  6794. > notice($user, "SplitOps is now \002OFF\002.");
  6795. > }
  6796. >
  6797. > return;
  6798. > }
  6799. >
  6800. > if($set =~ /^(hold|no-?expire)$/i) {
  6801. > cr_set_flag($chan, CRF_HOLD, $val);
  6802. >
  6803. > if($val) {
  6804. > notice($user, "\002$cn\002 will not expire");
  6805. > services::ulog($csnick, LOG_INFO(), "has held \002$cn\002", $user, $chan);
  6806. > } else {
  6807. > notice($user, "\002$cn\002 is no longer held from expiration");
  6808. > services::ulog($csnick, LOG_INFO(), "has removed \002$cn\002 from hold", $user, $chan);
  6809. > }
  6810. >
  6811. > return;
  6812. > }
  6813. >
  6814. > if($set =~ /^freeze$/i) {
  6815. > cr_set_flag($chan, CRF_FREEZE, $val);
  6816. >
  6817. > if($val) {
  6818. > notice($user, "\002$cn\002 is now frozen and access suspended");
  6819. > services::ulog($csnick, LOG_INFO(), "has frozen \002$cn\002", $user, $chan);
  6820. > } else {
  6821. > notice($user, "\002$cn\002 is now unfrozen and access restored");
  6822. > services::ulog($csnick, LOG_INFO(), "has unfrozen \002$cn\002", $user, $chan);
  6823. > }
  6824. >
  6825. > return;
  6826. > }
  6827. >
  6828. > if($set =~ /^botstay$/i) {
  6829. > cr_set_flag($chan, CRF_BOTSTAY, $val);
  6830. >
  6831. > if($val) {
  6832. > notice($user, "Bot will now always stay in \002$cn");
  6833. > botserv::bot_join($chan, undef);
  6834. > } else {
  6835. > notice($user, "Bot will now part if less than one user is in \002$cn");
  6836. > botserv::bot_part_if_needed(undef, $chan, "Botstay turned off");
  6837. > }
  6838. >
  6839. > return;
  6840. > }
  6841. > if($set =~ /^verbose$/i) {
  6842. > cr_set_flag($chan, CRF_VERBOSE, $val);
  6843. >
  6844. > if($val) {
  6845. > notice($user, "Verbose mode enabled on \002$cn");
  6846. > }
  6847. > else {
  6848. > notice($user, "Verbose mode disabled on \002$cn");
  6849. > }
  6850. > return;
  6851. > }
  6852. >
  6853. > if($set =~ /^greet$/i) {
  6854. > if($val) {
  6855. > notice($user, "$csnick SET $cn GREET ON is deprecated.",
  6856. > "Please use $csnick LEVELS $cn SET GREET <rank>");
  6857. > } else {
  6858. > cs_levels_set($user, $chan, 'GREET', 'nobody');
  6859. > }
  6860. >
  6861. > return;
  6862. > }
  6863. >
  6864. > if($set =~ /^dice$/i) {
  6865. > if($val) {
  6866. > notice($user, "$csnick SET $cn DICE ON is deprecated.",
  6867. > "Please use $csnick LEVELS $cn SET DICE <rank>");
  6868. > } else {
  6869. > cs_levels_set($user, $chan, 'DICE', 'nobody');
  6870. > }
  6871. >
  6872. > return;
  6873. > }
  6874. >
  6875. > if($set =~ /^welcomeinchan$/i) {
  6876. > cr_set_flag($chan, CRF_WELCOMEINCHAN(), $val);
  6877. >
  6878. > if($val) {
  6879. > notice($user, "WELCOME messages will be put in the channel.");
  6880. > } else {
  6881. > notice($user, "WELCOME messages will be sent privately.");
  6882. > }
  6883. >
  6884. > return;
  6885. > }
  6886. >
  6887. > if($set =~ /^log$/i) {
  6888. > unless(module::is_loaded('logserv')) {
  6889. > notice($user, "module logserv is not loaded, logging is not available.");
  6890. > return;
  6891. > }
  6892. >
  6893. > if($val) {
  6894. > logserv::addchan($user, $cn) and cr_set_flag($chan, CRF_LOG, $val);
  6895. > }
  6896. > else {
  6897. > logserv::delchan($user, $cn) and cr_set_flag($chan, CRF_LOG, $val);
  6898. > }
  6899. > return;
  6900. > }
  6901. >
  6902. > if($set =~ /^a(?:uto)?voice$/i) {
  6903. > cr_set_flag($chan, CRF_AUTOVOICE(), $val);
  6904. >
  6905. > if($val) {
  6906. > notice($user, "All users w/o access will be autovoiced on join.");
  6907. > } else {
  6908. > notice($user, "AUTOVOICE disabled.");
  6909. > }
  6910. >
  6911. > return;
  6912. > }
  6913. >
  6914. > if($set =~ /^(?:never|no)op$/i) {
  6915. > cr_set_flag($chan, CRF_NEVEROP(), $val);
  6916. >
  6917. > if($val) {
  6918. > notice($user, "Users will not be automatically opped on join.");
  6919. > } else {
  6920. > notice($user, "Users with access will now be automatically opped on join.");
  6921. > }
  6922. >
  6923. > return;
  6924. > }
  6925. > }
  6926. >
  6927. > sub cs_why($$@) {
  6928. > my ($user, $chan, @tnicks) = @_;
  6929. >
  6930. > chk_registered($user, $chan) or return;
  6931. >
  6932. > my ($candoNick, $override) = can_do($chan, 'ACCLIST', $user, undef);
  6933. > return unless $candoNick;
  6934. >
  6935. > my $cn = $chan->{CHAN};
  6936. > my @reply;
  6937. > foreach my $tnick (@tnicks) {
  6938. > my $tuser = { NICK => $tnick };
  6939. > unless(get_user_id($tuser)) {
  6940. > notice($user, "\002$tnick\002: No such user.");
  6941. > return;
  6942. > }
  6943. >
  6944. > my $has;
  6945. > if(is_online($tnick)) {
  6946. > $has = 'has';
  6947. > } else {
  6948. > $has = 'had';
  6949. > }
  6950. >
  6951. > my $n;
  6952. > $get_all_acc->execute(get_user_id($tuser), $cn);
  6953. > while(my ($rnick, $acc) = $get_all_acc->fetchrow_array) {
  6954. > $n++;
  6955. > push @reply, "\002$tnick\002 $has $plevels[$acc+$plzero] access to \002$cn\002 due to identification to the nick \002$rnick\002.";
  6956. > }
  6957. > $get_all_acc->finish();
  6958. >
  6959. > unless($n) {
  6960. > push @reply, "\002$tnick\002 has no access to \002$cn\002.";
  6961. > }
  6962. > if(services_conf_log_overrides && $override) {
  6963. > my $src = get_user_nick($user);
  6964. > wlog($csnick, LOG_INFO(), "\002$src\002 used override CS WHY $cn $tnick");
  6965. > }
  6966. > }
  6967. > notice($user, @reply);
  6968. > }
  6969. >
  6970. > sub cs_setmodes($$$@) {
  6971. > my ($user, $cmd, $chan, @args) = @_;
  6972. > no warnings 'void';
  6973. > my $agent = $user->{AGENT} or $csnick;
  6974. > my $src = get_user_nick($user);
  6975. > my $cn = $chan->{CHAN};
  6976. > my $self;
  6977. >
  6978. > if (cr_chk_flag($chan, CRF_FREEZE())) {
  6979. > notice($user, "\002$cn\002 is frozen and access suspended.");
  6980. > return;
  6981. > }
  6982. >
  6983. > if(scalar(@args) == 0) {
  6984. > @args = ($src);
  6985. > $self = 1;
  6986. > } elsif($args[0] =~ /^#/) {
  6987. > foreach my $chn ($cn, @args) {
  6988. > next unless $chn =~ /^#/;
  6989. > no warnings 'prototype'; # we call ourselves
  6990. > cs_setmodes($user, $cmd, { CHAN => $chn });
  6991. > }
  6992. > return;
  6993. > } elsif((scalar(@args) == 1) and (lc($args[0]) eq lc($src))) {
  6994. > $self = 1;
  6995. > }
  6996. >
  6997. > # PROTECT is deprecated. remove it in a couple versions.
  6998. > # It should be called ADMIN under PREFIX_AQ
  6999. > my @mperms = ('VOICE', 'HALFOP', 'OP', 'ADMIN', 'OWNER');
  7000. > my @l = ('v', 'h', 'o', 'a', 'q');
  7001. > my ($level, @modes, $count);
  7002. >
  7003. > if($cmd =~ /voice$/i) { $level = 0 }
  7004. > elsif($cmd =~ /h(alf)?op$/i) { $level = 1 }
  7005. > elsif($cmd =~ /op$/i) { $level = 2 }
  7006. > elsif($cmd =~ /(protect|admin)$/i) { $level = 3 }
  7007. > elsif($cmd =~ /owner$/i) { $level = 4 }
  7008. > my $de = 1 if($cmd =~ s/^de//i);
  7009. > #$cmd =~ s/^de//i;
  7010. >
  7011. > my $acc = get_best_acc($user, $chan);
  7012. >
  7013. > # XXX I'm not sure this is the best way to do it.
  7014. > unless(
  7015. > ($de and $self) or ($self and ($level + 2) <= $acc) or
  7016. > can_do($chan, $mperms[$level], $user, { ACC => $acc, NOREPLY => 1, OVERRIDE_MSG => "$cmd $cn @args" }) )
  7017. > {
  7018. > notice($user, "$cn: $err_deny");
  7019. > return;
  7020. > }
  7021. >
  7022. > my ($override, $check_override);
  7023. >
  7024. > foreach my $target (@args) {
  7025. > my ($tuser);
  7026. >
  7027. > $tuser = ($self ? $user : { NICK => $target } );
  7028. >
  7029. > unless(is_in_chan($tuser, $chan)) {
  7030. > notice($user, "\002$target\002 is not in \002$cn\002.");
  7031. > next;
  7032. > }
  7033. >
  7034. > my $top = get_op($tuser, $chan);
  7035. >
  7036. > if($de) {
  7037. > unless($top & (2**$level)) {
  7038. > notice($user, "\002$target\002 has no $cmd in \002$cn\002.");
  7039. > next;
  7040. > }
  7041. >
  7042. > if(!$override and get_best_acc($tuser, $chan) > $acc) {
  7043. > unless($check_override) {
  7044. > $override = adminserv::can_do($user, 'SUPER');
  7045. > $check_override = 1;
  7046. > }
  7047. > if($check_override and !$override) {
  7048. > notice($user, "\002$target\002 outranks you in \002$cn\002.");
  7049. > next;
  7050. > }
  7051. > }
  7052. > } else {
  7053. > if($top & (2**$level)) {
  7054. > if($self) {
  7055. > notice($user, "You already have $cmd in \002$cn\002.");
  7056. > } else {
  7057. > notice($user, "\002$target\002 already has $cmd in \002$cn\002.");
  7058. > }
  7059. > next;
  7060. > }
  7061. > if (cr_chk_flag($chan, CRF_OPGUARD()) and
  7062. > !can_keep_op($user, $chan, $tuser, $l[$level]))
  7063. > {
  7064. > notice($user, "$target may not hold ops in $cn because OpGuard is enabled. ".
  7065. > "Please respect the founders wishes.");
  7066. > next;
  7067. > }
  7068. > }
  7069. >
  7070. > push @modes, [($de ? '-' : '+').$l[$level], $target];
  7071. > $count++;
  7072. >
  7073. > }
  7074. >
  7075. > ircd::setmode2(agent($chan), $cn, @modes) if scalar @modes;
  7076. > ircd::notice(agent($chan), '%'.$cn, "$src used ".($de ? "de$cmd" : $cmd).' '.join(' ', @args))
  7077. > if !$self and (lc $user->{AGENT} eq lc $csnick) and cr_chk_flag($chan, CRF_VERBOSE);
  7078. > }
  7079. >
  7080. > sub cs_drop($$) {
  7081. > my ($user, $chan) = @_;
  7082. > my $cn = $chan->{CHAN};
  7083. >
  7084. > chk_registered($user, $chan) or return;
  7085. >
  7086. > unless(get_best_acc($user, $chan) == FOUNDER or adminserv::can_do($user, 'SERVOP')) {
  7087. > notice($user, $err_deny);
  7088. > return;
  7089. > }
  7090. >
  7091. > drop($chan);
  7092. > notice($user, $cn.' has been dropped.');
  7093. > services::ulog($csnick, LOG_INFO(), "dropped $cn", $user, $chan);
  7094. >
  7095. > undef($enforcers{lc $cn});
  7096. > botserv::bot_part_if_needed(undef(), $chan, "Channel dropped.");
  7097. > }
  7098. >
  7099. > sub cs_kick($$$;$$) {
  7100. > my ($user, $chan, $target, $ban, $reason) = @_;
  7101. >
  7102. > my $cmd = ($ban ? 'KICKBAN' : 'KICK');
  7103. > my $perm = ($ban ? 'BAN' : 'KICK');
  7104. > if(ref($chan) ne 'HASH' || !defined($chan->{CHAN})) {
  7105. > notice($user, "Invalid $cmd command, no channel specified");
  7106. > return;
  7107. > }
  7108. >
  7109. > my $srclevel = get_best_acc($user, $chan);
  7110. >
  7111. > my ($nick, $override) = can_do($chan, ($ban ? 'BAN' : 'KICK'), $user, { ACC => $srclevel });
  7112. > return unless $nick;
  7113. >
  7114. > my $src = get_user_nick($user);
  7115. > my $cn = $chan->{CHAN};
  7116. >
  7117. > $reason = "Requested by $src".($reason?": $reason":'');
  7118. >
  7119. > my @errors = (
  7120. > ["I'm sorry, $src, I'm afraid I can't do that."],
  7121. > ["They are not in \002$cn\002."],
  7122. > [$err_deny],
  7123. > ["User not found"],
  7124. > );
  7125. > my @notinchan = ();
  7126. > my $peace = ({modes::splitmodes(get_modelock($chan))}->{Q}->[0] eq '+');
  7127. >
  7128. > my @targets = split(/\,/, $target);
  7129. > foreach $target (@targets) {
  7130. > my $tuser = { NICK => $target };
  7131. > my $targetlevel = get_best_acc($tuser, $chan);
  7132. >
  7133. > if(lc $target eq lc agent($chan) or adminserv::is_service($tuser)) {
  7134. > push @{$errors[0]}, $target;
  7135. > next;
  7136. > }
  7137. >
  7138. > if(get_user_id($tuser)) {
  7139. > unless(is_in_chan($tuser, $chan)) {
  7140. > if ($ban) {
  7141. > push @notinchan, $tuser;
  7142. > } else {
  7143. > push @{$errors[1]}, $target;
  7144. > }
  7145. > next;
  7146. > }
  7147. > } else {
  7148. > push @{$errors[3]}, $target;
  7149. > next;
  7150. > }
  7151. >
  7152. > if( ( ($peace and $targetlevel > 0) or ($srclevel <= $targetlevel) )
  7153. > and not ($override && check_override($user, ($ban ? 'BAN' : 'KICK'), "$cmd $cn $target")) )
  7154. > {
  7155. > push @{$errors[2]}, $target;
  7156. > next;
  7157. > }
  7158. >
  7159. > if($ban) {
  7160. > kickban($chan, $tuser, undef, $reason);
  7161. > } else {
  7162. > ircd::kick(agent($chan), $cn, $target, $reason) unless adminserv::is_service($user);
  7163. > }
  7164. > }
  7165. >
  7166. > foreach my $errlist (@errors) {
  7167. > if(@$errlist > 1) {
  7168. > my $msg = shift @$errlist;
  7169. >
  7170. > foreach my $e (@$errlist) { $e = "\002$e\002" }
  7171. >
  7172. > notice($user,
  7173. > "Cannot $cmd ".
  7174. > enum("or", @$errlist).
  7175. > ": $msg"
  7176. > );
  7177. > }
  7178. > }
  7179. > cs_ban($user, $chan, '', @notinchan) if ($ban and scalar (@notinchan));
  7180. > }
  7181. >
  7182. > sub cs_kickmask($$$;$$) {
  7183. > my ($user, $chan, $mask, $ban, $reason) = @_;
  7184. >
  7185. > my $srclevel = get_best_acc($user, $chan);
  7186. > my $src = get_user_nick($user);
  7187. > my $cn = $chan->{CHAN};
  7188. >
  7189. > my $candoOpts = { ACC => $srclevel, OVERRIDE_MSG => 'KICK'.($ban ? 'BAN' : '')."MASK $cn $mask $reason" };
  7190. > my ($nick, $override) = can_do($chan, ($ban ? 'BAN' : 'KICK'), $user, $candoOpts);
  7191. > return unless $nick;
  7192. >
  7193. >
  7194. > $reason = "Requested by $src".($reason?": $reason":'');
  7195. >
  7196. > my $count = kickmask_noacc($chan, $mask, $reason, $ban);
  7197. > notice($user, ($count ? "Users kicked from \002$cn\002: $count." : "No users in \002$cn\002 matched $mask."))
  7198. > }
  7199. >
  7200. > sub cs_ban($$$@) {
  7201. > my ($user, $chan, $type, @targets) = @_;
  7202. > my $cn = $chan->{CHAN};
  7203. > my $src = get_user_nick($user);
  7204. >
  7205. > my $srclevel = get_best_acc($user, $chan);
  7206. > my ($nick, $override) = can_do($chan, 'BAN', $user, { ACC => $srclevel });
  7207. > return unless $nick;
  7208. >
  7209. > my @errors = (
  7210. > ["I'm sorry, $src, I'm afraid I can't do that."],
  7211. > ["User not found"],
  7212. > [$err_deny]
  7213. > );
  7214. >
  7215. > my (@bans, @unbans);
  7216. > foreach my $target (@targets) {
  7217. > my $tuser;
  7218. >
  7219. > if(ref($target)) {
  7220. > $tuser = $target;
  7221. > }
  7222. > elsif($target =~ /\,/) {
  7223. > push @targets, split(',', $target);
  7224. > next;
  7225. > }
  7226. > elsif($target eq '') {
  7227. > # Should never happen
  7228. > # but it could, given the split above
  7229. > next;
  7230. > }
  7231. > elsif($target =~ /^-/) {
  7232. > $target =~ s/^\-//;
  7233. > push @unbans, $target;
  7234. > next;
  7235. > }
  7236. > =cut
  7237. > elsif($target =~ /[!@]+/) {
  7238. > ircd::debug("normalizing hostmask $target");
  7239. > #$target = normalize_hostmask($target);
  7240. > #=cut
  7241. > my ($nick, $ident, $host) = parse_mask($target);
  7242. > $nick = '*' unless length($nick);
  7243. > $ident = '*' unless length($ident);
  7244. > $host = '*' unless length($host);
  7245. > $target = "$nick\!$ident\@$host";
  7246. > #=cut
  7247. > ircd::debug("normalized hostmask: $target");
  7248. >
  7249. > push @bans, $target;
  7250. > next;
  7251. > }
  7252. > =cut
  7253. > elsif(valid_nick($target)) {
  7254. > $tuser = { NICK => $target };
  7255. > }
  7256. > elsif($target = validate_ban($target)) {
  7257. > push @bans, $target;
  7258. > next;
  7259. > } else {
  7260. > notice($user, "Not a valid ban target: $target");
  7261. > next;
  7262. > }
  7263. > my $targetlevel = get_best_acc($tuser, $chan);
  7264. >
  7265. > if(lc $target eq lc agent($chan) or adminserv::is_service($tuser)) {
  7266. > push @{$errors[0]}, get_user_nick($tuser);
  7267. > next;
  7268. > }
  7269. >
  7270. > unless(get_user_id($tuser)) {
  7271. > push @{$errors[1]}, get_user_nick($tuser);
  7272. > next;
  7273. > }
  7274. > if( $srclevel <= $targetlevel and not ($override && check_override($user, 'BAN', "BAN $cn $target")) ) {
  7275. > push @{$errors[2]}, $target;
  7276. > next;
  7277. > }
  7278. >
  7279. > push @bans, make_banmask($chan, $tuser, $type);
  7280. > }
  7281. >
  7282. > foreach my $errlist (@errors) {
  7283. > if(@$errlist > 1) {
  7284. > my $msg = shift @$errlist;
  7285. >
  7286. > foreach my $e (@$errlist) { $e = "\002$e\002" }
  7287. >
  7288. > notice($user,
  7289. > "Cannot ban ".
  7290. > enum("or", @$errlist).
  7291. > ": $msg"
  7292. > );
  7293. > }
  7294. > }
  7295. >
  7296. > ircd::ban_list(agent($chan), $cn, +1, 'b', @bans) if (scalar(@bans));
  7297. > ircd::notice(agent($chan), $cn, "$src used BAN ".join(' ', @bans))
  7298. > if (lc $user->{AGENT} eq lc $csnick) and (cr_chk_flag($chan, CRF_VERBOSE) and scalar(@bans));
  7299. > cs_unban($user, $chan, @unbans) if scalar(@unbans);
  7300. > }
  7301. >
  7302. > sub cs_invite($$@) {
  7303. > my ($user, $chan, @targets) = @_;
  7304. > my $src = get_user_nick($user);
  7305. > my $cn = $chan->{CHAN};
  7306. > my $srclevel = get_best_acc($user, $chan);
  7307. >
  7308. > my @errors = (
  7309. > ["They are not online."],
  7310. > ["They are already in \002$cn\002."],
  7311. > [$err_deny]
  7312. > );
  7313. >
  7314. > my @invited;
  7315. > foreach my $target (@targets) {
  7316. > my $tuser;
  7317. > my $tnick;
  7318. > if(ref($target)) {
  7319. > $tuser = $target;
  7320. > $tnick = get_user_nick($tuser);
  7321. > } elsif(lc($src) eq lc($target)) {
  7322. > $tuser = $user;
  7323. > $tnick = $src;
  7324. > } elsif($target =~ /\,/) {
  7325. > push @targets, split(',', $target);
  7326. > next;
  7327. > } elsif($target eq '') {
  7328. > # Should never happen
  7329. > # but it could, given the split above
  7330. > next;
  7331. > } else {
  7332. > $tuser = { NICK => $target };
  7333. > $tnick = $target;
  7334. > }
  7335. >
  7336. > my $candoOpts = { ACC => $srclevel, NOREPLY => 1, OVERRIDE_MSG => "INVITE $cn $target" };
  7337. > if(lc($src) eq lc($tnick)) {
  7338. > unless(can_do($chan, 'InviteSelf', $user, $candoOpts)) {
  7339. > push @{$errors[2]}, $tnick;
  7340. > next;
  7341. > }
  7342. > }
  7343. > else {
  7344. > unless(can_do($chan, 'INVITE', $user, $candoOpts)) {
  7345. > push @{$errors[2]}, $tnick;
  7346. > next;
  7347. > }
  7348. >
  7349. > unless(nickserv::is_online($tnick)) {
  7350. > push @{$errors[0]}, $tnick;
  7351. > next;
  7352. > }
  7353. >
  7354. > # invite is annoying, so punish them mercilessly
  7355. > return if flood_check($user, 2);
  7356. > }
  7357. >
  7358. > if(is_in_chan($tuser, $chan)) {
  7359. > push @{$errors[1]}, $tnick;
  7360. > next;
  7361. > }
  7362. >
  7363. > ircd::invite(agent($chan), $cn, $tnick); push @invited, $tnick;
  7364. > ircd::notice(agent($chan), $tnick, "\002$src\002 has invited you to \002$cn\002.")
  7365. > unless(lc($src) eq lc($tnick));
  7366. > }
  7367. >
  7368. > foreach my $errlist (@errors) {
  7369. > if(@$errlist > 1) {
  7370. > my $msg = shift @$errlist;
  7371. >
  7372. > foreach my $e (@$errlist) { $e = "\002$e\002" }
  7373. >
  7374. > notice($user,
  7375. > "Cannot invite ".
  7376. > enum("or", @$errlist).
  7377. > ": $msg"
  7378. > );
  7379. > }
  7380. > }
  7381. >
  7382. > ircd::notice(agent($chan), $cn, "$src used INVITE ".join(' ', @invited))
  7383. > if (lc $user->{AGENT} eq lc $csnick)and cr_chk_flag($chan, CRF_VERBOSE) and scalar(@invited);
  7384. > }
  7385. >
  7386. > sub cs_close($$$) {
  7387. > my ($user, $chan, $reason, $type) = @_;
  7388. > # $type is a flag, either CRF_CLOSE or CRF_DRONE
  7389. > my $cn = $chan->{CHAN};
  7390. > my $oper;
  7391. >
  7392. > unless($oper = adminserv::can_do($user, 'SERVOP')) {
  7393. > notice($user, $err_deny);
  7394. > return;
  7395. > }
  7396. >
  7397. > my $rlength = length($reason);
  7398. > if($rlength >= 350) {
  7399. > notice($user, 'Close reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  7400. > return;
  7401. > }
  7402. >
  7403. > if(is_registered($chan)) {
  7404. > $drop_acc->execute($cn);
  7405. > $drop_lvl->execute($cn);
  7406. > $del_close->execute($cn);
  7407. > $drop_akick->execute($cn);
  7408. > $drop_welcome->execute($cn);
  7409. > $drop_chantext->execute($cn);
  7410. > $drop_nicktext->execute($cn); # Leftover channel auths
  7411. >
  7412. > $set_founder->execute($oper, $cn);
  7413. > }
  7414. > else {
  7415. > $register->execute($cn, $reason, $oper);
  7416. > }
  7417. > $set_modelock->execute('+rsnt', $cn);
  7418. > do_modelock($chan);
  7419. > set_acc($oper, undef, $chan, FOUNDER);
  7420. >
  7421. > $set_close->execute($cn, $reason, $oper, $type);
  7422. > cr_set_flag($chan, (CRF_FREEZE | CRF_CLOSE | CRF_DRONE), 0); #unset flags
  7423. > cr_set_flag($chan, CRF_HOLD, 1); #set flags
  7424. >
  7425. > my $src = get_user_nick($user);
  7426. > my $time = gmtime2(time);
  7427. > my $cmsg = "is closed [$src $time]: $reason";
  7428. >
  7429. > if ($type == CRF_CLOSE) {
  7430. > cr_set_flag($chan, CRF_CLOSE, 1); #set flags
  7431. > clear_users($chan, "Channel $cmsg");
  7432. > ircd::settopic(agent($chan), $cn, $src, time(), "Channel $cmsg")
  7433. > }
  7434. > elsif ($type == CRF_DRONE) {
  7435. > cr_set_flag($chan, CRF_DRONE, 1); #set flags
  7436. > chan_kill($chan, "$cn $cmsg");
  7437. > }
  7438. >
  7439. > notice($user, "The channel \002$cn\002 is now closed.");
  7440. > services::ulog($csnick, LOG_INFO(), "closed $cn with reason: $reason", $user, $chan);
  7441. > }
  7442. >
  7443. > sub cs_clear_pre($$) {
  7444. > my ($user, $chan) = @_;
  7445. > my $cn = $chan->{CHAN};
  7446. >
  7447. > my $srclevel = get_best_acc($user, $chan);
  7448. >
  7449. > my ($cando, $override) = can_do($chan, 'CLEAR', $user, { ACC => $srclevel });
  7450. > return 0 unless($cando);
  7451. >
  7452. > $get_highrank->execute($cn);
  7453. > my ($highrank_nick, $highrank_level) = $get_highrank->fetchrow_array();
  7454. > $get_highrank->finish();
  7455. >
  7456. > if($highrank_level > $srclevel && !$override) {
  7457. > notice($user, "$highrank_nick outranks you in $cn (level: $levels[$highrank_level])");
  7458. > return 0;
  7459. > }
  7460. >
  7461. > return 1;
  7462. > }
  7463. >
  7464. > sub cs_clear_users($$;$) {
  7465. > my ($user, $chan, $reason) = @_;
  7466. > my $src = get_user_nick($user);
  7467. >
  7468. > cs_clear_pre($user, $chan) or return;
  7469. >
  7470. > my $rlength = length($reason);
  7471. > if($rlength >= 350) {
  7472. > notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  7473. > return;
  7474. > }
  7475. >
  7476. > clear_users($chan, "CLEAR USERS by \002$src\002".($reason?" reason: $reason":''));
  7477. > }
  7478. >
  7479. > sub cs_clear_modes($$;$) {
  7480. > my ($user, $chan, $reason) = @_;
  7481. > my $cn = $chan->{CHAN};
  7482. > my $src = get_user_nick($user);
  7483. >
  7484. > cs_clear_pre($user, $chan) or return;
  7485. >
  7486. > my $rlength = length($reason);
  7487. > if($rlength >= 350) {
  7488. > notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  7489. > return;
  7490. > }
  7491. >
  7492. > my $agent = agent($chan);
  7493. > ircd::notice($agent, $cn, "CLEAR MODES by \002$src\002".($reason?" reason: $reason":''));
  7494. >
  7495. > $get_chanmodes->execute($cn);
  7496. > my ($curmodes) = $get_chanmodes->fetchrow_array;
  7497. > my $ml = get_modelock($chan);
  7498. >
  7499. > # This method may exceed the 12-mode limit
  7500. > # But it seems to succeed anyway, even with more than 12.
  7501. > my ($modes, $parms) = split(/ /, modes::merge(modes::invert($curmodes), $ml, 1). ' * *', 2);
  7502. > # we split this separately,
  7503. > # as otherwise it insists on taking the result of the split as a scalar quantity
  7504. > ircd::setmode($agent, $cn, $modes, $parms);
  7505. > do_modelock($chan);
  7506. > }
  7507. >
  7508. > sub cs_clear_ops($$;$) {
  7509. > my ($user, $chan, $reason) = @_;
  7510. > my $cn = $chan->{CHAN};
  7511. > my $src = get_user_nick($user);
  7512. >
  7513. > cs_clear_pre($user, $chan) or return;
  7514. >
  7515. > my $rlength = length($reason);
  7516. > if($rlength >= 350) {
  7517. > notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  7518. > return;
  7519. > }
  7520. >
  7521. > clear_ops($chan);
  7522. >
  7523. > ircd::notice(agent($chan), $cn, "CLEAR OPS by \002$src\002".($reason?" reason: $reason":''));
  7524. > return 1;
  7525. > }
  7526. >
  7527. > sub cs_clear_bans($$;$$) {
  7528. > my ($user, $chan, $type, $reason) = @_;
  7529. > my $cn = $chan->{CHAN};
  7530. > my $src = get_user_nick($user);
  7531. > $type = 0 unless defined $type;
  7532. >
  7533. > cs_clear_pre($user, $chan) or return;
  7534. >
  7535. > my $rlength = length($reason);
  7536. > if($rlength >= 350) {
  7537. > notice($user, 'Clear reason is too long by '. $rlength-350 .' character(s). Maximum length is 350 characters.');
  7538. > return;
  7539. > }
  7540. >
  7541. > clear_bans($chan, $type);
  7542. >
  7543. > ircd::notice(agent($chan), $cn, "CLEAR BANS by \002$src\002".($reason?" reason: $reason":''));
  7544. > }
  7545. >
  7546. > sub cs_welcome_pre($$) {
  7547. > my ($user, $chan) = @_;
  7548. >
  7549. > return can_do($chan, 'WELCOME', $user);
  7550. > }
  7551. >
  7552. > sub cs_welcome_add($$$) {
  7553. > my ($user, $chan, $msg) = @_;
  7554. > my $src = get_best_acc($user, $chan, 1);
  7555. > my $cn = $chan->{CHAN};
  7556. >
  7557. > cs_welcome_pre($user, $chan) or return;
  7558. >
  7559. > my $mlength = length($msg);
  7560. > if($mlength >= 350) {
  7561. > notice($user, 'Welcome Message is too long by '. $mlength-350 .' character(s). Maximum length is 350 characters.');
  7562. > return;
  7563. > }
  7564. >
  7565. > $count_welcome->execute($cn);
  7566. > my $count = $count_welcome->fetchrow_array;
  7567. > if ($count >= 5) {
  7568. > notice($user, 'There is a maximum of five (5) Channel Welcome Messages.');
  7569. > return;
  7570. > }
  7571. >
  7572. > $add_welcome->execute($cn, ++$count, $src, $msg);
  7573. >
  7574. > notice($user, "Welcome message number $count for \002$cn\002 set to:", " $msg");
  7575. > }
  7576. >
  7577. > sub cs_welcome_list($$) {
  7578. > my ($user, $chan) = @_;
  7579. > my $cn = $chan->{CHAN};
  7580. >
  7581. > cs_welcome_pre($user, $chan) or return;
  7582. >
  7583. > $list_welcome->execute($cn);
  7584. >
  7585. > my @data;
  7586. >
  7587. > while(my ($id, $time, $adder, $msg) = $list_welcome->fetchrow_array) {
  7588. > push @data, ["$id.", $adder, gmtime2($time), $msg];
  7589. > }
  7590. > $list_welcome->finish();
  7591. >
  7592. > notice($user, columnar {TITLE => "Welcome message list for \002$cn\002:", DOUBLE=>1,
  7593. > NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  7594. > }
  7595. >
  7596. > sub cs_welcome_del($$$) {
  7597. > my ($user, $chan, $id) = @_;
  7598. > my $cn = $chan->{CHAN};
  7599. >
  7600. > cs_welcome_pre($user, $chan) or return;
  7601. >
  7602. > if ($del_welcome->execute($cn, $id) == 1) {
  7603. > notice($user, "Welcome Message \002$id\002 deleted from \002$cn\002");
  7604. > $consolidate_welcome->execute($cn, $id);
  7605. > }
  7606. > else {
  7607. > notice($user,
  7608. > "Welcome Message number $id for \002$cn\002 does not exist.");
  7609. > }
  7610. > }
  7611. >
  7612. > sub cs_alist($$;$) {
  7613. > my ($user, $chan, $mask) = @_;
  7614. > my $cn = $chan->{CHAN};
  7615. >
  7616. > chk_registered($user, $chan) or return;
  7617. >
  7618. > my $slevel = get_best_acc($user, $chan);
  7619. >
  7620. > can_do($chan, 'ACCLIST', $user, { ACC => $slevel }) or return;
  7621. >
  7622. > my @reply;
  7623. >
  7624. > if($mask) {
  7625. > my ($mnick, $mident, $mhost) = glob2sql(parse_mask($mask));
  7626. > $mnick = '%' if($mnick eq '');
  7627. > $mident = '%' if($mident eq '');
  7628. > $mhost = '%' if($mhost eq '');
  7629. >
  7630. > $get_acc_list2_mask->execute($mnick, $cn, $mnick, $mident, $mhost);
  7631. > while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2_mask->fetchrow_array) {
  7632. > push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
  7633. > push @reply, ' '.($time ? 'Date/time added: '. gmtime2($time).' ' : '').
  7634. > ($last_used ? 'Last used '.time_ago($last_used).' ago' : '') if ($time or $last_used);
  7635. > }
  7636. > $get_acc_list2_mask->finish();
  7637. > } else {
  7638. > $get_acc_list2->execute($cn);
  7639. > while(my ($nick, $adder, $level, $time, $last_used, $ident, $vhost) = $get_acc_list2->fetchrow_array) {
  7640. > push @reply, "*) $nick ($ident\@$vhost) Rank: ".$levels[$level] . ($adder ? ' Added by: '.$adder : '');
  7641. > push @reply, ' '.($time ? 'Date/time added: '. gmtime2($time).' ' : '').
  7642. > ($last_used ? 'Last used '.time_ago($last_used).' ago' : '') if ($time or $last_used);
  7643. > }
  7644. > $get_acc_list2->finish();
  7645. > }
  7646. >
  7647. > notice($user, "Access list for \002$cn\002:", @reply);
  7648. >
  7649. > return;
  7650. > }
  7651. >
  7652. > sub cs_banlist($$) {
  7653. > my ($user, $chan) = @_;
  7654. > my $cn = $chan->{CHAN};
  7655. > can_do($chan, 'UnbanSelf', $user, { NOREPLY => 1 }) or can_do($chan, 'BAN', $user) or return;
  7656. >
  7657. > my $i = 0; my @data;
  7658. > $list_bans->execute($cn, 0);
  7659. > while(my ($mask, $setter, $time) = $list_bans->fetchrow_array()) {
  7660. > push @data, ["\002".++$i."\002", sql2glob($mask), $setter, ($time ? gmtime2($time) : '')];
  7661. > }
  7662. >
  7663. > notice($user, columnar {TITLE => "Ban list of \002$cn\002:", DOUBLE=>1,
  7664. > NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  7665. > }
  7666. >
  7667. > sub cs_unban($$@) {
  7668. > my ($user, $chan, @parms) = @_;
  7669. > my $cn = $chan->{CHAN};
  7670. >
  7671. > my $self = 1 if ( (scalar(@parms) == 1) and ( lc($parms[0]) eq lc(get_user_nick($user)) ) );
  7672. > if ($parms[0] eq '*') {
  7673. > cs_clear_bans($user, $chan);
  7674. > return;
  7675. > }
  7676. > else {
  7677. > can_do($chan, ($self ? 'UnbanSelf' : 'UNBAN'), $user) or return;
  7678. > }
  7679. >
  7680. > my (@userlist, @masklist);
  7681. > foreach my $parm (@parms) {
  7682. > if(valid_nick($parm)) {
  7683. > my $tuser = ($self ? $user : { NICK => $parm });
  7684. > unless(get_user_id($tuser)) {
  7685. > notice($user, "No such user: \002$parm\002");
  7686. > next;
  7687. > }
  7688. > push @userlist, $tuser;
  7689. > } elsif($parm =~ /^[0-9\.,-]+$/) {
  7690. > foreach my $num (makeSeqList($parm)) {
  7691. > push @masklist, get_ban_num($chan, $num);
  7692. > }
  7693. > } else {
  7694. > push @masklist, $parm;
  7695. > }
  7696. > }
  7697. >
  7698. > if(scalar(@userlist)) {
  7699. > unban_user($chan, @userlist);
  7700. > notice($user, "All bans affecting " .
  7701. > ( $self ? 'you' : enum( 'and', map(get_user_nick($_), @userlist) ) ) .
  7702. > " on \002$cn\002 have been removed.");
  7703. > }
  7704. > if(scalar(@masklist)) {
  7705. > ircd::ban_list(agent($chan), $cn, -1, 'b', @masklist);
  7706. > notice($user, "The following bans have been removed: ".join(' ', @masklist))
  7707. > if scalar(@masklist);
  7708. > }
  7709. > }
  7710. >
  7711. > sub cs_updown($$@) {
  7712. > my ($user, $cmd, @chans) = @_;
  7713. > return cs_updown2($user, $cmd, { CHAN => shift @chans }, @chans)
  7714. > if (defined($chans[1]) and $chans[1] !~ "^\#" and $chans[0] =~ "^\#");
  7715. >
  7716. > @chans = get_user_chans($user)
  7717. > unless (@chans);
  7718. >
  7719. > if (uc($cmd) eq 'UP') {
  7720. > foreach my $cn (@chans) {
  7721. > next unless ($cn =~ /^\#/);
  7722. > my $chan = { CHAN => $cn };
  7723. > next if cr_chk_flag($chan, (CRF_DRONE | CRF_CLOSE | CRF_FREEZE), 1);
  7724. > chanserv::set_modes($user, $chan, chanserv::get_best_acc($user, $chan));
  7725. > }
  7726. > }
  7727. > elsif (uc($cmd) eq 'DOWN') {
  7728. > foreach my $cn (@chans) {
  7729. > next unless ($cn =~ /^\#/);
  7730. > chanserv::unset_modes($user, { CHAN => $cn });
  7731. > }
  7732. > }
  7733. > }
  7734. >
  7735. > sub cs_updown2($$$@) {
  7736. > my ($user, $cmd, $chan, @targets) = @_;
  7737. > no warnings 'void';
  7738. > my $agent = $user->{AGENT} or $csnick;
  7739. > my $cn = $chan->{CHAN};
  7740. >
  7741. > return unless chk_registered($user, $chan);
  7742. > if (cr_chk_flag($chan, CRF_FREEZE())) {
  7743. > notice($user, "\002$cn\002 is frozen and access suspended.");
  7744. > return;
  7745. > }
  7746. >
  7747. > my $acc = get_best_acc($user, $chan);
  7748. > return unless(can_do($chan, 'UPDOWN', $user, { ACC => $acc }));
  7749. >
  7750. > my $updown = ((uc($cmd) eq 'UP') ? 1 : 0);
  7751. >
  7752. > my ($override, $check_override);
  7753. > my (@list, $count);
  7754. > foreach my $target (@targets) {
  7755. >
  7756. > my $tuser = { NICK => $target };
  7757. >
  7758. > unless(is_in_chan($tuser, $chan)) {
  7759. > notice($user, "\002$target\002 is not in \002$cn\002.");
  7760. > next;
  7761. > }
  7762. >
  7763. > if($updown) {
  7764. > push @list, $target;
  7765. > chanserv::set_modes($tuser, $chan, chanserv::get_best_acc($tuser, $chan));
  7766. > }
  7767. > else {
  7768. > my $top = get_op($tuser, $chan);
  7769. > unless($top) {
  7770. > notice($user, "\002$target\002 is already deopped in \002$cn\002.");
  7771. > next;
  7772. > }
  7773. >
  7774. > if(!$override and get_best_acc($tuser, $chan) > $acc) {
  7775. > unless($check_override) {
  7776. > $override = adminserv::can_do($user, 'SUPER');
  7777. > $check_override = 1;
  7778. > }
  7779. > if($check_override and !$override) {
  7780. > notice($user, "\002$target\002 outranks you in \002$cn\002.");
  7781. > next;
  7782. > }
  7783. > }
  7784. > push @list, $target;
  7785. > chanserv::unset_modes($tuser, { CHAN => $cn });
  7786. > }
  7787. > $count++;
  7788. > }
  7789. >
  7790. > my $src = get_user_nick($user);
  7791. > ircd::notice(agent($chan), '%'.$cn, "$src used $cmd ".join(' ', @list))
  7792. > if (lc $user->{AGENT} eq lc $csnick) and cr_chk_flag($chan, CRF_VERBOSE);
  7793. > }
  7794. >
  7795. > sub cs_getkey($$) {
  7796. > my ($user, $chan) = @_;
  7797. > my $cn = $chan->{CHAN};
  7798. >
  7799. > can_do($chan, 'GETKEY', $user) or return;
  7800. >
  7801. > $get_chanmodes->execute($cn);
  7802. > my $modes = $get_chanmodes->fetchrow_array; $get_chanmodes->finish();
  7803. >
  7804. > if(my $key = modes::get_key($modes)) {
  7805. > notice($user, "Channel key for \002$cn\002: $key");
  7806. > }
  7807. > else {
  7808. > notice($user, "\002$cn\002 has no channel key.");
  7809. > }
  7810. > }
  7811. >
  7812. > sub cs_auth($$$@) {
  7813. > my ($user, $chan, $cmd, @args) = @_;
  7814. > my $cn = $chan->{CHAN};
  7815. > $cmd = lc $cmd;
  7816. >
  7817. > return unless chk_registered($user, $chan);
  7818. > return unless can_do($chan, 'AccChange', $user);
  7819. > my $userlevel = get_best_acc($user, $chan);
  7820. > if($cmd eq 'list') {
  7821. > my @data;
  7822. > $list_auth_chan->execute($cn);
  7823. > while(my ($nick, $data) = $list_auth_chan->fetchrow_array()) {
  7824. > my ($adder, $old, $level, $time) = split(/:/, $data);
  7825. > push @data, ["\002$nick\002", $levels[$level], $adder, gmtime2($time)];
  7826. > }
  7827. > if ($list_auth_chan->rows()) {
  7828. > notice($user, columnar {TITLE => "Pending authorizations for \002$cn\002:",
  7829. > NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  7830. > }
  7831. > else {
  7832. > notice($user, "There are no pending authorizations for \002$cn\002");
  7833. > }
  7834. > $list_auth_chan->finish();
  7835. > }
  7836. > elsif($cmd eq 'remove' or $cmd eq 'delete' or $cmd eq 'del') {
  7837. > my ($nick, $adder, $old, $level, $time);
  7838. > my $parm = shift @args;
  7839. > if(misc::isint($parm) and ($nick, $adder, $old, $level, $time) = get_auth_num($cn, $parm))
  7840. > {
  7841. > }
  7842. > elsif (($adder, $old, $level, $time) = get_auth_nick($cn, $parm))
  7843. > {
  7844. > $nick = $parm;
  7845. > }
  7846. > unless ($nick) {
  7847. > # This should normally be an 'else' as the elsif above should prove false
  7848. > # For some reason, it doesn't work. the unless ($nick) fixes it.
  7849. > # It only doesn't work for numbered entries
  7850. > notice($user, "There is no entry for \002$parm\002 in \002$cn\002's AUTH list");
  7851. > return;
  7852. > }
  7853. > $nickserv::del_auth->execute($nick, $cn); $nickserv::del_auth->finish();
  7854. > my $log_str = "deleted AUTH entry $cn $nick $levels[$level]";
  7855. > my $src = get_user_nick($user);
  7856. > notice($user, "You have $log_str");
  7857. > ircd::notice(agent($chan), '%'.$cn, "has \002$src\002 has $log_str")
  7858. > if cr_chk_flag($chan, CRF_VERBOSE);
  7859. > services::ulog($chanserv::csnick, LOG_INFO(), "has $log_str", $user, $chan);
  7860. > }
  7861. > else {
  7862. > notice($user, "Unknown AUTH command \002$cmd\002");
  7863. > }
  7864. > }
  7865. >
  7866. > sub cs_mode($$$@) {
  7867. > my ($user, $chan, $modes_in, @parms_in) = @_;
  7868. > can_do($chan, 'MODE', $user) or return undef;
  7869. > ($modes_in, @parms_in) = validate_chmodes($modes_in, @parms_in);
  7870. >
  7871. > my %permhash = (
  7872. > 'q' => 'OWNER',
  7873. > 'a' => 'ADMIN',
  7874. > 'o' => 'OP',
  7875. > 'h' => 'HALFOP',
  7876. > 'v' => 'VOICE',
  7877. > );
  7878. > my $sign = '+'; my $cn = $chan->{CHAN};
  7879. > my ($modes_out, @parms_out, @bans);
  7880. > foreach my $mode (split(//, $modes_in)) {
  7881. > $sign = $mode if $mode =~ /[+-]/;
  7882. > if ($permhash{$mode}) {
  7883. > my $parm = shift @parms_in;
  7884. > cs_setmodes($user, ($sign eq '-' ? 'de' : '').$permhash{$mode}, $chan, $parm);
  7885. > }
  7886. > elsif ($mode eq 'b') {
  7887. > my $parm = shift @parms_in;
  7888. > if($sign eq '-') {
  7889. > $parm = '-'.$parm;
  7890. > }
  7891. > push @bans, $parm;
  7892. > }
  7893. > elsif($mode =~ /[eIlLkjf]/) {
  7894. > $modes_out .= $mode;
  7895. > push @parms_out, shift @parms_in;
  7896. > } else {
  7897. > $modes_out .= $mode;
  7898. > }
  7899. > }
  7900. >
  7901. > if(scalar(@bans)) {
  7902. > cs_ban($user, $chan, undef, @bans);
  7903. > }
  7904. > return if $modes_out =~ /^[+-]*$/;
  7905. > ircd::setmode(agent($chan), $chan->{CHAN}, $modes_out, join(' ', @parms_out));
  7906. > do_modelock($chan, $modes_out.' '.join(' ', @parms_out));
  7907. >
  7908. > $modes_out =~ s/^[+-]*([+-].*)$/$1/;
  7909. > ircd::notice(agent($chan), '%'.$cn, get_user_nick($user).' used MODE '.join(' ', $modes_out, @parms_out))
  7910. > if (lc $user->{AGENT} eq lc $csnick) and cr_chk_flag($chan, CRF_VERBOSE);
  7911. > }
  7912. >
  7913. > sub cs_copy($$@) {
  7914. > my ($user, $chan1, @args) = @_;
  7915. > my $cn1 = $chan1->{CHAN};
  7916. > my $cn2;
  7917. > my $type;
  7918. > if($args[0] =~ /^#/) {
  7919. > $cn2 = shift @args;
  7920. > $type = 'all';
  7921. > }
  7922. > if($args[0] =~ /(?:acc(?:ess)?|akick|levels|all)/i) {
  7923. > $type = shift @args;
  7924. > $cn2 = shift @args unless $cn2;
  7925. > }
  7926. > my $rank;
  7927. > if($type =~ /^acc(?:ess)?/i) {
  7928. > if($cn2 =~ /^#/) {
  7929. > $rank = shift @args;
  7930. > } else {
  7931. > $rank = $cn2;
  7932. > $cn2 = shift @args;
  7933. > }
  7934. > }
  7935. > unless(defined $cn2 and defined $type) {
  7936. > notice($user, 'Unknown COPY command', 'Syntax: COPY #chan1 [type] #chan2');
  7937. > }
  7938. > my $chan2 = { CHAN => $cn2 };
  7939. > if(lc($cn1) eq lc($cn2)) {
  7940. > notice($user, "You cannot copy a channel onto itself.");
  7941. > }
  7942. > unless(is_registered($chan1)) {
  7943. > notice($user, "Source channel \002$cn1\002 must be registered.");
  7944. > return;
  7945. > }
  7946. > can_do($chan1, 'COPY', $user) or return undef;
  7947. > if(lc $type eq 'all') {
  7948. > if(is_registered($chan2)) {
  7949. > notice($user, "When copying all channel details, destination channel cannot be registered.");
  7950. > return;
  7951. > } elsif(!(get_op($user, $chan2) & ($opmodes{o} | $opmodes{a} | $opmodes{q}))) {
  7952. > # This would be preferred to be a 'opmode_mask' or something
  7953. > # However that might be misleading due to hop not being enough to register
  7954. > notice($user, "You must have channel operator status to register \002$cn2\002.");
  7955. > return;
  7956. > } else {
  7957. > cs_copy_chan_all($user, $chan1, $chan2);
  7958. > return;
  7959. > }
  7960. > } else {
  7961. > unless(is_registered($chan2)) {
  7962. > notice($user, "When copying channel lists, destination channel must be registered.");
  7963. > return;
  7964. > }
  7965. > can_do($chan2, 'COPY', $user) or return undef;
  7966. > }
  7967. > if(lc $type eq 'akick') {
  7968. > cs_copy_chan_akick($user, $chan1, $chan2);
  7969. > } elsif(lc $type eq 'levels') {
  7970. > cs_copy_chan_levels($user, $chan1, $chan2);
  7971. > } elsif($type =~ /^acc(?:ess)?/i) {
  7972. > cs_copy_chan_acc($user, $chan1, $chan2, xop_byname($rank));
  7973. > }
  7974. > }
  7975. >
  7976. > sub cs_copy_chan_all($$$) {
  7977. > my ($user, $chan1, $chan2) = @_;
  7978. > cs_copy_chan_chanreg($user, $chan1, $chan2);
  7979. > cs_copy_chan_levels($user, $chan1, $chan2);
  7980. > cs_copy_chan_acc($user, $chan1, $chan2);
  7981. > cs_copy_chan_akick($user, $chan1, $chan2);
  7982. > return;
  7983. > }
  7984. >
  7985. > sub cs_copy_chan_chanreg($$$) {
  7986. > my ($user, $chan1, $chan2) = @_;
  7987. > my $cn1 = $chan1->{CHAN};
  7988. > my $cn2 = $chan2->{CHAN};
  7989. >
  7990. > copy_chan_chanreg($cn1, $cn2);
  7991. > botserv::bot_join($chan2) unless (lc(agent($chan2)) eq lc($csnick) );
  7992. > do_modelock($chan2);
  7993. > notice($user, "Registration for \002$cn1\002 copied to \002$cn2\002");
  7994. >
  7995. > my $log_str = "copied the channel registration for \002$cn1\002 to \002$cn2\002";
  7996. > services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  7997. >
  7998. > my $src = get_user_nick($user);
  7999. > ircd::notice(agent($chan1), '%'.$cn1, "\002$src\002 $log_str")
  8000. > if cr_chk_flag($chan1, CRF_VERBOSE);
  8001. > ircd::notice(agent($chan2), '%'.$cn2, "\002$src\002 $log_str")
  8002. > if cr_chk_flag($chan2, CRF_VERBOSE);
  8003. > }
  8004. >
  8005. > sub cs_copy_chan_acc($$$;$) {
  8006. > my ($user, $chan1, $chan2, $level) = @_;
  8007. > my $cn1 = $chan1->{CHAN};
  8008. > my $cn2 = $chan2->{CHAN};
  8009. >
  8010. > copy_chan_acc($cn1, $cn2, $level);
  8011. >
  8012. > unless(cr_chk_flag($chan2, CRF_NEVEROP)) {
  8013. > $get_chan_users->execute($cn2); my @targets;
  8014. > while (my ($nick, $uid) = $get_chan_users->fetchrow_array()) {
  8015. > push @targets, $nick unless nr_chk_flag_user({ NICK => $nick, ID => $uid }, NRF_NEVEROP);
  8016. > }
  8017. > cs_updown2($user, 'UP', $chan2, @targets);
  8018. > }
  8019. >
  8020. > notice($user, "Access list for \002$cn1\002 ".
  8021. > ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
  8022. > "copied to \002$cn2\002");
  8023. >
  8024. > my $log_str = "copied the channel access list for \002$cn1\002 ".
  8025. > ($level ? "(rank: \002".$plevels[$level + $plzero]."\002) " : '').
  8026. > "to \002$cn2\002";
  8027. > services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  8028. >
  8029. > my $src = get_user_nick($user);
  8030. > ircd::notice(agent($chan1), '%'.$cn1, "\002$src\002 $log_str")
  8031. > if cr_chk_flag($chan1, CRF_VERBOSE);
  8032. > ircd::notice(agent($chan2), '%'.$cn2, "\002$src\002 $log_str")
  8033. > if cr_chk_flag($chan2, CRF_VERBOSE);
  8034. > }
  8035. >
  8036. > sub cs_copy_chan_levels($$$) {
  8037. > my ($user, $chan1, $chan2) = @_;
  8038. > my $cn1 = $chan1->{CHAN};
  8039. > my $cn2 = $chan2->{CHAN};
  8040. >
  8041. > copy_chan_levels($cn1, $cn2);
  8042. > notice($user, "LEVELS for \002$cn1\002 copied to \002$cn2\002");
  8043. >
  8044. > my $log_str = "copied the LEVELS list for \002$cn1\002 to \002$cn2\002";
  8045. > services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  8046. >
  8047. > my $src = get_user_nick($user);
  8048. > ircd::notice(agent($chan1), '%'.$cn1, "\002$src\002 $log_str")
  8049. > if cr_chk_flag($chan1, CRF_VERBOSE);
  8050. > ircd::notice(agent($chan2), '%'.$cn2, "\002$src\002 $log_str")
  8051. > if cr_chk_flag($chan2, CRF_VERBOSE);
  8052. > }
  8053. >
  8054. > sub cs_copy_chan_akick($$$) {
  8055. > my ($user, $chan1, $chan2) = @_;
  8056. > my $cn1 = $chan1->{CHAN};
  8057. > my $cn2 = $chan2->{CHAN};
  8058. >
  8059. > copy_chan_akick($cn1, $cn2);
  8060. > notice($user, "Channel AKick list for \002$cn1\002 copied to \002$cn2\002");
  8061. >
  8062. > my $log_str = "copied the AKick list for \002$cn1\002 to \002$cn2\002";
  8063. > services::ulog($chanserv::csnick, LOG_INFO(), "$log_str", $user, $chan1);
  8064. >
  8065. > my $src = get_user_nick($user);
  8066. > ircd::notice(agent($chan1), '%'.$cn1, "\002$src\002 $log_str")
  8067. > if cr_chk_flag($chan1, CRF_VERBOSE);
  8068. > ircd::notice(agent($chan2), '%'.$cn2, "\002$src\002 $log_str")
  8069. > if cr_chk_flag($chan2, CRF_VERBOSE);
  8070. > }
  8071. >
  8072. > sub cs_mlock($$$@) {
  8073. > my ($user, $chan, $cmd, @args) = @_;
  8074. > my $cn = $chan->{CHAN};
  8075. > # does this need its own privilege now?
  8076. > can_do($chan, 'SET', $user) or return;
  8077. > my $modes;
  8078. > if(scalar(@args)) {
  8079. > my ($modes_in, @parms_in) = validate_chmodes(shift @args, @args);
  8080. > $modes = $modes_in.' '.join(' ', @parms_in);
  8081. > @args = undef;
  8082. > }
  8083. >
  8084. > my $cur_modelock = get_modelock($chan);
  8085. > if(lc $cmd eq 'add') {
  8086. > $modes = modes::merge($cur_modelock, $modes, 1);
  8087. > $modes = sanitize_mlockable($modes);
  8088. > $set_modelock->execute($modes, $cn);
  8089. > }
  8090. > elsif(lc $cmd eq 'del') {
  8091. > $modes =~ s/[+-]//g;
  8092. > $modes = modes::add($cur_modelock, "-$modes", 1);
  8093. > $set_modelock->execute($modes, $cn);
  8094. > }
  8095. > elsif(lc $cmd eq 'set') {
  8096. > $modes = modes::merge($modes, "+r", 1);
  8097. > $set_modelock->execute($modes, $cn);
  8098. > }
  8099. > elsif(lc $cmd eq 'reset') {
  8100. > $set_modelock->execute(services_conf_default_channel_mlock, $cn);
  8101. > } else {
  8102. > notice($user, "Unknown MLOCK command \"$cmd\"");
  8103. > return;
  8104. > }
  8105. >
  8106. > notice($user, "Mode lock for \002$cn\002 has been set to: \002$modes\002");
  8107. > do_modelock($chan);
  8108. >
  8109. > =cut
  8110. > notice($user, columnar {TITLE => "Ban list of \002$cn\002:", DOUBLE=>1,
  8111. > NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data);
  8112. > =cut
  8113. > }
  8114. >
  8115. > use SrSv::MySQL::Stub {
  8116. > getChanUsers => ['COLUMN', "SELECT user.nick FROM chanuser JOIN user ON (user.id=chanuser.nickid)
  8117. > WHERE chanuser.chan=? AND chanuser.joined=1"]
  8118. > };
  8119. >
  8120. > sub cs_resync($@) {
  8121. > my ($user, @cns) = @_;
  8122. > foreach my $cn (@cns) {
  8123. > my $chan = { CHAN => $cn };
  8124. > next unless cs_clear_ops($user, $chan, 'Resync');
  8125. > cs_updown2($user, 'up', $chan, getChanUsers($cn));
  8126. > if(can_do($chan, 'AKickEnforce', $user, { OVERRIDE_MSG => "AKICK $cn ENFORCE", NOREPLY => 1 })) {
  8127. > cs_akick_enforce($user, $chan);
  8128. > }
  8129. > }
  8130. > }
  8131. >
  8132. > sub cs_join($@) {
  8133. > my ($user, @cns) = @_;
  8134. > my @reply;
  8135. > my @out_cns;
  8136. > foreach my $cn (@cns) {
  8137. > if($cn =~ /,/) {
  8138. > push @cns, split(',', $cn);
  8139. > }
  8140. > elsif($cn eq '') {
  8141. > next;
  8142. > }
  8143. > my $chan = { CHAN => $cn };
  8144. > my $cando_opts = { NOREPLY => 1 };
  8145. > if(check_akick($user, $chan, 1)) {
  8146. > push @reply, "You are banned from $cn";
  8147. > next;
  8148. > } elsif(!can_do($chan, 'JOIN', $user, $cando_opts)) {
  8149. > push @reply, "$cn is a private channel.";
  8150. > next;
  8151. > }
  8152. > if(is_in_chan($user, $chan)) {
  8153. > next;
  8154. > }
  8155. > if(can_do($chan, 'InviteSelf', $user, $cando_opts)) {
  8156. > cs_invite($user, $chan, $user);
  8157. > }
  8158. > push @out_cns, $cn;
  8159. >
  8160. > }
  8161. > ircd::svsjoin(get_user_agent($user), get_user_nick($user), @out_cns) if scalar @out_cns;
  8162. > notice($user, @reply) if scalar @reply;
  8163. > }
  8164. >
  8165. > ### MISCELLANEA ###
  8166. >
  8167. > # these are helpers and do NOT check if $cn1 or $cn2 is reg'd
  8168. > sub copy_chan_acc($$;$) {
  8169. > my ($cn1, $cn2, $level) = @_;
  8170. > if($level) {
  8171. > $copy_acc_rank->execute($cn2, $cn1, $level);
  8172. > $copy_acc_rank->finish();
  8173. > } else {
  8174. > $get_founder->execute($cn2);
  8175. > my ($founder) = $get_founder->fetchrow_array;
  8176. > $get_founder->finish();
  8177. >
  8178. > $copy_acc->execute($cn2, $cn1, $founder);
  8179. > $copy_acc->finish();
  8180. > }
  8181. > }
  8182. >
  8183. > sub copy_chan_akick($$;$) {
  8184. > my ($cn1, $cn2) = @_;
  8185. > $copy_akick->execute($cn2, $cn1);
  8186. > $copy_akick->finish();
  8187. > copy_chan_acc($cn1, $cn2, -1);
  8188. > }
  8189. >
  8190. > sub copy_chan_levels($$) {
  8191. > my ($cn1, $cn2) = @_;
  8192. > $copy_levels->execute($cn2, $cn1);
  8193. > $copy_levels->finish();
  8194. > }
  8195. >
  8196. > sub copy_chan_chanreg($$) {
  8197. > my ($cn1, $cn2) = @_;
  8198. > $get_founder->execute($cn1);
  8199. > my ($founder) = $get_founder->fetchrow_array;
  8200. > $get_founder->finish();
  8201. > set_acc($founder, undef, { CHAN => $cn2 }, FOUNDER);
  8202. > $copy_chanreg->execute($cn2, $cn1);
  8203. > $copy_chanreg->finish();
  8204. > }
  8205. >
  8206. > sub do_welcome($$) {
  8207. > my ($user, $chan) = @_;
  8208. > my $cn = $chan->{CHAN};
  8209. >
  8210. > $get_welcomes->execute($cn);
  8211. > if($get_welcomes->rows) {
  8212. > my @welcomes;
  8213. > while(my ($msg) = $get_welcomes->fetchrow_array) {
  8214. > push @welcomes, (cr_chk_flag($chan, CRF_WELCOMEINCHAN) ? '' : "[$cn] " ).$msg;
  8215. > }
  8216. > if(cr_chk_flag($chan, CRF_WELCOMEINCHAN)) {
  8217. > ircd::privmsg(agent($chan), $cn, @welcomes);
  8218. > } else {
  8219. > notice($user, @welcomes);
  8220. > }
  8221. > }
  8222. > $get_welcomes->finish();
  8223. > }
  8224. >
  8225. > sub do_greet($$) {
  8226. > my ($user, $chan) = @_;
  8227. > my $cn = $chan->{CHAN};
  8228. >
  8229. > if(can_do($chan, 'GREET', $user)) {
  8230. > my $src = get_user_nick($user);
  8231. > $nickserv::get_greet->execute(get_user_id($user));
  8232. > my ($greet) = $nickserv::get_greet->fetchrow_array();
  8233. > $nickserv::get_greet->finish();
  8234. > ircd::privmsg(agent($chan), $cn, "[\002$src\002] $greet") if $greet;
  8235. > }
  8236. > }
  8237. >
  8238. > sub chk_registered($$) {
  8239. > my ($user, $chan) = @_;
  8240. >
  8241. > unless(is_registered($chan)) {
  8242. > my $cn = $chan->{CHAN};
  8243. >
  8244. > notice($user, "The channel \002$cn\002 is not registered.");
  8245. > return 0;
  8246. > }
  8247. >
  8248. > return 1;
  8249. > }
  8250. >
  8251. > sub make_banmask($$;$) {
  8252. > my ($chan, $tuser, $type) = @_;
  8253. > my $nick = get_user_nick($tuser);
  8254. >
  8255. > my ($ident, $vhost) = get_vhost($tuser);
  8256. > no warnings 'misc';
  8257. > my ($nick, $ident, $vhost) = make_hostmask(get_bantype($chan), $nick, $ident, $vhost);
  8258. > if($type eq 'q') {
  8259. > $type = '~q:';
  8260. > } elsif($type eq 'n') {
  8261. > $type = '~n:';
  8262. > } else {
  8263. > $type = '';
  8264. > }
  8265. > return $type."$nick!$ident\@$vhost";
  8266. > }
  8267. >
  8268. > sub kickban($$$$) {
  8269. > my ($chan, $user, $mask, $reason) = @_;
  8270. > my $cn = $chan->{CHAN};
  8271. > my $nick = get_user_nick($user);
  8272. >
  8273. > return 0 if adminserv::is_service($user);
  8274. >
  8275. > my $agent = agent($chan);
  8276. >
  8277. > unless($mask) {
  8278. > $mask = make_banmask($chan, $user);
  8279. > }
  8280. >
  8281. > enforcer_join($chan) if (get_user_count($chan) <= 1);
  8282. > ircd::setmode($agent, $cn, '+b', $mask);
  8283. > ircd::flushmodes();
  8284. > ircd::kick($agent, $cn, $nick, $reason);
  8285. > return 1;
  8286. > }
  8287. >
  8288. > sub kickban_multi($$$) {
  8289. > my ($chan, $users, $reason) = @_;
  8290. > my $cn = $chan->{CHAN};
  8291. > my $agent = agent($chan);
  8292. >
  8293. > enforcer_join($chan);
  8294. > ircd::setmode($agent, $cn, '+b', '*!*@*');
  8295. > ircd::flushmodes();
  8296. >
  8297. > foreach my $user (@$users) {
  8298. > next if adminserv::is_ircop($user) or adminserv::is_svsop($user, adminserv::S_HELP());
  8299. > ircd::kick($agent, $cn, get_user_nick($user), $reason);
  8300. > }
  8301. > }
  8302. >
  8303. > sub clear_users($$) {
  8304. > my ($chan, $reason) = @_;
  8305. > my $cn = $chan->{CHAN};
  8306. > my $agent = agent($chan);
  8307. > my $i;
  8308. >
  8309. > enforcer_join($chan);
  8310. > ircd::setmode($agent, $cn, '+b', '*!*@*');
  8311. > ircd::flushmodes();
  8312. > $get_chan_users->execute($cn);
  8313. > while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
  8314. > my $user = { NICK => $nick, ID => $uid };
  8315. > ircd::kick($agent, $cn, $nick, $reason)
  8316. > unless adminserv::is_ircop($user) or adminserv::is_svsop($user, adminserv::S_HELP());
  8317. > $i++;
  8318. > }
  8319. >
  8320. > return $i;
  8321. > }
  8322. >
  8323. > sub kickmask($$$$) {
  8324. > my ($chan, $mask, $reason, $ban) = @_;
  8325. > my $cn = $chan->{CHAN};
  8326. > my $agent = agent($chan);
  8327. >
  8328. > my ($nick, $ident, $host) = glob2sql(parse_mask($mask));
  8329. > $nick = '%' if ($nick eq '');
  8330. > $ident = '%' if ($ident eq '');
  8331. > $host = '%' if ($host eq '');
  8332. >
  8333. > if ($ban) {
  8334. > my $banmask = $nick.'!'.$ident.'@'.$host;
  8335. > $banmask =~ tr/%_/*?/;
  8336. > ircd::setmode($agent, $cn, '+b', $banmask);
  8337. > ircd::flushmodes();
  8338. > }
  8339. >
  8340. > my $i;
  8341. > $get_chan_users_mask->execute($cn, $nick, $ident, $host, $host, $host);
  8342. > while(my ($nick, $uid) = $get_chan_users_mask->fetchrow_array) {
  8343. > my $user = { NICK => $nick, ID => $uid };
  8344. > ircd::kick($agent, $cn, $nick, $reason)
  8345. > unless adminserv::is_service($user);
  8346. > $i++;
  8347. > }
  8348. > $get_chan_users_mask->finish();
  8349. >
  8350. > return $i;
  8351. > }
  8352. >
  8353. > sub kickmask_noacc($$$$) {
  8354. > my ($chan, $mask, $reason, $ban) = @_;
  8355. > my $cn = $chan->{CHAN};
  8356. > my $agent = agent($chan);
  8357. >
  8358. > my ($nick, $ident, $host) = glob2sql(parse_mask($mask));
  8359. > $nick = '%' if ($nick eq '');
  8360. > $ident = '%' if ($ident eq '');
  8361. > $host = '%' if ($host eq '');
  8362. >
  8363. > if ($ban) {
  8364. > my $banmask = $nick.'!'.$ident.'@'.$host;
  8365. > $banmask =~ tr/%_/*?/;
  8366. > ircd::setmode($agent, $cn, '+b', $banmask);
  8367. > ircd::flushmodes();
  8368. > }
  8369. >
  8370. > my $i;
  8371. > $get_chan_users_mask_noacc->execute($cn, $nick, $ident, $host, $host, $host);
  8372. > while(my ($nick, $uid) = $get_chan_users_mask_noacc->fetchrow_array) {
  8373. > my $user = { NICK => $nick, ID => $uid };
  8374. > ircd::kick($agent, $cn, $nick, $reason)
  8375. > unless adminserv::is_service($user);
  8376. > $i++;
  8377. > }
  8378. > $get_chan_users_mask_noacc->finish();
  8379. >
  8380. > return $i;
  8381. > }
  8382. >
  8383. > sub clear_ops($) {
  8384. > my ($chan) = @_;
  8385. > my $cn = $chan->{CHAN};
  8386. > my @modelist;
  8387. > my $agent = agent($chan);
  8388. >
  8389. > $get_chan_users->execute($cn);
  8390. > while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
  8391. > my $user = { NICK => $nick, ID => $uid };
  8392. > my $opmodes = get_op($user, $chan);
  8393. > for(my $i; $i < 5; $i++) {
  8394. > if($opmodes & 2**$i) {
  8395. > push @modelist, ['-'.$opmodes[$i], $nick];
  8396. > }
  8397. > }
  8398. > }
  8399. >
  8400. > ircd::setmode2($agent, $cn, @modelist);
  8401. > }
  8402. >
  8403. > sub clear_bans($;$) {
  8404. > my ($chan, $type) = @_;
  8405. > my $cn = $chan->{CHAN};
  8406. > my @args = ();
  8407. > my $agent = agent($chan);
  8408. > $type = 0 unless defined $type;
  8409. > my $mode = ($type == 128 ? 'e' : 'b');
  8410. >
  8411. > my @banlist = ();
  8412. > $get_all_bans->execute($cn, $type);
  8413. > while(my ($mask) = $get_all_bans->fetchrow_array) {
  8414. > $mask =~ tr/\%\_/\*\?/;
  8415. > push @banlist, $mask;
  8416. > }
  8417. >
  8418. > ircd::ban_list($agent, $cn, -1, $mode, @banlist);
  8419. > ircd::flushmodes();
  8420. > }
  8421. >
  8422. > sub unban_user($@) {
  8423. > my ($chan, @userlist) = @_;
  8424. > my $cn = $chan->{CHAN};
  8425. > my $count;
  8426. > if (defined(&ircd::unban_nick)) {
  8427. > my @nicklist;
  8428. > foreach my $tuser (@userlist) {
  8429. > push @nicklist, get_user_nick($tuser);
  8430. > }
  8431. > ircd::unban_nick(agent($chan), $cn, @nicklist);
  8432. > return scalar(@nicklist);
  8433. > }
  8434. >
  8435. > foreach my $tuser (@userlist) {
  8436. > my $tuid;
  8437. > unless($tuid = get_user_id($tuser)) {
  8438. > next;
  8439. > }
  8440. >
  8441. > my (@bans);
  8442. > # We don't handle extended bans. Yet.
  8443. > $find_bans_chan_user->execute($cn, $tuid, 0);
  8444. > while (my ($mask) = $find_bans_chan_user->fetchrow_array) {
  8445. > $mask =~ tr/\%\_/\*\?/;
  8446. > push @bans, $mask;
  8447. > }
  8448. > $find_bans_chan_user->finish();
  8449. >
  8450. > ircd::ban_list(agent($chan), $cn, -1, 'b', @bans) if scalar(@bans);
  8451. > $delete_bans_chan_user->execute($cn, $tuid, 0); $delete_bans_chan_user->finish();
  8452. > $count++;
  8453. > }
  8454. > return $count;
  8455. > }
  8456. >
  8457. > sub chan_kill($$;$) {
  8458. > my ($chan, $reason, $tusers) = @_;
  8459. > my $cn = $chan->{CHAN};
  8460. > my $agent = agent($chan);
  8461. > my $i;
  8462. >
  8463. > enforcer_join($chan);
  8464. > if ($tusers) {
  8465. > foreach my $tuser (@$tusers) {
  8466. > $tuser->{ID} = $tuser->{__ID} if defined($tuser->{__ID}); # user_join_multi does this.
  8467. > nickserv::kline_user($tuser, services_conf_chankilltime, $reason)
  8468. > unless adminserv::is_ircop($tuser) or adminserv::is_svsop($tuser, adminserv::S_HELP());
  8469. > $i++;
  8470. > }
  8471. > }
  8472. > else {
  8473. > $get_chan_users->execute($cn);
  8474. > while(my ($nick, $uid) = $get_chan_users->fetchrow_array) {
  8475. > my $tuser = { NICK => $nick, ID => $uid, AGENT => $agent };
  8476. > nickserv::kline_user($tuser, services_conf_chankilltime, $reason)
  8477. > unless adminserv::is_ircop($tuser) or adminserv::is_svsop($tuser, adminserv::S_HELP());
  8478. > $i++;
  8479. > }
  8480. > }
  8481. >
  8482. > return $i;
  8483. > }
  8484. >
  8485. > sub do_nick_akick($$;$) {
  8486. > my ($tuser, $chan, $root) = @_;
  8487. > my $cn = $chan->{CHAN};
  8488. > unless(defined($root)) {
  8489. > (undef, $root) = get_best_acc($tuser, $chan, 2);
  8490. > }
  8491. >
  8492. > $get_nick_akick->execute($cn, $root);
  8493. > my ($reason) = $get_nick_akick->fetchrow_array(); $get_nick_akick->finish();
  8494. >
  8495. > return 0 if adminserv::is_svsop($tuser, adminserv::S_HELP());
  8496. > my @reasonArr;
  8497. > if ($reason) {
  8498. > @reasonArr = split(/\|/,$reason,2);
  8499. > $reason = $reasonArr[0];
  8500. > }
  8501. > kickban($chan, $tuser, undef, "User has been banned from ".$cn.($reason?": $reason":''));
  8502. > }
  8503. >
  8504. > sub check_akick($$;$) {
  8505. > my ($user, $chan, $check_only) = @_;
  8506. >
  8507. > if(adminserv::is_svsop($user, adminserv::S_HELP())) {
  8508. > return 0;
  8509. > }
  8510. > my ($acc, $root) = get_best_acc($user, $chan, 2);
  8511. > if ($acc == -1) {
  8512. > do_nick_akick($user, $chan, $root) unless $check_only;
  8513. > return 1;
  8514. > }
  8515. > my $cn = $chan->{CHAN};
  8516. > my $uid = get_user_id($user);
  8517. > unless($acc) {
  8518. > $get_akick->execute($uid, $cn);
  8519. > if(my @akick = $get_akick->fetchrow_array) {
  8520. > akickban($cn, @akick) unless $check_only;
  8521. > return 1;
  8522. > }
  8523. > }
  8524. > return 0;
  8525. > }
  8526. >
  8527. > sub do_status($$;$) {
  8528. > my ($user, $chan, $check_only) = @_;
  8529. >
  8530. > return 0 if cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
  8531. >
  8532. > my $nick = get_user_nick($user);
  8533. >
  8534. > if(check_akick($user, $chan, $check_only)) {
  8535. > return 0;
  8536. > }
  8537. > my ($acc, $root) = get_best_acc($user, $chan, 2);
  8538. > if(!can_do($chan, 'JOIN', $user, { ACC => $acc, NOREPLY => 1 })) {
  8539. > kickban($chan, $user, undef, 'This is a private channel.')
  8540. > unless $check_only;
  8541. > return 0;
  8542. > }
  8543. >
  8544. > if( !$check_only && is_registered($chan) && !is_neverop_user($user) &&
  8545. > !cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE | CRF_NEVEROP)) )
  8546. > {
  8547. > set_modes($user, $chan, $acc, cr_chk_flag($chan, CRF_SPLITOPS, 0));
  8548. > }
  8549. >
  8550. > return 1;
  8551. > }
  8552. >
  8553. > sub akick_alluser($) {
  8554. > my ($user) = @_;
  8555. > my $uid = get_user_id($user);
  8556. >
  8557. > $get_akick_alluser->execute($uid);
  8558. > while(my @akick = $get_akick_alluser->fetchrow_array) {
  8559. > akickban(@akick);
  8560. > }
  8561. > }
  8562. >
  8563. > sub akick_allchan($) {
  8564. > my ($chan) = @_;
  8565. > my $cn = $chan->{CHAN};
  8566. >
  8567. > $get_akick_allchan->execute($cn);
  8568. > while(my @akick = $get_akick_allchan->fetchrow_array) {
  8569. > akickban($cn, @akick);
  8570. > }
  8571. > }
  8572. >
  8573. > sub akickban(@) {
  8574. > my ($cn, $knick, $bnick, $ident, $host, $reason, $bident) = @_;
  8575. >
  8576. > my $target = { NICK => $knick };
  8577. > my $chan = { CHAN => $cn };
  8578. > return 0 if adminserv::is_svsop($target, adminserv::S_HELP());
  8579. >
  8580. > if($bident) {
  8581. > ($bnick, $ident, $host) = make_hostmask(get_bantype($chan), $knick, $bident, $host);
  8582. > } elsif($host =~ /^(\d{1,3}\.){3}\d{1,3}$/) {
  8583. > ($bnick, $ident, $host) = make_hostmask(4, $knick, $bident, $host);
  8584. > } else {
  8585. > $bnick =~ tr/\%\_/\*\?/;
  8586. > $ident =~ tr/\%\_/\*\?/;
  8587. > $host =~ tr/\%\_/\*\?/;
  8588. > }
  8589. >
  8590. > my @reasonArr;
  8591. > if ($reason) {
  8592. > @reasonArr = split(/\|/,$reason,2);
  8593. > $reason = $reasonArr[0];
  8594. > }
  8595. >
  8596. > return kickban($chan, $target, "$bnick!$ident\@$host", "User has been banned from ".$cn.($reason?": $reason":''));
  8597. > }
  8598. >
  8599. > sub notice_all_nicks($$$) {
  8600. > my ($user, $nick, $msg) = @_;
  8601. > my $src = get_user_nick($user);
  8602. >
  8603. > notice($user, $msg);
  8604. > foreach my $u (get_nick_user_nicks $nick) {
  8605. > notice({ NICK => $u, AGENT => $csnick }, $msg) unless lc $src eq lc $u;
  8606. > }
  8607. > }
  8608. >
  8609. > sub xop_byname($) {
  8610. > my ($name) = @_;
  8611. > my $level;
  8612. >
  8613. > if($name =~ /^uop$/i) { $level=1; }
  8614. > elsif($name =~ /^vop$/i) { $level=2; }
  8615. > elsif($name =~ /^hop$/i) { $level=3; }
  8616. > elsif($name =~ /^aop$/i) { $level=4; }
  8617. > elsif($name =~ /^sop$/i) { $level=5; }
  8618. > elsif($name =~ /^co?f(ounder)?$/i) { $level=6; }
  8619. > elsif($name =~ /^founder$/i) { $level=7; }
  8620. > elsif($name =~ /^(any|all|user)/i) { $level=0; }
  8621. > elsif($name =~ /^akick$/i) { $level=-1; }
  8622. > elsif($name =~ /^(none|disabled?|nobody)$/i) { $level=8; }
  8623. >
  8624. > return $level;
  8625. > }
  8626. >
  8627. > sub expire {
  8628. > return if services_conf_noexpire;
  8629. >
  8630. > $get_expired->execute(time() - (86400 * services_conf_chanexpire));
  8631. > while(my ($cn, $founder) = $get_expired->fetchrow_array) {
  8632. > drop({ CHAN => $cn });
  8633. > wlog($csnick, LOG_INFO(), "\002$cn\002 has expired. Founder: $founder");
  8634. > }
  8635. > }
  8636. >
  8637. > sub enforcer_join($) {
  8638. > my ($chan) = @_;
  8639. > my $cn = $chan->{CHAN};
  8640. > my $bot = agent($chan);
  8641. >
  8642. > return if $enforcers{lc $cn};
  8643. > $enforcers{lc $cn} = lc $bot;
  8644. >
  8645. > botserv::bot_join($chan);
  8646. >
  8647. > add_timer("CSEnforce $bot $cn", 60, __PACKAGE__, 'chanserv::enforcer_part');
  8648. > }
  8649. >
  8650. > sub enforcer_part($) {
  8651. > my ($cookie) = @_;
  8652. > my ($junk, $bot, $cn) = split(/ /, $cookie);
  8653. >
  8654. > return unless $enforcers{lc $cn};
  8655. > undef($enforcers{lc $cn});
  8656. >
  8657. > botserv::bot_part_if_needed($bot, {CHAN => $cn}, 'Enforcer Leaving');
  8658. > }
  8659. >
  8660. > sub fix_private_join_before_id($) {
  8661. > my ($user) = @_;
  8662. >
  8663. > my @cns = get_recent_private_chans(get_user_id($user));
  8664. > foreach my $cn (@cns) {
  8665. > my $chan = { CHAN => $cn };
  8666. > unban_user($chan, $user);
  8667. > }
  8668. >
  8669. > ircd::svsjoin($csnick, get_user_nick($user), @cns) if @cns;
  8670. > }
  8671. >
  8672. > ### DATABASE UTILITY FUNCTIONS ###
  8673. >
  8674. > sub get_user_count($) {
  8675. > my ($chan) = @_;
  8676. > my $cn = $chan->{CHAN};
  8677. >
  8678. > $get_user_count->execute($cn);
  8679. >
  8680. > return $get_user_count->fetchrow_array;
  8681. > }
  8682. >
  8683. > sub get_lock($) {
  8684. > my ($chan) = @_;
  8685. >
  8686. > $chan = lc $chan;
  8687. >
  8688. > $chanuser_table++;
  8689. >
  8690. > if($cur_lock) {
  8691. > if($cur_lock ne $chan) {
  8692. > really_release_lock($chan);
  8693. > $chanuser_table--;
  8694. > die("Tried to get two locks at the same time: $cur_lock, $chan")
  8695. > }
  8696. > $cnt_lock++;
  8697. > } else {
  8698. > $cur_lock = $chan;
  8699. > $get_lock->execute(sql_conf_mysql_db.".chan.$chan");
  8700. > $get_lock->finish;
  8701. > }
  8702. > }
  8703. >
  8704. > sub release_lock($) {
  8705. > my ($chan) = @_;
  8706. >
  8707. > $chan = lc $chan;
  8708. >
  8709. > $chanuser_table--;
  8710. >
  8711. > if($cur_lock and $cur_lock ne $chan) {
  8712. > really_release_lock($cur_lock);
  8713. >
  8714. > die("Tried to release the wrong lock");
  8715. > }
  8716. >
  8717. > if($cnt_lock) {
  8718. > $cnt_lock--;
  8719. > } else {
  8720. > really_release_lock($chan);
  8721. > }
  8722. > }
  8723. >
  8724. > sub really_release_lock($) {
  8725. > my ($chan) = @_;
  8726. >
  8727. > $cnt_lock = 0;
  8728. > $release_lock->execute(sql_conf_mysql_db.".chan.$chan");
  8729. > $release_lock->finish;
  8730. > undef $cur_lock;
  8731. > }
  8732. >
  8733. > #sub is_free_lock($) {
  8734. > # $is_free_lock->execute($_[0]);
  8735. > # return $is_free_lock->fetchrow_array;
  8736. > #}
  8737. >
  8738. > sub get_modelock($) {
  8739. > my ($chan) = @_;
  8740. > my $cn;
  8741. > if(ref($chan)) {
  8742. > $cn = $chan->{CHAN}
  8743. > } else {
  8744. > $cn = $chan;
  8745. > }
  8746. >
  8747. > $get_modelock->execute($cn);
  8748. > my ($ml) = $get_modelock->fetchrow_array;
  8749. > $get_modelock->finish();
  8750. > return $ml;
  8751. > }
  8752. >
  8753. > sub do_modelock($;$) {
  8754. > my ($chan, $modes) = @_;
  8755. > my $cn = $chan->{CHAN};
  8756. >
  8757. > my $seq = $ircline;
  8758. >
  8759. > $get_modelock_lock->execute; $get_modelock_lock->finish;
  8760. >
  8761. > $get_chanmodes->execute($cn);
  8762. > my ($omodes) = $get_chanmodes->fetchrow_array;
  8763. > my $ml = get_modelock($chan);
  8764. >
  8765. > $ml = do_modelock_fast($cn, $modes, $omodes, $ml);
  8766. >
  8767. > $unlock_tables->execute; $unlock_tables->finish;
  8768. >
  8769. > ircd::setmode(agent($chan), $cn, $ml) if($ml);
  8770. > }
  8771. >
  8772. > sub do_modelock_fast($$$$) {
  8773. > my ($cn, $modes, $omodes, $ml) = @_;
  8774. > my $nmodes = modes::add($omodes, $modes, 1);
  8775. > $ml = modes::diff($nmodes, $ml, 1);
  8776. > $set_chanmodes->execute(modes::add($nmodes, $ml, 1), $cn);
  8777. >
  8778. > return $ml;
  8779. > }
  8780. >
  8781. > sub update_modes($$) {
  8782. > my ($cn, $modes) = @_;
  8783. >
  8784. > $get_update_modes_lock->execute; $get_update_modes_lock->finish;
  8785. > $get_chanmodes->execute($cn);
  8786. > my ($omodes) = $get_chanmodes->fetchrow_array;
  8787. >
  8788. > $set_chanmodes->execute(modes::add($omodes, $modes, 1), $cn);
  8789. > $unlock_tables->execute; $unlock_tables->finish;
  8790. > }
  8791. >
  8792. > sub is_level($) {
  8793. > my ($perm) = @_;
  8794. >
  8795. > $is_level->execute($perm);
  8796. >
  8797. > return $is_level->fetchrow_array;
  8798. > }
  8799. >
  8800. > sub is_neverop($) {
  8801. > return nr_chk_flag($_[0], NRF_NEVEROP(), 1);
  8802. > }
  8803. >
  8804. > sub is_neverop_user($) {
  8805. > return nr_chk_flag_user($_[0], NRF_NEVEROP(), 1);
  8806. > }
  8807. >
  8808. > sub is_in_chan($$) {
  8809. > my ($user, $chan) = @_;
  8810. > my $cn = $chan->{CHAN};
  8811. > my $uid = get_user_id($user);
  8812. >
  8813. > $is_in_chan->execute($uid, $cn);
  8814. > if($is_in_chan->fetchrow_array) {
  8815. > return 1;
  8816. > }
  8817. >
  8818. > return 0;
  8819. > }
  8820. >
  8821. > sub is_registered($) {
  8822. > my ($chan) = @_;
  8823. > my $cn = $chan->{CHAN};
  8824. >
  8825. > $is_registered->execute($cn);
  8826. > if($is_registered->fetchrow_array) {
  8827. > return 1;
  8828. > } else {
  8829. > return 0;
  8830. > }
  8831. > }
  8832. >
  8833. > sub get_user_chans($) {
  8834. > my ($user) = @_;
  8835. > my $uid = get_user_id($user);
  8836. > my @chans;
  8837. >
  8838. > $get_user_chans->execute($uid, $ircline, $ircline+1000);
  8839. > while(my ($chan) = $get_user_chans->fetchrow_array) {
  8840. > push @chans, $chan;
  8841. > }
  8842. >
  8843. > return (@chans);
  8844. > }
  8845. >
  8846. > sub get_user_chans_recent($) {
  8847. > my ($user) = @_;
  8848. > my $uid = get_user_id($user);
  8849. > my (@curchans, @oldchans);
  8850. >
  8851. > $get_user_chans_recent->execute($uid);
  8852. > while(my ($cn, $joined, $op) = $get_user_chans_recent->fetchrow_array) {
  8853. > if ($joined) {
  8854. > push @curchans, make_op_prefix($op).$cn;
  8855. > }
  8856. > else {
  8857. > push @oldchans, $cn;
  8858. > }
  8859. > }
  8860. >
  8861. > return (\@curchans, \@oldchans);
  8862. > }
  8863. >
  8864. > my ($prefixes, $modes);
  8865. > sub make_op_prefix($) {
  8866. > my ($op) = @_;
  8867. > return unless $op;
  8868. >
  8869. > unless(defined($prefixes) and defined($modes)) {
  8870. > $IRCd_capabilities{PREFIX} =~ /^\((\S+)\)(\S+)$/;
  8871. > ($modes, $prefixes) = ($1, $2);
  8872. > $modes = reverse $modes;
  8873. > $prefixes = reverse $prefixes;
  8874. > }
  8875. >
  8876. > my $op_prefix = '';
  8877. > for(my $i = 0; $i < length($prefixes); $i++) {
  8878. > $op_prefix = substr($prefixes, $i, 1).$op_prefix if ($op & (2**$i));
  8879. > }
  8880. > return $op_prefix;
  8881. > }
  8882. >
  8883. > sub get_op($$) {
  8884. > my ($user, $chan) = @_;
  8885. > my $cn = $chan->{CHAN};
  8886. > my $uid = get_user_id($user);
  8887. >
  8888. > $get_op->execute($uid, $cn);
  8889. > my ($op) = $get_op->fetchrow_array;
  8890. >
  8891. > return $op;
  8892. > }
  8893. >
  8894. > sub get_best_acc($$;$) {
  8895. > my ($user, $chan, $retnick) = @_;
  8896. > my $uid = get_user_id($user);
  8897. > my $cn = $chan->{CHAN};
  8898. >
  8899. > $get_best_acc->execute($uid, $cn);
  8900. > my ($bnick, $best) = $get_best_acc->fetchrow_array;
  8901. > $get_best_acc->finish();
  8902. >
  8903. > if($retnick == 2) {
  8904. > return ($best, $bnick);
  8905. > } elsif($retnick == 1) {
  8906. > return $bnick;
  8907. > } else {
  8908. > return $best;
  8909. > }
  8910. > }
  8911. >
  8912. > sub get_acc($$) {
  8913. > my ($nick, $chan) = @_;
  8914. > my $cn = $chan->{CHAN};
  8915. >
  8916. > return undef
  8917. > if cr_chk_flag($chan, (CRF_DRONE | CRF_CLOSE | CRF_FREEZE), 1);
  8918. >
  8919. > $get_acc->execute($cn, $nick);
  8920. > my ($acc) = $get_acc->fetchrow_array;
  8921. >
  8922. > return $acc;
  8923. > }
  8924. >
  8925. > sub set_acc($$$$) {
  8926. > my ($nick, $user, $chan, $level) = @_;
  8927. > my $cn = $chan->{CHAN};
  8928. > my $adder = get_best_acc($user, $chan, 1) if $user;
  8929. >
  8930. > $set_acc1->execute($cn, $level, $nick);
  8931. > $set_acc2->execute($level, $adder, $cn, $nick);
  8932. >
  8933. > if ( ( $level > 0 and !is_neverop($nick) and !cr_chk_flag($chan, CRF_NEVEROP) )
  8934. > or $level < 0)
  8935. > {
  8936. > set_modes_allnick($nick, $chan, $level);
  8937. > }
  8938. > }
  8939. >
  8940. > sub del_acc($$) {
  8941. > my ($nick, $chan) = @_;
  8942. > my $cn = $chan->{CHAN};
  8943. >
  8944. > $del_acc->execute($cn, $nick);
  8945. >
  8946. > foreach my $user (get_nick_users $nick) {
  8947. > set_modes($user, $chan, 0, 1) if is_in_chan($user, $chan);
  8948. > }
  8949. > }
  8950. >
  8951. > sub get_auth_nick($$) {
  8952. > my ($cn, $nick) = @_;
  8953. >
  8954. > $get_auth_nick->execute($cn, $nick);
  8955. > my ($data) = $get_auth_nick->fetchrow_array();
  8956. > $get_auth_nick->finish();
  8957. >
  8958. > return split(/:/, $data);
  8959. > }
  8960. > sub get_auth_num($$) {
  8961. > my ($cn, $num) = @_;
  8962. >
  8963. > $get_auth_num->execute($cn, $num - 1);
  8964. > my ($nick, $data) = $get_auth_num->fetchrow_array();
  8965. > $get_auth_num->finish();
  8966. >
  8967. > return ($nick, split(/:/, $data));
  8968. > }
  8969. > sub find_auth($$) {
  8970. > my ($cn, $nick) = @_;
  8971. >
  8972. > $find_auth->execute($cn, $nick);
  8973. > my ($ret) = $find_auth->fetchrow_array();
  8974. > $find_auth->finish();
  8975. >
  8976. > return $ret;
  8977. > }
  8978. >
  8979. > # Only call this if you've checked the user for NEVEROP already.
  8980. > sub set_modes_allchan($;$) {
  8981. > my ($user, $neverop) = @_;
  8982. > my $uid = get_user_id($user);
  8983. >
  8984. > $get_user_chans->execute($uid, $ircline, $ircline+1000);
  8985. > while(my ($cn) = $get_user_chans->fetchrow_array) {
  8986. > my $chan = { CHAN => $cn };
  8987. > my $acc = get_best_acc($user, $chan);
  8988. > if($acc > 0) {
  8989. > set_modes($user, $chan, $acc) unless ($neverop or cr_chk_flag($chan, CRF_NEVEROP));
  8990. > } elsif($acc < 0) {
  8991. > do_nick_akick($user, $chan);
  8992. > }
  8993. > }
  8994. > }
  8995. >
  8996. > # Only call this if you've checked for NEVEROP already.
  8997. > sub set_modes_allnick($$$) {
  8998. > my ($nick, $chan, $level) = @_;
  8999. > my $cn = $chan->{CHAN};
  9000. >
  9001. > $get_using_nick_chans->execute($nick, $cn);
  9002. > while(my ($n) = $get_using_nick_chans->fetchrow_array) {
  9003. > my $user = { NICK => $n };
  9004. > my $l = get_best_acc($user, $chan);
  9005. > if($l > 0) {
  9006. > set_modes($user, $chan, $level, 1) if($level == $l);
  9007. > } elsif($l < 0) {
  9008. > do_nick_akick($user, $chan);
  9009. > }
  9010. > }
  9011. > }
  9012. >
  9013. > # If channel has OPGUARD, $doneg is true.
  9014. > sub set_modes($$$;$) {
  9015. > my ($user, $chan, $acc, $doneg) = @_;
  9016. > my $cn = $chan->{CHAN};
  9017. >
  9018. >
  9019. > if ($acc < 0) {
  9020. > # Do akick stuff here.
  9021. > }
  9022. >
  9023. > my $dst = ( $acc > 0 ? $ops[$acc] : 0 );
  9024. > my $cur = get_op($user, $chan);
  9025. > my ($pos, $neg);
  9026. >
  9027. > if (cr_chk_flag($chan, CRF_FREEZE)) {
  9028. > set_mode_mask($user, $chan, $cur, undef);
  9029. > return;
  9030. > }
  9031. > if (($acc == 0) and cr_chk_flag($chan, CRF_AUTOVOICE)) {
  9032. > set_mode_mask($user, $chan, $cur, 1);
  9033. > return;
  9034. > }
  9035. >
  9036. > $pos = $dst ^ ($dst & $cur);
  9037. > $neg = ($dst ^ $cur) & $cur if $doneg;
  9038. >
  9039. > if($pos or $neg) {
  9040. > set_mode_mask($user, $chan, $neg, $pos);
  9041. > }
  9042. >
  9043. > if($pos) {
  9044. > set_lastop($cn);
  9045. > set_lastused($cn, get_user_id($user));
  9046. > }
  9047. > }
  9048. >
  9049. > sub unset_modes($$) {
  9050. > my ($user, $chan) = @_;
  9051. >
  9052. > my $mask = get_op($user, $chan);
  9053. >
  9054. > set_mode_mask($user, $chan, $mask, 0);
  9055. > }
  9056. >
  9057. > sub set_mode_mask($$$$) {
  9058. > my ($user, $chan, @masks) = @_;
  9059. > my $nick = get_user_nick($user);
  9060. > my $cn = $chan->{CHAN};
  9061. > my (@args, $out);
  9062. >
  9063. > for(my $sign; $sign < 2; $sign++) {
  9064. > next if($masks[$sign] == 0);
  9065. >
  9066. > $out .= '-' if $sign == 0;
  9067. > $out .= '+' if $sign == 1;
  9068. >
  9069. > for(my $i; $i < 5; $i++) {
  9070. > my @l = ('v', 'h', 'o', 'a', 'q');
  9071. >
  9072. > if($masks[$sign] & 2**$i) {
  9073. > $out .= $l[$i];
  9074. > push @args, $nick;
  9075. > }
  9076. > }
  9077. > }
  9078. >
  9079. > if(@args) {
  9080. > ircd::setmode(agent($chan), $cn, $out, join(' ', @args));
  9081. > }
  9082. > }
  9083. >
  9084. > sub get_level($$) {
  9085. > my ($chan, $perm) = @_;
  9086. > my $cn = $chan->{CHAN};
  9087. >
  9088. > $get_level->execute($cn, $perm);
  9089. > my ($level, $isnotnull) = $get_level->fetchrow_array;
  9090. > $get_level->finish();
  9091. >
  9092. > if (wantarray()) {
  9093. > return ($level, $isnotnull);
  9094. > }
  9095. > else {
  9096. > return $level;
  9097. > }
  9098. > }
  9099. >
  9100. > sub check_override($$;$) {
  9101. > my ($user, $perm, $logMsg) = @_;
  9102. > $perm = uc $perm;
  9103. >
  9104. > #{OVERRIDE::$perm} produces funny package problems, so wrap it in double-quotes.
  9105. > if(exists($user->{"OVERRIDE::$perm"}) && (my $nick = $user->{"OVERRIDE::$perm"})) {
  9106. > if(defined($nick)) {
  9107. > if(services_conf_log_overrides && $logMsg) {
  9108. > my $src = get_user_nick($user);
  9109. > wlog($csnick, LOG_INFO(), "\002$src\002 used override $logMsg");
  9110. > }
  9111. > return (wantarray ? ($nick, 1) : $nick);
  9112. > } else {
  9113. > return;
  9114. > }
  9115. > }
  9116. > foreach my $o (@override) {
  9117. > my ($operRank, $permHashRef) = @$o;
  9118. > if($permHashRef->{$perm} and my $nick = adminserv::can_do($user, $operRank)) {
  9119. > $user->{"OVERRIDE::$perm"} = $nick;
  9120. > if(services_conf_log_overrides && $logMsg) {
  9121. > my $src = get_user_nick($user);
  9122. > wlog($csnick, LOG_INFO(), "\002$src\002 used override $logMsg");
  9123. > }
  9124. > return (wantarray ? ($nick, 1) : $nick);
  9125. > }
  9126. > }
  9127. > $user->{"OVERRIDE::$perm"} = undef;
  9128. > }
  9129. >
  9130. > sub can_do($$$;$) {
  9131. > my ($chan, $perm, $user, $data) = @_;
  9132. > $data = {} unless defined $data;
  9133. > # $data is a hashref/struct
  9134. > my $noreply = $data->{NOREPLY};
  9135. > my $acc = $data->{ACC};
  9136. > my $overrideMsg = $data->{OVERRIDE_MSG};
  9137. >
  9138. > if(my $nick = __can_do($chan, $perm, $user, $acc)) {
  9139. > # This is becoming increasingly complicated
  9140. > # and checking if an override was used is becoming tricky.
  9141. > # We had a case in cs_kick where an oper should be able to override +Q/$peace
  9142. > # but cannot b/c they have regular access in that channel.
  9143. > my $override;
  9144. > if(defined($user)) {
  9145. > (undef, $override) = check_override($user, $perm);
  9146. > }
  9147. > return (wantarray ? ($nick, $override) : $nick);
  9148. > } elsif ( $user and adminserv::is_svsop($user, adminserv::S_HELP()) ) {
  9149. > #set_lastused($cn, get_user_id($user));
  9150. > my ($nick, $override) = check_override($user, $perm, $overrideMsg);
  9151. > return (wantarray ? ($nick, $override) : $nick) if $override;
  9152. > }
  9153. > if($user and !$noreply) {
  9154. > my $cn = $chan->{CHAN};
  9155. > if (cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE))) {
  9156. > notice($user, "\002$cn\002 is closed and cannot be used".
  9157. > ((uc $perm eq 'INFO') ? ': '.get_close($chan) : '.'));
  9158. > }
  9159. > elsif(cr_chk_flag($chan, CRF_FREEZE)) {
  9160. > notice($user, "\002$cn\002 is frozen and access suspended.");
  9161. > }
  9162. > else {
  9163. > notice($user, "$cn: $err_deny");
  9164. > }
  9165. > }
  9166. > return 0;
  9167. > }
  9168. >
  9169. > sub __can_do($$$;$) {
  9170. > my ($chan, $perm, $user, $acc) = @_;
  9171. > my $nick;
  9172. > my $cn = $chan->{CHAN};
  9173. > $perm = uc $perm;
  9174. >
  9175. > my $level;
  9176. > unless(exists($chan->{"PERM::$perm"})) {
  9177. > $level = $chan->{"PERM::$perm"} = get_level($chan, $perm);
  9178. > } else {
  9179. > $level = $chan->{"PERM::$perm"};
  9180. > }
  9181. >
  9182. > unless(defined($acc)) {
  9183. > unless (defined $user && ref($user) eq 'HASH') {
  9184. > die "invalid __can_do call";
  9185. > }
  9186. > my $chanuser = $user->{lc $cn};
  9187. > unless (defined($chanuser) && exists($chanuser->{ACC})) {
  9188. > ($acc, $nick) = get_best_acc($user, $chan, 2);
  9189. > ($chanuser->{ACC}, $chanuser->{ACCNICK}) = ($acc, $nick);
  9190. > } else {
  9191. > ($acc, $nick) = ($chanuser->{ACC}, $chanuser->{ACCNICK});
  9192. > }
  9193. > }
  9194. > $nick = 1 unless $nick;
  9195. >
  9196. > if($acc >= $level and !cr_chk_flag($chan, (CRF_CLOSE | CRF_FREEZE | CRF_DRONE))) {
  9197. > set_lastused($cn, get_user_id($user)) if $user;
  9198. > return (wantarray ? ($nick, 0) : $nick);
  9199. > }
  9200. >
  9201. > if(cr_chk_flag($chan, CRF_FREEZE) and ($perm eq 'JOIN')) {
  9202. > return (wantarray ? ($nick, 0) : $nick);
  9203. > }
  9204. >
  9205. > return 0;
  9206. > }
  9207. >
  9208. > sub can_keep_op($$$$) {
  9209. > # This is a na�ve implemenation using a loop.
  9210. > # If we ever do a more flexible version that further restricts how
  9211. > # LEVELS affect opguard, the loop will have to be unrolled.
  9212. > # --
  9213. > # Only call this if you've already checked opguard, as we do not check it here.
  9214. > # --
  9215. > # Remember, this isn't a permission check if someone is allowed to op someone [else],
  9216. > # rather this checks if the person being opped is allowed to keep/have it.
  9217. > my ($user, $chan, $tuser, $opmode) = @_;
  9218. > return 1 if $opmode eq 'v'; # why remove a voice?
  9219. > my %permhash = (
  9220. > 'q' => ['OWNER', 4],
  9221. > 'a' => ['ADMIN', 3],
  9222. > 'o' => ['OP', 2],
  9223. > 'h' => ['HALFOP', 1],
  9224. > 'v' => ['VOICE', 0]
  9225. > );
  9226. >
  9227. > my $self = (lc(get_user_nick($user)) eq lc(get_user_nick($tuser)));
  9228. >
  9229. > #my ($level, $isnotnull) = get_level($chan, $permhash{$opmode}[1]);
  9230. > my $level = get_level($chan, $permhash{$opmode}[0]);
  9231. >
  9232. > foreach my $luser ($tuser, $user) {
  9233. > # We check target first, as there seems no reason that
  9234. > # someone who has access can't be opped by someone
  9235. > # who technically doesn't.
  9236. > return 1 if (adminserv::is_svsop($luser, adminserv::S_HELP()) and
  9237. > check_override($luser, $permhash{$opmode}[0]));
  9238. >
  9239. > my $acc = get_best_acc($luser, $chan);
  9240. > return 1 if ($self and ($permhash{opmode}[2] + 2) <= $acc);
  9241. >
  9242. > if($acc < $level) {
  9243. > return 0;
  9244. > }
  9245. > }
  9246. >
  9247. > return 1;
  9248. > }
  9249. >
  9250. > sub agent($) {
  9251. > my ($chan) = @_;
  9252. >
  9253. > return $chan->{AGENT} if($chan->{AGENT});
  9254. >
  9255. > unless(initial_synced()) {
  9256. > return $csnick;
  9257. > }
  9258. >
  9259. > $botserv::get_chan_bot->execute($chan->{CHAN});
  9260. > my ($agent) = $botserv::get_chan_bot->fetchrow_array;
  9261. >
  9262. > $agent = $csnick unless $agent;
  9263. >
  9264. > return $chan->{AGENT} = $agent;
  9265. > }
  9266. >
  9267. > sub drop($) {
  9268. > my ($chan) = @_;
  9269. > my $cn = $chan->{CHAN};
  9270. >
  9271. > undef($enforcers{lc $cn});
  9272. > my $agent = agent($chan);
  9273. > agent_part($agent, $cn, 'Channel dropped') unless (lc($agent) eq lc($csnick));
  9274. > if (module::is_loaded('logserv')) {
  9275. > eval { logserv::delchan(undef, $cn); }
  9276. > }
  9277. >
  9278. > $drop_acc->execute($cn);
  9279. > $drop_lvl->execute($cn);
  9280. > $del_close->execute($cn);
  9281. > $drop_akick->execute($cn);
  9282. > $drop_welcome->execute($cn);
  9283. > $drop_chantext->execute($cn);
  9284. > $drop_nicktext->execute($cn); # Leftover channel auths
  9285. > $drop->execute($cn);
  9286. > ircd::setmode($csnick, $cn, '-r');
  9287. > }
  9288. >
  9289. > sub drop_nick_chans($) {
  9290. > my ($nick) = @_;
  9291. >
  9292. > $delete_successors->execute($nick);
  9293. >
  9294. > $get_nick_own_chans->execute($nick);
  9295. > while(my ($cn) = $get_nick_own_chans->fetchrow_array) {
  9296. > succeed_chan($cn, $nick);
  9297. > }
  9298. > }
  9299. >
  9300. > sub succeed_chan($$) {
  9301. > my ($cn, $nick) = @_;
  9302. >
  9303. > $get_successor->execute($cn);
  9304. > my ($suc) = $get_successor->fetchrow_array;
  9305. >
  9306. > if($suc) {
  9307. > $set_founder->execute($suc, $cn);
  9308. > set_acc($suc, undef, {CHAN => $cn}, FOUNDER);
  9309. > $del_successor->execute($cn);
  9310. > } else {
  9311. > drop({CHAN => $cn});
  9312. > wlog($csnick, LOG_INFO(), "\002$cn\002 has been dropped due to expiry/drop of \002$nick\002");
  9313. > }
  9314. > }
  9315. >
  9316. > sub get_close($) {
  9317. > my ($chan) = @_;
  9318. > my $cn = $chan->{CHAN};
  9319. > return undef unless cr_chk_flag($chan, CRF_CLOSE | CRF_DRONE);
  9320. >
  9321. > $get_close->execute($cn);
  9322. > my ($reason, $opnick, $time) = $get_close->fetchrow_array();
  9323. > $get_close->finish();
  9324. >
  9325. > $reason = "[$opnick ".gmtime2($time)."] - $reason";
  9326. >
  9327. > return (wantarray ? ($reason, $opnick, $time) : $reason);
  9328. > }
  9329. >
  9330. > sub get_users_nochans(;$) {
  9331. > my ($noid) = @_;
  9332. > my @users;
  9333. >
  9334. > if($noid) {
  9335. > $get_users_nochans_noid->execute();
  9336. > while (my ($usernick, $userid) = $get_users_nochans_noid->fetchrow_array()) {
  9337. > push @users, { NICK => $usernick, ID => $userid };
  9338. > }
  9339. > $get_users_nochans_noid->finish();
  9340. > }
  9341. > else {
  9342. > $get_users_nochans->execute();
  9343. > while (my ($usernick, $userid) = $get_users_nochans->fetchrow_array()) {
  9344. > push @users, { NICK => $usernick, ID => $userid };
  9345. > }
  9346. > $get_users_nochans->finish();
  9347. > }
  9348. >
  9349. > return @users;
  9350. > }
  9351. >
  9352. > sub get_bantype($) {
  9353. > my ($chan) = @_;
  9354. > my $cn = $chan->{CHAN};
  9355. >
  9356. > unless (exists($chan->{BANTYPE})) {
  9357. > $get_bantype->execute($cn);
  9358. > ($chan->{BANTYPE}) = $get_bantype->fetchrow_array();
  9359. > $get_bantype->finish();
  9360. > }
  9361. >
  9362. > return $chan->{BANTYPE};
  9363. > }
  9364. >
  9365. > sub memolog($$) {
  9366. > my ($chan, $log) = @_;
  9367. >
  9368. > my $level = get_level($chan, "MemoAccChange");
  9369. > return if $level == 8; # 8 is 'disable'
  9370. > $level = 1 if $level == 0;
  9371. > memoserv::send_chan_memo($csnick, $chan, $log, $level);
  9372. > }
  9373. >
  9374. > sub get_ban_num($$) {
  9375. > my ($chan, $num) = @_;
  9376. > $get_ban_num->execute($chan->{CHAN}, 0, $num-1);
  9377. > my ($mask) = $get_ban_num->fetchrow_array();
  9378. > $get_ban_num->finish();
  9379. > return sql2glob($mask);
  9380. > }
  9381. >
  9382. > ### IRC EVENTS ###
  9383. >
  9384. > sub user_join($$) {
  9385. > # Due to special casing of '0' this wrapper should be used
  9386. > # by anyone handling a JOIN (not SJOIN, it's a JOIN) event.
  9387. > # This is an RFC1459 requirement.
  9388. > my ($nick, $cn) = @_;
  9389. > my $user = { NICK => $nick };
  9390. > my $chan = { CHAN => $cn };
  9391. >
  9392. > if ($cn == 0) {
  9393. > # This should be treated as a number
  9394. > # Just in case we ever got passed '000', not that Unreal does.
  9395. > # In C, you could check that chan[0] != '#' && chan[0] == '0'
  9396. > user_part_multi($user, [ get_user_chans($user) ], 'Left all channels');
  9397. > }
  9398. > else {
  9399. > user_join_multi($chan, [$user]);
  9400. > }
  9401. > }
  9402. >
  9403. > sub handle_sjoin($$$$$$$) {
  9404. > my ($server, $cn, $ts, $chmodes, $chmodeparms, $userarray, $banarray, $exceptarray) = @_;
  9405. > my $chan = { CHAN => $cn };
  9406. >
  9407. > if(synced()) {
  9408. > chan_mode($server, $cn, $chmodes, $chmodeparms) if $chmodes;
  9409. > } else {
  9410. > update_modes($cn, "$chmodes $chmodeparms") if $chmodes;
  9411. > }
  9412. > user_join_multi($chan, $userarray) if scalar @$userarray;
  9413. >
  9414. > foreach my $ban (@$banarray) {
  9415. > process_ban($cn, $ban, $server, 0, 1);
  9416. > }
  9417. > foreach my $except (@$exceptarray) {
  9418. > process_ban($cn, $except, $server, 128, 1);
  9419. > }
  9420. > }
  9421. >
  9422. > sub user_join_multi($$) {
  9423. > my ($chan, $users) = @_;
  9424. > my $cn = $chan->{CHAN};
  9425. > my $seq = $ircline;
  9426. > my $multi_tradeoff = 2; # could use some synthetic-benchmark tuning
  9427. >
  9428. > foreach my $user (@$users) {
  9429. > $user->{__ID} = get_user_id($user);
  9430. > unless (defined($user->{__ID})) {
  9431. > # This does happen occasionally. it's a BUG.
  9432. > # At least we have a diagnostic for it now.
  9433. > # Normally we'd just get a [useless] warning from the SQL server
  9434. > ircd::debug($user->{NICK}.' has a NULL user->{__ID} in user_join_multi('.$cn.', ...');
  9435. > }
  9436. > }
  9437. >
  9438. > $get_joinpart_lock->execute; $get_joinpart_lock->finish;
  9439. >
  9440. > $chan_create->execute($seq, $cn);
  9441. >
  9442. > $get_user_count->execute($cn);
  9443. > my ($count) = $get_user_count->fetchrow_array;
  9444. >
  9445. > if(scalar(@$users) < $multi_tradeoff) {
  9446. > foreach my $user (@$users) {
  9447. > # see note above in get_user_id loop
  9448. > if (defined($user->{__ID})) {
  9449. > $chanjoin->execute($seq, $user->{__ID}, $cn, $user->{__OP});
  9450. > }
  9451. > }
  9452. > }
  9453. > else {
  9454. > my $query = "REPLACE INTO chanuser (seq, nickid, chan, op, joined) VALUES ";
  9455. > foreach my $user (@$users) {
  9456. > # a join(',', list) would be nice but would involve preparing the list first.
  9457. > # I think this will be faster.
  9458. > if (defined($user->{__ID})) {
  9459. > # see note above in get_user_id loop
  9460. > $query .= '('.$dbh->quote($seq).','.
  9461. > $dbh->quote($user->{__ID}).','.
  9462. > $dbh->quote($cn).','.
  9463. > $dbh->quote($user->{__OP}).', 1),';
  9464. > }
  9465. > }
  9466. > $query =~ s/\,$//;
  9467. > $dbh->do($query);
  9468. > }
  9469. >
  9470. > $unlock_tables->execute; $unlock_tables->finish;
  9471. >
  9472. > my $bot = agent($chan);
  9473. > foreach my $user (@$users) {
  9474. > $user->{AGENT} = $bot;
  9475. > }
  9476. >
  9477. > if(initial_synced() and cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE))) {
  9478. > my ($reason, $opnick, $time) = get_close($chan);
  9479. > my $cmsg = "$cn is closed: $reason";
  9480. > my $preenforce = $enforcers{lc $chan};
  9481. >
  9482. > if (cr_chk_flag($chan, CRF_CLOSE)) {
  9483. > kickban_multi($chan, $users, $cmsg);
  9484. > }
  9485. > elsif (cr_chk_flag($chan, CRF_DRONE)) {
  9486. > chan_kill($chan, $cmsg, $users);
  9487. > }
  9488. >
  9489. > unless($preenforce) {
  9490. > ircd::settopic($bot, $cn, $opnick, $time, $cmsg);
  9491. >
  9492. > my $ml = get_modelock($chan);
  9493. > ircd::setmode($bot, $cn, $ml) if($ml);
  9494. > }
  9495. > }
  9496. >
  9497. > if(($count == 0 or !is_agent_in_chan($bot, $cn)) and initial_synced()) {
  9498. > unless (lc($bot) eq lc($csnick)) {
  9499. > unless(is_agent_in_chan($bot, $cn)) {
  9500. > botserv::bot_join($chan);
  9501. > }
  9502. > }
  9503. > }
  9504. >
  9505. > return unless synced() and not cr_chk_flag($chan, (CRF_CLOSE | CRF_DRONE));
  9506. >
  9507. > my $n;
  9508. > foreach my $user (@$users) {
  9509. > if(do_status($user, $chan)) {
  9510. > $n++;
  9511. > $user->{__DO_WELCOME} = 1;
  9512. > }
  9513. > }
  9514. >
  9515. > if($count == 0 and $n) {
  9516. > my ($ml) = get_modelock($chan);
  9517. > ircd::setmode($bot, $cn, $ml) if($ml);
  9518. >
  9519. > $get_topic->execute($cn);
  9520. > my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
  9521. > ircd::settopic($bot, $cn, $nsetter, $ntime, $ntopic) if $ntopic;
  9522. > }
  9523. >
  9524. > ircd::flushmodes();
  9525. >
  9526. > if($n) {
  9527. > foreach my $user (@$users) {
  9528. > if ($user->{__DO_WELCOME} and chk_user_flag($user, UF_FINISHED())) {
  9529. > do_welcome($user, $chan);
  9530. > do_greet($user, $chan)
  9531. > if can_do($chan, 'GREET', $user, { NOREPLY => 1 });
  9532. > }
  9533. > }
  9534. > }
  9535. > }
  9536. >
  9537. > sub user_part($$$) {
  9538. > my ($nick, $cn, $reason) = @_;
  9539. >
  9540. > my $user = ( ref $nick eq 'HASH' ? $nick : { NICK => $nick });
  9541. >
  9542. > user_part_multi($user, [ $cn ], $reason);
  9543. > }
  9544. >
  9545. > sub user_part_multi($$$) {
  9546. > # user_join_multi takes a channel and multiple users
  9547. > # user_part_multi takes a user and multiple channels
  9548. > # There should probably be a user_join_* that takes one user, multiple channels
  9549. > # However, it seems that so far, Unreal splits both PART and JOIN (non-SJOIN)
  9550. > # into multiple events/cmds. The reason is unclear.
  9551. > # Other ircds may not do so.
  9552. > # There is also KICK. some IRCds allow KICK #chan user1,user2,...
  9553. > # Unreal it's _supposed_ to work, but it does not.
  9554. >
  9555. > my ($user, $chanlist, $reason) = @_;
  9556. > my @chans;
  9557. > foreach my $cn (@$chanlist) {
  9558. > push @chans, { CHAN => $cn };
  9559. >
  9560. > }
  9561. >
  9562. > my $uid = get_user_id($user);
  9563. > my $seq = $ircline;
  9564. >
  9565. > $get_joinpart_lock->execute; $get_joinpart_lock->finish;
  9566. >
  9567. > foreach my $chan (@chans) {
  9568. > my $cn = $chan->{CHAN};
  9569. > $chanpart->execute($seq, $uid, $cn, $seq, $seq+1000);
  9570. > $get_user_count->execute($cn);
  9571. > $chan->{COUNT} = $get_user_count->fetchrow_array;
  9572. > }
  9573. >
  9574. > $unlock_tables->execute; $unlock_tables->finish;
  9575. >
  9576. > foreach my $chan (@chans) {
  9577. > channel_emptied($chan) if $chan->{COUNT} == 0;
  9578. > }
  9579. > }
  9580. >
  9581. > sub channel_emptied($) {
  9582. > my ($chan) = @_;
  9583. >
  9584. > botserv::bot_part_if_needed(undef, $chan, 'Nobody\'s here', 1);
  9585. > $chan_delete->execute($chan->{CHAN});
  9586. > $wipe_bans->execute($chan->{CHAN});
  9587. > }
  9588. >
  9589. > sub process_kick($$$$) {
  9590. > my ($src, $cn, $target, $reason) = @_;
  9591. > my $tuser = { NICK => $target };
  9592. > user_part($tuser, $cn, 'Kicked by '.$src.' ('.$reason.')');
  9593. >
  9594. > my $chan = { CHAN => $cn };
  9595. > if ( !(is_agent($src) or $src =~ /\./ or adminserv::is_ircop({ NICK => $src })) and
  9596. > ({modes::splitmodes(get_modelock($chan))}->{Q}->[0] eq '+') )
  9597. > {
  9598. > my $srcUser = { NICK => $src };
  9599. > #ircd::irckill(agent($chan), $src, "War script detected (kicked $target past +Q in $cn)");
  9600. > nickserv::kline_user($srcUser, 300, "War script detected (kicked $target past +Q in $cn)");
  9601. > # SVSJOIN won't work while they're banned, unless you invite.
  9602. > ircd::invite(agent($chan), $cn, $target);
  9603. > ircd::svsjoin(undef, $target, $cn);
  9604. > unban_user($chan, $tuser);
  9605. > }
  9606. > }
  9607. >
  9608. > sub chan_mode($$$$) {
  9609. > my ($src, $cn, $modes, $args) = @_;
  9610. > my $user = { NICK => $src };
  9611. > my $chan = { CHAN => $cn };
  9612. > my ($sign, $num);
  9613. >
  9614. > # XXX This is not quite right, but maybe it's good enough.
  9615. > my $mysync = ($src =~ /\./ ? 0 : 1);
  9616. >
  9617. > if($modes !~ /^[beIvhoaq+-]+$/ and (!synced() or $mysync)) {
  9618. > do_modelock($chan, "$modes $args");
  9619. > }
  9620. >
  9621. > my $opguard = (!current_message->{SYNC} and cr_chk_flag($chan, CRF_OPGUARD, 1));
  9622. >
  9623. > my @perms = ('VOICE', 'HALFOP', 'OP', 'PROTECT');
  9624. > my $unmodes = '-';
  9625. > my @unargs;
  9626. >
  9627. > my @modes = split(//, $modes);
  9628. > my @args = split(/ /, $args);
  9629. >
  9630. > foreach my $mode (@modes) {
  9631. > if($mode eq '+') { $sign = 1; next; }
  9632. > if($mode eq '-') { $sign = 0; next; }
  9633. >
  9634. > my $arg = shift(@args) if($mode =~ $scm or $mode =~ $ocm);
  9635. > my $auser = { NICK => $arg };
  9636. >
  9637. > if($mode =~ /^[vhoaq]$/) {
  9638. > next if $arg eq '';
  9639. > next if is_agent($arg);
  9640. > $num = 0 if $mode eq 'v';
  9641. > $num = 1 if $mode eq 'h';
  9642. > $num = 2 if $mode eq 'o';
  9643. > $num = 3 if $mode eq 'a';
  9644. > $num = 4 if $mode eq 'q';
  9645. >
  9646. > if($opguard and $sign == 1 and
  9647. > !can_keep_op($user, $chan, $auser, $mode)
  9648. > ) {
  9649. > $unmodes .= $mode;
  9650. > push @unargs, $arg;
  9651. > } else {
  9652. > my $nid = get_user_id($auser) or next;
  9653. > my ($r, $i);
  9654. > do {
  9655. > if($sign) {
  9656. > $r = $chop->execute((2**$num), (2**$num), $nid, $cn);
  9657. > } else {
  9658. > $r = $chdeop->execute((2**$num), (2**$num), $nid, $cn);
  9659. > }
  9660. > $i++;
  9661. > } while($r==0 and $i<10);
  9662. > }
  9663. > }
  9664. > if ($mode eq 'b') {
  9665. > next if $arg eq '';
  9666. > process_ban($cn, $arg, $src, 0, $sign);
  9667. > }
  9668. > if ($mode eq 'e') {
  9669. > next if $arg eq '';
  9670. > process_ban($cn, $arg, $src, 128, $sign);
  9671. > }
  9672. > if ($mode eq 'I') {
  9673. > next;# if $arg eq '';
  9674. > #process_ban($cn, $arg, $src, 128, $sign);
  9675. > }
  9676. > }
  9677. > ircd::setmode(agent($chan), $cn, $unmodes, join(' ', @unargs)) if($opguard and @unargs);
  9678. > }
  9679. >
  9680. > sub process_ban($$$$) {
  9681. > my ($cn, $arg, $src, $type, $sign) = @_;
  9682. >
  9683. > $arg =~ tr/\*\?/\%\_/;
  9684. >
  9685. > if ($sign > 0) {
  9686. > $add_ban->execute($cn, $arg, $src, $type);
  9687. > } else {
  9688. > $delete_ban->execute($cn, $arg, $type);
  9689. > }
  9690. > }
  9691. >
  9692. > sub chan_topic {
  9693. > my ($src, $cn, $setter, $time, $topic) = @_;
  9694. > my $chan = { CHAN => $cn };
  9695. > my $suser = { NICK => $setter, AGENT => agent($chan) };
  9696. >
  9697. > return if cr_chk_flag($chan, CRF_CLOSE, 1);
  9698. >
  9699. > if(current_message->{SYNC}) { # We don't need to undo our own topic changes.
  9700. > $set_topic1->execute($setter, $time, $cn);
  9701. > $set_topic2->execute($cn, $topic);
  9702. > return;
  9703. > }
  9704. >
  9705. > if(!synced()) {
  9706. > $get_topic->execute($cn);
  9707. > my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
  9708. > if($topic ne '' and $time == $ntime or can_do($chan, 'SETTOPIC', undef, { ACC => 0 })) {
  9709. > $set_topic1->execute($setter, $time, $cn);
  9710. > $set_topic2->execute($cn, $topic);
  9711. > } else {
  9712. > ircd::settopic(agent($chan), $cn, $nsetter, $ntime, $ntopic);
  9713. > }
  9714. > }
  9715. >
  9716. > elsif(lc($src) ne lc($setter) or can_do($chan, 'SETTOPIC', $suser)) {
  9717. > $set_topic1->execute($setter, $time, $cn);
  9718. > $set_topic2->execute($cn, $topic);
  9719. > } else {
  9720. > $get_topic->execute($cn);
  9721. > my ($ntopic, $nsetter, $ntime) = $get_topic->fetchrow_array;
  9722. > ircd::settopic(agent($chan), $cn, $nsetter, $ntime, $ntopic);
  9723. > }
  9724. > }
  9725. >
  9726. > sub eos(;$) {
  9727. > my ($server) = @_;
  9728. > my $gsa;
  9729. >
  9730. > $get_all_closed_chans->execute(CRF_DRONE|CRF_CLOSE);
  9731. > while(my ($cn, $type, $reason, $opnick, $time) = $get_all_closed_chans->fetchrow_array) {
  9732. > my $chan = { CHAN => $cn };
  9733. >
  9734. > my $cmsg = " is closed [$opnick ".gmtime2($time)."]: $reason";
  9735. > if($type == CRF_DRONE) {
  9736. > chan_kill($chan, $cn.$cmsg);
  9737. > } else {
  9738. > ircd::settopic(agent($chan), $cn, $opnick, $time, "Channel".$cmsg);
  9739. > clear_users($chan, "Channel".$cmsg);
  9740. > }
  9741. > }
  9742. >
  9743. > while($chanuser_table > 0) { }
  9744. >
  9745. > $get_eos_lock->execute(); $get_eos_lock->finish;
  9746. > $get_akick_all->execute();
  9747. > if($server) {
  9748. > $get_status_all_server->execute($server);
  9749. > $gsa = $get_status_all_server;
  9750. > } else {
  9751. > $get_status_all->execute();
  9752. > $gsa = $get_status_all;
  9753. > }
  9754. > #$unlock_tables->execute(); $unlock_tables->finish;
  9755. >
  9756. > while(my @akick = $get_akick_all->fetchrow_array) {
  9757. > akickban(@akick);
  9758. > }
  9759. >
  9760. > $get_modelock_all->execute();
  9761. > while(my ($cn, $modes, $ml) = $get_modelock_all->fetchrow_array) {
  9762. > $ml = do_modelock_fast($cn, '', $modes, $ml);
  9763. > ircd::setmode(agent({CHAN=>$cn}), $cn, $ml) if $ml;
  9764. > }
  9765. >
  9766. > while(my ($cn, $cflags, $agent, $nick, $uid, $uflags, $level, $op, $neverop) = $gsa->fetchrow_array) {
  9767. > my $user = { NICK => $nick, ID => $uid };
  9768. > #next if chk_user_flag($user, UF_FINISHED);
  9769. > $agent = $csnick unless $agent;
  9770. > my $chan = { CHAN => $cn, FLAGS => $cflags, AGENT => $agent };
  9771. >
  9772. > set_modes($user, $chan, $level, ($cflags & CRF_OPGUARD)) if not $neverop and $ops[$level] != $op and not $cflags & (CRF_FREEZE | CRF_CLOSE | CRF_DRONE);
  9773. > do_welcome($user, $chan);
  9774. > }
  9775. >
  9776. > set_user_flag_all(UF_FINISHED());
  9777. > $unlock_tables->execute(); $unlock_tables->finish;
  9778. > }
  9779. >
  9780. > 1;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement