# CETK epic cryptographically secure auto-op script 1.0 # http://www.epicsol.org/~crazyed/cetk.opme # unload cetk.opme package cetk.opme # # Required (epic dist). load functions load commandqueues load data_array load guh # # Required (CETK) load cetk.functions load cetk.chanmgmt # # Suggested. # userlist for n!u@h authentication. load cetk.userlist load cetk.clonecheck # # # Please note that the ceop system has companion scripts for eggdrop and irssi, # which can be found along with this one in the full CETK collection at # www.epicsol.org/~crazyed/ . # # This script implements two different systems. # # The first system is /opme, which is an extensible system for automatically # finding and interacting with bots and services for which it has been # configured to do so. The primary purpose is for requesting ops in a channel, # but it can be used for other bot functions like "ghost" on services and # "identify" on eggdrops. # # The second system (ceop) implements a client and server for an extensible # auto-op system. It uses a cryptographic password system designed to be # secure enough for the average IRC network and is explained further down. Its # primary purpose is to allow anybody that knows a single easily configured # secret key on any opped server to be granted ops in a channel. It can, # however, be extended to perform other functions securely. It also comes with # the tools necessary (/ceop and /ceop.autoop) to make these requests. # # A few example extensions have been provided with both systems, and some of # them are far too dangerous for the average user to run, so it is necessary to # permit their use for each key and it is possible to remove the extensions # from the system entirely without too much difficulty. # # Both of these systems are completely independant, but they can be used # together. Some good reasons to do this stem from the fact that ceop can only # work automatically on its own by sending a request directly to a channel, # which will probably annoy a lot of people and won't work anyway if nobody # else is using this system. # # # The interfaces for this system are these: # # /opme.setop - Add and configure a bot or service. # /opme - Request ops or other features from a configured bot/service. # /ceop.setkey - Configure a ceop key for use with this and other clients. # /ceop.autoop - Make a ceop request to all channels for all configured keys. # /ceop - Make a ceop request, but specify key, destination, etc. # /ceop.opme - A stub which is called internally when ops is required. # /ceop.cmdstrip - A security feature for removing ceop extensions. # # All of these with the exception of /ceop.opme display information about what # they do or what they're doing if run with no arguments. # # /ceop.opme being a stub, is intended to be a configuration variable and by # default, simply calls "/opme opme #chan". You can redefine it to use # ceop.autoop or any other system you might require. Note that the first # argument given is the channel for which ops is required. # alias ceop.opme ceop.autoop $*; alias ceop.opme opme opme $*; # # Example CEOP usage: # # Set a key for use in the next examples. The default level is 0. # /ceop.setkey testkey * # # Join a test channel. # /join #qwer # # Launch a second client and follow these instructions up to this point for it # too. Then continue these instructions on the un opped one. # # Ask for ops from any server on the channel. # /ceop testkey #qwer # # Ask for ops from a particular server on the channel. # /ceop testkey opsnick #qwer # # Autoop does the same thing for every matching configured key automatically. # /ceop.autoop opsnick #qwer # # Distributed mass voice requires a higher key level. Run this on the server. # /ceop.setkey testkey 1 * # # Distributed mass voice everyone on a channel. This works best when there are # many servers configured with the same key on the channel. # /ceop.autoop #qwer voice * # # Exec requires yet a higher key level. Run this on the server. # /ceop.setkey testkey 1000 * # # Ask other clients to execute an irc command. # /ceop.autoop #qwer cmd msg #qwer My pid is not $pid(). # # Ask other clients to evaluate an irc command. # /ceop.autoop #qwer eval msg #qwer My pid is $pid(). # # Ask other clients to execute a program and send back the output. # /ceop.autoop #qwer exec id # # Echo a ctcp command that another user can use to trigger the mass voice # behaviour above. This is good for friends who can't or won't load this # script, but would like to make use of it anyway. This works particularly # well for static addresses. # /ceop.autoop #qwer #qwer voice * -f othernick # # Add a timestamp to the last example that causes it to expire after one day. # The ctcp will continue to work until the expiry date, or the n!u@h of the # user changes. # /ceop.autoop #qwer #qwer voice * -f othernick -T 86400 # # Kill the key in all clients on #qwer that know about it except yours. # /ceop testkey #qwer clobber # # These will both kill your copy of the key. # /ceop.setkey testkey -1 * # /ceop.setkey testkey # # This will clobber keys on other clients that are clobbered on yours. # /ceop.autoop #qwer #qwer clobber -l -1 # # The clobber command will make the key unusable on servers that receive it, # but autoop will still use clobbered keys. # # Remove the cmd, eval and exec commands so that they can't be used through # misconfigured keys. This is useful for putting in ~/.epicrc after the line # that loads this script. # /ceop.cmdstrip 1000 * # # Remove a particular command. # /ceop.cmdstrip 0 op # # # Example OPME usage: # # Configure details for X on Undernet. Replace "nick" and "pass" with yours. # /OPME.SETOP X X x *!cservice@undernet.org "nick pass" undernet * service # # Configure an eggdrop bot. You need to fill in your own details here. # /OPME.SETOP bot eggdrop botnick *!botuser@bothost.com mypass somenet #chan "" # # Configure details for another ceop client. CEOP _can_ be useful from outside # the channel, so we can sometimes give it a services flag. # /OPME.SETOP chankey ceop_priv nick *!user@host ceopkey somenet #chan "" # # Configure details for a channel in which ceop is used. The services flag can # be used here so long as the channel is not +n . The nick and userhost fields # are *'s here, but it may be worth setting them to someone you know to be on # the channel to use it from outside and to prevent it misfiring. # /OPME.SETOP chankey ceop_pub * * key somenet #chan "" # # Configuring a bot interactively involves running /OPME.SETOP with no # arguments, then re-entering the command with the detail it asks for. The up # arrow is useful here. When /ceop.setop accepts the command, up arrow again, # and cut'n'paste the command onto the end of ~/.epicrc . # # Beg ops on the current channel from all configured ops. "opme" will be the # default argument if none are supplied. Not all bots/services require the # login command. # /opme login # /opme opme # # Ask ceop_priv and ceop_pub clients to execute a ceop command. This is taken # from a ceop example above. The channel is purely arbitrary here. If the # "service" flag has been given and its nick has been configured, ceop_priv # bots don't even have to be on common channels, although, it will take 5 # seconds to message each bot. # /opme opme # exec id # # ceop_pub is roughly as useful as ceop.autoop, but doesn't require the key to # be available for others to use on your end of the client, and it doesn't # require you to change the ceop.opme stub just for the purpose of using the # autoop feature. # # # Technical Details. # # The "crypto" in this script is based on a hash based challenge handshake # protocol, where a "client" requests authentication from a "server", which # sends back a "challenge". The client then "encrypts" the challenge and key # using a hash algorithm such as md5, and sends the hash back to the server, # which performs the same encryption and compares the hashes. This method is # useful because the password is never handed to anybody that may want to use # it unscrupulously, and because so long as the challenge is not reused, nobody # else will be able to use the same hash for authentication. Neat, huh? # # On IRC, doing a challenge handshake with all individual ops in a channel is # not so easy, because this protocol cannot be implemented without at least one # initial public message and two private messages for each op. It would be # completely unworkable. What we do here is reduce it to one public message to # the channel by faking the challenge. # # This particular implementation cheats by using your nick!user@host as the # challenge, meaning that on the average irc network, the request is only valid # when it's coming from you, but also, since this isn't a true challenge # handshake protocol, that anybody on the network that can change their own # nick!user@host to exactly what yours is, is able to reuse any request you # make and fool this protocol. Typically the only people on an irc network # that can do this are people that can hack ops using other means anyway, and # in any case, there is no way to find the original key by reusing a request, # and there is no way to change the request without making the hash invalid, so # this flaw in the protocol _probably_ isn't much of an issue. # # miscelaneous notes: # # There are no limits to the key length since it doesn't even pass through the # server to get the opportunity to meet the 512 char limit. # # No global channel configuration is required. As long as there are two # clients on a channel using a common key for that channel, this script can be # useful. This is particularly true since a valid request received by an # unopped client will cause it to attempt to op itself recursively using # methods the sender may not know about or have access to. # # This is not a typical authentication mechanism in that the shared password is # not bound to a username. This is a weakness because anybody fishing for # passwords gets to try everybody on a channel at once. You can however embed # the identity of key users into the key itself by specifying user/key (note # the slash) as a key, or by reserving part of the key as a "username". This # also means that multiple keys can be configured for a channel representing # different groups and that universal keys can be set for groups that cover # multiple channels. # # This script doesn't implement a key exchange protocol. You need to figure # out your own secure ways to do that. I suggest using dcc, email, the phone # or face to face meetings, perhaps with pgp or gnupg encryption, depending on # your security requirements. # # NOTE: Key management can be implemented with something as simple as the # following lines which do however require a properly configured gnupg: # ON #^ctcp_req + "% % pgp **" exec -name gpg_$0_$1_$N -line {$*} gpg --batch;exec -in %gpg_$0_$1_$N $3- # ON #^ctcp_req + "% % pgp ---%end * message%---" exec -closein %gpg_$0_$1_$N # # This script doesn't implement a feature that allows a key to be changed, but # it does permit any keyholder to have it "clobbered" in case of emergencies. # # # The script itself starts here. # # # Supporting functions. Just md5 at the moment. # alias ceop.md5 (args) { if (perl(use Digest::MD5 qw(md5 md5_hex md5_base64);defined &md5_hex)) { ^alias ceop.md5 (args) return \$perlcall(md5_hex $args) } elsif (0 <= tcl(package require md5;lsearch -exact [package names] md5)) { ^alias ceop.md5 (args) return \$tcl(md5::md5 -hex [join [epic expr args]]) } else { ^alias ceop.md5 (args) { @ :exe = glob(\{$sar(g/:/,/$PATH)\}/md5\{sum,\}) fe ($exec($shift(exe))) in out err {break} @ close($err) @ writeb($in $args) @ close($in) @ function_return = read($out) @ function_return = shift(function_return) @ close($out) } } return $ceop.md5($args) } # # Send a ceop request. [key] is required but [dest] will be the current # channel and [#channel] is extrapolated by the receiver if not supplied. If # the key contains a slash (/), whatever is before it is taken for a user name, # and the rest is the key. # # [dest] is where the request will be sent to, and [#channel] is the one the # request is in reguards to. These arguments are handy if you can't or don't # want to send the request to the channel. # alias ceop (args) { @ :oargs = args #@ :cmd = [q1cmd 0 9 ] @ :cmd = [] while (:option = getopt(:optopt :optarg d:f:L:l:m:T:t $args)) { #echo $option $optopt $optarg @ ++:opts switch ($option) { (d) {@ :dest = uniq($dest $optarg)} (f) { @ :nick = uniq($:nick $split(, $optarg)) @ :cmd = [echo \$nick: /] } (l) { unless (index(L $option)) {@ :minlev = optarg} unless (index(l $option)) {@ :maxlev = optarg} } (m) {push :mask $optarg} (t) {@ :ts = optarg + time()} (!) (-) { echo usage: echo ceop {[user]/}[key] {[dest] {[#channel] {[additional-protocol-commands]}}} {[options]} echo ceop.autoop {[dest] {[#channel] {[additional-protocol-commands]}}} {[options]} echo Invalid arg or option: -$optopt $optarg echo -d nick Specify an explicit destination here instead. echo -f nick Echo a ctcp command for a friend to use. echo -m mask Key must match mask. For use with autoop. echo -l level Key must have at most this level. (autoop) echo -L level Key must have at least this level. (autoop) echo -t Add an expiry timestamp to the request. echo -T number Add a particular timestamp to the request. return } } } @ :args = opts ? optarg : args @ :args = sar(/ -- / /$args) #echo $args @ :nick = nick ? nick : servernick() @ :uh = nick == servernick() && userhost($nick) == userhost(,) ? X : userhost($nick) @ :key = shift(args) @ :id = index(,/ $key) + 1 @ :user = left($id $key) @ :key = rest($id $key) @ :euser = encode($chop(1 $user)) @ :ekey = encode($key) @ :dest = dest ? uniq($dest) : #args ? shift(args) : C if (1>#key) { echo /ceop requires a key. ceop -h } elsif (dest =~ [*,*]) { fe dest dest { if (dest =~ [*,*]) { fe ($split(, $dest)) ndest {q1cmd 300,300 9 ceop $sar(g/ $dest / $ndest /$oargs)} } } } elsif (!cmd && ischanop($nick $dest) && !#args) { } elsif (!cmd && ischanop($nick $args) && 1==#args) { } elsif (maxlev && maxlev < ceop[uk][$euser][$ekey][cmdlevel]) { } elsif (minlev && minlev > ceop[uk][$euser][$ekey][cmdlevel]) { } elsif (mask && !rmatch($user$key $mask)) { } else { unshift args $unsplit(" " ${ts && ischannel($args) ? shift(args) : []} $ts) fe ($joinstr(! nick uh)) nuh { @ :nick = before(! $nuh) eval ^local ecmd $cmd if (nuh == [$nick!$userhost(,)]) { echo Cannot find userhost for $nick . } else { fe ($dest) dst { ${ecmd}ctcp $dst op $user$ceop.md5($nuh $key $dst $args) $args } } } if (isdisplaying() && !mychannels()) { echo It may be necessary to join a channel for ceop to work. } } } # # Send a template ceop request to all matching channels for all defined keys. # This is really a "wrapper" alias. All options processing is done by /ceop. # # It will surely bother people if you use this with keys with badly chosen # channel masks. # alias ceop.autoop (args) { @ :w0 = word(0 $args) @ :mc = ischannel($w0) @ :mc|= 0 <= index(*%? $w0) @ :mc|= w0 && userhost(,) != userhost($w0) @ :mc = mc ? shift(args) : C @ :mc = split(, $mc) fe mc mc {@ :mc = 0 > index(?*% $mc) ? mc : pattern($mc $mychannels())} @ :mc = uniq($mc) foreach ceop[uk] user { foreach ceop[uk][$user] ekey { @ :chans = ceop[uk][$user][$ekey][chans] fe chans chans {@ :chans = pattern($chans $mc)} @ :chans = uniq($chans) @ :user#= user ? [CM] : [] fe chans chan { if (args || !ischanop($servernick() $chan)) { @ :requests++ ceop $decode($user$ekey) $chan $args } else { @ chan = [] } } push :requested $chans } } qcmd.421 9 if (requests && isdisplaying()) { echo sending $requests ceop requests to $uniq($requested) } } # # Define or undefine a key, and its parameters. # # [cmdlevel] is numeric and not mandatory. It defines which of the ceop # commands are available for this key. If set to -1, the key is "clobbered" # and invalid. Setting this value above 999 basically makes epic a botnet # client, and setting it above 899 makes it an X style service. Don't set it # above 0 without a good reason. # # [expiry] is the maximum permitted expiry time for time stamped requests and # [grace] is the grace period. If the senders clock is out of sync with the # receivers, the test is more likely to fail. If these args are specified, # they should be set to a reasonably high value. # # [usermask] and [shitmask] are also numeric and not mandatory. They are # references to the cetk.userlist script and default to 0 which turn these # features off. If any of the given usermask bits match any of the nuhs # usermask bits and none of the given shitmask bits match the nuhs shitmask # bits, the request is accepted. # # The remaining args are channel masks for which the key applies. Undefine a # key by supplying no args. # alias ceop.setkey (args) { if (1>#args) { echo requires: {[user]/}[key] {[cmdlevel] {[expiry] {[grace] {[usermask] {[shitmask]}}}}} {[chanmask]} ... } else { @ :key = shift(args) @ :ind = index(,/ $key) @ :user = left($ind $key) @ :user = encode($user) @ :key = rest(${++ind} $key) @ :ekey = encode($key) @ ceop[uk][$user][$ekey][cmdlevel] = isnumber(b10 $args) ? shift(args) : [] @ ceop[uk][$user][$ekey][expiry] = :expiry = isnumber(b10 $args) ? shift(args) : [] @ ceop[uk][$user][$ekey][grace] = isnumber(b10 $args) ? shift(args) : expiry @ ceop[uk][$user][$ekey][user] = isnumber(b10 $args) ? shift(args) : [] @ ceop[uk][$user][$ekey][shit] = isnumber(b10 $args) ? shift(args) : [] @ ceop[uk][$user][$ekey][chans] = args } } # # List all configured ceop data. At the moment this is just keys. Do it in # such a way that we can cut and paste it back into the configuration command. # alias ceop.list (args default *) { echo /$chr(2)ceop.setkey$chr(2) keys matching: $args foreach ceop[uk] user { foreach ceop[uk][$user] ekey { @ :key = decode(${user}CM${ekey}) @ :echo = [cmdlevel expiry grace user shit chans] fe echo echo { @ :echo = ceop[uk][$user][$ekey][$echo] } if (rmatch("$key $echo" $args)) { echo $key $echo } } } } # # Process an incoming ceop request. # # The first arg to the request is an md5 hash. If the hash matches the md5 of # the entire request including the users nick!user@host and any secret key we # have, but excluding the hash, the request is valid. # # The second is an optional channel that the request is to be in reguards to. # If not supplied, it is the destination of the request. # # The third is an optional unix timestamp. If this is supplied and older than # the maximum age of the key, the request is discarded. # # Remaining args if any are the request. The default action is to unban, # invite or op the user on the channel requested if there are no additional # args. # # [commands] can be given after the channel, but the command level for the key # (the first arg in setkey) must be above the commands level. The source code # of this hook is authoritative reguarding the commands and levels, but here's # a brief explanation of how the levels have been defined: # # -1: Reserved for invalid keys. # 0: Channel management level. Allows a user to be opped, keys to be # clobbered and modes to be set. # 200: join, part. # 600: quit, pretend. # 800: Allows use of any oper commands available to the client. # 1000: Allows use of eval and exec commands. Whoever knows a key set to this # level can be assumed to 0wn the shell account this client is running # on. # # Note that the inbuilt commands (no command and "clobber") in this hook do # respect the command level, however, there is no command to define these # levels, which might be necessary if you want to add commands that say, voice # a user without giving them ops. The clobber command should _probably_ be set # at a very low level though, because it represents the least amount of damage # that can possibly be done with a comprimised key. # # I also want to add true challenge handshaking. It's probably best to add it # here, after the point where the far end has shown that it knows the key. # This should save time and bandwidth by bypassing unnecessary handshakes with # other clients that don't share a key. # # What would also be cool is if ctcps could be "wrapped" in recursive fashion, # to give "authenticated" ctcp. This is what the pretend command is for at # this point, but this will probably change. # on #^ctcp_request 0 "% % \[ceop op\] *" { @ :nick = [$0] @ :args = [$3-] @ :md5 = shift(args) @ :chan = ischannel($args) ? shift(args) : [$1] @ :utim = isnumber(b10 $args) ? shift(args) : 0 @ :cmd = shift(args) #if (cmd == [dist]) {@ :dist=1, :cmd = shift(args)} @ :cmd = pass($jotc(09azAZ)._ $cmd) @ :ind = index(,/ $md5) + 1 @ :duser = chop(1 $left($ind $md5)) @ :euser = encode($duser) @ :md5 = rest($ind $md5) foreach ceop[uk][$euser] ekey { @ :key = decode($ekey) if (!rmatch($chan $ceop[uk][$euser][$ekey][chans])) { continue } elsif (md5 != ceop.md5($0!$userhost() $key $1 $4-)) { uhfix $0 continue } elsif (ceop[uk][$euser][$ekey][cmdlevel] && ceop[uk][$euser][$ekey][cmdlevel] < 0) { echo attempted use of clobbered ceop key: $key : $0-1 $3- } elsif (ceop[uk][$euser][$ekey][cmdlevel] < ceop[cmdlev][$cmd]) { echo valid ceop request without required key command level: $0-1 $3- } elsif (ceop[uk][$euser][$ekey][expiry] < (utim - time()) && utim && ceop[uk][$euser][$ekey][expiry]) { echo valid ceop request, but dated at $strftime($utim %F %T): $0-1 $3- } elsif (ceop[uk][$euser][$ekey][grace] < (time() - utim) && utim && ceop[uk][$euser][$ekey][grace]) { echo valid ceop request, but dated at $strftime($utim %F %T): $0-1 $3- } elsif (ceop[uk][$euser][$ekey][user] && !(ceop[uk][$euser][$ekey][user] & checkuser($0!$userhost() $chan))) { echo valid ceop request from non-user: $0-1 $3- } elsif (ceop[uk][$euser][$ekey][shit] && (ceop[uk][$euser][$ekey][shit] & checkshit($0!$userhost() $chan))) { echo valid ceop request from shit: $0-1 $3- } elsif (cmd == []) { if (!ischanop($servernick() $chan)) { 1cmd 120 ceop.opme $chan } if ([b] == matchban(beI $0!$userhost() $chan)) { unban $chan $rpattern($0!$userhost() $chanbans(b $chan)) } elsif (!onchannel($0 $chan)) { invite.all $0 $chan } elsif (!ischanop($0 $chan)) { qmode $chan +o $0 } else { echo valid (unrequired) ceop request: $0-1 $3- } } elsif (cmd == [clobber]) { @ ceop[uk][$euser][$ekey][cmdlevel] = -1 echo clobbered comprimised ceop key: $duser,$key : $0-1 $3- echo you must manually edit this key from your configuration files. } elsif (cmd == [help]) { @ :cmds = aliasctl(alias match ceopcmd.) fe cmds cmds {@ cmds = after(. $cmds)} fe cmds cmds {@ cmds = ceop[uk][$euser][$ekey][cmdlevel] < ceop[cmdlev][$cmd] ? [] : cmds} notice $0 Available commands: $cmds } elsif (aliasctl(alias exists ceopcmd.$cmd)) { if (:dist) {q1cmd 300,300 9 ceop $duser,$key -- $chan $unsplit(, $mychannels()) dist $cmd $args} wait for ceopcmd.\$cmd \$* } if (rmatch("$cmd" $ceop.cmdlog)) { echo Processed valid ceop request: $0-1 $3- } return } } alias ceop.cmdstrip (args) { @ :lev = isnumber($args) ? shift(args) : 0 unless (args) { echo required: [level] [command-masks] echo remove all matching ceop command extensions not below a certain level. } foreach -ceopcmd cmd { if (lev <= ceop[cmdlev][$cmd] && rmatch($cmd $args)) { alias.ceopcmd 0 - $cmd } } } alias alias.ceopcmd (enable,level,cmd,alias) { if (enable) { @ ceop[cmdlev][$cmd] = level if (#alias) {alias ceopcmd.$cmd $alias} } else { @ ceop[cmdlev][$cmd] = [] alias -ceopcmd.$cmd } } # # These are defined or undefined ceop commands and their levels. The op and # voice commands come from cetk.chanmgmt, and cka is from cetk.clonecheck. # # dist and adist retransmit (distribute) the ceop request to all other # channels. adist will make use of keys not used in the original request. # alias.ceopcmd 1 1 pgpeval {bless;exec -direct -name gpg_${nick}_${chan}_$servernick() -line {$*} gpg --batch;exec -in %gpg_${nick}_${chan}_$servernick() $args} alias.ceopcmd 1 1 pgpevgo {bless;exec -closein %gpg_${nick}_${chan}_$servernick()} alias.ceopcmd 1 1 declone {bless;cka b $chan;} alias.ceopcmd 1 1 devoice {bless;devoice $chan $args;} alias.ceopcmd 1 1 voice {bless;envoice $chan $args;} alias.ceopcmd 1 20 dehop {bless;dehop $chan $args;} alias.ceopcmd 1 20 hop {bless;enhop $chan $args;} alias.ceopcmd 1 50 cka {bless;fe args arg {@arg = ischannel($arg) ? [] : arg};cka $args $chan;} alias.ceopcmd 1 50 deop {bless;deop $chan $args;} alias.ceopcmd 1 50 op {bless;enop $chan $args;} alias.ceopcmd 1 200 join {bless;q1cmd 0 9 join $chan $args;} alias.ceopcmd 0 200 mode {bless;qmode $chan $args;} alias.ceopcmd 1 200 part {bless;q1cmd 0 9 part $chan $args;} alias.ceopcmd 1 200 say {bless;q1cmd 0 9 msg $chan $args;} alias.ceopcmd 1 200 chat {bless;q1cmd 0 9 dcc chat $nick;} alias.ceopcmd 0 600 pretend {bless;pretend $args;} alias.ceopcmd 1 600 quit {bless;q1cmd 0 9 quit $args;} alias.ceopcmd 1 800 quote {bless;q1cmd 0 9 quote $args;} alias.ceopcmd 1 800 samode {bless;q1cmd 0 9 quote samode $args;} alias.ceopcmd 1 800 svsmode {bless;q1cmd 0 9 quote svsmode $args;} alias.ceopcmd 1 1000 cmd {bless;$args;} alias.ceopcmd 1 1000 eval {bless;eval $args;} alias.ceopcmd 1 1000 exec {bless;exec -window -line \{q1cmd 0 9 notice $chan \$*\} $args;} alias.ceopcmd 1 9999 adist {bless;defer q1cmd 300,300 9 ceop.autoop -t -- * $chan adist \$decode\($encode($args)\);wait for \$decode\($encode(ceopcmd.$shift(args) $args)\)} alias.ceopcmd 1 0 dist {bless;defer q1cmd 300,300 9 ceop $duser,$key -t -- $unsplit(, $remw($1 $remw($chan $mychannels()))) $chan dist \$decode\($encode($args)\);wait for \$decode\($encode(ceopcmd.$shift(args) $args)\)} # # This is a more generic auto-op system. With no args it will search for ops # and services that have been configured with opme.setop and send the commands # needed to gain ops. Note that although this has been designed to work easily # with the ceop stuff above, it is a different system, based on different # principles. # alias opme (args) { @ :oxd = xdebug(dword extractw) xdebug dword -extractw @ :servergroup = servergroup() @ :cmd = !args || ischannel($args) ? [opme] : shift(args) @ :chan = ischannel($args) ? shift(args) : C @ :unkuh = userhost(,) foreach oplist op { if (!match("service" $oplist[$op][flags])) { } elsif (!rmatch("$chan" $oplist[$op][chans])) { } elsif (!rmatch("$servergroup" $oplist[$op][nets])) { } else { xdebug -dword {push :nicks $oplist[$op][nicks]} } } @ :notify = ::notify() @ :notify = notify ? notify : notify() @ :notifyon = ::notify(on) @ :notifyon = notifyon ? notifyon : notify(on) ^notify $rfilter("\\[$notify\\]" $nicks) @ :nicks = pattern("\\[$nicks\\]" $notifyon) @ :nicks = uniq($chops($chan) $nicks $nochops($chan)) @ :userhosts = userhost($nicks) unless (nicks || !chan || onchannel($N $chan)) {qcmd 9 join $chan} opme.nuhcheck $cmd $chan "$joinstr(! nicks userhosts)" $args @ :chan = [\$decode\($encode($chan)\)] @ :args = [\$decode\($encode($args)\)] ^userhost $copattern($unkuh userhosts nicks) -cmd \{opme.nuhcheck $cmd $chan \$0!\$3@\$4 $args\} xdebug $oxd } # alias opme.nuhcheck (cmd,chan,nuhs dwords,args) { @ :oxd = xdebug(dword) xdebug dword @ :nuhs = filter(*!$userhost(,) $nuhs) @ :servergroup = servergroup() foreach oplist op { if (!rmatch("$chan" $oplist[$op][chans])) {continue} if (!rmatch("$servergroup" $oplist[$op][nets])) {continue} fe ($pattern("$oplist[$op][nuhs]" $nuhs)) nuh { @ :nick = before(! $nuh!) @ :type = oplist[$op][type] @ :pass = oplist[$op][pass] if (!ischanop($nick $chan) && !match(service $oplist[$op][flags])) { } elsif (aliasctl(alias exists oplist.${op}.${cmd})) { wait for oplist.\${op}.\${cmd} \$nick \$chan \$args if (isdisplaying()) { echo $cmd request for $chan sent to $op at $nuh } } elsif (aliasctl(alias exists oplist.${type}.${cmd})) { wait for oplist.\${type}.\${cmd} \$nick \$chan \$args if (isdisplaying()) { echo $cmd request for $chan sent to $op at $nuh } } else { echo no such command $cmd for $op } } } xdebug $oxd } alias opme.setop { @ :args = [$*] @ :oxd = xdebug(extractw dword) xdebug extractw dword switch ($#args) { (1) {echo Required: "{server-type}" - Codebase being run. Determines commands used. "/alias oplist.".} (2) {echo Required: "{nickmasks}" - Nicks this op is using. Required when you or it aren't on the channel.} (3) {echo Required: "{nick!user@host masks}" - Userhosts this client is using, for authentication.} (4) {echo Required: "{password}" - Password to use when authenticating with this op.} (5) {echo Required: "{nets}" - Networks this op is using. Matched against the 5'th field of the server spec.} (6) {echo Required: "{channel-masks}" - Channels this op is callable for.} (7) { echo Required: "{flags}" - Features of this service. echo "service" - This op can be used when not opped or on common channels. } (8) { @ :op = shift(args) echo Configuring "$op" for use with /opme. @ oplist[$op][type] = shift(args) @ oplist[$op][nicks] = shift(args) @ oplist[$op][nuhs] = shift(args) @ oplist[$op][pass] = shift(args) @ oplist[$op][nets] = shift(args) @ oplist[$op][chans] = shift(args) @ oplist[$op][flags] = shift(args) } (*) { echo requires: "{op-name}" "{server-type}" "{nickmasks}" "{nick!user@host masks}" "{password}" "{nets}" "{channel-masks}" "{flags}" echo All arguments may have any number of words, but must be double quoted if not a single word. echo Required: "{op-name}" - Name you want to give this server. } } xdebug $oxd } alias oplist.newservices.ghost {bless;q1cmd 300 9 quote nickserv ghost $2 $pass;} alias oplist.newservices.login {bless;q1cmd 300 9 quote nickserv identify $pass;} alias oplist.newservices.opme {bless;q1cmd 300 9 quote chanserv op $1 $servernick();} alias oplist.oldservices.ghost {bless;q1cmd 300 9 msg nickserv ghost $2 $pass;} alias oplist.oldservices.login {bless;q1cmd 300 9 msg nickserv identify $pass;} alias oplist.oldservices.opme {bless;q1cmd 300 9 msg chanserv op $1 $servernick();} alias oplist.eggdrop.login {bless;if ([$0]=~[=*]){msg $0 $pass}{q1cmd 5 9 msg $0 ident $pass $2-}} alias oplist.eggdrop.opme {bless;q1cmd 5 9 msg $0 op $pass $1;} alias oplist.ceop_priv.login {bless;q1cmd 5 9 ceop $pass -d $*;} alias oplist.ceop_priv.opme {bless;q1cmd 5 9 ceop $pass -d $*;} alias oplist.ceop_pub.opme {bless;q1cmd 5 9 ceop $pass -d $1-;} alias oplist.austnet.login {bless;q1cmd 300 9 msg nickop@austnet.org identify $pass;} alias oplist.austnet.opme {bless;q1cmd 300 9 msg chanop op $1 ${args ? args : servernick()};} alias oplist.X.opme {bless;q1cmd 300 9 msg $0 op $1;} alias oplist.X.login {bless;q1cmd 300 9 msg x@channels.undernet.org login $pass;} alias oplist.z.login {bless;q1cmd 300 9 msg z@channels.oz.org login ${args ? args : servernick()} $pass;} alias oplist.q.login {bless;q1cmd 300 9 msg Q@CServe.quakenet.org AUTH ${args ? args : servernick()} $pass;} # Services login on connect # on #-connect - * qcmd 1 opme login