daily pastebin goal
67%
SHARE
TWEET

Untitled

a guest May 30th, 2017 63 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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;
RAW Paste Data