ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-IRC3/lib/Net/IRC3/Util.pm
Revision: 1.11
Committed: Tue Mar 6 21:44:53 2007 UTC (17 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.10: +3 -1 lines
Log Message:
Implemented topic handling and fixed a bug in nick-change detection
and fixed a bug in prefix matching.

File Contents

# User Rev Content
1 elmex 1.1 package Net::IRC3::Util;
2     use strict;
3 elmex 1.9 no warnings;
4 elmex 1.1 use Exporter;
5     our @ISA = qw/Exporter/;
6     our @EXPORT_OK =
7     qw(mk_msg parse_irc_msg split_prefix prefix_nick
8 elmex 1.7 decode_ctcp filter_ctcp_text_attr prefix_user prefix_host
9     rfc_code_to_name);
10 elmex 1.1
11     =head1 NAME
12    
13     Net::IRC3::Util - Common utilities that help with IRC protocol handling
14    
15 elmex 1.2 =head1 SYNOPSIS
16    
17     use Net::IRC3 qw/parse_irc_msg mk_msg/;
18    
19 elmex 1.7 my $msgdata = mk_msg (undef, PRIVMSG => "my hands glow!", "mcmanus");
20 elmex 1.2
21 elmex 1.1 =head1 FUNCTIONS
22    
23     These are some utility functions that might come in handy when
24     handling the IRC protocol.
25    
26     You can export these with eg.:
27    
28     use Net::IRC3 qw/parse_irc_msg/;
29    
30     =over 4
31    
32     =item B<parse_irc_msg ($ircline)>
33    
34     This method parses the C<$ircline>, which is one line of the IRC protocol
35     without the trailing "\015\012".
36    
37     It returns a hash which has the following entrys:
38    
39     =over 4
40    
41     =item prefix
42    
43     The message prefix.
44    
45     =item command
46    
47     The IRC command.
48    
49     =item params
50    
51     The parameters to the IRC command in a array reference,
52     this includes the trailing parameter (the one after the ':' or
53     the 14th parameter).
54    
55     =item trailing
56    
57     This is set if there was a trailing parameter (the one after the ':' or
58     the 14th parameter).
59    
60     =back
61    
62     =cut
63    
64     sub parse_irc_msg {
65     my ($msg) = @_;
66    
67     my $cmd;
68     my $pref;
69     my $t;
70     my @a;
71    
72     my $p = $msg =~ s/^(:([^ ]+)[ ])?([A-Za-z]+|\d{3})//;
73     $pref = $2;
74     $cmd = $3;
75    
76     my $i = 0;
77    
78     while ($msg =~ s/^[ ]([^ :\015\012\0][^ \015\012\0]*)//) {
79    
80     push @a, $1 if defined $1;
81     if (++$i > 13) { last; }
82     }
83    
84     if ($i == 14) {
85    
86     if ($msg =~ s/^[ ]:?([^\015\012\0]*)//) {
87     $t = $1 if $1 ne "";
88     }
89    
90     } else {
91    
92     if ($msg =~ s/^[ ]:([^\015\012\0]*)//) {
93     $t = $1 if $1 ne "";
94     }
95     }
96    
97     push @a, $t if defined $t;
98    
99     my $m = { prefix => $pref, command => $cmd, params => \@a, trailing => $t };
100     return $p ? $m : undef;
101     }
102    
103     =item B<mk_msg ($prefix, $command, $trailing, @params)>
104    
105     This function assembles a IRC message. The generated
106     message will look like (pseudo code!)
107    
108     :<prefix> <command> <params> :<trail>
109    
110     Please refer to RFC 2812 how IRC messages normally look like.
111    
112     The prefix and the trailing string will be omitted if they are C<undef>.
113    
114     EXAMPLES:
115    
116     mk_msg (undef, "PRIVMSG", "you suck!", "magnus");
117     # will return: "PRIVMSG magnus :you suck!\015\012"
118    
119     mk_msg (undef, "JOIN", undef, "#test");
120 elmex 1.5 # will return: "JOIN #test\015\012"
121 elmex 1.1
122     =cut
123    
124     sub mk_msg {
125     my ($prefix, $command, $trail, @params) = @_;
126     my $msg = "";
127    
128     $msg .= defined $prefix ? ":$prefix " : "";
129     $msg .= "$command";
130    
131     # FIXME: params must be counted, and if > 13 they have to be
132     # concationated with $trail
133     map { $msg .= " $_" } @params;
134    
135     $msg .= defined $trail ? " :$trail" : "";
136     $msg .= "\015\012";
137    
138     return $msg;
139     }
140    
141    
142 elmex 1.8 =item B<decode_ctcp ($line)>
143    
144     TODO
145 elmex 1.1
146     =cut
147    
148     sub decode_ctcp {
149 elmex 1.6 my ($line) = @_;
150 elmex 1.1
151 elmex 1.6 while ($line =~ /\G\001([^\001]*)\001/g) {
152     my $req = $1;
153     }
154 elmex 1.1
155 elmex 1.6 $line =~ s/\001[^\001]*\001//g;
156 elmex 1.1
157 elmex 1.6 return $line;
158     }
159 elmex 1.1
160 elmex 1.8 =item B<filter_ctcp_text_attr ($line, $cb)>
161    
162     TODO
163    
164     =cut
165 elmex 1.6 # implemented after the below CTCP spec, but
166     # doesnt seem to be used by anyone... so it's untested.
167     sub filter_ctcp_text_attr {
168     my ($line, $cb) = @_;
169 elmex 1.10 return unless $cb;
170 elmex 1.6 $line =~ s/\006([BVUSI])/{warn "FIL\n"; my $c = $cb->($1); defined $c ? $c : "\006$1"}/ieg;
171     $line =~ s/\006CA((?:I[0-9A-F]|#[0-9A-F]{3}){2})/{my $c = $cb->($1); defined $c ? $c : "\006CA$1"}/ieg;
172     $line =~ s/\006C([FB])(I[0-9A-F]|#[0-9A-F]{3})/{my $c = $cb->($1, $2); defined $c ? $c : "\006C$1$2"}/ieg;
173     $line =~ s/\006CX([AFB])/{my $c = $cb->($1); defined $c ? $c : "\006CX$1"}/ieg;
174     return $line;
175 elmex 1.1 }
176    
177     =item B<split_prefix ($prefix)>
178    
179     This function splits an IRC user prefix as described by RFC 2817
180     into the three parts: nickname, user and host. Which will be
181     returned as a list with that order.
182    
183     C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
184    
185     =cut
186    
187     sub split_prefix {
188     my ($prfx) = @_;
189    
190     if (ref ($prfx) eq 'HASH') {
191     $prfx = $prfx->{prefix};
192     }
193    
194 elmex 1.11 # this splitting does indeed use the servername as nickname, but there
195     # is no way for a client to distinguish.
196     $prfx =~ m/^\s*([^!]*)(?:!([^@]*))?(?:@(.*?))?\s*$/;
197 elmex 1.1 return ($1, $2, $3);
198     }
199    
200     =item B<prefix_nick ($prefix)>
201    
202     A shortcut to extract the nickname from the C<$prefix>.
203    
204     C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
205    
206     =cut
207    
208     sub prefix_nick {
209     my ($prfx) = @_;
210     return (split_prefix ($prfx))[0];
211     }
212    
213     =item B<prefix_user ($prefix)>
214    
215     A shortcut to extract the username from the C<$prefix>.
216    
217     C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
218    
219     =cut
220    
221     sub prefix_user {
222     my ($prfx) = @_;
223     return (split_prefix ($prfx))[1];
224     }
225    
226     =item B<prefix_host ($prefix)>
227    
228     A shortcut to extract the hostname from the C<$prefix>.
229    
230     C<$prefix> can also be a hash like it is returned by C<parse_irc_msg>.
231    
232     =cut
233    
234     sub prefix_host {
235 elmex 1.8 my ($prfx) = @_;
236 elmex 1.1 return (split_prefix ($prfx))[2];
237     }
238    
239    
240 elmex 1.7 =item B<rfc_code_to_name ($code)>
241    
242     This function is a interface to the internal mapping or numeric
243     replies to the reply name in RFC 2812 (which you may also consult).
244    
245     C<$code> is returned if no name for C<$code> exists
246     (as some server may extended the protocol).
247    
248 elmex 1.8 =back
249    
250 elmex 1.7 =cut
251    
252     our %RFC_NUMCODE_MAP = (
253     '001' => 'RPL_WELCOME',
254     '002' => 'RPL_YOURHOST',
255     '003' => 'RPL_CREATED',
256     '004' => 'RPL_MYINFO',
257     '005' => 'RPL_BOUNCE',
258     '200' => 'RPL_TRACELINK',
259     '201' => 'RPL_TRACECONNECTING',
260     '202' => 'RPL_TRACEHANDSHAKE',
261     '203' => 'RPL_TRACEUNKNOWN',
262     '204' => 'RPL_TRACEOPERATOR',
263     '205' => 'RPL_TRACEUSER',
264     '206' => 'RPL_TRACESERVER',
265     '207' => 'RPL_TRACESERVICE',
266     '208' => 'RPL_TRACENEWTYPE',
267     '209' => 'RPL_TRACECLASS',
268     '210' => 'RPL_TRACERECONNECT',
269     '211' => 'RPL_STATSLINKINFO',
270     '212' => 'RPL_STATSCOMMANDS',
271     '219' => 'RPL_ENDOFSTATS',
272     '221' => 'RPL_UMODEIS',
273     '233' => 'RPL_SERVICE',
274     '234' => 'RPL_SERVLIST',
275     '235' => 'RPL_SERVLISTEND',
276     '242' => 'RPL_STATSUPTIME',
277     '243' => 'RPL_STATSOLINE',
278     '250' => 'RPL_STATSDLINE',
279     '251' => 'RPL_LUSERCLIENT',
280     '252' => 'RPL_LUSEROP',
281     '253' => 'RPL_LUSERUNKNOWN',
282     '254' => 'RPL_LUSERCHANNELS',
283     '255' => 'RPL_LUSERME',
284     '256' => 'RPL_ADMINME',
285     '257' => 'RPL_ADMINLOC1',
286     '258' => 'RPL_ADMINLOC2',
287     '259' => 'RPL_ADMINEMAIL',
288     '261' => 'RPL_TRACELOG',
289     '262' => 'RPL_TRACEEND',
290     '263' => 'RPL_TRYAGAIN',
291     '301' => 'RPL_AWAY',
292     '302' => 'RPL_USERHOST',
293     '303' => 'RPL_ISON',
294     '305' => 'RPL_UNAWAY',
295     '306' => 'RPL_NOWAWAY',
296     '311' => 'RPL_WHOISUSER',
297     '312' => 'RPL_WHOISSERVER',
298     '313' => 'RPL_WHOISOPERATOR',
299     '314' => 'RPL_WHOWASUSER',
300     '315' => 'RPL_ENDOFWHO',
301     '317' => 'RPL_WHOISIDLE',
302     '318' => 'RPL_ENDOFWHOIS',
303     '319' => 'RPL_WHOISCHANNELS',
304     '321' => 'RPL_LISTSTART',
305     '322' => 'RPL_LIST',
306     '323' => 'RPL_LISTEND',
307     '324' => 'RPL_CHANNELMODEIS',
308     '325' => 'RPL_UNIQOPIS',
309     '331' => 'RPL_NOTOPIC',
310     '332' => 'RPL_TOPIC',
311     '341' => 'RPL_INVITING',
312     '342' => 'RPL_SUMMONING',
313     '346' => 'RPL_INVITELIST',
314     '347' => 'RPL_ENDOFINVITELIST',
315     '348' => 'RPL_EXCEPTLIST',
316     '349' => 'RPL_ENDOFEXCEPTLIST',
317     '351' => 'RPL_VERSION',
318     '352' => 'RPL_WHOREPLY',
319     '353' => 'RPL_NAMREPLY',
320     '364' => 'RPL_LINKS',
321     '365' => 'RPL_ENDOFLINKS',
322     '366' => 'RPL_ENDOFNAMES',
323     '367' => 'RPL_BANLIST',
324     '368' => 'RPL_ENDOFBANLIST',
325     '369' => 'RPL_ENDOFWHOWAS',
326     '371' => 'RPL_INFO',
327     '372' => 'RPL_MOTD',
328     '374' => 'RPL_ENDOFINFO',
329     '375' => 'RPL_MOTDSTART',
330     '376' => 'RPL_ENDOFMOTD',
331     '381' => 'RPL_YOUREOPER',
332     '382' => 'RPL_REHASHING',
333     '383' => 'RPL_YOURESERVICE',
334     '384' => 'RPL_MYPORTIS',
335     '391' => 'RPL_TIME',
336     '392' => 'RPL_USERSSTART',
337     '393' => 'RPL_USERS',
338     '394' => 'RPL_ENDOFUSERS',
339     '395' => 'RPL_NOUSERS',
340     '401' => 'ERR_NOSUCHNICK',
341     '402' => 'ERR_NOSUCHSERVER',
342     '403' => 'ERR_NOSUCHCHANNEL',
343     '404' => 'ERR_CANNOTSENDTOCHAN',
344     '405' => 'ERR_TOOMANYCHANNELS',
345     '406' => 'ERR_WASNOSUCHNICK',
346     '407' => 'ERR_TOOMANYTARGETS',
347     '408' => 'ERR_NOSUCHSERVICE',
348     '409' => 'ERR_NOORIGIN',
349     '411' => 'ERR_NORECIPIENT',
350     '412' => 'ERR_NOTEXTTOSEND',
351     '413' => 'ERR_NOTOPLEVEL',
352     '414' => 'ERR_WILDTOPLEVEL',
353     '415' => 'ERR_BADMASK',
354     '421' => 'ERR_UNKNOWNCOMMAND',
355     '422' => 'ERR_NOMOTD',
356     '423' => 'ERR_NOADMININFO',
357     '424' => 'ERR_FILEERROR',
358     '431' => 'ERR_NONICKNAMEGIVEN',
359     '432' => 'ERR_ERRONEUSNICKNAME',
360     '433' => 'ERR_NICKNAMEINUSE',
361     '436' => 'ERR_NICKCOLLISION',
362     '437' => 'ERR_UNAVAILRESOURCE',
363     '441' => 'ERR_USERNOTINCHANNEL',
364     '442' => 'ERR_NOTONCHANNEL',
365     '443' => 'ERR_USERONCHANNEL',
366     '444' => 'ERR_NOLOGIN',
367     '445' => 'ERR_SUMMONDISABLED',
368     '446' => 'ERR_USERSDISABLED',
369     '451' => 'ERR_NOTREGISTERED',
370     '461' => 'ERR_NEEDMOREPARAMS',
371     '462' => 'ERR_ALREADYREGISTRED',
372     '463' => 'ERR_NOPERMFORHOST',
373     '464' => 'ERR_PASSWDMISMATCH',
374     '465' => 'ERR_YOUREBANNEDCREEP',
375     '466' => 'ERR_YOUWILLBEBANNED',
376     '467' => 'ERR_KEYSET',
377     '471' => 'ERR_CHANNELISFULL',
378     '472' => 'ERR_UNKNOWNMODE',
379     '473' => 'ERR_INVITEONLYCHAN',
380     '474' => 'ERR_BANNEDFROMCHAN',
381     '475' => 'ERR_BADCHANNELKEY',
382     '476' => 'ERR_BADCHANMASK',
383     '477' => 'ERR_NOCHANMODES',
384     '478' => 'ERR_BANLISTFULL',
385     '481' => 'ERR_NOPRIVILEGES',
386     '482' => 'ERR_CHANOPRIVSNEEDED',
387     '483' => 'ERR_CANTKILLSERVER',
388     '484' => 'ERR_RESTRICTED',
389     '485' => 'ERR_UNIQOPPRIVSNEEDED',
390     '491' => 'ERR_NOOPERHOST',
391     '492' => 'ERR_NOSERVICEHOST',
392     '501' => 'ERR_UMODEUNKNOWNFLAG',
393     '502' => 'ERR_USERSDONTMATCH',
394     );
395    
396     sub rfc_code_to_name {
397     my ($code) = @_;
398     return $RFC_NUMCODE_MAP{$code} || $code;
399     }
400    
401 elmex 1.1 =head1 AUTHOR
402    
403     Robin Redeker, C<< <elmex@ta-sa.org> >>
404    
405     =head1 SEE ALSO
406    
407 elmex 1.6 Internet Relay Chat Client To Client Protocol from February 2, 1997
408     http://www.invlogic.com/irc/ctcp.html
409    
410 elmex 1.1 RFC 2812 - Internet Relay Chat: Client Protocol
411    
412     =head1 COPYRIGHT & LICENSE
413    
414 elmex 1.3 Copyright 2006 Robin Redeker, all rights reserved.
415 elmex 1.1
416     This program is free software; you can redistribute it and/or modify it
417     under the same terms as Perl itself.
418    
419     =cut
420    
421     1;