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