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

# Content
1 package Net::IRC3::Util;
2 use strict;
3 no warnings;
4 use Exporter;
5 our @ISA = qw/Exporter/;
6 our @EXPORT_OK =
7 qw(mk_msg parse_irc_msg split_prefix prefix_nick
8 decode_ctcp filter_ctcp_text_attr prefix_user prefix_host
9 rfc_code_to_name);
10
11 =head1 NAME
12
13 Net::IRC3::Util - Common utilities that help with IRC protocol handling
14
15 =head1 SYNOPSIS
16
17 use Net::IRC3 qw/parse_irc_msg mk_msg/;
18
19 my $msgdata = mk_msg (undef, PRIVMSG => "my hands glow!", "mcmanus");
20
21 =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 # will return: "JOIN #test\015\012"
121
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 =item B<decode_ctcp ($line)>
143
144 TODO
145
146 =cut
147
148 sub decode_ctcp {
149 my ($line) = @_;
150
151 while ($line =~ /\G\001([^\001]*)\001/g) {
152 my $req = $1;
153 }
154
155 $line =~ s/\001[^\001]*\001//g;
156
157 return $line;
158 }
159
160 =item B<filter_ctcp_text_attr ($line, $cb)>
161
162 TODO
163
164 =cut
165 # 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 return unless $cb;
170 $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 }
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 # 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 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 my ($prfx) = @_;
236 return (split_prefix ($prfx))[2];
237 }
238
239
240 =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 =back
249
250 =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 =head1 AUTHOR
402
403 Robin Redeker, C<< <elmex@ta-sa.org> >>
404
405 =head1 SEE ALSO
406
407 Internet Relay Chat Client To Client Protocol from February 2, 1997
408 http://www.invlogic.com/irc/ctcp.html
409
410 RFC 2812 - Internet Relay Chat: Client Protocol
411
412 =head1 COPYRIGHT & LICENSE
413
414 Copyright 2006 Robin Redeker, all rights reserved.
415
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;