]>
Commit | Line | Data |
---|---|---|
7b261bb8 | 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 adminserv; | |
17 | ||
18 | use strict; | |
19 | ||
20 | use SrSv::Agent; | |
21 | ||
22 | use SrSv::Text::Format qw(columnar); | |
23 | use SrSv::Errors; | |
24 | ||
25 | use SrSv::User qw(get_user_nick get_user_id); | |
26 | use SrSv::User::Notice; | |
27 | use SrSv::Help qw( sendhelp ); | |
28 | ||
29 | use SrSv::Log; | |
30 | ||
31 | use SrSv::NickReg::Flags qw(NRF_NOHIGHLIGHT nr_chk_flag_user); | |
32 | ||
33 | use SrSv::MySQL '$dbh'; | |
34 | ||
35 | use constant { | |
36 | S_HELP => 1, | |
37 | S_OPER => 2, | |
38 | S_ADMIN => 3, | |
39 | S_ROOT => 4, | |
40 | }; | |
41 | ||
d851df6a | 42 | our (%flags, @levels, @defflags, $allflags); |
43 | ||
44 | BEGIN { | |
6596c525 | 45 | # BE CAREFUL CHANGING THESE |
7b261bb8 | 46 | my @flags = ( |
912ac8a0 | 47 | 'LOG', |
7b261bb8 | 48 | 'SERVOP', |
49 | 'FJOIN', | |
50 | 'SUPER', | |
51 | 'HOLD', | |
52 | 'FREEZE', | |
53 | 'BOT', | |
54 | 'QLINE', | |
55 | 'KILL', | |
56 | 'HELP', | |
57 | ); | |
d851df6a | 58 | |
7696b939 | 59 | for(my $i = scalar(@flags) - 1; $i >= 0; $i--) { |
6596c525 | 60 | $flags{$flags[$i]} = 1 << $i; |
7b261bb8 | 61 | } |
6596c525 | 62 | $allflags = (1 << scalar(@flags)) - 1; |
7b261bb8 | 63 | our @levels = ('Normal User', 'HelpOp', 'Operator', 'Administrator', 'Root'); |
6596c525 | 64 | # BE CAREFUL CHANGING THESE |
d851df6a | 65 | our @defflags = ( |
7b261bb8 | 66 | 0, # Unused |
67 | $flags{HELP}, # HelpOp | |
68 | $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}, # Operator | |
d851df6a | 69 | $flags{HELP}|$flags{FJOIN}|$flags{QLINE}|$flags{SUPER}|$flags{FREEZE}|$flags{KILL}| |
912ac8a0 | 70 | $flags{HOLD}|$flags{BOT}|$flags{SERVOP}|$flags{LOG}, # Admin |
7b261bb8 | 71 | $allflags # Root |
72 | ); | |
73 | ||
d851df6a | 74 | } |
75 | our $asnick_default = 'AdminServ'; | |
76 | our $asnick = $asnick_default; | |
77 | ||
78 | ||
7b261bb8 | 79 | our ( |
80 | $create_svsop, $delete_svsop, $rename_svsop, | |
81 | ||
82 | $get_svs_list, $get_all_svsops, | |
83 | ||
84 | $get_svs_level, $set_svs_level, $get_best_svs_level, | |
85 | ||
86 | $chk_pass, $get_pass, $set_pass | |
87 | ); | |
88 | ||
89 | sub init() { | |
90 | $create_svsop = $dbh->prepare("INSERT IGNORE INTO svsop SELECT id, NULL, NULL FROM nickreg WHERE nick=?"); | |
91 | $delete_svsop = $dbh->prepare("DELETE FROM svsop USING svsop, nickreg WHERE nickreg.nick=? AND svsop.nrid=nickreg.id"); | |
92 | ||
93 | $get_svs_list = $dbh->prepare("SELECT nickreg.nick, svsop.adder FROM svsop, nickreg WHERE svsop.level=? AND svsop.nrid=nickreg.id ORDER BY nickreg.nick"); | |
94 | $get_all_svsops = $dbh->prepare("SELECT nickreg.nick, svsop.level, svsop.adder FROM svsop, nickreg WHERE svsop.nrid=nickreg.id ORDER BY svsop.level, nickreg.nick"); | |
95 | ||
96 | $get_svs_level = $dbh->prepare("SELECT svsop.level FROM svsop, nickalias WHERE nickalias.alias=? AND svsop.nrid=nickalias.nrid"); | |
97 | $set_svs_level = $dbh->prepare("UPDATE svsop, nickreg SET svsop.level=?, svsop.adder=? WHERE nickreg.nick=? AND svsop.nrid=nickreg.id"); | |
98 | $get_best_svs_level = $dbh->prepare("SELECT svsop.level, nickreg.nick FROM nickid, nickreg, svsop WHERE nickid.nrid=nickreg.id AND svsop.nrid=nickreg.id AND nickid.id=? ORDER BY level DESC LIMIT 1"); | |
99 | ||
100 | $chk_pass = $dbh->prepare("SELECT 1 FROM ircop WHERE nick=? AND pass=?"); | |
101 | $get_pass = $dbh->prepare("SELECT pass FROM ircop WHERE nick=?"); | |
102 | $set_pass = $dbh->prepare("UPDATE ircop SET pass=? WHERE nick=?"); | |
103 | } | |
104 | ||
105 | ### ADMINSERV COMMANDS ### | |
106 | ||
107 | sub dispatch($$$) { | |
108 | my ($src, $dst, $msg) = @_; | |
109 | $msg =~ s/^\s+//; | |
110 | my @args = split(/\s+/, $msg); | |
111 | my $cmd = shift @args; | |
112 | ||
113 | my $user = { NICK => $src, AGENT => $dst }; | |
114 | ||
115 | services::ulog($asnick, LOG_INFO(), "cmd: [$msg]", $user); | |
116 | ||
117 | unless(is_svsop($user) or is_ircop($user)) { | |
118 | notice($user, $err_deny); | |
119 | ircd::globops($asnick, "\002$src\002 failed access to $asnick $msg"); | |
120 | return; | |
121 | } | |
122 | ||
123 | if($cmd =~ /^svsop$/i) { | |
124 | my $cmd2 = shift @args; | |
125 | ||
126 | if($cmd2 =~ /^add$/i) { | |
127 | if(@args == 2 and $args[1] =~ /^[aoh]$/i) { | |
128 | as_svs_add($user, $args[0], num_level($args[1])); | |
129 | } else { | |
130 | notice($user, 'Syntax: SVSOP ADD <nick> <A|O|H>'); | |
131 | } | |
132 | } | |
133 | elsif($cmd2 =~ /^del$/i) { | |
134 | if(@args == 1) { | |
135 | as_svs_del($user, $args[0]); | |
136 | } else { | |
137 | notice($user, 'Syntax: SVSOP DEL <nick>'); | |
138 | } | |
139 | } | |
140 | elsif($cmd2 =~ /^list$/i) { | |
141 | if(@args == 1 and $args[0] =~ /^[raoh]$/i) { | |
142 | as_svs_list($user, num_level($args[0])); | |
143 | } else { | |
144 | notice($user, 'Syntax: SVSOP LIST <R|A|O|H>'); | |
145 | } | |
146 | } | |
147 | else { | |
148 | notice($user, 'Syntax: SVSOP <ADD|DEL|LIST> [...]'); | |
149 | } | |
150 | } | |
151 | elsif($cmd =~ /^whois$/i) { | |
152 | if(@args == 1) { | |
153 | as_whois($user, $args[0]); | |
154 | } else { | |
155 | notice($user, 'Syntax: WHOIS <nick>'); | |
156 | } | |
157 | } | |
158 | elsif($cmd =~ /^help$/i) { | |
159 | sendhelp($user, 'adminserv', @args) | |
160 | } | |
161 | elsif($cmd =~ /^staff$/i) { | |
162 | if(@args == 0) { | |
163 | as_staff($user); | |
164 | } | |
165 | else { | |
166 | notice($user, 'Syntax: STAFF'); | |
167 | } | |
168 | } | |
169 | else { | |
170 | notice($user, "Unrecognized command. For help, type: \002/msg adminserv help\002"); | |
171 | } | |
172 | } | |
173 | ||
174 | sub as_svs_add($$$) { | |
674fc60a | 175 | my ($user, $target_nick, $target_level) = @_; |
7b261bb8 | 176 | my $src = get_user_nick($user); |
674fc60a | 177 | my $target_nickreg = nickserv::get_root_nick($target_nick); |
178 | my ($src_level, $user_oper) = get_best_svs_level($user); | |
7b261bb8 | 179 | |
674fc60a | 180 | if($target_level >= $src_level) { |
7b261bb8 | 181 | notice($user, $err_deny); |
182 | return; | |
183 | } | |
184 | ||
68579027 | 185 | if (!$target_nickreg) { |
186 | notice($user, "$target_nick is not registered."; | |
187 | return; | |
188 | } | |
189 | ||
674fc60a | 190 | $create_svsop->execute($target_nickreg); |
191 | $set_svs_level->execute($target_level, $user_oper, $target_nickreg); | |
192 | ||
193 | notice($user, "\002$target_nick\002 is now a \002Services $levels[$target_level]\002."); | |
194 | wlog($asnick, LOG_INFO(), "$src added $target_nickreg as a Services $levels[$target_level]."); | |
7b261bb8 | 195 | } |
196 | ||
197 | sub as_svs_del($$) { | |
674fc60a | 198 | my ($user, $target_nick) = @_; |
7b261bb8 | 199 | my $src = get_user_nick($user); |
674fc60a | 200 | my $target_nickreg = nickserv::get_root_nick($target_nick); |
7b261bb8 | 201 | |
674fc60a | 202 | if(get_svs_level($target_nickreg) >= get_svs_level($src)) { |
7b261bb8 | 203 | notice($user, $err_deny); |
204 | return; | |
205 | } | |
674fc60a | 206 | |
207 | $delete_svsop->execute($target_nickreg); | |
208 | notice($user, "\002$target_nickreg\002 has been stripped of services rank."); | |
209 | wlog($asnick, LOG_INFO(), "$src stripped $target_nickreg of services rank.") | |
7b261bb8 | 210 | } |
211 | ||
212 | sub as_svs_list($$) { | |
213 | my ($user, $level) = @_; | |
214 | my (@data, @reply); | |
215 | ||
216 | $get_svs_list->execute($level); | |
217 | ||
218 | while(my ($nick, $adder) = $get_svs_list->fetchrow_array) { | |
219 | push @data, [$nick, "($adder)"]; | |
220 | } | |
221 | ||
222 | notice($user, columnar({TITLE => "Services $levels[$level] list:", | |
223 | NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data)); | |
224 | } | |
225 | ||
226 | sub as_whois($$) { | |
227 | my ($user, $nick) = @_; | |
228 | ||
229 | my ($level, $root) = get_best_svs_level({ NICK => $nick }); | |
230 | notice($user, "\002$nick\002 is a Services $levels[$level]".($level ? ' due to identification to the nick '."\002$root\002." : '')); | |
231 | } | |
232 | ||
233 | sub as_staff($) { | |
234 | my ($user) = @_; | |
235 | my (@data); | |
236 | ||
237 | $get_all_svsops->execute(); | |
238 | ||
239 | while(my ($nick, $level, $adder) = $get_all_svsops->fetchrow_array) { | |
240 | push @data, [$nick, $levels[$level], "($adder)"]; | |
241 | } | |
242 | ||
243 | notice($user, columnar({TITLE => 'Staff list:', | |
244 | NOHIGHLIGHT => nr_chk_flag_user($user, NRF_NOHIGHLIGHT)}, @data)); | |
245 | } | |
246 | ||
247 | ||
248 | ### DATABASE UTILITY FUNCTIONS ### | |
249 | ||
250 | sub validate_chg($$) { | |
674fc60a | 251 | my ($user, $target_nick) = @_; |
252 | my ($user_oper); | |
7b261bb8 | 253 | |
674fc60a | 254 | unless($user_oper = is_svsop($user, S_ROOT)) { |
7b261bb8 | 255 | notice($user, $err_deny); |
256 | return undef; | |
257 | } | |
258 | ||
674fc60a | 259 | my $target_nickreg = nickserv::get_root_nick($target_nick); |
260 | unless($target_nickreg) { | |
261 | notice($user, "The nick \002$target_nick\002 is not registered."); | |
7b261bb8 | 262 | return undef; |
263 | } | |
264 | ||
674fc60a | 265 | return ($target_nickreg, $user_oper); |
7b261bb8 | 266 | } |
267 | ||
268 | sub can_do($$) { | |
269 | my ($user, $flag) = @_; | |
270 | my $nflag = $flags{$flag}; | |
271 | ||
272 | my ($level, $nick) = get_best_svs_level($user); | |
273 | ||
274 | if($defflags[$level] & $nflag) { | |
275 | return $nick if (($nflag == $flags{'HELP'}) or is_ircop($user)); | |
276 | } | |
277 | ||
278 | return undef; | |
279 | } | |
280 | ||
281 | sub is_svsop($;$) { | |
282 | my ($user, $rlev) = @_; | |
283 | ||
284 | my ($level, $nick) = get_best_svs_level($user); | |
285 | return $nick if(defined($level) and !defined($rlev)); | |
286 | ||
287 | if($level >= $rlev) { | |
288 | return $nick if (($rlev == S_HELP) or is_ircop($user)); | |
289 | } | |
290 | ||
291 | return undef; | |
292 | } | |
293 | ||
294 | sub is_ircop($) { | |
295 | my ($user) = @_; | |
296 | ||
297 | return undef if is_agent($user->{NICK}); | |
298 | ||
299 | return $user->{IRCOP} if(exists($user->{IRCOP})); | |
300 | ||
301 | my %umodes = modes::splitumodes(nickserv::get_user_modes($user)); | |
302 | ||
303 | no warnings 'deprecated'; | |
304 | if(($umodes{'o'} eq '+') or ($umodes{'S'} eq '+')) { | |
305 | $user->{IRCOP} = 1; | |
306 | } | |
307 | else { | |
308 | $user->{IRCOP} = 0; | |
309 | } | |
310 | ||
311 | return $user->{IRCOP}; | |
312 | } | |
313 | ||
314 | sub is_service($) { | |
315 | # detect if a user belongs to another service like NeoStats. only works if they set umode +S | |
316 | # is_ircop() includes is_service(), so no reason to call both. | |
317 | my ($user) = @_; | |
318 | ||
319 | return undef if is_agent($user->{NICK}); | |
320 | ||
321 | return $user->{SERVICE} if(exists($user->{SERVICE})); | |
322 | ||
323 | my %umodes = modes::splitumodes(nickserv::get_user_modes($user)); | |
324 | ||
325 | if($umodes{'S'} eq '+') { | |
326 | $user->{SERVICE} = 1; | |
327 | $user->{IRCOP} = 1; | |
328 | } | |
329 | else { | |
330 | $user->{SERVICE} = 0; | |
331 | } | |
332 | ||
333 | return $user->{SERVICE}; | |
334 | } | |
335 | ||
336 | sub get_svs_level($) { | |
337 | my ($nick) = @_; | |
338 | ||
339 | return undef if is_agent($nick); | |
340 | ||
341 | $get_svs_level->execute($nick); | |
342 | my ($level) = $get_svs_level->fetchrow_array; | |
343 | ||
344 | return $level or 0; | |
345 | } | |
346 | ||
347 | sub get_best_svs_level($) { | |
348 | my ($user) = @_; | |
349 | ||
350 | return undef if is_agent($user->{NICK}); | |
351 | ||
352 | if(exists($user->{SVSOP_LEVEL}) && exists($user->{SVSOP_NICK})) { | |
353 | if(wantarray) { | |
354 | return ($user->{SVSOP_LEVEL}, $user->{SVSOP_NICK}); | |
355 | } else { | |
356 | return $user->{SVSOP_LEVEL}; | |
357 | } | |
358 | } | |
359 | ||
360 | my $uid = get_user_id($user); | |
361 | $get_best_svs_level->execute($uid); | |
362 | my ($level, $nick) = $get_best_svs_level->fetchrow_array; | |
363 | ||
364 | $user->{SVSOP_LEVEL} = $level; $user->{SVSOP_NICK} = $nick; | |
365 | ||
366 | if(wantarray) { | |
367 | return ($level, $nick); | |
368 | } else { | |
369 | return $level; | |
370 | } | |
371 | } | |
372 | ||
373 | ### MISCELLANEA ### | |
374 | ||
375 | sub num_level($) { | |
376 | my ($x) = @_; | |
377 | $x =~ tr/hoarHOAR/12341234/; | |
378 | return $x; | |
379 | } | |
380 | ||
381 | ### IRC EVENTS ### | |
382 | ||
383 | 1; |