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