]> jfr.im git - irc/SurrealServices/srsv.git/blob - branches/0.5.0/SrSv/Agent.pm
2856bdfcb1516e97dacc9276b347d223bc998e81
[irc/SurrealServices/srsv.git] / branches / 0.5.0 / SrSv / Agent.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
17 package SrSv::Agent;
18
19 use strict;
20
21 use Exporter 'import';
22 BEGIN { our @EXPORT = qw(
23 is_agent is_agent_in_chan
24 agent_connect agent_quit agent_quit_all
25 agent_join agent_part set_agent_umode
26 agent_sync is_invalid_agentname
27 ); }
28
29 use SrSv::Process::InParent qw(
30 is_agent is_agent_in_chan
31 agent_connect agent_quit agent_quit_all
32 agent_join agent_part agent_sync
33 whois_callback kill_callback
34 );
35
36 use SrSv::Conf2Consts qw(main);
37
38 use SrSv::Debug;
39 use SrSv::Unreal::Tokens qw( :tokens );
40 use SrSv::Unreal::Base64 qw(itob64);
41 use SrSv::IRCd::State qw(synced $ircd_ready %IRCd_capabilities);
42 use SrSv::IRCd::IO qw(ircsend ircsendimm);
43 use SrSv::IRCd::Event qw(addhandler);
44 use SrSv::IRCd::Validate qw(valid_nick);
45 use SrSv::RunLevel 'main_shutdown';
46
47 # FIXME
48 BEGIN { *SJB64 = \&ircd::SJB64 }
49
50 our %agents;
51 our @defer_join;
52
53 addhandler('WHOIS', undef(), undef(), 'whois_callback', 1);
54 addhandler('KILL', undef(), undef(), 'kill_callback', 1);
55
56 sub is_agent($) {
57 my ($nick) = @_;
58 return (defined($agents{lc $nick}));
59 }
60
61 sub is_agent_in_chan($$) {
62 my ($agent, $chan) = @_;
63 $agent = lc $agent; $chan = lc $chan;
64
65 if($agents{$agent} and $agents{$agent}{CHANS} and $agents{$agent}{CHANS}{$chan}) {
66 return 1;
67 } else {
68 return 0;
69 }
70 }
71
72 sub agent_connect($$$$$) {
73 my ($nick, $ident, $host, $modes, $gecos) = @_;
74 my $time = time();
75
76 my @chans;
77 if(defined($agents{lc $nick}) and ref($agents{lc $nick}{CHANS})) {
78 @chans = keys(%{$agents{lc $nick}{CHANS}});
79 }
80
81 $agents{lc $nick}{PARMS} = [ @_ ];
82
83 $host = main_conf_local unless $host;
84 ircsend("@{[TOK_NICK]} $nick 1 $time $ident $host ".
85 (SJB64 ? itob64(main_conf_numeric) : main_conf_local).
86 " 1 $modes * :$gecos");
87
88 foreach my $chan (@chans) {
89 ircsend(":$nick @{[TOK_JOIN]} $chan");
90 # If we tracked chanmodes for agents, that would go here as well.
91 }
92 }
93
94 sub agent_quit($$) {
95 my ($nick, $msg) = @_;
96
97 delete($agents{lc $nick}{CHANS});
98 delete($agents{lc $nick});
99
100 ircsendimm(":$nick @{[TOK_QUIT]} :$msg");
101 }
102
103 sub agent_quit_all($) {
104 my ($msg) = @_;
105
106 my @agents;
107 @agents = keys(%agents);
108
109 foreach my $a (@agents) {
110 agent_quit($a, $msg);
111 }
112 }
113
114 sub is_invalid_agentname($$$) {
115 my ($botnick, $botident, $bothost) = @_;
116
117 unless(valid_nick($botnick)) {
118 return "Invalid nickname.";
119 }
120 unless($botident =~ /^[[:alnum:]_]+$/) {
121 return "Invalid ident.";
122 }
123 unless($bothost =~ /^[[:alnum:].-]+$/) {
124 return "Invalid vhost.";
125 }
126 unless($bothost =~ /\./) {
127 return "A vhost must contain at least one dot.";
128 }
129 return undef;
130 }
131
132 sub agent_join($$) {
133 my ($agent, $chan) = @_;
134
135 if($agents{lc $agent}) {
136 $agents{lc $agent}{CHANS}{lc $chan} = 1;
137 ircsend(":$agent @{[TOK_JOIN]} $chan");
138 } else {
139 if($ircd_ready) {
140 print "Tried to make nonexistent agent ($agent) join channel ($chan)" if DEBUG;
141 } else {
142 print "Deferred join: $agent $chan\n" if DEBUG;
143 push @defer_join, "$agent $chan";
144 }
145 }
146 }
147
148 sub agent_part($$$) {
149 my ($agent, $chan, $reason) = @_;
150
151 delete($agents{lc $agent}{CHANS}{lc $chan});
152 ircsend(":$agent @{[TOK_PART]} $chan :$reason");
153 }
154
155 sub set_agent_umode($$) {
156 my ($src, $modes) = @_;
157
158 ircsend(":$src @{[TOK_UMODE2]} $modes");
159 }
160
161 sub agent_sync() {
162 foreach my $j (@defer_join) {
163 print "Processing join: $j\n" if DEBUG;
164 my ($agent, $chan) = split(/ /, $j);
165 agent_join($agent, $chan);
166 }
167 undef(@defer_join);
168 }
169
170 sub whois_callback {
171 #:wyvern.surrealchat.net 311 blah2 tabris northman SCnet-E5870F84.dsl.klmzmi.ameritech.net * :Sponsored by Skuld
172 #:wyvern.surrealchat.net 307 blah2 tabris :is a registered nick
173 #:wyvern.surrealchat.net 312 blah2 tabris wyvern.surrealchat.net :SurrealChat - aphrodite.wcshells.com - Chicago.IL
174 #:wyvern.surrealchat.net 671 blah2 tabris :is using a Secure Connection
175 #:wyvern.surrealchat.net 317 blah2 tabris 54 1118217330 :seconds idle, signon time
176 #:wyvern.surrealchat.net 401 blah2 nikanoru :No such nick/channel
177 #:wyvern.surrealchat.net 311 blah2 somebot bot SCnet-DA158DBF.hsd1.nh.comcast.net * :Some sort of bot
178 #:wyvern.surrealchat.net 312 blah2 somebot nascent.surrealchat.net :SurrealChat - Hub
179 #:wyvern.surrealchat.net 335 blah2 somebot :is a Bot on SurrealChat.net
180 #:wyvern.surrealchat.net 318 blah2 tabris,nikanoru,somebot :End of /WHOIS list.
181
182 # Also reference http://www.alien.net.au/irc/irc2numerics.html
183
184 my ($src, $nicklist) = @_;
185
186 my @nicks = split(/\,/, $nicklist);
187 my @reply;
188 foreach my $nick (@nicks) {
189 if (is_agent($nick)) {
190 my ($nick, $ident, $host, $modes, $gecos) = @{$agents{lc $nick}{PARMS}};
191 $host = main_conf_local unless $host;
192 push @reply, ':'.main_conf_local." 311 $src $nick $ident $host * :$gecos";
193 push @reply, ':'.main_conf_local." 312 $src $nick ".main_conf_local.' :'.main_conf_info;
194 foreach my $mode (split(//, $modes)) {
195 if ($mode eq 'z') {
196 push @reply, ':'.main_conf_local." 671 $src $nick :is using a Secure Connection";
197 }
198 elsif($mode eq 'S') {
199 #313 tab ChanServ :is a Network Service
200 push @reply, ':'.main_conf_local." 313 $src $nick :is a Network Service";
201 }
202 elsif($mode eq 'B') {
203 #335 blah2 TriviaBot :is a Bot on SurrealChat.net
204 push @reply, ':'.main_conf_local.
205 " 335 $src $nick :is a \002Bot\002 on ".$IRCd_capabilities{NETWORK};
206 }
207 }
208 }
209 else {
210 push @reply, ':'.main_conf_local." 401 $src $nick :No such service";
211 }
212
213 }
214 push @reply, ':'.main_conf_local." 318 $src $nicklist :End of /WHOIS list.";
215 ircsend(@reply);
216 }
217
218 sub kill_callback($$$$) {
219 my ($src, $dst, $path, $reason) = @_;
220 if (defined($agents{lc $dst})) {
221 if (defined ($agents{lc $dst}{KILLED}) and ($agents{lc $dst}{KILLED} == time())) {
222 if ($agents{lc $dst}{KILLCOUNT} > 3) {
223 ircd::debug("Caught in a kill loop for $dst, dying now.");
224 main_shutdown;
225 } else {
226 $agents{lc $dst}{KILLCOUNT}++;
227 }
228 } else {
229 $agents{lc $dst}{KILLED} = time();
230 $agents{lc $dst}{KILLCOUNT} = 1;
231 }
232
233 if($src =~ /\./) {
234 # let's NOT loopback this event
235 ircsendimm(':'.main_conf_local.' '."@{[TOK_KILL]} $dst :Nick Collision");
236 } elsif (defined($agents{lc $src})) {
237 # Do Nothing.
238 } else {
239 ircd::irckill($main::rsnick, $src, "Do not kill services agents.");
240 }
241
242 &agent_connect(@{$agents{lc $dst}{PARMS}}) if synced();
243 }
244 }
245
246 1;