ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-IRC3/lib/Net/IRC3.pm
Revision: 1.1
Committed: Sun Jul 16 02:09:12 2006 UTC (18 years, 3 months ago) by elmex
Branch: MAIN
Log Message:
initial checkin

File Contents

# Content
1 package Net::IRC3;
2 use strict;
3 use AnyEvent;
4 use IO::Socket::INET;
5
6 =head1 NAME
7
8 Net::IRC3 - An IRC Protocol module which is event system independend
9
10 =head1 VERSION
11
12 Version 0.01
13
14 =cut
15
16 our $VERSION = '0.1';
17
18 =head1 SYNOPSIS
19
20 use Net::IRC3;
21
22 my $irc3 = new Net::IRC3;
23
24 my $con = $irc3->connect_server ("test.not.at.irc.net", 6667);
25
26 ...
27
28 =head1 DESCRIPTION
29
30 L<Net::IRC3> itself is a simple building block for an IRC client.
31 It manages connections and parses and constructs IRC messages.
32
33 L<Net::IRC3> is I<very> simple, if you don't want to care about
34 all the other things that a client still has to do (like replying to
35 PINGs and remembering who is on a channel), I recommend to read
36 the L<Net::IRC3::Client> page instead.
37
38 =head1 METHODS
39
40 =over 4
41
42 =item B<new ()>
43
44 This just creates a L<Net::IRC3> object, which is a management
45 class for creating and managing connections.
46
47 =cut
48
49 sub new
50 {
51 my $this = shift;
52 my $class = ref($this) || $this;
53
54 my $self = { };
55
56 bless $self, $class;
57
58 return $self;
59 }
60
61 =item B<connect_server ($host, $port)>
62
63 Tries to open a socket to the host C<$host> and the port C<$port>.
64 If successfull it will return a Net::IRC3::Connection object.
65 If an error occured it will die (use eval to catch the exception).
66
67 =cut
68
69 sub connect_server {
70 my ($self, $host, $port) = @_;
71
72 defined $self->{connections}->{"$host:$port"}
73 and return;
74
75 my $sock = IO::Socket::INET->new (
76 PeerAddr => $host,
77 PeerPort => $port,
78 Proto => 'tcp',
79 Blocking => 0
80 ) or die "couldn't connect to irc server '$host:$port': $!\n";;
81
82 my $con = Net::IRC3::Connection->new ($self, $sock, $host, $port);
83
84 $con->{rw} =
85 AnyEvent->io (poll => 'r', fh => $sock, cb => sub {
86 my $l = sysread $sock, my $data, 1024;
87
88 $con->feed_irc_data ($data);
89
90 unless ($l) {
91
92 if (defined $l) {
93 $con->disconnect_server ("EOF from IRC server '$host:$port'");
94 return;
95
96 } else {
97 $con->disconnect_server ("Error while reading from IRC server '$host:$port': $!");
98 return;
99 }
100 }
101 });
102
103 return $con;
104 }
105
106 =back
107
108 =head1 FUNCTIONS
109
110 These are some utility functions that might come in handy when
111 handling the IRC protocol.
112
113 =over 4
114
115 =item B<parse_irc_msg ($ircline)>
116
117 This method parses the C<$ircline>, which is one line of the IRC protocol
118 without the trailing "\015\012".
119
120 It returns a hash which has the following entrys:
121
122 =over 4
123
124 =item prefix
125
126 The message prefix.
127
128 =item command
129
130 The IRC command.
131
132 =item params
133
134 The parameters to the IRC command in a array reference,
135 this includes the trailing parameter (the one after the ':' or
136 the 14th parameter).
137
138 =item trailing
139
140 This is set if there was a trailing parameter (the one after the ':' or
141 the 14th parameter).
142
143 =back
144
145 =cut
146
147 sub parse_irc_msg {
148 my ($msg) = @_;
149
150 my $cmd;
151 my $pref;
152 my $t;
153 my @a;
154
155 my $p = $msg =~ s/^(:([^ ]+)[ ])?([A-Za-z]+|\d{3})//;
156 $pref = $2;
157 $cmd = $3;
158
159 my $i = 0;
160
161 while ($msg =~ s/^[ ]([^ :\015\012\0][^ \015\012\0]*)//) {
162
163 push @a, $1 if defined $1;
164 if (++$i > 13) { last; }
165 }
166
167 if ($i == 14) {
168
169 if ($msg =~ s/^[ ]:?([^\015\012\0]*)//) {
170 $t = $1 if $1 ne "";
171 }
172
173 } else {
174
175 if ($msg =~ s/^[ ]:([^\015\012\0]*)//) {
176 $t = $1 if $1 ne "";
177 }
178 }
179
180 push @a, $t if defined $t;
181
182 my $m = { prefix => $pref, command => $cmd, params => \@a, trailing => $t };
183 return $p ? $m : undef;
184 }
185
186 =item B<mk_msg ($prefix, $command, $trailing, @params)>
187
188 This function assembles a IRC message. The generated
189 message will look like (pseudo code!)
190
191 :<prefix> <command> <params> :<trail>
192
193 Please refer to RFC 2812 how IRC messages normally look like.
194
195 The prefix and the trailing string will be omitted if they are C<undef>.
196
197 EXAMPLES:
198
199 $con->mk_msg (undef, "PRIVMSG", "you suck!", "magnus");
200 # will return: "PRIVMSG magnus :you suck!\015\012"
201
202 $con->mk_msg (undef, "JOIN", undef, "#test");
203 # will return: "JOIN #magnus\015\012"
204
205 =cut
206
207 sub mk_msg {
208 my ($prefix, $command, $trail, @params) = @_;
209 my $msg = "";
210
211 $msg .= defined $prefix ? ":$prefix " : "";
212 $msg .= "$command";
213
214 # FIXME: params must be counted, and if > 13 they have to be
215 # concationated with $trail
216 map { $msg .= " $_" } @params;
217
218 $msg .= defined $trail ? " :$trail" : "";
219 $msg .= "\015\012";
220
221 return $msg;
222 }
223
224 =item B<split_prefix ($prefix)>
225
226 This function splits an IRC user prefix as described by RFC 2817
227 into the three parts: nickname, user and host. Which will be
228 returned as a list with that order.
229
230 C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
231
232 =cut
233
234 sub split_prefix {
235 my ($prfx) = @_;
236
237 if (ref ($prfx) eq 'HASH') {
238 $prfx = $prfx->{prefix};
239 }
240
241 $prfx =~ m/^\s*([^!]*)!([^@]*)@(.*?)\s*$/;
242 return ($1, $2, $3);
243 }
244
245 =item B<prefix_nick ($prefix)>
246
247 A shortcut to extract the nickname from the C<$prefix>.
248
249 C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
250
251 =cut
252
253 sub prefix_nick {
254 my ($prfx) = @_;
255 return (split_prefix ($prfx))[0];
256 }
257
258 =item B<prefix_user ($prefix)>
259
260 A shortcut to extract the username from the C<$prefix>.
261
262 C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
263
264 =cut
265
266 sub prefix_user {
267 my ($prfx) = @_;
268 return (split_prefix ($prfx))[1];
269 }
270
271 =item B<prefix_host ($prefix)>
272
273 A shortcut to extract the hostname from the C<$prefix>.
274
275 C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
276
277 =cut
278
279 sub prefix_host {
280 my ($self, $prfx) = @_;
281 return (split_prefix ($prfx))[2];
282 }
283
284 =head1 Net::IRC3::Connection
285
286 The connection class. Here the actual interesting stuff can be done,
287 such as sending and receiving IRC messages.
288
289 =head2 METHODS
290
291 =over 4
292
293 =cut
294
295 package Net::IRC3::Connection;
296
297 use strict;
298 use AnyEvent;
299 use IO::Socket::INET;
300
301 sub new
302 {
303 my $this = shift;
304 my $class = ref($this) || $this;
305
306 my $self = {
307 pirc => $_[0],
308 s => $_[1],
309 h => $_[2],
310 p => $_[3],
311 cbs => {},
312 heap => {}
313 };
314
315 bless $self, $class;
316
317 return $self;
318 }
319
320 =item B<disconnect_server ($reason)>
321
322 Unregisters the connection in the main Net::IRC3 object, closes
323 the sockets and send a 'disconnect' event with C<$reason> as argument.
324
325 =cut
326
327 sub disconnect_server {
328 my ($self, $reason) = @_;
329
330 $self->event (disconnect => $reason);
331
332 delete $self->{rw};
333 delete $self->{ww};
334 delete $self->{pirc}->{connections}->{$self->{h} . ":" . $self->{p}};
335
336 eval { $self->{s}->close }
337 }
338
339 =item B<heap ()>
340
341 Returns a hash reference that is local to this connection object
342 that lets you store any information you want.
343
344 =cut
345
346 sub heap {
347 my ($self) = @_;
348 return $self->{heap};
349 }
350
351 =item B<send_msg (@ircmsg)>
352
353 This function sends a message to the server. C<@ircmsg> is the argumentlist
354 for C<mk_msg>.
355
356 =cut
357
358 sub send_msg {
359 my ($self, @msg) = @_;
360 my $data = mk_msg (@msg);
361
362 my ($host, $port) = ($self->{h}, $self->{p});
363 $self->{outbuf} .= $data;
364
365 unless (defined $self->{ww}) {
366 my $sock = $self->{s};
367 $self->{ww} =
368 AnyEvent->io (poll => 'w', fh => $sock, cb => sub {
369 my $l = syswrite $sock, $self->{outbuf};
370
371 substr $self->{outbuf}, 0, $l, "";
372
373 if (length ($self->{outbuf}) == 0) { delete $self->{ww} }
374
375 unless ($l) {
376 # XXX: is this behaviour correct or ok?
377 $self->disconnect_server ("Error while writing to IRC server '$host:$port': $!");
378 return;
379 }
380 });
381 }
382 }
383
384 =item B<reg_cb ($cmd, $cb)>
385
386 This registers a callback in the connection class.
387 These callbacks will be called by internal events and
388 by IRC protocol commands.
389
390 The first argument to the callbacks is always the connection object
391 itself.
392
393 If a callback returns a false value, it will be unregistered.
394
395 NOTE: I<A callback has to return true to stay alive>
396
397 If C<$cmd> starts with 'irc_' the callback C<$cb> will be registered
398 for a IRC protocol command. The command is the suffix of C<$cmd> then.
399 The second argument to the callback is the message hash reference
400 that has the layout that is returned by C<parse_irc_msg>.
401
402 EXAMPLE:
403
404 $con->reg_cb (irc_privmsg => \&privmsg_handler);
405 # privmsg_handler will be called if an IRC message
406 # with the command 'PRIVMSG' arrives.
407
408 If C<$cmd> is not prefixed with a 'irc_' it will be called when an event
409 with the name C<$cmd> is emitted. The arguments to the callback depend
410 on the event that is emitted (but remember: the first argument will always be the
411 connection object)
412
413 Following events are emitted by this module and shouldn't be emitted
414 from a module user call to C<event>.
415
416 =over 4
417
418 =item B<disconnect $reason>
419
420 This event will be generated if the connection is somehow terminated.
421 It will also be emitted when C<disconnect_server> is called.
422 The second argument to the callback is C<$reason>, a string that contains
423 a clue about why the connection terminated.
424
425 =back
426
427 =cut
428
429 sub reg_cb {
430 my ($self, $cmd, $cb) = @_;
431
432 if ($cmd =~ m/^irc_(\S+)/i) {
433 push @{$self->{cbs}->{lc $1}}, $cb;
434
435 } else {
436 push @{$self->{events}->{$cmd}}, $cb;
437 }
438
439 1;
440 }
441
442 =item B<event ($event, @args)>
443
444 This function emits an event with the name C<$event> and the arguments C<@args>.
445 The registerd callback that has been registered with C<reg_cb> will be called
446 with the first argument being the connection object and the rest of the arguments
447 being C<@args>.
448
449 EXAMPLE
450
451 $con->reg_cb (test_event => sub { print "Yay, i love $_[1]!!\n");
452 $con->event (test_event => "IRC");
453
454 # will print "Yay, i love IRC!!\n"
455
456 =cut
457
458 sub event {
459 my ($self, $ev, @arg) = @_;
460
461 my $nxt = [];
462
463 for (@{$self->{events}->{lc $ev}}) {
464 $_->($self, @arg) and push @$nxt, $_;
465 }
466
467 $self->{events}->{lc $ev} = $nxt;
468 }
469
470 # internal function, called by the read callbacks above.
471 sub feed_irc_data {
472 my ($self, $data) = @_;
473
474 $self->{buffer} .= $data;
475
476 my @msg;
477 while ($self->{buffer} =~ s/^([^\015\012]*)\015?\012//) {
478 push @msg, $1;
479 }
480
481 for (@msg) {
482 my $m = parse_irc_msg ($_);
483
484 my $nxt = [];
485
486 for (@{$self->{cbs}->{lc $m->{command}}}) {
487 $_->($self, $m) and push @$nxt, $_;
488 }
489
490 $self->{cbs}->{lc $m->{command}} = $nxt;
491
492 $nxt = [];
493
494 for (@{$self->{cbs}->{'*'}}) {
495 $_->($self, $m) and push @$nxt, $_;
496 }
497
498 $self->{cbs}->{'*'} = $nxt;
499 }
500 }
501
502
503 =back
504
505 =head1 AUTHOR
506
507 Robin Redeker, C<< <elmex@ta-sa.org> >>
508
509 =head1 SEE ALSO
510
511 RFC 2812 - Internet Relay Chat: Client Protocol
512
513 =head1 BUGS
514
515 Please report any bugs or feature requests to
516 C<bug-net-irc3 at rt.cpan.org>, or through the web interface at
517 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Net-IRC3>.
518 I will be notified, and then you'll automatically be notified of progress on
519 your bug as I make changes.
520
521 =head1 SUPPORT
522
523 You can find documentation for this module with the perldoc command.
524
525 perldoc Net::IRC3
526
527 You can also look for information at:
528
529 =over 4
530
531 =item * AnnoCPAN: Annotated CPAN documentation
532
533 L<http://annocpan.org/dist/Net-IRC3>
534
535 =item * CPAN Ratings
536
537 L<http://cpanratings.perl.org/d/Net-IRC3>
538
539 =item * RT: CPAN's request tracker
540
541 L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-IRC3>
542
543 =item * Search CPAN
544
545 L<http://search.cpan.org/dist/Net-IRC3>
546
547 =back
548
549 =head1 ACKNOWLEDGEMENTS
550
551 Thanks to Marc Lehmann for the new AnyEvent module!
552
553 =head1 COPYRIGHT & LICENSE
554
555 Copyright 2006 Robin Redker, all rights reserved.
556
557 This program is free software; you can redistribute it and/or modify it
558 under the same terms as Perl itself.
559
560 =cut
561
562 1;