]> jfr.im git - irc/SurrealServices/srsv.git/blob - branches/0.5.0/modules/serviceslibs/nickserv.pm
19631885f2efe25631c7e7b116c8f97da83c84b7
[irc/SurrealServices/srsv.git] / branches / 0.5.0 / modules / serviceslibs / nickserv.pm
1 # This file is part of SurrealServices.
2 #
3 # SurrealServices is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # SurrealServices is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with SurrealServices; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 package nickserv;
17
18 use strict;
19 use Time::Local;
20 use SrSv::Timer qw(add_timer);
21 use SrSv::IRCd::State qw($ircline synced initial_synced %IRCd_capabilities);
22 use SrSv::Agent;
23 use SrSv::Conf qw(main services sql);
24 use SrSv::Conf2Consts qw(main services sql);
25 use SrSv::HostMask qw(normalize_hostmask hostmask_to_regexp parse_mask parse_hostmask make_hostmask);
26
27 use SrSv::MySQL qw( $dbh :sql_types );
28 use SrSv::MySQL::Glob;
29
30 use SrSv::Shared qw(%newuser %olduser);
31
32 use SrSv::Time;
33 use SrSv::Text::Format qw(columnar);
34 use SrSv::Errors;
35
36 use SrSv::Log;
37
38 use SrSv::User '/./';
39 use SrSv::User::Notice;
40 use SrSv::Help qw( sendhelp );
41
42 use SrSv::NickReg::Flags;
43 use SrSv::NickReg::User '/./';
44 use SrSv::Hash::Passwords;
45
46 use SrSv::NickControl::Enforcer qw(%enforcers);
47
48 use SrSv::Email;
49 use SrSv::Insp::UUID;
50 use SrSv::Util qw( makeSeqList );
51 use SrSv::IRCd::Send qw (getAgentRevUuid ircd::getAgentUuid);
52 use SrSv::Debug;
53
54 use SrSv::NickReg::NickText;
55
56 use SrSv::IPv6;
57
58 require SrSv::MySQL::Stub;
59 use Data::Dumper;
60 use constant {
61 # Clone exception max limit.
62 # This number typically means infinite/no-limit.
63 # It is 2**24-1
64 MAX_LIM => 16777215,
65
66 # This could be made a config option
67 # But our config system currently sucks.
68 MAX_PROFILE => 10,
69 # This value likely cannot be increased very far
70 # as the following limits would apply:
71 # 106 (nick/hostmask), 6 (NOTICE), 30 (destination-nick), 32 (key length) = 174
72 # 510 - 174 = 336
73 # but this does not take into account additional spaces/colons
74 # or reformatting by the SrSv::Format code.
75 # Likely the maximum value is ~300
76 MAX_PROFILE_LEN => 250,
77 };
78
79 our $nsnick_default = 'NickServ';
80 our $nsnick = $nsnick_default;
81 our $nsuser = { NICK => $nsnick, ID => ircd::getAgentUuid($nsnick) }; #FIXME - erry
82 our $cur_lock;
83 our $cnt_lock = 0;
84
85 our @protect_short = ('none', 'normal', 'high', 'kill');
86 our @protect_long = (
87 'You will not be required to identify to use this nick.',
88 'You must identify within 60 seconds to use this nick.',
89 'You must identify before using this nick.',
90 'You must identify before using this nick or you will be disconnected.'
91 );
92 our %protect_level = (
93 'none' => 0,
94 'no' => 0,
95 'false' => 0,
96 'off' => 0,
97 '0' => 0,
98
99 'true' => 1,
100 'yes' => 1,
101 'on' => 1,
102 'normal' => 1,
103 '1' => 1,
104
105 'high' => 2,
106 '2' => 2,
107
108 'kill' => 3,
109 '3' => 3
110 );
111
112 our (
113 $nick_check, $nick_checkExists,
114 $nick_create, $nick_create2, $nick_create_old, $nick_change, $nick_quit, $nick_delete, $nick_id_delete,
115 $get_quit_empty_chans, $nick_chan_delete, $chan_user_partall,
116 $get_hostless_nicks,
117 $id_change,
118 $get_squit_lock, $squit_users, $squit_nickreg, $get_squit_empty_chans, $squit_lastquit,
119
120 $del_nickchg_id, $add_nickchg, $reap_nickchg,
121
122 $get_nick_inval, $inc_nick_inval,
123 $is_registered,
124 $is_alias_of,
125
126 $get_guest, $set_guest,
127
128 $get_lock, $release_lock,
129
130 $get_umodes, $set_umodes,
131
132 $get_info,
133 $set_vhost, $set_ident, $set_ip,
134 $update_regnick_vhost, $get_regd_time, $get_nickreg_quit,
135
136 $chk_clone_except, $count_clones,
137
138 $set_pass,
139 $set_email,
140
141 $get_root_nick, $get_id_nick, $chk_pass, $identify, $identify_ign, $id_update, $logout, $unidentify, $unidentify_single,
142 $update_lastseen, $quit_update, $update_nickalias_last,
143 $set_protect_level,
144
145 $get_register_lock, $register, $create_alias, $drop, $change_root,
146
147 $get_aliases, $get_glist, $count_aliases, $get_random_alias, $delete_alias, $delete_aliases,
148 $get_all_access, $del_all_access, $change_all_access, $change_akicks, $change_founders,
149 $change_successors, $change_svsops,
150
151 $lock_user_table, $unlock_tables,
152
153 $get_matching_nicks,
154
155 $cleanup_nickid, $cleanup_users, $cleanup_chanuser,
156 $get_dead_users,
157
158 $get_expired, $get_near_expired, $set_near_expired,
159
160 $get_watches, $check_watch, $set_watch, $del_watch, $drop_watch,
161 $get_silences, $check_silence, $set_silence, $del_silence, $drop_silence,
162 $get_silence_by_num,
163 $get_expired_silences, $del_expired_silences,
164
165 $get_seen,
166
167 $set_greet, $get_greet, $get_greet_nick, $del_greet,
168 $get_num_nicktext_type, $drop_nicktext,
169
170 $get_auth_chan, $get_auth_num, $del_auth, $list_auth, $add_auth,
171
172 $del_nicktext,
173
174 $set_umode_ntf, $get_umode_ntf,
175
176 $set_vacation_ntf, $get_vacation_ntf,
177
178 $set_authcode_ntf, $get_authcode_ntf,
179
180 $get_nicks_by_email,
181
182 $nick_deleteChanUser, $nick_deleteNickCh, $nick_deleteNickId,
183 $id_delUser, $nick_delUser,
184 );
185
186 sub init() {
187 $nick_check = $dbh->prepare("SELECT id FROM user WHERE nick=? AND online=0 AND time=?");
188 $nick_checkExists = $dbh -> prepare ("SELECT nick FROM user WHERE id=? AND time=?");
189 $nick_deleteChanUser = $dbh -> prepare ("DELETE FROM chanuser WHERE nickid=?");
190 $nick_deleteNickCh = $dbh -> prepare ("DELETE FROM nickchg WHERE nickid=?");
191 $nick_deleteNickId = $dbh -> prepare ("DELETE FROM nickid WHERE id=?");
192 $id_delUser = $dbh->prepare ("DELETE FROM user WHERE id=?");
193 $nick_delUser = $dbh->prepare ("DELETE FROM user WHERE nick=?");
194 $nick_create = $dbh->prepare("INSERT INTO user SET nick=?, time=?, inval=0, ident=?, host=?, vhost=?, server=?, modes=?,
195 gecos=?, flags=?, cloakhost=?, online=1");
196 $nick_create2 = $dbh->prepare("INSERT INTO user SET id=?, nick=?, time=?, inval=0, ident=?, host=?, vhost=?, server=?, modes=?,
197 gecos=?, flags=?, cloakhost=?, online=1");
198 # $nick_create = $dbh->prepare("INSERT INTO user SET id=(RAND()*294967293)+1, nick=?, time=?, inval=0, ident=?, host=?, vhost=?, server=?, modes=?, gecos=?, flags=?, cloakhost=?, online=1");
199 $nick_create_old = $dbh->prepare("UPDATE user SET nick=?, ident=?, host=?, vhost=?, server=?, modes=?, gecos=?,
200 flags=?, cloakhost=?, online=1 WHERE id=?");
201 $nick_change = $dbh->prepare("UPDATE user SET nick=? WHERE nick=?");
202 $nick_quit = $dbh->prepare("UPDATE user SET online=0, quittime=UNIX_TIMESTAMP() WHERE nick=?");
203 $nick_delete = $dbh->prepare("DELETE FROM user WHERE nick=?");
204 $nick_id_delete = $dbh->prepare("DELETE FROM nickid WHERE id=?");
205 $get_quit_empty_chans = $dbh->prepare("SELECT cu2.chan, COUNT(*) AS c
206 FROM chanuser AS cu1, chanuser AS cu2
207 WHERE cu1.nickid=?
208 AND cu1.chan=cu2.chan AND cu1.joined=1 AND cu2.joined=1
209 GROUP BY cu2.chan HAVING c=1 ORDER BY NULL");
210 $nick_chan_delete = $dbh->prepare("DELETE FROM chanuser WHERE nickid=?");
211 $chan_user_partall = $dbh->prepare("UPDATE chanuser SET joined=0 WHERE nickid=?");
212 $get_hostless_nicks = $dbh->prepare("SELECT nick FROM user WHERE vhost='*'");
213
214 $get_squit_lock = $dbh->prepare("LOCK TABLES chanuser WRITE, chanuser AS cu1 READ LOCAL, chanuser AS cu2 READ LOCAL, user WRITE, nickreg WRITE, nickid WRITE, chanban WRITE, chan WRITE, chanreg READ LOCAL, nicktext WRITE");
215 $squit_users = $dbh->prepare("UPDATE chanuser, user
216 SET chanuser.joined=0, user.online=0, user.quittime=UNIX_TIMESTAMP()
217 WHERE user.id=chanuser.nickid AND user.server=?");
218 # Must call squit_nickreg and squit_lastquit before squit_users as it modifies user.online
219 $squit_nickreg = $dbh->prepare("UPDATE nickreg, nickid, user
220 SET nickreg.last=UNIX_TIMESTAMP()
221 WHERE nickreg.id=nickid.nrid AND nickid.id=user.id
222 AND user.online=1 AND user.server=?");
223 =cut
224 $squit_lastquit = $dbh->prepare("UPDATE nickid, user, nicktext
225 SET nicktext.data=?
226 WHERE nicktext.nrid=nickid.nrid AND nickid.id=user.id
227 AND user.online=1 AND user.server=?");
228 =cut
229 $squit_lastquit = $dbh->prepare("REPLACE INTO nicktext ".
230 "SELECT nickid.nrid, ".NTF_QUIT.", 0, '', ? ".
231 "FROM nickid JOIN user ON (nickid.id=user.id) ".
232 "WHERE user.online=1 AND user.server=?");
233 $get_squit_empty_chans = $dbh->prepare("SELECT cu2.chan, COUNT(*) AS c
234 FROM user, chanuser AS cu1, chanuser AS cu2
235 WHERE user.server=? AND cu1.nickid=user.id
236 AND cu1.chan=cu2.chan AND cu1.joined=1 AND cu2.joined=1
237 GROUP BY cu2.chan HAVING c=1 ORDER BY NULL");
238
239 $del_nickchg_id = $dbh->prepare("DELETE FROM nickchg WHERE nickid=?");
240 $add_nickchg = $dbh->prepare("REPLACE INTO nickchg SELECT ?, id, ? FROM user WHERE nick=?");
241 $reap_nickchg = $dbh->prepare("DELETE FROM nickchg WHERE seq<?");
242
243 $get_nick_inval = $dbh->prepare("SELECT nick, inval FROM user WHERE id=?");
244 $inc_nick_inval = $dbh->prepare("UPDATE user SET inval=inval+1 WHERE id=?");
245
246 $is_registered = $dbh->prepare("SELECT 1 FROM nickalias WHERE alias=?");
247 $is_alias_of = $dbh->prepare("SELECT 1 FROM nickalias AS n1 LEFT JOIN nickalias AS n2 ON n1.nrid=n2.nrid
248 WHERE n1.alias=? AND n2.alias=? LIMIT 1");
249
250 $get_guest = $dbh->prepare("SELECT flags & @{[UF_GUEST]} FROM user WHERE nick=?");
251 $set_guest = $dbh->prepare("UPDATE user SET flags = IF(?, flags | @{[UF_GUEST]}, flags & ~@{[UF_GUEST]})
252 WHERE nick=?");
253
254 $get_lock = $dbh->prepare("SELECT GET_LOCK(?, 10)");
255 $release_lock = $dbh->prepare("SELECT RELEASE_LOCK(?)");
256
257 $get_umodes = $dbh->prepare("SELECT modes FROM user WHERE id=?");
258 $set_umodes = $dbh->prepare("UPDATE user SET modes=? WHERE id=?");
259
260 $get_info = $dbh->prepare("SELECT nickreg.email, nickreg.regd, nickreg.last, nickreg.flags, nickreg.ident,
261 nickreg.vhost, nickreg.gecos, nickalias.last
262 FROM nickreg, nickalias WHERE nickalias.nrid=nickreg.id AND nickalias.alias=?");
263 $get_nickreg_quit = $dbh->prepare("SELECT nicktext.data FROM nickreg, nicktext, nickalias
264 WHERE nickalias.nrid=nickreg.id AND nickalias.alias=? AND
265 (nicktext.nrid=nickreg.id AND nicktext.type=".NTF_QUIT.")");
266 $set_ident = $dbh->prepare("UPDATE user SET ident=? WHERE id=?");
267 $set_vhost = $dbh->prepare("UPDATE user SET vhost=? WHERE id=?");
268 $set_ip = $dbh->prepare("UPDATE user SET ip=?, ipv6=? WHERE id=?");
269 $update_regnick_vhost = $dbh->prepare("UPDATE nickreg,nickid SET nickreg.vhost=?
270 WHERE nickreg.id=nickid.nrid AND nickid.id=?");
271 $get_regd_time = $dbh->prepare("SELECT nickreg.regd FROM nickreg, nickalias
272 WHERE nickalias.nrid=nickreg.id and nickalias.alias=?");
273
274 $chk_clone_except = $dbh->prepare("SELECT
275 GREATEST(IF((user.ip >> (32 - sesexip.mask)) = (sesexip.ip >> (32 - sesexip.mask)), sesexip.lim, 0),
276 IF(IF(sesexname.serv, user.server, user.host) LIKE sesexname.host, sesexname.lim, 0)) AS n
277 FROM user, sesexip, sesexname WHERE user.id=? ORDER BY n DESC LIMIT 1");
278 $count_clones = $dbh->prepare("SELECT COUNT(*) FROM user WHERE ip=? AND online=1");
279
280 $get_root_nick = $dbh->prepare("SELECT nickreg.nick FROM nickreg, nickalias WHERE nickreg.id=nickalias.nrid AND nickalias.alias=?");
281 $get_id_nick = $dbh->prepare("SELECT nickreg.nick FROM nickreg WHERE nickreg.id=?");
282 $identify = $dbh->prepare("INSERT INTO nickid SELECT ?, nickalias.nrid FROM nickalias WHERE alias=?");
283 $identify_ign = $dbh->prepare("INSERT IGNORE INTO nickid SELECT ?, nickalias.nrid FROM nickalias WHERE alias=?");
284 $id_update = $dbh->prepare("UPDATE nickreg, user SET
285 nickreg.last=UNIX_TIMESTAMP(), nickreg.ident=user.ident,
286 nickreg.vhost=user.vhost, nickreg.gecos=user.gecos,
287 nickreg.nearexp=0, nickreg.flags = (nickreg.flags & ~". NRF_VACATION .")
288 WHERE nickreg.nick=? AND user.id=?");
289 $logout = $dbh->prepare("DELETE FROM nickid WHERE id=?");
290 $unidentify = $dbh->prepare("DELETE FROM nickid USING nickreg, nickid WHERE nickreg.nick=? AND nickid.nrid=nickreg.id");
291
292 $update_lastseen = $dbh->prepare("UPDATE nickreg,nickid SET nickreg.last=UNIX_TIMESTAMP()
293 WHERE nickreg.id=nickid.nrid AND nickid.id=?");
294 $update_nickalias_last = $dbh->prepare("UPDATE nickalias SET last=UNIX_TIMESTAMP() WHERE alias=?");
295 $quit_update = $dbh->prepare("REPLACE INTO nicktext
296 SELECT nickreg.id, ".NTF_QUIT().", 0, NULL, ? FROM nickreg, nickid
297 WHERE nickreg.id=nickid.nrid AND nickid.id=?");
298
299 $set_protect_level = $dbh->prepare("UPDATE nickalias SET protect=? WHERE alias=?");
300
301
302 $set_email = $dbh->prepare("UPDATE nickreg, nickalias SET nickreg.email=? WHERE nickalias.nrid=nickreg.id AND nickalias.alias=?");
303
304 $set_pass = $dbh->prepare("UPDATE nickreg, nickalias SET nickreg.pass=? WHERE nickalias.nrid=nickreg.id AND nickalias.alias=?");
305
306 $get_register_lock = $dbh->prepare("LOCK TABLES nickalias WRITE, nickreg WRITE");
307 $register = $dbh->prepare("INSERT INTO nickreg SET nick=?, pass=?, email=?, flags=".NRF_HIDEMAIL().", regd=UNIX_TIMESTAMP(), last=UNIX_TIMESTAMP()");
308 $create_alias = $dbh->prepare("INSERT INTO nickalias SELECT id, ?, NULL, NULL FROM nickreg WHERE nick=?");
309
310 $drop = $dbh->prepare("DELETE FROM nickreg WHERE nick=?");
311
312 $get_aliases = $dbh->prepare("SELECT nickalias.alias FROM nickalias, nickreg WHERE
313 nickalias.nrid=nickreg.id AND nickreg.nick=? ORDER BY nickalias.alias");
314 $get_glist = $dbh->prepare("SELECT nickalias.alias, nickalias.protect, nickalias.last
315 FROM nickalias, nickreg WHERE
316 nickalias.nrid=nickreg.id AND nickreg.nick=? ORDER BY nickalias.alias");
317 $count_aliases = $dbh->prepare("SELECT COUNT(*) FROM nickalias, nickreg WHERE
318 nickalias.nrid=nickreg.id AND nickreg.nick=?");
319 $get_random_alias = $dbh->prepare("SELECT nickalias.alias FROM nickalias, nickreg WHERE
320 nickalias.nrid=nickreg.id AND nickreg.nick=? AND nickalias.alias != nickreg.nick LIMIT 1");
321 $delete_alias = $dbh->prepare("DELETE FROM nickalias WHERE alias=?");
322 $delete_aliases = $dbh->prepare("DELETE FROM nickalias USING nickreg, nickalias WHERE
323 nickalias.nrid=nickreg.id AND nickreg.nick=?");
324
325 $get_all_access = $dbh->prepare("SELECT chanacc.chan, chanacc.level, chanacc.adder, chanacc.time FROM nickalias, chanacc WHERE chanacc.nrid=nickalias.nrid AND nickalias.alias=? ORDER BY chanacc.chan");
326 $del_all_access = $dbh->prepare("DELETE FROM chanacc USING chanacc, nickreg WHERE chanacc.nrid=nickreg.id AND nickreg.nick=?");
327
328 $change_root = $dbh->prepare("UPDATE nickreg SET nick=? WHERE nick=?");
329
330 $unlock_tables = $dbh->prepare("UNLOCK TABLES");
331
332 $get_matching_nicks = $dbh->prepare("SELECT nickalias.alias, nickreg.nick, nickreg.ident, nickreg.vhost FROM nickalias, nickreg WHERE nickalias.nrid=nickreg.id AND nickalias.alias LIKE ? AND nickreg.ident LIKE ? AND nickreg.vhost LIKE ? LIMIT 50");
333
334 $cleanup_chanuser = $dbh->prepare("DELETE FROM chanuser USING chanuser
335 LEFT JOIN user ON (chanuser.nickid=user.id) WHERE user.id IS NULL;");
336 $cleanup_nickid = $dbh->prepare("DELETE FROM nickid USING nickid
337 LEFT JOIN user ON(nickid.id=user.id)
338 WHERE user.id IS NULL");
339 $cleanup_users = $dbh->prepare("DELETE FROM user WHERE online=0 AND quittime>0 AND quittime<?");
340 $get_dead_users = $dbh->prepare("SELECT id,nick,time,online,quittime FROM user
341 WHERE online=0 AND quittime>0 AND quittime<?");
342
343 $get_expired = $dbh->prepare("SELECT nickreg.nick, nickreg.email, nickreg.ident, nickreg.vhost
344 FROM nickreg LEFT JOIN nickid ON(nickreg.id=nickid.nrid)
345 LEFT JOIN svsop ON(nickreg.id=svsop.nrid)
346 WHERE nickid.nrid IS NULL AND svsop.nrid IS NULL ".
347 'AND ('.(services_conf_nearexpire ? 'nickreg.nearexp!=0 AND' : '').
348 " ( !(nickreg.flags & " . NRF_HOLD . ") AND !(nickreg.flags & " . NRF_VACATION . ") AND nickreg.last<? ) OR
349 ( (nickreg.flags & " . NRF_VACATION . ") AND nickreg.last<? ) ) OR
350 ( (nickreg.flags & ". NRF_EMAILREG .") AND nickreg.last<?)");
351 $get_near_expired = $dbh->prepare("SELECT nickreg.nick, nickreg.email, nickreg.flags, nickreg.last
352 FROM nickreg LEFT JOIN nickid ON(nickreg.id=nickid.nrid)
353 LEFT JOIN svsop ON(nickreg.id=svsop.nrid)
354 WHERE nickid.nrid IS NULL AND svsop.nrid IS NULL AND nickreg.nearexp=0 AND
355 ( ( !(nickreg.flags & " . NRF_HOLD . ") AND !(nickreg.flags & " . NRF_VACATION . ") AND nickreg.last<? ) OR
356 ( (nickreg.flags & " . NRF_VACATION . ") AND nickreg.last<? )
357 )");
358 $set_near_expired = $dbh->prepare("UPDATE nickreg SET nearexp=1 WHERE nick=?");
359
360 $get_watches = $dbh->prepare("SELECT watch.mask, watch.time
361 FROM watch
362 JOIN nickalias ON (watch.nrid=nickalias.nrid)
363 WHERE nickalias.alias=?");
364 $check_watch = $dbh->prepare("SELECT 1
365 FROM watch
366 JOIN nickalias ON (watch.nrid=nickalias.nrid)
367 WHERE nickalias.alias=? AND watch.mask=?");
368 $set_watch = $dbh->prepare("INSERT INTO watch SELECT nrid, ?, ? FROM nickalias WHERE alias=?");
369 $del_watch = $dbh->prepare("DELETE FROM watch USING watch
370 JOIN nickalias ON (watch.nrid=nickalias.nrid)
371 WHERE nickalias.alias=? AND watch.mask=?");
372 $drop_watch = $dbh->prepare("DELETE FROM watch
373 USING nickreg JOIN watch ON (watch.nrid=nickreg.id)
374 WHERE nickreg.nick=?");
375 $get_silences = $dbh->prepare("SELECT silence.mask, silence.time, silence.expiry, silence.comment
376 FROM silence
377 JOIN nickalias ON (silence.nrid=nickalias.nrid)
378 WHERE nickalias.alias=? ORDER BY silence.time");
379 $check_silence = $dbh->prepare("SELECT 1 FROM silence
380 JOIN nickalias ON (silence.nrid=nickalias.nrid)
381 WHERE nickalias.alias=? AND silence.mask=?");
382 $set_silence = $dbh->prepare("INSERT INTO silence SELECT nrid, ?, ?, ?, ? FROM nickalias WHERE alias=?");
383 $del_silence = $dbh->prepare("DELETE FROM silence USING silence, nickalias
384 WHERE silence.nrid=nickalias.nrid AND nickalias.alias=? AND silence.mask=?");
385 $drop_silence = $dbh->prepare("DELETE FROM silence USING nickreg, silence
386 WHERE silence.nrid=nickreg.id AND nickreg.nick=?");
387 $get_expired_silences = $dbh->prepare("SELECT nickreg.nick, silence.mask, silence.comment
388 FROM nickreg
389 JOIN silence ON (nickreg.id=silence.nrid)
390 WHERE silence.expiry < UNIX_TIMESTAMP() AND silence.expiry!=0 ORDER BY nickreg.nick");
391 $del_expired_silences = $dbh->prepare("DELETE silence.* FROM silence
392 WHERE silence.expiry < UNIX_TIMESTAMP() AND silence.expiry!=0");
393 $get_silence_by_num = $dbh->prepare("SELECT silence.mask, silence.time, silence.expiry, silence.comment
394 FROM silence
395 JOIN nickalias ON (silence.nrid=nickalias.nrid)
396 WHERE nickalias.alias=? ORDER BY silence.time LIMIT 1 OFFSET ?");
397 $get_silence_by_num->bind_param(2, 0, SQL_INTEGER);
398
399 $get_seen = $dbh->prepare("SELECT nickalias.alias, nickreg.nick, nickreg.last FROM nickreg, nickalias
400 WHERE nickalias.nrid=nickreg.id AND nickalias.alias=?");
401
402 $set_greet = $dbh->prepare("REPLACE INTO nicktext SELECT nickreg.id, ".NTF_GREET.", 0, NULL, ?
403 FROM nickreg, nickalias WHERE nickreg.id=nickalias.nrid AND nickalias.alias=?");
404 $get_greet = $dbh->prepare("SELECT nicktext.data FROM nicktext, nickid
405 WHERE nicktext.nrid=nickid.nrid AND nicktext.type=".NTF_GREET." AND nickid.id=?
406 LIMIT 1");
407 $get_greet_nick = $dbh->prepare("SELECT nicktext.data FROM nicktext, nickalias
408 WHERE nicktext.nrid=nickalias.nrid AND nicktext.type=".NTF_GREET." AND nickalias.alias=?");
409 $del_greet = $dbh->prepare("DELETE nicktext.* FROM nicktext, nickreg, nickalias WHERE
410 nicktext.type=".NTF_GREET." AND nickreg.id=nickalias.nrid AND nickalias.alias=?");
411
412 $get_num_nicktext_type = $dbh->prepare("SELECT COUNT(nicktext.id) FROM nicktext, nickalias
413 WHERE nicktext.nrid=nickalias.nrid AND nickalias.alias=? AND nicktext.type=?");
414 $drop_nicktext = $dbh->prepare("DELETE FROM nicktext USING nickreg
415 JOIN nicktext ON (nicktext.nrid=nickreg.id)
416 WHERE nickreg.nick=?");
417
418 $get_auth_chan = $dbh->prepare("SELECT nicktext.data FROM nicktext, nickalias WHERE
419 nicktext.nrid=nickalias.nrid AND nicktext.type=(".NTF_AUTH().") AND nickalias.alias=? AND nicktext.chan=?");
420 $get_auth_num = $dbh->prepare("SELECT nicktext.chan, nicktext.data FROM nicktext, nickalias WHERE
421 nicktext.nrid=nickalias.nrid AND nicktext.type=(".NTF_AUTH().") AND nickalias.alias=? LIMIT 1 OFFSET ?");
422 $get_auth_num->bind_param(2, 0, SQL_INTEGER);
423 $del_auth = $dbh->prepare("DELETE nicktext.* FROM nicktext, nickalias WHERE
424 nicktext.nrid=nickalias.nrid AND nicktext.type=(".NTF_AUTH().") AND nickalias.alias=? AND nicktext.chan=?");;
425 $list_auth = $dbh->prepare("SELECT nicktext.chan, nicktext.data FROM nicktext, nickalias WHERE
426 nicktext.nrid=nickalias.nrid AND nicktext.type=(".NTF_AUTH().") AND nickalias.alias=?");
427
428 $del_nicktext = $dbh->prepare("DELETE nicktext.* FROM nickreg
429 JOIN nickalias ON (nickalias.nrid=nickreg.id)
430 JOIN nicktext ON (nicktext.nrid=nickreg.id)
431 WHERE nicktext.type=? AND nickalias.alias=?");
432
433 $set_umode_ntf = $dbh->prepare("REPLACE INTO nicktext SELECT nickreg.id, ".NTF_UMODE().", 1, ?, NULL
434 FROM nickreg, nickalias WHERE nickreg.id=nickalias.nrid AND nickalias.alias=?");
435 $get_umode_ntf = $dbh->prepare("SELECT nicktext.chan FROM nickreg, nickalias, nicktext
436 WHERE nicktext.type=(".NTF_UMODE().") AND nicktext.nrid=nickalias.nrid AND nickalias.alias=?");
437
438 $set_vacation_ntf = $dbh->prepare("INSERT INTO nicktext SELECT nickreg.id, ".NTF_VACATION().", 0, ?, NULL
439 FROM nickreg, nickalias WHERE nickreg.id=nickalias.nrid AND nickalias.alias=?");
440 $get_vacation_ntf = $dbh->prepare("SELECT nicktext.chan FROM nickalias, nicktext
441 WHERE nicktext.nrid=nickalias.nrid AND nicktext.type=".NTF_VACATION()." AND nickalias.alias=?");
442
443 $set_authcode_ntf = $dbh->prepare("REPLACE INTO nicktext SELECT nickreg.id, ".NTF_AUTHCODE().", 0, '', ?
444 FROM nickreg, nickalias WHERE nickreg.id=nickalias.nrid AND nickalias.alias=?");
445 $get_authcode_ntf = $dbh->prepare("SELECT 1 FROM nickalias, nicktext
446 WHERE nicktext.nrid=nickalias.nrid AND nicktext.type=".NTF_AUTHCODE()." AND nickalias.alias=? AND nicktext.data=?");
447
448 $get_nicks_by_email = $dbh->prepare("SELECT nickreg.nick, nickreg.ident, nickreg.vhost FROM nickreg
449 WHERE nickreg.email LIKE ? GROUP BY nickreg.nick");
450
451 }
452 import SrSv::MySQL::Stub {
453 add_profile_ntf => ['INSERT', "REPLACE INTO nicktext SELECT nickreg.id, @{[NTF_PROFILE]}, 0, ?, ?
454 FROM nickreg JOIN nickalias ON (nickreg.id=nickalias.nrid) WHERE nickalias.alias=?"],
455 get_profile_ntf => ['ARRAY', "SELECT chan, data FROM nicktext
456 JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
457 WHERE nicktext.type=@{[NTF_PROFILE]} AND nickalias.alias=?"],
458 del_profile_ntf => ['NULL', "DELETE nicktext.* FROM nicktext
459 JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
460 WHERE nicktext.type=@{[NTF_PROFILE]} AND nickalias.alias=? AND nicktext.chan=?"],
461 wipe_profile_ntf => ['NULL', "DELETE nicktext.* FROM nicktext
462 JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
463 WHERE nicktext.type=@{[NTF_PROFILE]} AND nickalias.alias=?"],
464 count_profile_ntf => ['SCALAR', "SELECT COUNT(chan) FROM nicktext
465 JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
466 WHERE nicktext.type=@{[NTF_PROFILE]} AND nickalias.alias=?"],
467
468 protect_level => ['SCALAR', 'SELECT protect FROM nickalias WHERE alias=?'],
469 get_pass => ['SCALAR', "SELECT nickreg.pass
470 FROM nickreg JOIN nickalias ON (nickreg.id=nickalias.nrid)
471 WHERE nickalias.alias=?"],
472 get_email => ['SCALAR', "SELECT nickreg.email
473 FROM nickalias JOIN nickreg ON (nickreg.id=nickalias.nrid)
474 WHERE nickalias.alias=?"],
475 count_silences => ['SCALAR', "SELECT COUNT(silence.nrid) FROM silence
476 JOIN nickalias ON (silence.nrid=nickalias.nrid)
477 WHERE nickalias.alias=?"],
478 count_watches => ['SCALAR', "SELECT COUNT(watch.nrid) FROM watch
479 JOIN nickalias ON (watch.nrid=nickalias.nrid)
480 WHERE nickalias.alias=?"],
481
482 add_autojoin_ntf => ['INSERT', "INSERT INTO nicktext
483 SELECT nickreg.id, @{[NTF_JOIN]}, 0, ?, NULL
484 FROM nickreg JOIN nickalias ON (nickreg.id=nickalias.nrid)
485 WHERE nickalias.alias=?"],
486 get_autojoin_ntf => ['COLUMN', "SELECT chan
487 FROM nicktext
488 JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
489 WHERE nicktext.type=@{[NTF_JOIN]} AND nickalias.alias=?"],
490 wipe_autojoin_ntf => ['NULL', "DELETE nicktext.* FROM nickreg
491 JOIN nickalias ON (nickalias.nrid=nickreg.id)
492 JOIN nicktext ON (nicktext.nrid=nickreg.id)
493 WHERE nicktext.type=@{[NTF_JOIN]} AND nickalias.alias=?"],
494 del_autojoin_ntf => ['NULL', "DELETE nicktext.* FROM nickreg
495 JOIN nickalias ON (nickalias.nrid=nickreg.id)
496 JOIN nicktext ON (nicktext.nrid=nickreg.id)
497 WHERE nicktext.type=@{[NTF_JOIN]} AND nickalias.alias=? AND nicktext.chan=?"],
498 check_autojoin_ntf => ['SCALAR', "SELECT 1 FROM nicktext
499 JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
500 WHERE nicktext.type=@{[NTF_JOIN]} AND nickalias.alias=? AND nicktext.chan=?"],
501 get_autojoin_by_num => ['SCALAR', "SELECT nicktext.chan
502 FROM nicktext
503 JOIN nickalias ON (nicktext.nrid=nickalias.nrid)
504 WHERE nicktext.type=@{[NTF_JOIN]} AND nickalias.alias=? LIMIT 1 OFFSET ?"],
505 };
506
507
508 ### NICKSERV COMMANDS ###
509
510 sub ns_ajoin_list($$) {
511 my ($user, $nick) = @_;
512 my @data;
513 my $i = 0;
514 foreach my $chan (get_autojoin_ntf($nick)) {
515 push @data, [++$i, $chan];
516 }
517
518 notice( $user, columnar( {TITLE => "Channels in \002$nick\002's ajoin",
519 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data ) );
520 }
521 sub ns_ajoin_del($$@) {
522 my ($user, $nick, @args) = @_;
523 my ($subj, $obj);
524 if(lc(get_user_nick($user)) eq lc($nick)) {
525 $subj='your';
526 $obj='you';
527 } else {
528 $subj="\002$nick\002\'s";
529 $obj="\002$nick\002";
530 }
531 my @entries;
532 foreach my $arg (@args) {
533 if ($arg =~ /^[0-9\.,-]+$/) {
534 foreach my $num (makeSeqList($arg)) {
535 if(my $chan = get_autojoin_by_num($nick, $num - 1)) {
536 push @entries, $chan;
537 } else {
538 notice($user, "No entry \002#$num\002 was found in $subj ajoin list");
539 }
540 }
541 } elsif($arg =~ /^#.*?,#/) {
542 push @entries, split(',', $arg);
543 } else {
544 push @entries, $arg;
545 }
546 }
547 foreach my $entry (@entries) {
548 if(check_autojoin_ntf($nick, $entry)) {
549 del_autojoin_ntf($nick, $entry);
550 notice($user,"Successfully removed \002$entry\002 from $subj ajoin list.");
551 }
552 else {
553 notice($user, "\002$entry\002 was not in $subj ajoin!");
554 }
555 }
556 }
557 sub ns_ajoin_wipe($$) {
558 my ($user, $nick) = @_;
559 my ($subj, $obj);
560 if(lc(get_user_nick($user)) eq lc($nick)) {
561 $subj='your';
562 $obj='you';
563 } else {
564 $subj="\002$nick\002\'s";
565 $obj="\002$nick\002";
566 }
567 my $count = wipe_autojoin_ntf($nick);
568 if($count) {
569 notice($user,"Successfully wiped \002$count\002 entries from $subj ajoin list.");
570 } else {
571 notice($user,"No entries deleted.");
572 }
573 }
574
575 sub ns_ajoin_join($$) {
576 my ($user, $nick) = @_;
577 #ns_ajoin_list($user, $nick);
578 do_ajoin($user, $nick);
579 }
580
581 sub ns_ajoin($@) {
582 my ($user, @args) = @_;
583 my $nick;
584 my $src = get_user_nick($user);
585 my @chans = grep(/^(#|\d)/, @args);
586 my @parms = grep(!/^(#|\d)/, @args);
587 if(scalar(@parms) > 1) {
588 $nick = shift @parms;
589 } else {
590 $nick = $src;
591 }
592 my $cmd = shift @parms;
593 my ($subj, $obj);
594 if(lc($src) eq lc($nick)) {
595 $subj='Your';
596 $obj='You';
597 } else {
598 $subj="\002$nick\002\'s";
599 $obj="\002$nick\002";
600 }
601
602 my $override = adminserv::can_do($user, 'SERVOP');
603 if(is_identified($user, $nick) || $override) {
604 if(!is_registered($src)) {
605 notice($user, "\002$nick\002 is not registered.");
606 return;
607 }
608 } else {
609 notice($user, "Permission denied for \002$nick\002");
610 return;
611 }
612 if ($cmd =~ /^add$/i) {
613 if(!scalar(@chans)) {
614 notice($user, "Syntax: \002AJOIN ADD #channel\002");
615 notice ($user, "Type \002/msg NickServ HELP AJOIN\002 for more help");
616 }
617 foreach my $chanlist (@chans) {
618 if (defined($chanlist) && $chanlist !~ /^#/) {
619 $chanlist = "#" . $chanlist;
620 }
621 foreach my $chan (split(',', $chanlist)) {
622 if(check_autojoin_ntf($nick, $chan)) {
623 notice ($user, $chan . " is already in $subj ajoin list! ");
624 next;
625 } else {
626 add_autojoin_ntf($chan, $nick);
627 notice($user, "\002$chan\002 added to $subj ajoin.");
628 }
629 }
630 }
631 }
632 elsif ($cmd =~ /^list$/i) {
633 ns_ajoin_list($user, $nick);
634 }
635 elsif ($cmd =~ /^join$/i) {
636 ns_ajoin_join($user, $nick);
637 }
638 elsif ($cmd =~ /^del(ete)?$/i) {
639 ns_ajoin_del($user, $nick, @chans);
640 }
641 elsif ($cmd =~ /^(clear|wipe)$/i) {
642 ns_ajoin_wipe($user, $nick);
643 }
644 else {
645 notice($user,"Syntax: AJOIN ADD/DEL/LIST/WIPE");
646 notice ($user,"Type \002/msg NickServ HELP AJOIN\002 for more help!");
647 }
648 }
649
650 our %high_priority_cmds = (
651 'id' => 1,
652 'identify' => 1,
653 'sid' => 1,
654 'sidentify' => 1,
655 'gidentify' => 1,
656 'ghost' => 1,
657 );
658
659 sub dispatch($$$) {
660 $nsuser = { NICK => $nsnick, ID => ircd::getAgentUuid($nsnick) };
661 my ($user, $dstUser, $msg) = @_;
662 return unless (lc $dstUser->{NICK} eq lc $nsnick);
663 $msg =~ s/^\s+//;
664 my @args = split(/\s+/, $msg);
665 my $cmd = shift @args;
666 get_user_id ($user);
667 my $src = $user->{NICK};
668 $user->{AGENT} = $nsuser;
669 return if flood_check($user);
670 if(!defined($high_priority_cmds{lc $cmd}) &&
671 !adminserv::is_svsop($user) &&
672 $SrSv::IRCd::State::queue_depth > main_conf_highqueue)
673 {
674 notice($user, get_user_agent($user)." is too busy right now. Please try your command again later.");
675 return;
676 }
677
678 if($cmd =~ /^help$/i) {
679 sendhelp($user, 'nickserv', @args)
680 }
681 elsif ($cmd =~ /^ajoin$/i) {
682 ns_ajoin($user, shift @args, @args);
683 }
684 elsif($cmd =~ /^id(entify)?$/i) {
685 if(@args == 1) {
686 ns_identify($user, $src, $args[0]);
687 } elsif(@args == 2) {
688 ns_identify($user, $args[0], $args[1]);
689 } else {
690 notice($user, 'Syntax: IDENTIFY [nick] <password>');
691 }
692 }
693 elsif($cmd =~ /^sid(entify)?$/i) {
694 if(@args == 2) {
695 ns_identify($user, $args[0], $args[1], 1);
696 } else {
697 notice($user, 'Syntax: SIDENTIFY <nick> <password>');
698 }
699 }
700 elsif($cmd =~ /^gid(entify)?$/i) {
701 if(@args == 2) {
702 ns_identify($user, $args[0], $args[1], 2);
703 } else {
704 notice($user, 'Syntax: GIDENTIFY <nick> <password>');
705 }
706 }
707 elsif($cmd =~ /^logout$/i) {
708 ns_logout($user);
709 }
710 elsif($cmd =~ /^release$/i) {
711 if(@args == 1) {
712 ns_release($user, $args[0]);
713 } elsif(@args == 2) {
714 ns_release($user, $args[0], $args[1]);
715 } else {
716 notice($user, 'Syntax: RELEASE <nick> [password]');
717 }
718 }
719 elsif($cmd =~ /^ghost$/i) {
720 if(@args == 1) {
721 ns_ghost($user, $args[0]);
722 } elsif(@args == 2) {
723 ns_ghost($user, $args[0], $args[1]);
724 } else {
725 notice($user, 'Syntax: GHOST <nick> [password]');
726 }
727 }
728 elsif($cmd =~ /^register$/i) {
729 if(@args == 2) {
730 ns_register($user, $args[0], $args[1]);
731 } else {
732 notice($user, 'Syntax: REGISTER <password> <email>');
733 }
734 }
735 elsif($cmd =~ /^(?:link|group)$/i) {
736 if(@args == 2) {
737 ns_link($user, $args[0], $args[1]);
738 } else {
739 notice($user, 'Syntax: LINK <nick> <password>');
740 }
741 }
742 elsif($cmd =~ /^info$/i) {
743 if(@args >= 1) {
744 ns_info($user, @args);
745 } else {
746 notice($user, 'Syntax: INFO <nick> [nick ...]');
747 }
748 }
749 elsif($cmd =~ /^set$/i) {
750 ns_set_parse($user, @args);
751 }
752 elsif($cmd =~ /^(drop|unlink)$/i) {
753 if(@args == 1) {
754 ns_unlink($user, $src, $args[0]);
755 }
756 elsif(@args == 2) {
757 ns_unlink($user, $args[0], $args[1]);
758 }
759 else {
760 notice($user, 'Syntax: UNLINK [nick] <password>');
761 }
762 }
763 elsif($cmd =~ /^dropgroup$/i) {
764 if(@args == 1) {
765 ns_dropgroup($user, $src, $args[0]);
766 }
767 elsif(@args == 2) {
768 ns_dropgroup($user, $args[0], $args[1]);
769 }
770 else {
771 notice($user, 'Syntax: DROPGROUP [nick] <password>');
772 }
773 }
774 elsif($cmd =~ /^chgroot$/i) {
775 if(@args == 1) {
776 ns_changeroot($user, $src, $args[0]);
777 }
778 elsif(@args == 2) {
779 ns_changeroot($user, $args[0], $args[1]);
780 }
781 else {
782 notice($user, 'Syntax: CHGROOT [oldroot] <newroot>');
783 }
784 }
785 elsif($cmd =~ /^sendpass$/i) {
786 if(@args == 1) {
787 ns_sendpass($user, $args[0]);
788 } else {
789 notice($user, 'Syntax: SENDPASS <nick>');
790 }
791 }
792 elsif($cmd =~ /^(?:glist|links)$/i) {
793 if(@args == 0) {
794 ns_glist($user, $src);
795 }
796 elsif(@args >= 1) {
797 ns_glist($user, @args);
798 }
799 else {
800 notice($user, 'Syntax: GLIST [nick] [nick ...]');
801 }
802 }
803 elsif($cmd =~ /^(?:alist|listchans)$/i) {
804 if(@args == 0) {
805 ns_alist($user, $src);
806 }
807 elsif(@args >= 1) {
808 ns_alist($user, @args);
809 }
810 else {
811 notice($user, 'Syntax: ALIST [nick] [nick ...]');
812 }
813 }
814 elsif($cmd =~ /^list$/i) {
815 if(@args == 1) {
816 ns_list($user, $args[0]);
817 } else {
818 notice($user, 'Syntax: LIST <mask>');
819 }
820 }
821 elsif($cmd =~ /^watch$/i) {
822 if ($args[0] =~ /^(add|del|list)$/i) {
823 ns_watch($user, $src, @args);
824 }
825 elsif ($args[1] =~ /^(add|del|list)$/i) {
826 ns_watch($user, @args);
827 }
828 else {
829 notice($user, 'Syntax: WATCH <ADD|DEL|LIST> [nick]');
830 }
831 }
832 elsif($cmd =~ /^silence$/i) {
833 if ($args[0] =~ /^(add|del|list)$/i) {
834 ns_silence($user, $src, @args);
835 }
836 elsif ($args[1] =~ /^(add|del|list)$/i) {
837 ns_silence($user, @args);
838 }
839 else {
840 notice($user, 'Syntax: SILENCE [nick] <ADD|DEL|LIST> [mask] [+expiry] [comment]');
841 }
842 }
843 elsif($cmd =~ /^(acc(ess)?|stat(us)?)$/i) {
844 if (@args >= 1) {
845 ns_acc($user, @args);
846 }
847 else {
848 notice($user, 'Syntax: ACC <nick> [nick ...]');
849 }
850 }
851 elsif($cmd =~ /^seen$/i) {
852 if(@args >= 1) {
853 ns_seen($user, @args);
854 }
855 else {
856 notice($user, 'Syntax: SEEN <nick> [nick ...]');
857 }
858 }
859 elsif($cmd =~ /^recover$/i) {
860 if(@args == 1) {
861 ns_recover($user, $args[0]);
862 } elsif(@args == 2) {
863 ns_recover($user, $args[0], $args[1]);
864 } else {
865 notice($user, 'Syntax: RECOVER <nick> [password]');
866 }
867 }
868 elsif($cmd =~ /^auth$/i) {
869 if (@args >= 1) {
870 ns_auth($user, @args);
871 }
872 else {
873 notice($user, 'Syntax: AUTH [nick] <LIST|ACCEPT|DECLINE> [num|chan]');
874 }
875 }
876 elsif($cmd =~ /^(?:emailreg|(?:auth|email)code)$/i) {
877 if(scalar(@args) >= 2 and scalar(@args) <= 3) {
878 ns_authcode($user, @args);
879 } else {
880 notice($user, 'Syntax: AUTHCODE <nick> <code> [newpassword]');
881 }
882 }
883 elsif($cmd =~ /^profile$/i) {
884 ns_profile($user, @args);
885 }
886 elsif($cmd =~ /^liste?mail/i) {
887 if ($#args == 0) {
888 ns_listemail($user, $args[0]);
889 } else {
890 notice($user, 'Syntax: LISTEMAIL <email@domain.tld>');
891 }
892 }
893 else {
894 notice($user, "Unrecognized command.", "For help, type: \002/msg nickserv help\002");
895 wlog($nsnick, LOG_DEBUG(), "$src tried to use NickServ $msg");
896 }
897 }
898
899 sub ns_identify($$$;$) {
900 my ($user, $nick, $pass, $svsnick) = @_;
901 my $src = get_user_nick($user);
902
903 my $root = get_root_nick($nick);
904 unless($root) {
905 notice($user, 'Your nick is not registered.');
906 return 0;
907 }
908
909 if($svsnick) {
910 if(lc($src) ne lc($nick) and is_online($nick)) {
911 if($svsnick == 2) {
912 ns_ghost($user, $nick, $pass) or return;
913 } else {
914 notice($user, $nick.' is already in use. Please use GHOST, GIDENTIFY or RECOVER');
915 $svsnick = 0;
916 }
917 }
918 if (is_identified($user, $nick)) {
919 if(lc $src eq lc $nick) {
920 notice($user, "Cannot only change case of nick");
921 return;
922 }
923 ircd::svsnick($nsuser, $user, $nick);
924 ircd::setumode($nsuser, $user, '+r');
925 return 1;
926 }
927 }
928 # cannot be an else, note change of $svsnick above.
929 if (!$svsnick and is_identified($user, $nick)) {
930 notice($user, 'You are already identified for nick '.$nick.'.');
931 return 0;
932 }
933
934 my $flags = nr_get_flags($root);
935
936 if($flags & NRF_FREEZE) {
937 notice($user, "This nick has been frozen and may not be used.", $err_deny);
938 services::ulog($nsnick, LOG_INFO(), "\00305attempted to identify to frozen nick \003\002$nick\002", $user);
939 return;
940 }
941
942 if($flags & NRF_EMAILREG) {
943 notice($user, "This nick is awaiting an email validation code. Please check your email for instructions.");
944 return;
945 }
946
947 elsif($flags & NRF_SENDPASS) {
948 notice($user, "This nick is awaiting a SENDPASS authentication code. Please check your email for instructions.");
949 return;
950 }
951
952 my $uid = get_user_id($user);
953 unless(chk_pass($root, $pass, $user)) {
954 if(inc_nick_inval($user)) {
955 notice($user, $err_pass);
956 }
957 services::ulog($nsnick, LOG_INFO(), "failed to identify to nick $nick (root: $root)", $user);
958 return 0;
959 }
960
961 return do_identify($user, $nick, $root, $flags, $svsnick);
962 }
963
964 sub ns_logout($) {
965 my ($user) = @_;
966 my $uid = get_user_id($user);
967
968 $update_lastseen->execute($uid);
969 $logout->execute($uid);
970 delete($user->{NICKFLAGS});
971 ircd::nolag($nsnick, '-', $user);
972 notice($user, 'You are now logged out');
973 services::ulog($nsnick, LOG_INFO(), "used NickServ LOGOUT", $user);
974 }
975
976 sub ns_release($$;$) {
977 my ($user, $nick, $pass) = @_;
978
979 if(nr_chk_flag($nick, NRF_FREEZE)) {
980 notice($user, "This nick has been frozen and may not be used.", $err_deny);
981 services::ulog($nsnick, LOG_INFO(), "\00305attempted to release frozen nick \003\002$nick\002", $user);
982 return;
983 }
984
985 unless(is_identified($user, $nick)) {
986 if($pass) {
987 my $s = ns_identify($user, $nick, $pass);
988 return if($s == 0); #failed to identify
989 if($s == 1) {
990 notice($user, "Nick $nick is not being held.");
991 return;
992 }
993 } else {
994 notice($user, $err_deny);
995 return;
996 }
997 }
998 elsif(enforcer_quit($nick)) {
999 notice($user, 'Your nick has been released from custody.');
1000 } else {
1001 notice($user, "Nick $nick is not being held.");
1002 }
1003 }
1004
1005 sub ns_ghost($$;$) {
1006
1007 my @ghostbusters_quotes = (
1008 'Ray. If someone asks if you are a god, you say, "yes!"',
1009 'I feel like the floor of a taxicab.',
1010 'I don\'t have to take this abuse from you, I\'ve got hundreds of people dying to abuse me.',
1011 'He slimed me.',
1012 'This chick is *toast*.',
1013 '"Where do these stairs go?" "They go up."',
1014 '"That\'s the bedroom, but nothing ever happened in there." "What a crime."',
1015 'NOBODY steps on a church in my town.',
1016 'Whoa, whoa, whoa! Nice shootin\', Tex!',
1017 'It\'s the Stay Puft Marshmallow Man.',
1018 '"Symmetrical book stacking. Just like the Philadelphia mass turbulence of 1947." "You\'re right, no human being would stack books like this."',
1019 '"Egon, this reminds me of the time you tried to drill a hole through your head. Remember that?" "That would have worked if you hadn\'t stopped me."',
1020 '"Ray has gone bye-bye, Egon... what\'ve you got left?" "Sorry, Venkman, I\'m terrified beyond the capacity for rational thought."',
1021 'Listen! Do you smell something?',
1022 'As they say in T.V., I\'m sure there\'s one big question on everybody\'s mind, and I imagine you are the man to answer that. How is Elvis, and have you seen him lately?',
1023 '"You know, you don\'t act like a scientist." "They\'re usually pretty stiff." "You\'re more like a game show host."',
1024 );
1025 my ($user, $nick, $pass) = @_;
1026 my $src = get_user_nick($user);
1027
1028 if(nr_chk_flag($nick, NRF_FREEZE)) {
1029 notice($user, "This nick has been frozen and may not be used.", $err_deny);
1030 services::ulog($nsnick, LOG_INFO(), "\00305attempted to ghost frozen nick \003\002$nick\002", $user);
1031 return 0;
1032 }
1033
1034 unless(is_identified($user, $nick)) {
1035 if($pass) {
1036 my $s = ns_identify($user, $nick, $pass);
1037 return 0 if($s == 0); #failed to identify
1038 } else {
1039 notice($user, $err_deny);
1040 return 0;
1041 }
1042 }
1043
1044 if(!is_online($nick)) {
1045 notice($user, "\002$nick\002 is not online");
1046 return 0;
1047 } elsif(lc $src eq lc $nick) {
1048 notice($user, "I'm sorry, $src, I'm afraid I can't do that.");
1049 return 0;
1050
1051 } else {
1052 my $ghostbusters = @ghostbusters_quotes[int rand(scalar(@ghostbusters_quotes))];
1053 my $baduser = {NICK => $nick};
1054 get_user_id ($baduser);
1055 ircd::irckill($nsuser, $baduser, "GHOST command used by $src ($ghostbusters)");
1056 notice($user, "Your ghost has been disconnected");
1057 services::ulog($nsnick, LOG_INFO(), "used NickServ GHOST on $nick", $user);
1058 #nick_delete($nick);
1059 return 1;
1060 }
1061 }
1062
1063 sub ns_register($$$) {
1064 my ($user, $pass, $email) = @_;
1065 my $src = get_user_nick($user);
1066
1067 if($src =~ /^guest/i) {
1068 notice($user, $err_deny);
1069 return;
1070 }
1071
1072 unless(validate_email($email)) {
1073 notice($user, $err_email);
1074 return;
1075 }
1076
1077 if ($pass =~ /pass/i) {
1078 notice($user, 'Try a more secure password.');
1079 return;
1080 }
1081
1082 my $uid = get_user_id($user);
1083
1084 $get_register_lock->execute; $get_register_lock->finish;
1085
1086 if(not is_registered($src)) {
1087 $register->execute($src, hash_pass($pass), $email); $register->finish();
1088 $create_alias->execute($src, $src); $create_alias->finish;
1089 if (defined(services_conf_default_protect)) {
1090 $set_protect_level->execute((defined(services_conf_default_protect) ?
1091 $protect_level{lc services_conf_default_protect} : 1), $src);
1092 $set_protect_level->finish();
1093 }
1094 $unlock_tables->execute; $unlock_tables->finish;
1095
1096 if(services_conf_validate_email) {
1097 nr_set_flag($src, NRF_EMAILREG());
1098 authcode($src, 'emailreg', $email);
1099 notice($user, "Your registration is not yet complete.",
1100 "Your nick will expire within ".
1101 (services_conf_validate_expire == 1 ? '24 hours' : services_conf_validate_expire.' days').
1102 " if you do not enter the validation code.",
1103 "Check your email for further instructions.");
1104 }
1105 else {
1106 $identify->execute($uid, $src); $identify->finish();
1107 notice($user, 'You are now registered and identified.');
1108 ircd::setumode($nsuser, $user, '+r');
1109 }
1110
1111 $id_update->execute($src, $uid); $id_update->finish();
1112 services::ulog($nsuser, LOG_INFO(), "registered $src (email: $email)".
1113 (services_conf_validate_email ? ' requires email validation code' : ''),
1114 $src);
1115 } else {
1116 $unlock_tables->execute; $unlock_tables->finish;
1117 notice($user, 'Your nickname has already been registered.');
1118 }
1119 }
1120
1121 sub ns_link($$$) {
1122 my ($user, $nick, $pass) = @_;
1123
1124 my $root = get_root_nick($nick);
1125 my $src = get_user_nick($user);
1126 my $uid = get_user_id($user);
1127
1128 if($src =~ /^guest/i) {
1129 notice($user, $err_deny);
1130 return;
1131 }
1132
1133 unless (is_registered($nick)) {
1134 if(is_registered($src)) {
1135 notice($user, "The nick \002$nick\002 is not registered. You need to change your nick to \002$nick\002 and then link to \002$src\002.");
1136 } else { # if neither $nick nor $src are registered
1137 notice($user, "You need to register your nick first. For help, type \002/ns help register");
1138 }
1139 return;
1140 }
1141
1142 unless(chk_pass($root, $pass, $user)) {
1143 notice($user, $err_pass);
1144 return;
1145 }
1146
1147 if(nr_chk_flag($nick, NRF_FREEZE) and (lc $pass ne 'force')) {
1148 notice($user, "\002$root\002 has been frozen and may not be used.");
1149 return;
1150 }
1151
1152 if(is_alias_of($src, $nick)) {
1153 notice($user, "\002$nick\002 is already linked to \002$src\002.");
1154 return;
1155 }
1156
1157 $get_register_lock->execute; $get_register_lock->finish;
1158
1159 if(is_registered($src)) {
1160 $unlock_tables->execute; $unlock_tables->finish;
1161
1162 if(is_identified($user, $src)) {
1163 notice($user, "You cannot link an already registered nick. Type this and try again: \002/ns drop $src <password>");
1164 return;
1165 } else {
1166 notice($user, 'Your nickname has already been registered.');
1167 return;
1168 }
1169 } else {
1170 $create_alias->execute($src, $root); $create_alias->finish();
1171 if (defined(services_conf_default_protect)) {
1172 $set_protect_level->execute((defined(services_conf_default_protect) ?
1173 $protect_level{lc services_conf_default_protect} : 1), $src);
1174 $set_protect_level->finish();
1175 }
1176 $unlock_tables->execute; $unlock_tables->finish;
1177
1178 if(is_identified($user, $root)) {
1179 $identify_ign->execute($uid, $root); $identify_ign->finish();
1180 $id_update->execute($root, $uid); $id_update->finish();
1181 } else {
1182 ns_identify($user, $root, $pass);
1183 }
1184 }
1185
1186 notice($user, "\002$src\002 is now linked to \002$root\002.");
1187 services::ulog($nsnick, LOG_INFO(), "made $src an alias of $root.", $user);
1188
1189 check_identify($user);
1190 }
1191
1192 sub ns_unlink($$$) {
1193 my ($user, $nick, $pass) = @_;
1194 my $uid = get_user_id($user);
1195 my $src = get_user_nick($user);
1196
1197 my $root = get_root_nick($nick);
1198 unless(chk_pass($root, $pass, $user)) {
1199 notice($user, $err_pass);
1200 return;
1201 }
1202
1203 if(nr_chk_flag($nick, NRF_FREEZE) and (lc $pass ne 'force')) {
1204 notice($user, "\002$root\002 has been frozen and may not be used.", $err_deny);
1205 services::ulog($nsnick, LOG_INFO(), "\00305attempted to unlink \002$nick\002 from frozen nick \002$root\002", $user);
1206 return;
1207 }
1208
1209 if(lc $root eq lc $nick) {
1210 $count_aliases->execute($root);
1211 my ($count) = $count_aliases->fetchrow_array;
1212 if($count == 1) {
1213 ns_dropgroup_real($user, $root);
1214 return;
1215 }
1216
1217 $get_random_alias->execute($root);
1218 my ($new) = $get_random_alias->fetchrow_array;
1219 ns_changeroot($user, $root, $new, 1);
1220
1221 $root = $new;
1222 }
1223
1224 unidentify_single($nick);
1225 delete_alias($nick);
1226 enforcer_quit($nick);
1227
1228 notice($user, "\002$nick\002 has been unlinked from \002$root\002.");
1229 services::ulog($nsnick, LOG_INFO(), "removed alias $nick from $root.", $user);
1230 }
1231
1232 sub ns_dropgroup($$$) {
1233 my ($user, $nick, $pass) = @_;
1234 my $uid = get_user_id($user);
1235 my $src = get_user_nick($user);
1236 my $root = get_root_nick($nick);
1237
1238 if(adminserv::get_svs_level($root)) {
1239 notice($user, "A nick with services access may not be dropped.");
1240 return;
1241 }
1242
1243 unless(chk_pass($root, $pass, $user)) {
1244 notice($user, $err_pass);
1245 return;
1246 }
1247
1248 if(nr_chk_flag($nick, NRF_FREEZE) and (lc $pass ne 'force')) {
1249 notice($user, "This nick has been frozen and may not be used.", $err_deny);
1250 services::ulog($nsnick, LOG_INFO(), "\00305attempted to dropgroup frozen nick \002$root\002", $user);
1251 return;
1252 }
1253
1254 ns_dropgroup_real($user, $root);
1255 }
1256
1257 sub ns_dropgroup_real($$) {
1258 my ($user, $root) = @_;
1259 my $src = get_user_nick($user);
1260
1261 unidentify($root, "Your nick, \002$root\002, was dropped by \002$src\002.", $src);
1262 dropgroup($root);
1263 #enforcer_quit($nick);
1264 notice($user, "Your nick(s) have been dropped. Thanks for playing.");
1265
1266 services::ulog($nsnick, LOG_INFO(), "dropped group $root.", $user);
1267 }
1268
1269 sub ns_changeroot($$$;$) {
1270 my ($user, $old, $new, $force) = @_;
1271
1272 $force or chk_identified($user, $old) or return;
1273
1274 my $root = get_root_nick($old);
1275
1276 if(lc($new) eq lc($root)) {
1277 notice($user, "\002$root\002 is already your root nick.");
1278 return;
1279 }
1280
1281 unless(get_root_nick($new) eq $root) {
1282 notice($user, "\002$new\002 is not an alias of your nick. Type \002/msg nickserv help link\002 for information about creating aliases.");
1283 return;
1284 }
1285
1286 changeroot($root, $new);
1287
1288 notice($user, "Your root nick is now \002$new\002.");
1289 services::ulog($nsnick, LOG_INFO(), "changed root $root to $new.", $user);
1290 }
1291
1292 sub ns_info($@) {
1293 my ($user, @nicks) = @_;
1294
1295 foreach my $nick (@nicks) {
1296 my $root = get_root_nick($nick);
1297
1298 $get_info->execute($nick);
1299 my @result = $get_info->fetchrow_array;
1300 $get_info->finish();
1301
1302 unless(@result) {
1303 notice($user, "The nick \002$nick\002 is not registered.");
1304 next;
1305 }
1306
1307 my ($email, $regd, $last, $flags, $ident, $vhost, $gecos, $alias_used) = @result;
1308 # the quit entry might not exist if the user hasn't quit yet.
1309 $get_nickreg_quit->execute($nick);
1310 my ($quit) = $get_nickreg_quit->fetchrow_array(); $get_nickreg_quit->finish();
1311 my $hidemail = $flags & NRF_HIDEMAIL;
1312
1313 $get_greet_nick->execute($nick);
1314 my ($greet) = $get_greet_nick->fetchrow_array(); $get_greet_nick->finish();
1315 $get_umode_ntf->execute($nick);
1316 my ($umode) = $get_umode_ntf->fetchrow_array(); $get_umode_ntf->finish();
1317
1318 my $svslev = adminserv::get_svs_level($root);
1319 my $protect = protect_level($nick);
1320 my $showprivate = (is_identified($user, $nick) or
1321 adminserv::is_svsop($user, adminserv::S_HELP()));
1322
1323 my ($seens, $seenm) = do_seen($nick);
1324
1325 my @data;
1326
1327 push @data, {FULLROW=>"(Online now, $seenm.)"} if $seens == 2;
1328 push @data, ["Last seen:", "$seenm."] if $seens == 1;
1329
1330 push @data,
1331 ["Last seen address:", "$ident\@$vhost"],
1332 ["Registered:", gmtime2($regd)];
1333 push @data, ["Last used:", ($alias_used ? gmtime2($alias_used) : 'Unknown')] if $showprivate;
1334 push @data, ["Last real name:", $gecos];
1335
1336 push @data, ["Services Rank:", $adminserv::levels[$svslev]]
1337 if $svslev;
1338 push @data, ["E-mail:", $email] unless $hidemail;
1339 push @data, ["E-mail:", "$email (Hidden)"]
1340 if($hidemail and $showprivate);
1341 push @data, ["Alias of:", $root]
1342 if ((lc $root ne lc $nick) and $showprivate);
1343
1344 my @extra;
1345
1346 push @extra, "Last quit: $quit" if $quit;
1347 push @extra, $protect_long[$protect] if $protect;
1348 push @extra, "Does not accept memos." if($flags & NRF_NOMEMO);
1349 push @extra, "Cannot be added to channel access lists." if($flags & NRF_NOACC);
1350 push @extra, "Will not be automatically opped in channels." if($flags & NRF_NEVEROP);
1351 push @extra, "Requires authorization to be added to channel access lists."
1352 if($flags & NRF_AUTH);
1353 push @extra, "Is frozen and may not be used." if($flags & NRF_FREEZE);
1354 push @extra, "Will not expire." if($flags & NRF_HOLD);
1355 push @extra, "Is currently on vacation." if($flags & NRF_VACATION);
1356 push @extra, "Registration pending email-code verification." if($flags & NRF_EMAILREG);
1357 push @extra, "UModes on Identify: ".$umode if ($umode and $showprivate);
1358 push @extra, "Greeting: ".$greet if ($greet and $showprivate);
1359 push @extra, "Disabled highlighting of alternating lines." if ($flags & NRF_NOHIGHLIGHT);
1360
1361 notice($user, columnar({TITLE => "NickServ info for \002$nick\002:",
1362 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)},
1363 @data, {COLLAPSE => \@extra, BULLET => 1}));
1364 }
1365 }
1366
1367 sub ns_set_parse($@) {
1368 my ($user, @parms) = @_;
1369 my $src = get_user_nick($user);
1370 # This is a new NS SET parser
1371 # required due to it's annoying syntax
1372 #
1373 # Most commands have only 2 params at most
1374 # the target (which is implied to be src when not spec'd)
1375 # However in the case of GREET num-params is unbounded
1376 #
1377 # Alternative parsings would be possible,
1378 # one being to use a regexp for valid set/keys
1379 if (lc($parms[1]) eq 'greet') {
1380 ns_set($user, @parms);
1381 }
1382 elsif(lc($parms[0]) eq 'greet') {
1383 ns_set($user, $src, @parms);
1384 }
1385 else {
1386 if(@parms == 2) {
1387 ns_set($user, $src, $parms[0], $parms[1]);
1388 }
1389 elsif(@parms == 3) {
1390 ns_set($user, $parms[0], $parms[1], $parms[2]);
1391 }
1392 else {
1393 notice($user, 'Syntax: SET [nick] <option> <value>');
1394 return;
1395 }
1396 }
1397 }
1398
1399 sub ns_set($$$$) {
1400 my ($user, $target, $set, @parms) = @_;
1401 my $src = get_user_nick($user);
1402 my $override = (adminserv::can_do($user, 'SERVOP') or
1403 (adminserv::can_do($user, 'FREEZE') and $set =~ /^freeze$/i) ? 1 : 0);
1404
1405 unless(is_registered($target)) {
1406 notice($user, "\002$target\002 is not registered.");
1407 return;
1408 }
1409 unless(is_identified($user, $target) or $override) {
1410 notice($user, $err_deny);
1411 return;
1412 }
1413
1414 unless (
1415 $set =~ /^protect$/i or
1416 $set =~ /^e?-?mail$/i or
1417 $set =~ /^pass(?:w(?:or)?d)?$/i or
1418 $set =~ /^hidee?-?mail$/i or
1419 $set =~ /^nomemo$/i or
1420 $set =~ /^no(?:acc|op)$/i or
1421 $set =~ /^neverop$/i or
1422 $set =~ /^auth$/i or
1423 $set =~ /^(hold|no-?expire)$/i or
1424 $set =~ /^freeze$/i or
1425 $set =~ /^vacation$/i or
1426 $set =~ /^greet$/i or
1427 $set =~ /^u?modes?$/i or
1428 $set =~ /^(email)?reg$/i or
1429 $set =~ /^nohighlight$/i or
1430 $set =~ /^(?:(?:chg)?root|display)$/i
1431 ) {
1432 notice($user, qq{"$set" is not a valid NickServ setting.});
1433 return;
1434 }
1435
1436 my ($subj, $obj);
1437 if($src eq $target) {
1438 $subj='Your';
1439 $obj='You';
1440 } else {
1441 $subj="\002$target\002\'s";
1442 $obj="\002$target\002";
1443 }
1444 delete($user->{NICKFLAGS});
1445
1446 if($set =~ /^protect$/i) {
1447 my $level = $protect_level{lc shift @parms};
1448 unless (defined($level)) {
1449 notice($user, "Syntax: SET PROTECT <none|normal|high|kill>");
1450 return;
1451 }
1452
1453 $set_protect_level->execute($level, $target);
1454 notice($user, "$subj protection level is now set to \002".$protect_short[$level]."\002. ".$protect_long[$level]);
1455
1456 return;
1457 }
1458
1459 elsif($set =~ /^e?-?mail$/i) {
1460 unless(@parms == 1) {
1461 notice($user, 'Syntax: SET EMAIL <address>');
1462 return;
1463 }
1464 my $email = $parms[0];
1465
1466 unless(validate_email($email)) {
1467 notice($user, $err_email);
1468 return;
1469 }
1470
1471 $set_email->execute($email, $target);
1472 notice($user, "$subj email address has been changed to \002$email\002.");
1473 services::ulog($nsnick, LOG_INFO(), "changed email of \002$target\002 to $email", $user);
1474
1475 return;
1476 }
1477
1478 elsif($set =~ /^pass(?:w(?:or)?d)?$/i) {
1479 unless(@parms == 1) {
1480 notice($user, 'Syntax: SET PASSWD <address>');
1481 return;
1482 }
1483 if($parms[0] =~ /pass/i) {
1484 notice($user, 'Try a more secure password.');
1485 }
1486
1487 $set_pass->execute(hash_pass($parms[0]), $target);
1488 notice($user, "$subj password has been changed.");
1489 services::ulog($nsnick, LOG_INFO(), "changed password of \002$target\002", $user);
1490 if(nr_chk_flag($target, NRF_SENDPASS())) {
1491 $del_nicktext->execute(NTF_AUTHCODE, $target); $del_nicktext->finish();
1492 nr_set_flag($target, NRF_SENDPASS(), 0);
1493 }
1494
1495 return;
1496 }
1497
1498 elsif($set =~ /^greet$/i) {
1499 unless(@parms) {
1500 notice($user, 'Syntax: SET [nick] GREET <NONE|greeting>');
1501 return;
1502 }
1503
1504 my $greet = join(' ', @parms);
1505 if ($greet =~ /^(none|off)$/i) {
1506 $del_greet->execute($target);
1507 notice($user, "$subj greet has been deleted.");
1508 services::ulog($nsnick, LOG_INFO(), "deleted greet of \002$target\002", $user);
1509 }
1510 else {
1511 $set_greet->execute($greet, $target);
1512 notice($user, "$subj greet has been set to \002$greet\002");
1513 services::ulog($nsnick, LOG_INFO(), "changed greet of \002$target\002", $user);
1514 }
1515
1516 return;
1517 }
1518 elsif($set =~ /^u?modes?$/i) {
1519 unless(@parms == 1) {
1520 notice($user, 'Syntax: SET UMODE <+modes-modes|none>');
1521 return;
1522 }
1523
1524 if (lc $parms[0] eq 'none') {
1525 $del_nicktext->execute(NTF_UMODE, $target); $del_nicktext->finish();
1526 notice($user, "$obj will not receive any automatic umodes.");
1527 }
1528 else {
1529 my ($modes, $rejected) = modes::allowed_umodes($parms[0]);
1530 $del_nicktext->execute(NTF_UMODE, $target); $del_nicktext->finish(); # don't allow dups
1531 $set_umode_ntf->execute($modes, $target); $set_umode_ntf->finish();
1532 foreach my $usernick (get_nick_user_nicks $target) {
1533 ircd::setumode($nsuser, $user, $modes)
1534 }
1535
1536 my @out;
1537 push @out, "Cannot set these umodes: " . $rejected if $rejected;
1538 push @out, "$subj automatic umodes have been set to: \002" . ($modes ? $modes : 'none');
1539 notice($user, @out);
1540 }
1541 return;
1542 }
1543 elsif($set =~ /^(?:(?:chg)?root|display)$/i) {
1544 ns_changeroot($user, $target, $parms[0], $override);
1545 return;
1546 }
1547
1548 my $val;
1549 if($parms[0] =~ /^(?:no|off|false|0)$/i) { $val = 0; }
1550 elsif($parms[0] =~ /^(?:yes|on|true|1)$/i) { $val = 1; }
1551 else {
1552 notice($user, "Please say \002on\002 or \002off\002.");
1553 return;
1554 }
1555
1556 if($set =~ /^hidee?-?mail$/i) {
1557 nr_set_flag($target, NRF_HIDEMAIL, $val);
1558
1559 if($val) {
1560 notice($user, "$subj email address is now hidden.");
1561 } else {
1562 notice($user, "$subj email address is now visible.");
1563 }
1564
1565 return;
1566 }
1567
1568 if($set =~ /^nomemo$/i) {
1569 nr_set_flag($target, NRF_NOMEMO, $val);
1570
1571 if($val) {
1572 notice($user, "$subj memos will be blocked.");
1573 } else {
1574 notice($user, "$subj memos will be delivered.");
1575 }
1576
1577 return;
1578 }
1579
1580 if($set =~ /^no(?:acc|op)$/i) {
1581 nr_set_flag($target, NRF_NOACC, $val);
1582
1583 if($val) {
1584 notice($user, "$obj may not be added to channel access lists.");
1585 } else {
1586 notice($user, "$obj may be added to channel access lists.");
1587 }
1588
1589 return;
1590 }
1591
1592 if($set =~ /^neverop$/i) {
1593 nr_set_flag($target, NRF_NEVEROP, $val);
1594
1595 if($val) {
1596 notice($user, "$obj will not be granted status upon joining channels.");
1597 } else {
1598 notice($user, "$obj will be granted status upon joining channels.");
1599 }
1600
1601 return;
1602 }
1603
1604 if($set =~ /^auth$/i) {
1605 nr_set_flag($target, NRF_AUTH, $val);
1606
1607 if($val) {
1608 notice($user, "$obj must now authorize additions to channel access lists.");
1609 } else {
1610 notice($user, "$obj will not be asked to authorize additions to channel access lists.");
1611 }
1612
1613 return;
1614 }
1615
1616 if($set =~ /^(hold|no-?expire)$/i) {
1617 unless (adminserv::can_do($user, 'SERVOP') or
1618 is_identified($user, $target) and adminserv::is_ircop($user))
1619 {
1620 notice($user, $err_deny);
1621 return;
1622 }
1623
1624 nr_set_flag($target, NRF_HOLD, $val);
1625
1626 if($val) {
1627 notice($user, "\002$target\002 is now held from expiration.");
1628 services::ulog($nsnick, LOG_INFO(), "has held \002$target\002", $user);
1629 } else {
1630 notice($user, "\002$target\002 will now expire normally.");
1631 services::ulog($nsnick, LOG_INFO(), "released \002$target\002 from hold", $user);
1632 }
1633
1634 return;
1635 }
1636
1637 if($set =~ /^freeze$/i) {
1638 unless (adminserv::can_do($user, 'FREEZE') or
1639 is_identified($user, $target) and adminserv::is_ircop($user))
1640 {
1641 notice($user, $err_deny);
1642 return;
1643 }
1644
1645 nr_set_flag($target, NRF_FREEZE, $val);
1646
1647 if($val) {
1648 notice($user, "\002$target\002 is now frozen.");
1649 unidentify($target, "Your nick, \002$target\002, has been frozen and may no longer be used.");
1650 services::ulog($nsnick, LOG_INFO(), "froze \002$target\002", $user);
1651 } else {
1652 notice($user, "\002$target\002 is no longer frozen.");
1653 services::ulog($nsnick, LOG_INFO(), "unfroze \002$target\002", $user);
1654 }
1655
1656 return;
1657 }
1658
1659 if($set =~ /^vacation$/i) {
1660 if ($val) {
1661 $get_regd_time->execute($target);
1662 my ($regd) = $get_regd_time->fetchrow_array;
1663 $get_regd_time->finish();
1664
1665 if(($regd > (time() - 86400 * int(services_conf_vacationexpire / 3))) and !$override) {
1666 notice($user, "$target is not old enough to use VACATION",
1667 'Minimum age is '.int(services_conf_vacationexpire / 3).' days');
1668 return;
1669 }
1670
1671 $get_vacation_ntf->execute($target);
1672 my ($last_vacation) = $get_vacation_ntf->fetchrow_array();
1673 $get_vacation_ntf->finish();
1674 if(defined($last_vacation)) {
1675 $last_vacation = unpack('N', MIME::Base64::decode($last_vacation));
1676 if ($last_vacation > (time() - 86400 * int(services_conf_vacationexpire / 3)) and !$override) {
1677 notice($user, "I'm sorry, \002$src\002, I'm afraid I can't do that.",
1678 "Last vacation ended ".gmtime2($last_vacation),
1679 'Minimum time between vacations is '.int(services_conf_vacationexpire / 3).' days.');
1680 return;
1681 }
1682 }
1683 }
1684
1685 nr_set_flag($target, NRF_VACATION, $val);
1686
1687 services::ulog($nsnick, LOG_INFO(),
1688 ($val ? 'enabled' : 'disabled')." vacation mode for \002$target\002", $user);
1689 notice($user, "Vacation mode ".($val ? 'enabled' : 'disabled')." for \002$target\002");
1690 return;
1691 }
1692
1693 if($set =~ /^(email)?reg$/i) {
1694 unless (adminserv::can_do($user, 'SERVOP'))
1695 {
1696 notice($user, $err_deny);
1697 return;
1698 }
1699
1700 nr_set_flag($target, NRF_EMAILREG, $val);
1701
1702 if($val) {
1703 authcode($target, 'emailreg');
1704 notice($user, "\002$target\002 now needs an email validation code.");
1705 unidentify($target, ["Your nick, \002$target\002, has been flagged for an email validation audit.",
1706 "Your nick will expire within 24 hours if you do not enter the validation code.",
1707 "Check your email for further instructions."]);
1708 services::ulog($nsnick, LOG_INFO(), "requested an email audit for \002$target\002", $user);
1709 } else {
1710 $del_nicktext->execute(NTF_AUTHCODE, $target); $del_nicktext->finish();
1711 notice($user, "\002$target\002 is now fully registered.");
1712 services::ulog($nsnick, LOG_INFO(), "validated the email for \002$target\002", $user);
1713 }
1714
1715 return;
1716 }
1717
1718 if($set =~ /^nohighlight$/i) {
1719 nr_set_flag($target, NRF_NOHIGHLIGHT, $val);
1720
1721 if($val) {
1722 notice($user, "$obj will no longer have alternative highlighting of lists.");
1723 } else {
1724 notice($user, "$obj will have alternative highlighting of lists.");
1725 }
1726
1727 return;
1728 }
1729
1730 }
1731
1732 sub ns_sendpass($$) {
1733 my ($user, $nick) = @_;
1734
1735 unless(adminserv::is_svsop($user, adminserv::S_HELP() )) {
1736 notice($user, $err_deny);
1737 return;
1738 }
1739
1740 my $email = get_email($nick);
1741
1742 unless($email) {
1743 notice($user, "\002$nick\002 is not registered or does not have an email address.");
1744 return;
1745 }
1746
1747 my $pass = get_pass($nick);
1748 if ($pass and !is_hashed($pass)) {
1749 send_email($email, "$nsnick Password Reminder",
1750 "The password for the nick $nick is:\n$pass");
1751 notice($user, "Password for \002$nick\002 has been sent to \002$email\002.");
1752 } else {
1753 authcode($nick, 'sendpass', $email);
1754 nr_set_flag($nick, NRF_SENDPASS);
1755 notice($user, "Password authentication code for \002$nick\002 has been sent to \002$email\002.");
1756 }
1757
1758 services::ulog($nsnick, LOG_INFO(), "used SENDPASS on $nick ($email)", $user);
1759 }
1760
1761 sub ns_glist($@) {
1762 my ($user, @targets) = @_;
1763
1764 foreach my $target (@targets) {
1765 my $root = get_root_nick($target);
1766 unless($root) {
1767 notice $user, "\002$target\002 is not registered.";
1768 next;
1769 }
1770
1771 unless(is_identified($user, $target) or
1772 adminserv::is_svsop($user, adminserv::S_HELP())
1773 ) {
1774 notice $user, "$target: $err_deny";
1775 next;
1776 }
1777
1778 my @data;
1779 $get_glist->execute($root);
1780 while(my ($alias, $protect, $last) = $get_glist->fetchrow_array) {
1781 my $time_ago;
1782 if(0) {
1783 # This needs a new NS GLIST cmd, like NS GLISTA or something.
1784 # The idea is a command that shows the long version of the time_ago.
1785 $time_ago = time_ago($last, 1);
1786 } else {
1787 $time_ago = time_ago($last);
1788 }
1789 push @data, ["\002$alias\002", "Protect: $protect_short[$protect]",
1790 ($last ? "Last used $time_ago ago" : '')
1791 ];
1792 }
1793
1794 notice $user, columnar {TITLE => "Group list for \002$root\002 (" . $get_glist->rows . " nicks):",
1795 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data;
1796
1797 $get_glist->finish();
1798 }
1799 }
1800
1801 sub ns_alist($@) {
1802 my ($user, @targets) = @_;
1803
1804 foreach my $target (@targets) {
1805 (adminserv::is_svsop($user, adminserv::S_HELP()) and (
1806 chk_registered($user, $target) or next)
1807 ) or chk_identified($user, $target) or next;
1808
1809 my @data;
1810
1811 $get_all_access->execute($target);
1812 while(my ($c, $l, $a, $t) = $get_all_access->fetchrow_array) {
1813 next unless $l > 0;
1814 push @data, [$c, $chanserv::plevels[$l+$chanserv::plzero], ($a ? "($a)" : ''),
1815 gmtime2($t)];
1816 }
1817
1818 notice $user, columnar {TITLE => "Access listing for \002$target\002 (".scalar(@data)." entries)",
1819 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data;
1820 }
1821 }
1822
1823 sub ns_list($$) {
1824 my ($user, $mask) = @_;
1825
1826 unless(adminserv::is_svsop($user, adminserv::S_HELP())) {
1827 notice($user, $err_deny);
1828 return;
1829 }
1830
1831 my ($mnick, $mident, $mhost) = glob2sql(parse_mask($mask));
1832
1833 $mnick = '%' if($mnick eq '');
1834 $mident = '%' if($mident eq '');
1835 $mhost = '%' if($mhost eq '');
1836
1837 my @data;
1838 $get_matching_nicks->execute($mnick, $mident, $mhost);
1839 while(my ($rnick, $rroot, $rident, $rhost) = $get_matching_nicks->fetchrow_array) {
1840 push @data, [$rnick, ($rroot ne $rnick ? $rroot : ''), $rident . '@' . $rhost];
1841 }
1842
1843 notice $user, columnar {TITLE => "Registered nicks matching \002$mask\002:",
1844 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data;
1845 }
1846
1847 sub ns_watch($$$;$) {
1848 my ($user, $target, $cmd, $mask) = @_;
1849 my $src = get_user_nick($user);
1850
1851 my $root = get_root_nick($target);
1852 unless ($root) {
1853 notice($user, "\002$target\002 is not registered.");
1854 return;
1855 }
1856 unless(is_identified($user, $target)) {
1857 notice($user, $err_deny);
1858 return;
1859 }
1860
1861 if ($cmd =~ /^add$/i) {
1862 my $max_watches = $IRCd_capabilities{WATCH}; # load here for caching.
1863 if ($max_watches eq "") {
1864 notice ($user, "The IRCd is not configured to support WATCH. Please contact your friendly network administrators.");
1865 return;
1866 }
1867 if(count_watches($root) >= $max_watches) {
1868 notice($user, "WATCH list for $target full, there is a limit of $max_watches. Please trim your list.");
1869 return;
1870 }
1871
1872 if($mask =~ /\!/ or $mask =~ /\@/) {
1873 my ($mnick, $mident, $mhost) = parse_mask($mask);
1874 if ($mnick =~ /\*/) {
1875 notice($user, "Invalid mask: \002$mask\002",
1876 'A WATCH mask cannot wildcard the nick.');
1877 return;
1878 }
1879 }
1880
1881 $check_watch->execute($root, $mask);
1882 if ($check_watch->fetchrow_array) {
1883 notice($user, "\002$mask\002 is already in \002$target\002's watch list.");
1884 return;
1885 }
1886
1887 $set_watch->execute($mask, time(), $root);
1888 ircd::svswatch($nsuser, $user, "+$mask");
1889 notice($user, "\002$mask\002 added to \002$target\002's watch list.");
1890 return;
1891 }
1892 elsif ($cmd =~ /^del(ete)?$/i) {
1893 $check_watch->execute($root, $mask);
1894 if ($IRCd_capabilities{WATCH} eq "") {
1895 notice ($user, "The IRCd is not configured to support WATCH. Please contact your friendly network administrators.");
1896 return;
1897 }
1898 unless ($check_watch->fetchrow_array) {
1899 notice($user, "\002$mask\002 is not in \002$target\002's watch list.");
1900 return;
1901 }
1902 $del_watch->execute($root, $mask);
1903 ircd::svswatch($nsuser, $user, "-$mask");
1904 notice($user, "\002$mask\002 removed from \002$target\002's watch list.");
1905 }
1906 elsif ($cmd =~ /^list$/i) {
1907 my @data;
1908
1909 $get_watches->execute($root);
1910 while(my ($mask, $time) = $get_watches->fetchrow_array) {
1911 push @data, [$mask, gmtime2($time)];
1912 }
1913
1914 notice $user, columnar {TITLE => "Watch list for \002$target\002:",
1915 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data;
1916 }
1917 else {
1918 notice($user, 'Syntax: WATCH <ADD|DEL|LIST> [nick]');
1919 }
1920 }
1921
1922 sub ns_silence($$$;$@) {
1923 my ($user, $target, $cmd, $mask, @args) = @_;
1924 my ($expiry, $comment);
1925 my $src = get_user_nick($user);
1926 my ($subj, $obj);
1927 if(lc(get_user_nick($user)) eq lc($target)) {
1928 $subj='your';
1929 $obj='you';
1930 } else {
1931 $subj="\002$target\002\'s";
1932 $obj="\002$target\002";
1933 }
1934
1935 sub get_silence_by_num($$) {
1936 # This one cannot be converted to SrSv::MySQL::Stub, due to bind_param call
1937 my ($nick, $num) = @_;
1938 $get_silence_by_num->execute($nick, $num-1);
1939 my ($mask) = $get_silence_by_num->fetchrow_array();
1940 $get_silence_by_num->finish();
1941 return $mask;
1942 }
1943
1944 my $root = get_root_nick($target);
1945 my $isRegistered;
1946 if(!defined($root)) {
1947 #notice($user, "\002$target\002 is not registered.");
1948 $isRegistered = 0;
1949 #return;
1950 } else {
1951 $isRegistered = 1;
1952 }
1953
1954 if($isRegistered && !is_identified($user, $target)) {
1955 notice($user, $err_deny);
1956 return;
1957 }
1958
1959 if ($cmd =~ /^add$/i) {
1960 my $max_silences = $IRCd_capabilities{SILENCE};
1961 if ($max_silences eq "") {
1962 notice ($user, "The IRCd is not configured to support SILENCE. Please contact your friendly network administrators.");
1963 return;
1964 }
1965 if(count_silences($root) >= $max_silences) {
1966 notice($user, "SILENCE list for $target full, there is a limit of $max_silences. Please trim your list.");
1967 return;
1968 }
1969
1970 if (substr($args[0],0,1) eq '+') {
1971 $expiry = shift @args;
1972 }
1973 elsif (substr($args[-1],0,1) eq '+') {
1974 $expiry = pop @args;
1975 }
1976 $comment = join(' ', @args);
1977
1978 if($mask !~ /[!@.]/) {
1979 my $target_user = { NICK => $mask };
1980 unless(get_user_id($target_user)) {
1981 notice($user, qq{"\002$mask\002" is not a known user, nor a valid hostmask.});
1982 return;
1983 }
1984 $comment = $mask unless $comment;
1985 no warnings 'misc';
1986 my ($ident, $vhost) = get_vhost($target_user);
1987 my ($nick, $ident, $vhost) = make_hostmask(10, $mask, $ident, $vhost);
1988 $mask = $nick.'!'.$ident.'@'.$vhost;
1989 }
1990 else {
1991 $mask = normalize_hostmask($mask);
1992 }
1993
1994 =cut
1995 if("$nsnick!services\@".main_conf_local =~ hostmask_to_regexp($mask)) {
1996 notice($user, "You shouldn't add NickServ to your SILENCE list.");
1997 return;
1998 }
1999 =cut
2000
2001 if(defined $expiry) {
2002 $expiry = parse_time($expiry) + time();
2003 }
2004 else {
2005 $expiry = 0;
2006 };
2007 if($isRegistered) {
2008 $check_silence->execute($root, $mask);
2009 if ($check_silence->fetchrow_array) {
2010 notice($user, "\002$mask\002 is already in $subj SILENCE list.");
2011 return;
2012 }
2013
2014 $set_silence->execute($mask, time(), $expiry, $comment, $root);
2015 }
2016 ircd::svssilence($nsuser, $user, "+$mask");
2017 notice($user, "\002$mask\002 added to $subj SILENCE list.");
2018 }
2019 elsif ($cmd =~ /^del(ete)?$/i) {
2020 my @masks;
2021 if ($mask =~ /^[0-9\.,-]+$/) {
2022 foreach my $num (makeSeqList($mask)) {
2023 push @masks, get_silence_by_num($root, $num) or next;
2024 }
2025 if(scalar(@masks) == 0) {
2026 notice($user, "Unable to find any silences matching $mask");
2027 return;
2028 }
2029 } else {
2030 @masks = ($mask);
2031 }
2032 my @reply; my @out_masks;
2033 foreach my $mask (@masks) {
2034 $check_silence->execute($root, $mask);
2035 unless ($check_silence->fetchrow_array) {
2036 $mask = normalize_hostmask($mask);
2037
2038 $check_silence->execute($root, $mask);
2039 unless ($check_silence->fetchrow_array) {
2040 push @reply, "\002$mask\002 is not in $subj SILENCE list.";
2041 next;
2042 }
2043 }
2044 $del_silence->execute($root, $mask);
2045 push @out_masks, "-$mask";
2046 push @reply, "\002$mask\002 removed from $subj SILENCE list.";
2047 }
2048 ircd::svssilence($nsuser, $user, @out_masks);
2049 notice($user, @reply);
2050 }
2051 elsif ($cmd =~ /^list$/i) {
2052 $get_silences->execute($root);
2053
2054 my @reply; my $i = 1;
2055 while(my ($mask, $time, $expiry, $comment) = $get_silences->fetchrow_array) {
2056 push @reply, "$i \002[\002 $mask \002]\002 Date added: ".gmtime2($time),
2057 ' '.($comment ? "\002[\002 $comment \002]\002 " : '').
2058 ($expiry ? 'Expires in '.time_rel($expiry-time()) :
2059 "\002[\002 Never expires \002]\002");
2060 $i++;
2061 }
2062
2063 notice($user, "SILENCE list for $obj:", (scalar @reply ? @reply : " list empty"));
2064 }
2065 else {
2066 notice($user, 'Syntax: SILENCE [nick] <ADD|DEL|LIST> [mask] [+expiry] [comment]');
2067 }
2068
2069 }
2070
2071 sub ns_acc($@) {
2072 my ($user, @targets) = @_;
2073 my @reply;
2074
2075 foreach my $target (@targets) {
2076 unless(is_registered($target)) {
2077 push @reply, "ACC 0 \002$target\002 is not registered.";
2078 next;
2079 }
2080
2081 unless(is_online($target)) {
2082 push @reply, "ACC 1 \002$target\002 is registered and offline.";
2083 next;
2084 }
2085
2086 unless(is_identified({NICK => $target}, $target)) {
2087 push @reply, "ACC 2 \002$target\002 is online but not identified.";
2088 next;
2089 }
2090
2091 push @reply, "ACC 3 \002$target\002 is registered and identified.";
2092 }
2093 notice($user, @reply);
2094 }
2095
2096 sub ns_seen($@) {
2097 my ($user, @nicks) = @_;
2098
2099 foreach my $nick (@nicks) {
2100 if(lc $nick eq lc (($user->{AGENT})->{NICK})) {
2101 notice($user, "Oh, a wise guy, eh?");
2102 next;
2103 }
2104 my ($status, $msg) = do_seen($nick);
2105 if($status == 2) {
2106 notice($user, "\002$nick\002 is online now, ".$msg.'.');
2107 } elsif($status == 1) {
2108 notice($user, "\002$nick\002 was last seen ".$msg.'.');
2109 } else {
2110 notice($user, "The nick \002$nick\002 is not registered.");
2111 }
2112 }
2113 }
2114
2115 sub ns_recover($$;$) {
2116 my ($user, $nick, $pass) = @_;
2117 my $src = get_user_nick($user);
2118
2119 if(nr_chk_flag($nick, NRF_FREEZE)) {
2120 notice($user, "This nick has been frozen and may not be used.", $err_deny);
2121 services::ulog($nsnick, LOG_INFO(), "\00305attempted to recover frozen nick \003\002$nick\002", $user);
2122 return;
2123 }
2124
2125 unless(is_identified($user, $nick)) {
2126 if($pass) {
2127 my $s = ns_identify($user, $nick, $pass);
2128 return if($s == 0); #failed to identify
2129 } else {
2130 notice($user, $err_deny);
2131 return;
2132 }
2133 }
2134
2135 if(!is_online($nick)) {
2136 notice($user, "\002$nick\002 is not online");
2137 return;
2138 } elsif(lc $src eq lc $nick) {
2139 notice($user, "I'm sorry, $src, I'm afraid I can't do that.");
2140 return;
2141
2142 } else {
2143 collide($nick);
2144 notice($user, "User claiming your nick has been collided",
2145 "/msg NickServ RELEASE $nick to get it back before the one-minute timeout.");
2146 services::ulog($nsnick, LOG_INFO(), "used NickServ RECOVER on $nick", $user);
2147 return;
2148 }
2149 }
2150
2151 sub ns_auth($@) {
2152 my ($user, @args) = @_;
2153 my ($target, $cmd);
2154
2155 #These helpers shouldn't be needed anywhere else.
2156 # If they ever are, move them to the helpers section
2157 sub get_auth_num($$) {
2158 # this cannot be converted to SrSv::MySQL::Stub, due to bind_param
2159 my ($nick, $num) = @_;
2160 $get_auth_num->execute($nick, $num - 1);
2161 my ($cn, $data) = $get_auth_num->fetchrow_array();
2162 $get_auth_num->finish();
2163 return ($data ? ($cn, split(/:/, $data)) : undef);
2164 }
2165 sub get_auth_chan($$) {
2166 my ($nick, $cn) = @_;
2167 $get_auth_chan->execute($nick, $cn);
2168 my ($data) = $get_auth_chan->fetchrow_array();
2169 $get_auth_chan->finish();
2170 return (split(/:/, $data));
2171 }
2172
2173 if ($args[0] =~ /^(list|accept|approve|decline|reject)$/i) {
2174 $target = get_user_nick($user);
2175 $cmd = lc shift @args;
2176 }
2177 else {
2178 $target = shift @args;
2179 $cmd = lc shift @args;
2180 }
2181
2182 unless (is_registered($target)) {
2183 notice($user, "The nickname \002$target\002 is not registered");
2184 return;
2185 }
2186 unless (is_identified($user, $target)) {
2187 notice($user, $err_deny);
2188 return;
2189 }
2190
2191 if ($cmd eq 'list') {
2192 my @data;
2193 $list_auth->execute($target);
2194 while (my ($cn, $data) = $list_auth->fetchrow_array()) {
2195 my ($adder, $old, $level, $time) = split(':', $data);
2196 push @data, [$cn, $chanserv::levels[$level], $adder, gmtime2($time)];
2197 }
2198 if ($list_auth->rows()) {
2199 notice $user, columnar {TITLE => "Pending authorizations for \002$target\002:",
2200 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data;
2201 }
2202 else {
2203 notice($user, "There are no pending authorizations for \002$target\002");
2204 }
2205 }
2206 elsif ($cmd eq 'accept' or $cmd eq 'approve') {
2207 my $parm = shift @args;
2208 my ($cn, $adder, $old, $level, $time);
2209 if(misc::isint($parm) and
2210 ($cn, $adder, $old, $level, $time) = get_auth_num($target, $parm))
2211 {
2212 }
2213 elsif ($parm =~ /^\#/ and
2214 ($adder, $old, $level, $time) = get_auth_chan($target, $parm))
2215 {
2216 $cn = $parm;
2217 }
2218 unless ($cn) {
2219 # This should normally be an 'else' as the elsif above should prove false
2220 # For some reason, it doesn't work. the unless ($cn) fixes it.
2221 # It only doesn't work for numbered entries
2222 notice($user, "There is no entry for \002$parm\002 in \002$target\002's AUTH list");
2223 return;
2224 }
2225 my $chan = { CHAN => $cn };
2226 my $root = get_root_nick($target);
2227
2228 # These next 3 lines should use chanserv::set_acc() but it doesn't seem to work.
2229 # It won't let me use a $nick instead of $user
2230 $chanserv::set_acc1->execute($cn, $level, $root);
2231 $chanserv::set_acc2->execute($level, $adder, $cn, $root);
2232 chanserv::set_modes_allnick($root, $chan, $level) unless chanserv::is_neverop($root);
2233
2234 my $log_str = ($old?'move':'addition')." \002$root\002"
2235 . ($old ? ' from the '.$chanserv::levels[$old] : '') .
2236 ' to the '.$chanserv::levels[$level]." list of \002$cn\002";
2237 services::ulog($chanserv::csnick, LOG_INFO(), "accepted the $log_str from $adder", $user, $chan);
2238 notice($user, "You have accepted the $log_str");
2239 $del_auth->execute($target, $cn);
2240 $del_auth->finish();
2241 memoserv::send_memo($chanserv::csnick, $adder, "$target accepted the $log_str");
2242 }
2243 elsif ($cmd eq 'decline' or $cmd eq 'reject') {
2244 my $parm = shift @args;
2245 my ($cn, $adder, $old, $level, $time);
2246 if(misc::isint($parm) and
2247 ($cn, $adder, $old, $level, $time) = get_auth_num($target, $parm))
2248 {
2249 }
2250 elsif ($parm =~ /^\#/ and
2251 ($adder, $old, $level, $time) = get_auth_chan($target, $parm))
2252 {
2253 $cn = $parm;
2254 }
2255 unless ($cn) {
2256 # This should normally be an 'else' as the elsif above should prove false
2257 # For some reason, it doesn't work. the unless ($cn) fixes it.
2258 # It only doesn't work for numbered entries
2259 notice($user, "There is no entry for \002$parm\002 in \002$target\002's AUTH list");
2260 return;
2261 }
2262 my $chan = { CHAN => $cn };
2263
2264 my $root = get_root_nick($target);
2265 my $log_str = ($old?'move':'addition')." \002$root\002"
2266 . ($old ? ' from the '.$chanserv::plevels[$old+$chanserv::plzero] : '') .
2267 ' to the '.$chanserv::plevels[$level+$chanserv::plzero]." list of \002$cn\002";
2268 services::ulog($chanserv::csnick, LOG_INFO(), "declined the $log_str from $adder", $user, $chan);
2269 notice($user, "You have declined $log_str");
2270 $del_auth->execute($target, $cn);
2271 $del_auth->finish();
2272 memoserv::send_memo($chanserv::csnick, $adder, "$target declined the $log_str");
2273 }
2274 #elsif ($cmd eq 'read') {
2275 #}
2276 else {
2277 notice($user, "Unknown AUTH cmd");
2278 }
2279 }
2280
2281 sub ns_authcode($$$;$) {
2282 my ($user, $target, $code, $pass) = @_;
2283
2284 if ($pass and $pass =~ /pass/i) {
2285 notice($user, 'Try a more secure password.');
2286 return;
2287 }
2288
2289 unless(is_registered($target)) {
2290 notice($user, "\002$target\002 isn't registered.");
2291 return;
2292 }
2293
2294 if(authcode($target, undef, $code)) {
2295 notice($user, "\002$target\002 authenticated.");
2296 services::ulog($nsnick, LOG_INFO(), "logged in to \002$target\002 using an authcode", $user);
2297
2298 do_identify($user, $target, $target);
2299 if($pass) {
2300 ns_set($user, $target, 'PASSWD', $pass)
2301 } elsif(nr_chk_flag($target, NRF_SENDPASS())) {
2302 notice($user, "YOU MUST CHANGE YOUR PASSWORD NOW", "/NS SET $target PASSWD <newpassword>");
2303 }
2304 }
2305 else {
2306 notice($user, "\002$target\002 authentication failed. Please verify that you typed or pasted the code correctly.");
2307 }
2308 }
2309
2310 sub ns_profile($@) {
2311 my ($user, $first, @args) = @_;
2312
2313 my %profile_dispatch = (
2314 'read' => \&ns_profile_read,
2315 'info' => \&ns_profile_read,
2316
2317 'del' => \&ns_profile_del,
2318 'delete' => \&ns_profile_del,
2319
2320 'set' => \&ns_profile_update,
2321 'update' => \&ns_profile_update,
2322 'add' => \&ns_profile_update,
2323
2324 'wipe' => \&ns_profile_wipe,
2325 );
2326
2327 no warnings 'misc';
2328 if(my $sub = $profile_dispatch{$args[0]}) {
2329 # Second command with nick
2330 shift @args;
2331 $sub->($user, $first, @args);
2332 }
2333 elsif(my $sub = $profile_dispatch{$first}) {
2334 # Second command without nick
2335 $sub->($user, get_user_nick($user), @args);
2336 }
2337 elsif(@args == 0) {
2338 # No second command
2339 ns_profile_read($user, ($first || get_user_nick($user)));
2340 }
2341 else {
2342 notice $user,
2343 "Syntax: PROFILE [nick] [SET|DEL|READ|WIPE ...]",
2344 "For help, type: \002/ns help profile\002";
2345 }
2346 }
2347
2348 sub ns_profile_read($$@) {
2349 my ($user, $target, @args) = @_;
2350
2351 foreach my $nick ((scalar(@args) ? @args : $target)) {
2352 next unless chk_registered($user, $nick);
2353 my @profile_entries = get_profile_ntf($nick);
2354 if(scalar(@profile_entries)) {
2355 notice $user, columnar({TITLE => "Profile information for \002$nick\002:",
2356 NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)},
2357 map( ["$_->[0]:", $_->[1]], @profile_entries )
2358 );
2359 }
2360 else {
2361 notice $user, "\002$nick\002 has not created a profile.";
2362 }
2363 }
2364 }
2365
2366 sub ns_profile_update($$@) {
2367 my ($user, $target, @args) = @_;
2368
2369 return unless chk_registered($user, $target);
2370
2371 unless(is_identified($user, $target) or
2372 adminserv::is_svsop($user, adminserv::S_HELP())
2373 ) {
2374 notice($user, "$target: $err_deny");
2375 return;
2376 }
2377
2378 my ($key, $data) = (shift @args, join(' ', @args));
2379
2380 unless ($key and $data) {
2381 notice $user, "Syntax: PROFILE [nick] SET <item> <text>",
2382 "For help, type: \002/ns help profile\002";
2383 return;
2384 }
2385
2386 if(count_profile_ntf($target) >= MAX_PROFILE) {
2387 notice($user, "You may not have more than ".MAX_PROFILE." profile items.");
2388 return;
2389 }
2390 elsif (length($key) > 32) {
2391 notice($user, "Item name may not be longer than 32 characters.");
2392 return;
2393 }
2394 elsif (length($data) > MAX_PROFILE_LEN) {
2395 my $over = length($data) - MAX_PROFILE_LEN;
2396 notice($user, "Your entry is $over characters too long. (".MAX_PROFILE_LEN." max.)");
2397 return;
2398 }
2399 add_profile_ntf($key, $data, $target);
2400 notice($user, "\002$target\002's \002$key\002 is now \002$data\002");
2401 }
2402
2403 sub ns_profile_del($$@) {
2404 my ($user, $target, @args) = @_;
2405
2406 return unless chk_registered($user, $target);
2407
2408 unless(is_identified($user, $target) or
2409 adminserv::is_svsop($user, adminserv::S_HELP())
2410 ) {
2411 notice($user, "$target: $err_deny");
2412 return;
2413 }
2414
2415 my $key = shift @args;
2416
2417 unless ($key) {
2418 notice $user, "Syntax: PROFILE [nick] DEL <item>",
2419 "For help, type: \002/ns help profile\002";
2420 return;
2421 }
2422
2423 if(del_profile_ntf($target, $key) == 0) {
2424 notice($user, "There is no profile item \002$key\002 for \002$target\002");
2425 } else {
2426 notice($user, "Profile item \002$key\002 for \002$target\002 deleted.");
2427 }
2428 }
2429
2430 sub ns_profile_wipe($$@) {
2431 my ($user, $target, undef) = @_;
2432
2433 unless (is_registered($target)) {
2434 notice($user, "$target is not registered.");
2435 next;
2436 }
2437 unless(is_identified($user, $target) or
2438 adminserv::is_svsop($user, adminserv::S_HELP())
2439 ) {
2440 notice($user, "$target: $err_deny");
2441 return;
2442 }
2443
2444 wipe_profile_ntf($target);
2445 notice($user, "Profile for \002$target\002 wiped.");
2446 }
2447
2448 sub ns_listemail($$) {
2449 my ($user, $email) = @_;
2450 unless(adminserv::is_svsop($user, adminserv::S_HELP())) {
2451 notice($user, $err_deny);
2452 return;
2453 }
2454 my $likeemail = glob2sql($email);
2455 my (@found, $count);
2456
2457 $get_nicks_by_email->execute($likeemail);
2458 while (my ($nick, $ident, $host) = $get_nicks_by_email->fetchrow_array) {
2459 push @found, " $nick ($ident\@$host)";
2460 }
2461 $email =~ s/\%/\*/g;
2462 if ($#found >= 0) {
2463 notice($user, "Nicks matching an email address consisting of \002$email\002");
2464 for(@found) {
2465 notice($user, $_);
2466 $count++;
2467 }
2468 notice($user, "Found \002$count\002 matching nicks.");
2469 } else {
2470 notice($user, "There were no nicknames registered with an email address consisting of \002$email\002");
2471 }
2472 }
2473
2474 ### MISCELLANEA ###
2475
2476 sub do_seen($$) {
2477 my ($nick) = @_;
2478 my ($status, $msg);
2479
2480 $get_seen->execute($nick);
2481 if (my ($alias, $root, $lastseen) = $get_seen->fetchrow_array) {
2482 if(my @usernicks = get_nick_user_nicks($nick)) {
2483 $status = 2;
2484 $msg = "using ".(@usernicks==1 ? 'the nick ' : 'the following nicks: ').join(', ', map "\002$_\002", @usernicks);
2485 }
2486 else {
2487 $status = 1;
2488 $msg = time_ago($lastseen) . " ago (".gmtime2($lastseen).")";
2489 }
2490 }
2491 else {
2492 $status = 0; $msg = undef();
2493 }
2494
2495 return ($status, $msg);
2496 }
2497
2498 # For a whole group:
2499 sub unidentify($$;$) {
2500 my ($nick, $msg, $src) = @_;
2501
2502 $nick = get_root_nick($nick);
2503
2504 foreach my $t (get_nick_user_nicks $nick) {
2505 my $user = { NICK => $nick, AGENT => $nsuser };
2506 get_user_id ($user);
2507 ircd::notice($nsuser, $user, (ref $msg ? @$msg : $msg)) unless(lc $t eq lc $src);
2508 if(is_alias_of($nick, $t)) {
2509 ircd::setumode($nsuser, $user, '-r');
2510 }
2511 }
2512
2513 $unidentify->execute($nick);
2514 }
2515
2516 # For a single alias:
2517 sub unidentify_single($$) {
2518 my ($nick, $msg) = @_;
2519 my $user = { NICK => $nick, AGENT => $nsuser };
2520 get_user_id ($user);
2521 if(is_online($nick)) {
2522 ircd::setumode($nsuser, $user, '-r');
2523 }
2524 }
2525
2526 sub kill_clones($$) {
2527 my ($user, $ip) = @_;
2528 my $uid = get_user_id($user);
2529 my $src = get_user_nick($user);
2530
2531 return 0 if $ip == 0;
2532
2533 $chk_clone_except->execute($uid);
2534 my ($lim) = $chk_clone_except->fetchrow_array;
2535 return 0 if $lim == MAX_LIM();
2536 $lim = services_conf_clone_limit unless $lim;
2537
2538 $count_clones->execute($ip);
2539 my ($c) = $count_clones->fetchrow_array;
2540
2541 if($c > $lim) {
2542 ircd::irckill($nsuser, $user, "Session Limit Exceeded");
2543 return 1;
2544 }
2545 }
2546
2547 sub do_ajoin($$) {
2548 my ($user, $nick) = @_;
2549 my $src = get_user_nick($user);
2550 if(my @chans = get_autojoin_ntf($nick)) {
2551 chanserv::cs_join($user, @chans);
2552 }
2553 }
2554
2555 sub do_identify ($$$;$$) {
2556 my ($user, $nick, $root, $flags, $svsnick) = @_;
2557 my $uid = get_user_id($user);
2558 my $src = get_user_nick($user);
2559 $identify_ign->execute($uid, $root);
2560 $id_update->execute($root, $uid);
2561
2562 notice($user, 'You are now identified.');
2563
2564 delete($user->{NICKFLAGS});
2565 if($flags & NRF_VACATION) {
2566 notice($user, "Welcome back from your vacation, \002$nick\002.");
2567 my $ts = MIME::Base64::encode(pack('N', time()));
2568 chomp $ts;
2569 $del_nicktext->execute(NTF_VACATION, $root); $del_nicktext->finish(); #don't allow dups
2570 $set_vacation_ntf->execute($ts, $root);
2571 $set_vacation_ntf->finish();
2572 }
2573
2574 $get_umode_ntf->execute($nick);
2575 my ($umodes) = $get_umode_ntf->fetchrow_array();
2576 $get_umode_ntf->finish();
2577 if(adminserv::get_svs_level($root)) {
2578 $umodes = modes::merge_umodes('+h', $umodes);
2579 ircd::nolag($nsuser, '+', $user);
2580 }
2581 $umodes = modes::merge_umodes('+r', $umodes) if(is_identified($user, $src));
2582
2583 hostserv::hs_on($user, $root, 1);
2584
2585 if(my @chans = get_autojoin_ntf($nick)) {
2586 ircd::svsjoin($nsuser, $user, @chans);
2587 }
2588
2589 my $enforced;
2590 if(enforcer_quit($nick)) {
2591 notice($user, 'Your nick has been released from custody.');
2592 $enforced = 1;
2593 }
2594
2595 if (lc($src) eq lc($nick)) {
2596 ircd::setumode($nsuser, $user, $umodes);
2597 $update_nickalias_last->execute($nick); $update_nickalias_last->finish();
2598 }
2599 elsif($svsnick) {
2600 ircd::svsnick($nsuser, $user, $nick);
2601 ircd::setumode($nsuser, $user, modes::merge_umodes('+r', $umodes) );
2602 # the update _should_ be taken care of in nick_change()
2603 #$update_nickalias_last->execute($nick); $update_nickalias_last->finish();
2604 }
2605 elsif(defined $umodes) {
2606 ircd::setumode($nsuser, $user, $umodes);
2607 }
2608
2609 do_ajoin($user, $nick);
2610
2611 nickserv::do_svssilence($user, $root);
2612 nickserv::do_svswatch($user, $root);
2613
2614 chanserv::akick_alluser($user);
2615 chanserv::set_modes_allchan($user, $flags & NRF_NEVEROP);
2616 chanserv::fix_private_join_before_id($user);
2617
2618 services::ulog($nsnick, LOG_INFO(), "identified to nick $nick (root: $root)", $user);
2619
2620 memoserv::notify($user, $root);
2621 notify_auths($user, $root) if $flags & NRF_AUTH;
2622 return ($enforced ? 2 : 1);
2623 }
2624
2625 sub authcode($;$$) {
2626 my ($nick, $type, $email) = @_;
2627 if($type) {
2628 unless (defined($email)) {
2629 $email = get_email($nick);
2630 }
2631
2632 my $authcode = misc::gen_uuid(4, 5);
2633 $set_authcode_ntf->execute($authcode, $nick); $set_authcode_ntf->finish();
2634 send_email($email, "Nick Authentication Code for $nick",
2635 "Hello $nick,\n\n".
2636
2637 "You are receiving this message from the automated nickname\n".
2638 "management system of the ".$IRCd_capabilities{NETWORK}." network.\n\n".
2639 (lc($type) eq 'emailreg' ?
2640 "If you did not try to register your nickname with us, you can\n".
2641 "ignore this message. If you continue getting similar e-mails\n".
2642 "from us, chances are that someone is intentionally abusing your\n".
2643 "e-mail address. Please contact an administrator for help.\n".
2644
2645 "In order to complete your registration, you must follow the\n".
2646 "instructions in this e-mail before ".gmtime2(time+86400)."\n".
2647
2648 "To complete the registration, the next time you connect, issue the\n".
2649 "following command to NickServ:\n\n".
2650
2651 "After you issue the command, your registration will be complete and\n".
2652 "you will be able to use your nickname.\n\n"
2653
2654 : '').
2655 (lc($type) eq 'sendpass' ?
2656 "You requested a password authentication code for the nickname '$nick'\n".
2657 "on the ".$IRCd_capabilities{'NETWORK'}." IRC Network.\n".
2658 "As per our password policies, an authcode has been created for\n".
2659 "you and e-mailed to the address you set in NickServ.\n".
2660 "To complete the process, you need to return to ".$IRCd_capabilities{'NETWORK'}.",\n".
2661 "and execute the following command: \n\n"
2662 : '').
2663
2664 "/NS EMAILCODE $nick $authcode\n\n".
2665
2666 (lc($type) eq 'sendpass' ?
2667 "YOU MUST CHANGE YOUR PASSWORD AT THIS POINT.\n".
2668 "You can do so via the following command: \n\n".
2669 "/NS SET $nick PASSWD newpassword\n\n".
2670 "alternately, try this command: \n\n".
2671
2672 "/NS EMAILCODE $nick $authcode <password>\n\n"
2673 : '').
2674
2675 "---\n".
2676 "If you feel you have gotten this e-mail in error, please contact\n".
2677 "an administrator.\n\n".
2678
2679 "----\n".
2680 "If this e-mail came to you unsolicited and appears to be spam -\n".
2681 "please e-mail ".main_conf_replyto." with a copy of this e-mail\n".
2682 "including all headers.\n\n".
2683
2684 "Thank you.\n");
2685 }
2686 else {
2687 $get_authcode_ntf->execute($nick, $email);
2688 my ($passed) = $get_authcode_ntf->fetchrow_array();
2689 $get_authcode_ntf->finish();
2690 if ($passed) {
2691 nr_set_flag($nick, NRF_EMAILREG(), 0);
2692 unless(nr_chk_flag($nick, NRF_SENDPASS)) {
2693 $del_nicktext->execute(NTF_AUTHCODE, $nick); $del_nicktext->finish();
2694 }
2695 return 1;
2696 }
2697 else {
2698 return 0;
2699 }
2700 }
2701 }
2702
2703 # This is mostly for logging, be careful using it for anything else
2704 sub get_hostmask($) {
2705 my ($user) = @_;
2706 my ($ident, $host);
2707 my $src = get_user_nick($user);
2708
2709 ($ident, $host) = get_host($user);
2710
2711 return "$src!$ident\@$host";
2712 }
2713
2714 sub guestnick($) {
2715 my ($nick) = @_;
2716 $set_guest->execute(1, $nick);
2717 my $randnick = 'Guest'.int(rand(10)).int(rand(10)).int(rand(10)).int(rand(10)).int(rand(10));
2718 #Prevent collisions.
2719 while (is_online($randnick)) {
2720 $randnick = 'Guest'.int(rand(10)).int(rand(10)).int(rand(10)).int(rand(10)).int(rand(10));
2721 }
2722 my $user = { NICK => $nick, AGENT => $nsuser };
2723 get_user_id ($user);
2724 ircd::svsnick($nsuser, $user, $randnick);
2725
2726 return $randnick;
2727 }
2728
2729 sub expire {
2730 return if services_conf_noexpire;
2731
2732 =cut
2733 my ($ne, $e, $ve, $eve) = (services_conf_nearexpire, services_conf_nickexpire, services_conf_vacationexpire,
2734 services_conf_validate_expire);
2735 =cut
2736
2737 $get_expired->execute(time() - (86400 * services_conf_nickexpire),
2738 time() - (86400 * services_conf_vacationexpire),
2739 time() - (86400 * services_conf_validate_expire));
2740 while(my ($nick, $email, $ident, $vhost) = $get_expired->fetchrow_array) {
2741 dropgroup($nick);
2742 wlog($nsnick, LOG_INFO(), "$nick has expired. Email: $email Vhost: $ident\@$vhost");
2743 }
2744
2745 my $time = time();
2746
2747 return unless services_conf_nearexpire; # if nearexpire is zero, don't.
2748 $get_near_expired->execute(
2749 $time - (86400 * (services_conf_nickexpire - services_conf_nearexpire)),
2750 $time - (86400 * (services_conf_vacationexpire - services_conf_nearexpire))
2751 );
2752 while(my ($nick, $email, $flags, $last) = $get_near_expired->fetchrow_array) {
2753 my $expire_days = services_conf_nearexpire;
2754 if ( ( $flags & NRF_VACATION ) and ( $last < time() - (86400 * services_conf_vacationexpire) )
2755 or (($last < time() - (86400 * services_conf_nickexpire)) ) )
2756 {
2757 $expire_days = 0;
2758 } elsif ( ( $flags & NRF_VACATION ) and ( $last > time() - (86400 * services_conf_vacationexpire) )
2759 or (($last > time() - (86400 * services_conf_nickexpire)) ) )
2760 {
2761 # this terrible invention is to determine how many days until their nick will expire.
2762 # this should almost always be ~7, unless something weird happens like
2763 # F_HOLD or svsop status is removed.
2764 # int truncates, so we add 0.5.
2765 $expire_days = -int(($time - ($last + (86400 *
2766 ( ( $flags & NRF_VACATION ) ? services_conf_vacationexpire : services_conf_nickexpire ) )))
2767 / 86400 + .5);
2768 }
2769 if($expire_days >= 1) {
2770
2771 $get_aliases->execute($nick);
2772 my $aliases = $get_aliases->fetchrow_arrayref();
2773
2774 my $message = "We would like to remind you that your registered nick, $nick, will expire\n".
2775 "in approximately $expire_days days unless you sign on and identify.";
2776 if(scalar(@$aliases) > 1) {
2777 $message .= "\n\nThe following nicks are linked in this group:\n " . join("\n ", @$aliases);
2778 }
2779
2780 send_email($email, "$nsnick Expiration Notice", $message);
2781 }
2782
2783 wlog($nsnick, LOG_INFO(), "$nick will expire ".($expire_days <= 0 ? "today" : "in $expire_days days.")." ($email)");
2784 $set_near_expired->execute($nick);
2785 }
2786 }
2787
2788 sub expire_silence_timed {
2789 my ($time) = shift;
2790 $time = 60 unless $time;
2791 add_timer('', $time, __PACKAGE__, 'nickserv::expire_silence_timed');
2792
2793 find_expired_silences();
2794 }
2795
2796 # This code is a mess b/c we can only pull one entry at a time
2797 # and we want to batch the list to the user and to the ircd.
2798 # our SQL statement explicitly orders the silence entries by nickreg.nick
2799 sub find_expired_silences() {
2800 $get_expired_silences->execute();
2801 my ($lastnick, @entries);
2802 while(my ($nick, $mask, $comment) = $get_expired_silences->fetchrow_array()) {
2803 if ($nick eq $lastnick) {
2804 } else {
2805 do_expired_silences($lastnick, \@entries);
2806 @entries = ();
2807 $lastnick = $nick;
2808 }
2809 push @entries, [$mask, $comment];
2810 }
2811 if (@entries) {
2812 do_expired_silences($lastnick, \@entries);
2813 }
2814 $get_expired_silences->finish();
2815 $del_expired_silences->execute(); $del_expired_silences->finish();
2816 return;
2817 }
2818
2819 sub do_expired_silences($$) {
2820 my $nick = $_[0];
2821 my (@entries) = @{$_[1]};
2822
2823 foreach my $user (get_nick_users $nick) {
2824 $user->{AGENT} = $nsnick;
2825 get_user_id ($user);
2826 ircd::svssilence($nsuser, $user, map ( { '-'.$_->[0] } @entries) );
2827 #notice($user, "The following SILENCE entries have expired: ".
2828 # join(', ', map ( { $_->[0] } @entries) ));
2829 notice($user, map( { "The following SILENCE entry has expired: \002".$_->[0]."\002 ".$_->[1] } @entries ) );
2830 }
2831 }
2832 sub do_svssilence($$) {
2833 my ($user, $rootnick) = @_;
2834 if ($IRCd_capabilities{SILENCE} eq "") {
2835 notice ($user, "The IRCd is not configured to support SILENCE. Please contact your friendly network administrators.");
2836 return;
2837 }
2838 my $target = get_user_nick($user);
2839
2840 $get_silences->execute($rootnick);
2841 my $count = $get_silences->rows;
2842 unless ($get_silences->rows) {
2843 $get_silences->finish;
2844 return;
2845 }
2846 my @silences;
2847 for(my $i = 1; $i <= $count; $i++) {
2848 my ($mask, $time, $expiry) = $get_silences->fetchrow_array;
2849 push @silences, "+$mask";
2850 }
2851 $get_silences->finish;
2852 ircd::svssilence($nsuser, $user, @silences);
2853 return;
2854 }
2855
2856 sub do_svswatch($$) {
2857 my ($user, $rootnick) = @_;
2858 if ($IRCd_capabilities{WATCH} eq "") {
2859 notice ($user, "The IRCd is not configured to support WATCH. Please contact your friendly network administrators.");
2860 return;
2861 }
2862 my $target = get_user_nick($user);
2863
2864 $get_watches->execute($rootnick);
2865 my $count = $get_watches->rows;
2866 unless ($get_watches->rows) {
2867 $get_watches->finish;
2868 return;
2869 }
2870 my @watches;
2871 for(my $i = 1; $i <= $count; $i++) {
2872 my ($mask, $time, $expiry) = $get_watches->fetchrow_array;
2873 push @watches, "+$mask";
2874 }
2875 $get_watches->finish;
2876 ircd::svswatch($nsuser, $user, @watches);
2877 return;
2878 }
2879
2880 sub do_umode($$) {
2881 my ($user, $rootnick) = @_;
2882 my $target = get_user_nick($user);
2883
2884 $get_umode_ntf->execute($rootnick);
2885 my ($umodes) = $get_umode_ntf->fetchrow_array; $get_umode_ntf->finish();
2886
2887 ircd::setumode($nsuser, $user, $umodes) if $umodes;
2888 return
2889 }
2890
2891 sub notify_auths($$) {
2892 my ($user, $nick) = @_;
2893 $get_num_nicktext_type->execute($nick, NTF_AUTH);
2894 my ($count) = $get_num_nicktext_type->fetchrow_array(); $get_num_nicktext_type->finish();
2895 notice($user, "$nick has $count channel authorizations awaiting action.",
2896 "To list them, type /ns auth $nick list") if $count;
2897 }
2898
2899 ### PROTECTION AND ENFORCEMENT ###
2900
2901 sub protect($) {
2902 my ($nick) = @_;
2903
2904 return if nr_chk_flag($nick, NRF_EMAILREG());
2905 my $lev = protect_level($nick);
2906 my $user = { NICK => $nick, AGENT => $nsuser };
2907 get_user_id ($user);
2908 notice($user,
2909 "This nickname is registered and protected. If it is your",
2910 "nick, type \002/msg NickServ IDENTIFY <password>\002. Otherwise,",
2911 "please choose a different nick."
2912 ) unless($lev==3);
2913
2914 if($lev == 1) {
2915 warn_countdown("$nick 60");
2916 }
2917 elsif($lev==2) {
2918 collide($nick);
2919 }
2920 elsif($lev==3) {
2921 ircd::svshold($nick, 60, "If this is your nick, type /NS SIDENTIFY $nick \002password\002");
2922 kill_user($user, "Unauthorized nick use with KILL protection enabled.");
2923 $enforcers{lc $nick} = 1;
2924 add_timer($nick, 60, __PACKAGE__, "nickserv::enforcer_delete");
2925 }
2926
2927 return;
2928 }
2929
2930 sub warn_countdown($) {
2931 my ($cookie) = @_;
2932 my ($nick, $rem) = split(/ /, $cookie);
2933 $nsuser = { NICK => $nsnick, ID => ircd::getAgentUuid($nsnick) };
2934 my $user = { NICK => $nick, AGENT => $nsuser };
2935 get_user_id($user);
2936 if (is_identified($user, $nick)) {
2937 print "Line 2778\n";
2938 $update_nickalias_last->execute($nick); $update_nickalias_last->finish();
2939 return;
2940 }
2941 elsif(!(is_online($nick)) or !(is_registered($nick))) { print "Line 2782\n"; return; }
2942
2943 if($rem == 0) {
2944 notice($user, 'Your nick is now being changed.');
2945 collide($nick);
2946 } else {
2947 notice($user,
2948 "If you do not identify or change your nick in $rem seconds, your nick will be changed.");
2949 $rem -= 20;
2950 add_timer("$nick $rem", 20, __PACKAGE__, "nickserv::warn_countdown");
2951 }
2952 }
2953
2954 sub collide($) {
2955 my ($nick) = @_;
2956 my $newnick = guestnick($nick);
2957 ircd::svshold($nick, 60, "If this is your nick, type /NS SIDENTIFY $nick \002password\002");
2958 $enforcers{lc $nick} = 1;
2959 add_timer($nick, 60, __PACKAGE__, "nickserv::enforcer_delete");
2960 return $newnick;
2961 }
2962
2963 sub enforcer_delete($) {
2964 my ($nick) = @_;
2965 delete($enforcers{lc $nick});
2966 };
2967
2968 sub enforcer_quit($) {
2969 my ($nick) = @_;
2970 if($enforcers{lc $nick}) {
2971 enforcer_delete($nick);
2972 ircd::svsunhold($nick);
2973 return 1;
2974 }
2975 return 0;
2976 }
2977
2978 ### DATABASE UTILITY FUNCTIONS ###
2979
2980 sub get_lock($) {
2981 my ($nick) = @_;
2982
2983 $nick = lc $nick;
2984
2985 if($cur_lock) {
2986 if($cur_lock ne $nick) {
2987 really_release_lock($nick);
2988 die("Tried to get two locks at the same time");
2989 }
2990 $cnt_lock++;
2991 } else {
2992 $cur_lock = $nick;
2993 $get_lock->execute(sql_conf_mysql_db.".user.$nick");
2994 $get_lock->finish;
2995 }
2996 }
2997
2998 sub release_lock($) {
2999 my ($nick) = @_;
3000
3001 $nick = lc $nick;
3002
3003 if($cur_lock and $cur_lock ne $nick) {
3004 really_release_lock($cur_lock);
3005
3006 die("Tried to release the wrong lock");
3007 }
3008
3009 if($cnt_lock) {
3010 $cnt_lock--;
3011 } else {
3012 really_release_lock($nick);
3013 }
3014 }
3015
3016 sub really_release_lock($) {
3017 my ($nick) = @_;
3018
3019 $cnt_lock = 0;
3020 $release_lock->execute(sql_conf_mysql_db.".user.$nick");
3021 $release_lock->finish;
3022 undef $cur_lock;
3023 }
3024
3025 sub get_user_modes($) {
3026 my ($user) = @_;
3027
3028 my $uid = get_user_id($user);
3029 $get_umodes->execute($uid);
3030 my ($umodes) = $get_umodes->fetchrow_array;
3031 $get_umodes->finish();
3032 print "UMODES $umodes\n";
3033 return $umodes;
3034 };
3035
3036 sub set_vhost($$) {
3037 my ($user, $vhost) = @_;
3038 my $id = get_user_id($user);
3039
3040 return $set_vhost->execute($vhost, $id);
3041 }
3042
3043 sub set_ident($$) {
3044 my ($user, $ident) = @_;
3045 my $id = get_user_id($user);
3046
3047 return $set_ident->execute($ident, $id);
3048 }
3049
3050 sub set_ipv6($$$) {
3051 my ($user, $ip, $ipv6) = @_;
3052 my $id = get_user_id($user);
3053
3054 return $set_ip->execute($ip, $ipv6, $id);
3055 }
3056 sub set_ip($$) {
3057 my ($user, $ip) = @_;
3058 my $id = get_user_id($user);
3059
3060 return $set_ip->execute($ip, undef, $id);
3061 }
3062
3063 sub get_root_nick($) {
3064 my ($nick) = @_;
3065
3066 $get_root_nick->execute($nick);
3067 my ($root) = $get_root_nick->fetchrow_array;
3068
3069 return $root;
3070 }
3071
3072 sub get_id_nick($) {
3073 my ($id) = @_;
3074
3075 $get_id_nick->execute($id);
3076 my ($root) = $get_id_nick->fetchrow_array;
3077
3078 return $root;
3079 }
3080
3081 sub drop($) {
3082 my ($nick) = @_;
3083
3084 my $ret = $drop->execute($nick);
3085 $drop->finish();
3086 return $ret;
3087 }
3088
3089 sub changeroot($$) {
3090 my ($old, $new) = @_;
3091
3092 return if(lc $old eq lc $new);
3093
3094 $change_root->execute($new, $old);
3095 }
3096
3097 sub dropgroup($) {
3098 my ($root) = @_;
3099
3100 $del_all_access->execute($root);
3101 $memoserv::delete_all_memos->execute($root);
3102 $memoserv::wipe_ignore->execute($root);
3103 $memoserv::purge_ignore->execute($root);
3104 chanserv::drop_nick_chans($root);
3105 hostserv::del_vhost($root);
3106 $drop_watch->execute($root);
3107 $drop_silence->execute($root);
3108 $drop_nicktext->execute($root);
3109 $delete_aliases->execute($root);
3110 $chanserv::drop_nick_akick->execute($root);
3111 drop($root);
3112 }
3113
3114 sub is_alias($) {
3115 my ($nick) = @_;
3116
3117 return (get_root_nick($nick) eq $nick);
3118 }
3119
3120 sub delete_alias($) {
3121 my ($nick) = @_;
3122 return $delete_alias->execute($nick);
3123 }
3124
3125 sub delete_aliases($) {
3126 my ($root) = @_;
3127 return $delete_aliases->execute($root);
3128 }
3129
3130 sub get_all_access($) {
3131 my ($nick) = @_;
3132
3133 $get_all_access->execute($nick);
3134 return $get_all_access->fetchrow_array;
3135 }
3136
3137 sub del_all_access($) {
3138 my ($root) = @_;
3139
3140 return $del_all_access->execute($root);
3141 }
3142
3143 sub chk_pass($$$) {
3144 my ($nick, $pass, $user) = @_;
3145
3146 if(lc($pass) eq 'force' and adminserv::can_do($user, 'SERVOP')) {
3147 if(adminserv::get_best_svs_level($user) > adminserv::get_svs_level($nick)) {
3148 return 1;
3149 }
3150 }
3151
3152 return validate_pass(get_pass($nick), $pass);
3153 }
3154
3155 sub inc_nick_inval($) {
3156 my ($user) = @_;
3157 my $id = get_user_id($user);
3158
3159 $inc_nick_inval->execute($id);
3160 $get_nick_inval->execute($id);
3161 my ($nick, $inval) = $get_nick_inval->fetchrow_array;
3162 if($inval > 3) {
3163 ircd::irckill($nsuser, $user, 'Too many invalid passwords.');
3164 # unnecessary as irckill calls the quit handler.
3165 #nick_delete($nick);
3166 return 0;
3167 } else {
3168 return 1;
3169 }
3170 }
3171
3172 sub is_registered($) {
3173 my ($nick) = @_;
3174
3175 $is_registered->execute($nick);
3176 if($is_registered->fetchrow_array) {
3177 return 1;
3178 } else {
3179 return 0;
3180 }
3181 }
3182
3183 sub chk_registered($;$) {
3184 my ($user, $nick) = @_;
3185 my $src = get_user_nick($user);
3186 my $what;
3187
3188 if($nick) {
3189 if(lc $src eq lc $nick) {
3190 $what = "Your nick";
3191 } else {
3192 $what = "The nick \002$nick\002";
3193 }
3194 } else {
3195 $nick = get_user_nick($user) unless $nick;
3196 $what = "Your nick";
3197 }
3198
3199 unless(is_registered($nick)) {
3200 notice($user, "$what is not registered.");
3201 return 0;
3202 }
3203
3204 return 1;
3205 }
3206
3207 sub is_alias_of($$) {
3208 $is_alias_of->execute($_[0], $_[1]);
3209 return ($is_alias_of->fetchrow_array ? 1 : 0);
3210 }
3211
3212 sub check_identify($) {
3213 my ($user) = @_;
3214 my $nick = get_user_nick($user);
3215 if(is_registered($nick)) {
3216 if(is_identified($user, $nick)) {
3217 ircd::setumode($nsuser, $user, '+r');
3218 $update_nickalias_last->execute($nick); $update_nickalias_last->finish();
3219 return 1;
3220 } else {
3221 protect($nick);
3222 }
3223 }
3224 return 0;
3225 }
3226
3227 sub cleanup_users() {
3228 add_timer('', services_conf_old_user_age, __PACKAGE__, 'nickserv::cleanup_users');
3229 if(DEBUG) {
3230 ircd::privmsg('ServServ', main_conf_diag, "Starting cleanup_users()");
3231 }
3232
3233 my $time = (time() - (services_conf_old_user_age * 2));
3234 if(DEBUG) {
3235 $get_dead_users->execute($time);
3236 my $arrayRef = $get_dead_users->fetchall_arrayref();
3237 if($arrayRef && scalar(@$arrayRef)) {
3238 ircd::privmsg('ServServ', main_conf_diag, columnar( { BORDER => 1, NOHIGHLIGHT => 1 }, @$arrayRef ) );
3239 }
3240 $get_dead_users->finish();
3241 }
3242 my $rows = $cleanup_users->execute($time) + 0;
3243 $cleanup_nickid->execute();
3244 $cleanup_chanuser->execute();
3245 if(DEBUG) {
3246 ircd::privmsg('ServServ', main_conf_diag, "Deleted $rows dead users\n");
3247 ircd::privmsg('ServServ', main_conf_diag, "Ending cleanup_users()");
3248 }
3249 }
3250
3251 sub fix_vhosts() {
3252 return; # XXX
3253 add_timer('fix_vhosts', 5, __PACKAGE__, 'nickserv::fix_vhosts');
3254 $get_hostless_nicks->execute();
3255 while (my ($nick) = $get_hostless_nicks->fetchrow_array) {
3256 ircd::notice($nsuser, main_conf_diag, "HOSTLESS NICK $nick");
3257 ircd::userhost($nick);
3258 ircd::userip($nick);
3259 }
3260 $get_hostless_nicks->finish();
3261 }
3262
3263 sub nick_cede($) {
3264 my ($nick) = @_;
3265 my $id;
3266
3267 $get_user_id->execute($nick);
3268 if($id = $get_user_id->fetchrow_array) {
3269 $nick_id_delete->execute($id);
3270 $nick_delete->execute($nick);
3271 }
3272 }
3273
3274 ### IRC EVENTS ###
3275
3276 sub nick_create {
3277 my ($user, $time, $ident, $host, $vhost, $server, $svsstamp, $modes, $gecos, $ip, $cloakhost) = @_;
3278 my $nick = get_user_nick ($user);
3279 get_lock($nick);
3280 if ($vhost eq '*') {
3281 if ({modes::splitumodes($modes)}->{x} eq '+') {
3282 if(defined($cloakhost)) {
3283 $vhost = $cloakhost;
3284 }
3285 else { # This should never happen with CLK or VHP
3286 ircd::userhost($nick);
3287 }
3288 } else {
3289 $vhost = $host;
3290 }
3291 }
3292
3293 my $id;
3294 if ($id = get_user_id( $user )) {
3295 #$id = decodeUUID ($id);
3296 $nick_checkExists->execute ($id, $time);
3297 my $exists = $nick_checkExists -> fetchrow_array();
3298 my $flags = (synced() ? UF_FINISHED() : 0);
3299 unless (defined($exists)) {
3300 $nick_deleteChanUser -> execute ($id);
3301 $nick_deleteNickCh -> execute ($id);
3302 $nick_deleteNickId -> execute ($id);
3303 $id_delUser -> execute ($id);
3304 $nick_delUser -> execute ($nick);
3305 $nick_create2 -> execute ($id, $nick, $time, $ident, $host, $vhost, $server, $modes, $gecos, $flags, $cloakhost);
3306 }
3307 else {
3308 $nick_create_old->execute ($nick, $ident, $host, $vhost, $server, $modes, $gecos, $flags, $cloakhost, $id);
3309 }
3310 $add_nickchg->execute($ircline, $nick, $nick);
3311 release_lock($nick);
3312 check_identify($user);
3313 return $id;
3314 }
3315 if($svsstamp) {
3316 $get_user_nick->execute($svsstamp);
3317 my ($oldnick) = $get_user_nick->fetchrow_array();
3318 $id = $svsstamp if defined($oldnick);
3319 }
3320 else {
3321 $nick_check->execute($nick, $time);
3322 ($id) = $nick_check->fetchrow_array;
3323 }
3324
3325 if($id) {
3326 $olduser{lc $nick} = 1;
3327 $nick_create_old->execute($nick, $ident, $host, $vhost, $server, $modes, $gecos, UF_FINISHED(), $cloakhost, $id);
3328 } else {
3329 nick_cede($nick);
3330
3331 my $flags = (synced() ? UF_FINISHED() : 0);
3332 my $i;
3333 while($i < 10 and !$nick_create->execute($nick, $time, $ident, $host, $vhost, $server, $modes, $gecos, $flags, $cloakhost)) { $i++ }
3334 $id = get_user_id( { NICK => $nick } ); # There needs to be a better way to do this
3335 }
3336 ircd::setsvsstamp($nsuser, $user, $id) unless $svsstamp == $id;
3337
3338 $add_nickchg->execute($ircline, $nick, $nick);
3339
3340 release_lock($nick);
3341
3342 $newuser{lc $nick} = 1;
3343
3344 if($ip) {
3345 nickserv::userip(undef, $nick, $ip);
3346 }
3347 else { # This should never happen with NICKIP
3348 ircd::userip($nick);
3349 }
3350
3351 return $id;
3352 }
3353
3354 sub nick_create_post($) {
3355 my ($nick) = @_;
3356 my $user = { NICK => $nick };
3357 my $old = $olduser{lc $nick};
3358 delete $olduser{lc $nick};
3359
3360 operserv::do_news($nick, 'u') unless($old);
3361
3362 get_lock($nick);
3363
3364 check_identify($user);
3365
3366 release_lock($nick);
3367 }
3368
3369 sub nick_delete($$) {
3370 my ($user, $quit) = @_;
3371 my $nick = $user->{NICK};
3372 get_lock($nick);
3373 my $id = get_user_id($user);
3374 print "DELETE: $nick, $id\n";
3375 $del_nickchg_id->execute($id); $del_nickchg_id->finish();
3376 $quit_update->execute($quit, $id); $quit_update->finish();
3377 $update_lastseen->execute($id); $update_lastseen->finish();
3378 $get_quit_empty_chans->execute($id);
3379 $chan_user_partall->execute($id); $chan_user_partall->finish();
3380 #$nick_chan_delete->execute($id); $nick_chan_delete->finish();
3381 $nick_quit->execute($nick); $nick_quit->finish();
3382 release_lock($nick);
3383 while(my ($cn) = $get_quit_empty_chans->fetchrow_array) {
3384 chanserv::channel_emptied({CHAN => $cn});
3385 }
3386 $get_quit_empty_chans->finish();
3387 }
3388
3389 sub squit($$$) {
3390 my (undef, $servers, $reason) = @_;
3391
3392 $get_squit_lock->execute; $get_squit_lock->finish;
3393
3394 foreach my $server (@$servers) {
3395 $get_squit_empty_chans->execute($server);
3396
3397 $squit_nickreg->execute($server);
3398 $squit_nickreg->finish;
3399
3400 $squit_lastquit->execute("Netsplit from $server", $server);
3401 $squit_lastquit->finish;
3402
3403 $squit_users->execute($server);
3404 $squit_users->finish;
3405
3406 while(my ($cn) = $get_squit_empty_chans->fetchrow_array) {
3407 chanserv::channel_emptied({CHAN => $cn});
3408 }
3409 $get_squit_empty_chans->finish;
3410 }
3411
3412 $unlock_tables->execute; $unlock_tables->finish;
3413 }
3414
3415 sub nick_change($$$) {
3416 my ($user, $new, $time) = @_;
3417 my $old = $user->{NICK};
3418 return if(lc $old eq lc $new);
3419 print "NICK CHANGE: $old -> $new ($time)\n";
3420 get_lock($old);
3421 nick_cede($new);
3422 $nick_change->execute($new, $old);
3423 $add_nickchg->execute($ircline, $new, $new);
3424 release_lock($old);
3425 if($new =~ /^guest/i) {
3426 $get_guest->execute($new);
3427 my ($guest) = $get_guest->fetchrow_array();
3428 if($guest) {
3429 $set_guest->execute(0, $new);
3430 } else {
3431 guestnick($new);
3432 }
3433 return;
3434 }
3435 my $user = { NICK => $new, AGENT => $nsuser };
3436 get_user_id($user);
3437 ircd::setumode($nsuser, $user, '-r')
3438 unless check_identify({ NICK => $new });
3439 }
3440 sub handle_oper($) {
3441 my ($user) = @_;
3442 my $nick = $user->{NICK};
3443 get_lock($nick);
3444 my $id = get_user_id($user);
3445 $get_umodes->execute($id);
3446 my ($omodes) = $get_umodes->fetchrow_array;
3447 $set_umodes->execute(modes::add($omodes, "o", 0), $id);
3448 #this is _safe_. even an oper block with no privs gets +o
3449 #it's just not passed to srsv for some reason, all we get is :UID opertype X
3450 release_lock ($nick);
3451 }
3452 sub umode($$) {
3453 my ($user, $modes) = @_;
3454 my $nick = $user->{NICK};
3455 get_lock($nick);
3456
3457 my $id = get_user_id($user);
3458
3459 $get_umodes->execute($id);
3460 my ($omodes) = $get_umodes->fetchrow_array;
3461 $set_umodes->execute(modes::add($omodes, $modes, 0), $id);
3462
3463
3464 my %modelist = modes::splitumodes($modes);
3465 if (defined($modelist{x})) {
3466 if($modelist{x} eq '-') {
3467 my ($ident, $host) = get_host($user);
3468 do_chghost(undef, $nick, $host, 1);
3469 }
3470 elsif(($modelist{x} eq '+') and !defined($modelist{t}) ) {
3471 my (undef, $cloakhost) = get_cloakhost($user);
3472 if($cloakhost) {
3473 do_chghost(undef, $nick, $cloakhost, 1);
3474 } else {
3475 ircd::userhost($nick);
3476 }
3477 }
3478 }
3479 =cut
3480 # awaiting resolution UnrealIRCd bug 2613
3481 elsif ($modelist{t} eq '-') {
3482 my %omodelist = modes::splitumodes($omodes);
3483 if($omodelist{x} eq '+') {
3484 my (undef, $cloakhost) = get_cloakhost($user);
3485 if($cloakhost) {
3486 do_chghost(undef, $nick, $cloakhost, 1);
3487 } else {
3488 ircd::userhost($nick);
3489 }
3490 }
3491 }
3492 =cut
3493 release_lock($nick);
3494
3495 # Else we will get it in a sethost or chghost
3496 # Also be aware, our tracking of umodes xt is imperfect
3497 # as the ircd doesn't always report it to us
3498 # This might need fixing up in chghost()
3499 }
3500
3501 sub killhandle($$$$) {
3502 my ($srcUser, $dstUser, $path, $reason) = @_;
3503 my $dst = $dstUser->{NICK};
3504 my $src = $srcUser->{NICK};
3505 print "KILL $src $dst\n";
3506 unless (is_agent($dst)) {
3507 nick_delete($dstUser, "Killed ($src ($reason))");
3508 }
3509 }
3510
3511 sub userip($$$) {
3512 my($src, $nick, $ip) = @_;
3513 my $is_ipv6;
3514 ($is_ipv6, $ip) = is_ipv6($ip);
3515 my $user = { 'NICK' => $nick };
3516 my $new = $newuser{lc $nick};
3517 delete $newuser{lc $nick};
3518 #my $targetid = get_nick_id($target);
3519 my $iip;
3520 if(!$is_ipv6) {
3521 my @ips = split(/\./, $ip);
3522 for(my $i; $i < 4; $i++) {
3523 $iip += $ips[$i] * (2 ** ((3 - $i) * 8));
3524 }
3525 } else {
3526 $iip = Socket6::inet_pton(&AF_INET6, $ip);
3527 }
3528
3529 get_lock($nick);
3530
3531 my $id = get_user_id($user);
3532 if(!$is_ipv6) {
3533 set_ip($user, $iip);
3534 } else {
3535 $iip = get_ipv6_net($ip);
3536 set_ipv6($user, $iip, $ip);
3537 }
3538 my $killed = kill_clones($user, $iip);
3539
3540 release_lock($nick);
3541
3542 nick_create_post($nick) if(!$killed and $new);
3543 }
3544
3545 sub chghost($$$) {
3546 my ($src, $dst, $vhost) = @_;
3547 my $user = { NICK => $dst };
3548 my $uid = get_user_id($user);
3549
3550 get_lock($dst);
3551 do_chghost($src, $dst, $vhost, 1);
3552
3553 $get_umodes->execute($uid);
3554 my ($omodes) = $get_umodes->fetchrow_array;
3555 # I'm told that this is only valid if CLK is set, and
3556 # there is no good way yet to get info from the ircd/net
3557 # module to this code. it stinks of ircd-specific too
3558 # Also, we currently do any USERHOST replies as CHGHOST events
3559 # However, that is no longer necessary with CLK
3560 $set_umodes->execute(modes::add($omodes, '+xt', 0), $uid);
3561 release_lock($dst);
3562 }
3563
3564 sub do_chghost($$$;$) {
3565 # Don't use this for the handler,
3566 # this is only for internal use
3567 # where we don't want full loopback semantics.
3568 # We call it from the normal handler.
3569 my ($src, $dst, $vhost, $no_lock) = @_;
3570 # $no_lock is for where we already took the lock in the caller
3571 # MySQL's GET LOCK doesn't allow recursive locks
3572 my $user = { NICK => $dst };
3573 my $uid = get_user_id($user);
3574
3575 $update_regnick_vhost->execute($vhost, $uid);
3576 $update_regnick_vhost->finish();
3577
3578 get_lock($dst) unless $no_lock;
3579
3580 set_vhost($user, $vhost);
3581 chanserv::akick_alluser($user);
3582
3583 release_lock($dst) unless $no_lock;
3584 }
3585
3586 sub chgident($$$) {
3587 my ($src, $dst, $ident) = @_;
3588 my $user = { NICK => $dst };
3589
3590 set_ident($user, $ident);
3591 chanserv::akick_alluser($user);
3592 }
3593
3594 1;