File Coverage

blib/lib/POE/Filter/CTCP/Hybrid.pm
Criterion Covered Total %
statement 12 89 13.4
branch 0 40 0.0
condition 0 3 0.0
subroutine 4 12 33.3
pod 4 4 100.0
total 20 148 13.5


line stmt bran cond sub pod time code
1             # $Id: Filter-CTCP.pm,v 1.3 1999/12/12 11:48:07 dennis Exp $
2             #
3             # POE::Filter::CTCP, by Dennis Taylor
4             #
5             # This module may be used, modified, and distributed under the same
6             # terms as Perl itself. Please see the license that came with your Perl
7             # distribution for details.
8             #
9              
10             package POE::Filter::CTCP::Hybrid;
11              
12             push (@INC,".");
13 1     1   7 use strict;
  1         2  
  1         23  
14 1     1   5 use Carp;
  1         2  
  1         40  
15 1     1   5 use File::Basename ();
  1         2  
  1         11  
16 1     1   4 use POE::Filter::IRC::Hybrid;
  1         2  
  1         918  
17              
18              
19             # Create a new, empty POE::Filter::CTCP object.
20             sub new {
21 0     0 1   my $class = shift;
22 0           my %args = @_;
23              
24 0           my $self = { 'irc_filter' => POE::Filter::IRC::Hybrid->new() };
25 0           bless $self, $class;
26             }
27              
28              
29             # Set/clear the 'debug' flag.
30             sub debug {
31 0     0 1   my $self = shift;
32 0 0         $self->{'debug'} = $_[0] if @_;
33 0           return( $self->{'debug'} );
34             }
35              
36              
37             # For each line of raw CTCP input data that we're fed, spit back the
38             # appropriate CTCP and normal message events.
39             sub get {
40 0     0 1   my ($self, $lineref) = @_;
41 0           my ($who, $type, $where, $ctcp, $text, $name, $args);
42 0           my $events = [];
43              
44             LINE:
45 0           foreach my $line (@$lineref) {
46 0           ($who, $type, $where, $ctcp, $text) = _ctcp_dequote( $line );
47              
48 0           foreach (@$ctcp) {
49             ($name, $args) = $_ =~ /^(\w+)(?: (.*))?/
50 0 0         or do { warn "Received malformed CTCP message: \"$_\""; next LINE; };
  0            
  0            
51 0 0         if (lc $name eq 'dcc') {
52             $args =~ /^(\w+) (\S+) (\d+) (\d+)(?: (\d+))?$/
53 0 0         or do { warn "Received malformed DCC request: \"$_\""; next LINE; };
  0            
  0            
54 0           my $basename = File::Basename::basename( $2 );
55 0           push @$events, { name => 'dcc_request',
56             args => [ $who, uc $1, $4, { open => undef,
57             nick => $who,
58             type => uc $1,
59             file => $basename,
60             size => $5,
61             done => 0,
62             addr => $3,
63             port => $4,
64             }, $basename, $5 ]
65             };
66              
67             } else {
68 0 0         push @$events, { name => $type . '_' . lc $name,
69             args => [ $who, [split /,/, $where],
70             (defined $args ? $args : '') ]
71             };
72             }
73             }
74              
75 0 0 0       if ($text and @$text > 0) {
76 0 0         $line =~ /^(:\S+ +\w+ +\S+ +)/ or warn "What the heck? \"$line\"";
77 0           $text = $1 . ':' . join '', @$text;
78 0           $text =~ s/\cP/^P/g;
79 0 0         warn "CTCP: $text\n" if $self->{'debug'};
80 0           push @$events, @{$self->{irc_filter}->get( [$text] )};
  0            
81             }
82             }
83              
84 0           return $events;
85             }
86              
87              
88             # For each line of text we're fed, spit back a CTCP-quoted version of
89             # that line.
90             sub put {
91 0     0 1   my ($self, $lineref) = @_;
92 0           my $quoted = [];
93              
94 0           foreach my $line (@$lineref) {
95 0           push @$quoted, _ctcp_quote( $line );
96             }
97              
98 0           return $quoted;
99             }
100              
101              
102             # Quotes a string in a low-level, protocol-safe, utterly brain-dead
103             # fashion. Returns the quoted string.
104             sub _low_quote {
105 0     0     my $line = shift;
106 0           my %enquote = ("\012" => 'n', "\015" => 'r', "\0" => '0', "\cP" => "\cP");
107              
108 0 0         unless (defined $line) {
109 0           die "Not enough arguments to POE::Filter::CTCP->_low_quote";
110             }
111              
112 0 0         if ($line =~ tr/[\012\015\0\cP]//) { # quote \n, \r, ^P, and \0.
113 0           $line =~ s/([\012\015\0\cP])/\cP$enquote{$1}/g;
114             }
115              
116 0           return $line;
117             }
118              
119              
120             # Does low-level dequoting on CTCP messages. I hate this protocol.
121             # Yes, I copied this whole section out of Net::IRC.
122             sub _low_dequote {
123 0     0     my $line = shift;
124 0           my %dequote = (n => "\012", r => "\015", 0 => "\0", "\cP" => "\cP");
125              
126 0 0         unless (defined $line) {
127 0           die "Not enough arguments to POE::Filter::CTCP->_low_dequote";
128             }
129              
130             # Thanks to Abigail (abigail@foad.org) for this clever bit.
131 0 0         if ($line =~ tr/\cP//) { # dequote \n, \r, ^P, and \0.
132 0           $line =~ s/\cP([nr0\cP])/$dequote{$1}/g;
133             }
134              
135 0           return $line;
136             }
137              
138              
139             # Properly CTCP-quotes a message. Whoop.
140             sub _ctcp_quote {
141 0     0     my $line = shift;
142              
143 0           $line = _low_quote( $line );
144             # $line =~ s/\\/\\\\/g;
145 0           $line =~ s/\001/\\a/g;
146              
147 0           return "\001" . $line . "\001";
148             }
149              
150              
151             # Splits a message into CTCP and text chunks. This is gross. Most of
152             # this is also stolen from Net::IRC, but I wrote that too, so it's
153             # used with permission. ;-)
154             sub _ctcp_dequote {
155 0     0     my $line = shift;
156 0           my (@chunks, $ctcp, $text, $who, $type, $where, $msg);
157              
158             # CHUNG! CHUNG! CHUNG!
159              
160 0 0         unless (defined $line) {
161 0           die "Not enough arguments to POE::Filter::CTCP->_ctcp_dequote";
162             }
163              
164             # Strip out any low-level quoting in the text.
165 0           $line = _low_dequote( $line );
166              
167             # Filter misplaced \001s before processing... (Thanks, tchrist!)
168 0 0         substr($line, rindex($line, "\001"), 1) = '\\a'
169             unless ($line =~ tr/\001//) % 2 == 0;
170              
171 0 0         return unless $line =~ tr/\001//;
172              
173 0 0         ($who, $type, $where, $msg) = ($line =~ /^:(\S+) +(\w+) +(\S+) +:?(.*)$/)
174             or return;
175 0           @chunks = split /\001/, $msg;
176 0 0         shift @chunks unless length $chunks[0]; # FIXME: Is this safe?
177              
178 0           foreach (@chunks) {
179             # Dequote unnecessarily quoted chars, and convert escaped \'s and ^A's.
180 0           s/\\([^\\a])/$1/g;
181 0           s/\\\\/\\/g;
182 0           s/\\a/\001/g;
183             }
184              
185             # If the line begins with a control-A, the first chunk is a CTCP
186             # message. Otherwise, it starts with text and alternates with CTCP
187             # messages. Really stupid protocol.
188              
189 0 0         if ($msg =~ /^\001/) {
190 0           push @$ctcp, shift @chunks;
191             }
192              
193 0           while (@chunks) {
194 0           push @$text, shift @chunks;
195 0 0         push @$ctcp, shift @chunks if @chunks;
196             }
197              
198             # Is this a CTCP request or reply?
199 0 0         if ($type eq 'PRIVMSG') {
200 0           $type = 'ctcp';
201             } else {
202 0           $type = 'ctcpreply';
203             }
204              
205 0           return ($who, $type, $where, $ctcp, $text);
206             }
207              
208              
209             1;
210              
211             __END__