File Coverage

blib/lib/Circle/Net/Raw.pm
Criterion Covered Total %
statement 42 152 27.6
branch 0 18 0.0
condition 0 6 0.0
subroutine 14 36 38.8
pod 1 17 5.8
total 57 229 24.8


line stmt bran cond sub pod time code
1             # You may distribute under the terms of the GNU General Public License
2             #
3             # (C) Paul Evans, 2008-2012 -- leonerd@leonerd.org.uk
4              
5             package Circle::Net::Raw;
6              
7 1     1   4470 use strict;
  1         2  
  1         24  
8 1     1   3 use warnings;
  1         1  
  1         27  
9              
10 1     1   4 use base qw( Tangence::Object Circle::WindowItem Circle::Ruleable );
  1         0  
  1         126  
11             __PACKAGE__->APPLY_Ruleable;
12              
13 1     1   5 use constant NETTYPE => 'raw';
  1         1  
  1         62  
14              
15 1     1   4 use base qw( Circle::Rule::Store ); # for the attributes
  1         1  
  1         63  
16              
17 1     1   4 use Text::Balanced qw( extract_delimited );
  1         1  
  1         48  
18              
19 1     1   3 use Circle::TaggedString;
  1         1  
  1         20  
20              
21 1     1   3 use Circle::Widget::Box;
  1         1  
  1         13  
22 1     1   6 use Circle::Widget::Label;
  1         1  
  1         254  
23              
24             sub new
25             {
26 0     0 0   my $class = shift;
27 0           my %args = @_;
28              
29 0           my $self = $class->SUPER::new( %args );
30              
31 0           $self->{loop} = $args{loop};
32 0           $self->{root} = $args{root};
33              
34 0           $self->set_prop_tag( $args{tag} );
35              
36 0           $self->{host} = undef;
37 0           $self->{port} = undef;
38 0           $self->{echo} = 1;
39              
40 0           my $rulestore = $self->init_rulestore( parent => $args{root}->{rulestore} );
41              
42 0           $rulestore->register_action( "sendline" => $self );
43              
44 0           $rulestore->new_chain( "input" );
45 0           $rulestore->new_chain( "output" );
46 0           $rulestore->new_chain( "connected" );
47              
48 0           return $self;
49             }
50              
51             sub describe
52             {
53 0     0 1   my $self = shift;
54 0           return __PACKAGE__."[" . $self->get_prop_tag . "]";
55             }
56              
57             sub parse_action_sendline
58             : Rule_description("Send a line of text to the peer")
59             : Rule_format('$text')
60             {
61 0     0 0 0 my $self = shift;
62 0         0 my ( $spec ) = @_;
63              
64 0         0 my $text = extract_delimited( $spec, q{"} );
65            
66             # Trim leading and trailing "
67 0         0 s/^"//, s/"$// for $text;
68              
69             # Unescape intermediate \\ and \"
70 0         0 $text =~ s/\\([\\"])/$1/g;
71              
72 0         0 return $text;
73 1     1   4 }
  1         1  
  1         5  
74              
75             sub deparse_action_sendline
76             {
77 0     0 0   my $self = shift;
78 0           my ( $text ) = @_;
79              
80 0           $text =~ s/([\\"])/\\$1/g;
81 0           return qq{"$text"};
82             }
83              
84             sub eval_action_sendline
85             {
86 0     0 0   my $self = shift;
87 0           my ( $event, $results, $text ) = @_;
88              
89 0 0         if( my $conn = $self->{conn} ) {
90 0           $conn->write( "$text\r\n" );
91             }
92             }
93              
94             sub command_connect
95             : Command_description("Connect to the server")
96             : Command_arg('host?')
97             : Command_arg('port?')
98             {
99 0     0 0 0 my $self = shift;
100 0         0 my ( $host, $port, $cinv ) = @_;
101              
102 0   0     0 $host ||= $self->{host};
103 0   0     0 $port ||= $self->{port}; # 0 is not a valid TCP port
104              
105 0 0       0 defined $host or return $cinv->responderr( "Cannot connect - no host defined" );
106 0 0       0 defined $port or return $cinv->responderr( "Cannot connect - no port defined" );
107              
108 0         0 my $loop = $self->{loop};
109             $loop->connect(
110             host => $host,
111             service => $port,
112             socktype => 'stream',
113              
114             on_connected => sub {
115 0     0   0 my ( $sock ) = @_;
116              
117 0         0 $cinv->respond( "Connected to $host:$port", level => 1 );
118              
119             my $conn = $self->{conn} = IO::Async::Stream->new(
120             handle => $sock,
121             on_read => sub {
122 0         0 my ( undef, $buffref, $closed ) = @_;
123 0 0       0 return 0 unless $$buffref =~ s/^([^\r\n]*)\r?\n//;
124              
125 0         0 $self->incoming_text( $1 );
126              
127 0         0 return 1;
128             },
129              
130             on_closed => sub {
131 0         0 $self->push_displayevent( "status", { text => "Connection closed by peer" } );
132              
133 0         0 $self->set_prop_connected(0);
134 0         0 $self->fire_event( disconnected => );
135 0         0 undef $self->{conn};
136             },
137 0         0 );
138              
139 0         0 $loop->add( $conn );
140              
141 0         0 $self->run_rulechain( "connected" );
142              
143 0         0 $self->set_prop_connected(1);
144 0         0 $self->fire_event( connected => $host, $port );
145             },
146              
147             on_resolve_error => sub {
148 0     0   0 $cinv->responderr( "Unable to resolve $host:$port - $_[0]", level => 3 );
149             },
150              
151             on_connect_error => sub {
152 0     0   0 $cinv->responderr( "Unable to connect to $host:$port", level => 3 );
153             },
154 0         0 );
155              
156 0         0 return;
157 1     1   556 }
  1         1  
  1         3  
158              
159             sub command_discon
160             : Command_description( "Disconnect TCP port" )
161             {
162 0     0 0 0 my $self = shift;
163 0         0 my ( $cinv ) = @_;
164              
165 0 0       0 if( my $conn = $self->{conn} ) {
166 0         0 $conn->close;
167 0         0 undef $self->{conn};
168              
169 0         0 $cinv->respond( "Disconnected", level => 1 );
170             }
171             else {
172 0         0 $cinv->responderr( "Not connected" );
173             }
174              
175 0         0 return;
176 1     1   195 }
  1         1  
  1         4  
177              
178             sub connected
179             {
180 0     0 0   my $self = shift;
181 0           defined $self->{conn};
182             }
183              
184             sub command_close
185             : Command_description("Disconnect and close the window")
186             {
187 0     0 0 0 my $self = shift;
188              
189 0 0       0 if( my $conn = $self->{conn} ) {
190 0         0 $conn->close;
191 0         0 undef $self->{conn};
192             }
193              
194 0         0 $self->destroy;
195 1     1   211 }
  1         1  
  1         4  
196              
197             sub do_send
198             {
199 0     0 0   my $self = shift;
200 0           my ( $text ) = @_;
201              
202             # TODO: Line separator
203              
204 0 0         if( my $conn = $self->{conn} ) {
205 0           my $event = {
206             text => Circle::TaggedString->new( $text ),
207             };
208              
209 0           $self->run_rulechain( "output", $event );
210              
211 0           my $str = $event->{text}->str;
212 0           $conn->write( "$str\r\n" );
213              
214 0 0         $self->push_displayevent( "text", { text => $event->{text} } ) if $self->{echo};
215             }
216             else {
217 0           $self->responderr( "Not connected" );
218             }
219             }
220              
221             sub enter_text
222             {
223 0     0 0   my $self = shift;
224 0           my ( $text ) = @_;
225              
226 0           $self->do_send( $text );
227             }
228              
229             sub command_send
230             : Command_description('Send a line of text')
231             : Command_arg('text', eatall => 1)
232             {
233 0     0 0   my $self = shift;
234 0           my ( $text, $cinv ) = @_;
235              
236 0           $self->do_send( $text );
237 1     1   267 }
  1         2  
  1         3  
238              
239             sub incoming_text
240             {
241 0     0 0   my $self = shift;
242 0           my ( $text ) = @_;
243              
244 0           my $event = {
245             text => Circle::TaggedString->new( $text ),
246             level => 2,
247             };
248              
249 0           $self->run_rulechain( "input", $event );
250              
251 0           $self->push_displayevent( "text", { text => $event->{text} } );
252 0 0         $self->bump_level( $event->{level} ) if defined $event->{level};
253             }
254              
255             sub commandable_parent
256             {
257 0     0 0   my $self = shift;
258 0           return $self->{root};
259             }
260              
261             sub enumerable_name
262             {
263 0     0 0   my $self = shift;
264 0           return $self->get_prop_tag;
265             }
266              
267             sub parent
268             {
269 0     0 0   my $self = shift;
270 0           return $self->{root};
271             }
272              
273             __PACKAGE__->APPLY_Setting( host =>
274             description => "Hostname of the server",
275             type => 'str',
276             );
277              
278             __PACKAGE__->APPLY_Setting( port =>
279             description => "Port number of the server",
280             type => 'int',
281             );
282              
283             __PACKAGE__->APPLY_Setting( echo =>
284             description => "Local line echo",
285             type => 'bool',
286             );
287              
288             ###
289             # Widgets
290             ###
291              
292             sub get_widget_statusbar
293             {
294 0     0 0   my $self = shift;
295              
296 0           my $registry = $self->{registry};
297              
298 0           my $statusbar = $registry->construct(
299             "Circle::Widget::Box",
300             classes => [qw( status )],
301             orientation => "horizontal",
302             );
303              
304 0           my $serverlabel = $registry->construct(
305             "Circle::Widget::Label",
306             classes => [qw( label )],
307             );
308             $self->subscribe_event( connected => sub {
309 0     0     my ( $self, $host, $port ) = @_;
310 0           $serverlabel->set_prop_text( "$host:$port" );
311 0           } );
312             $self->subscribe_event( disconnected => sub {
313 0     0     $serverlabel->set_prop_text( "--unconnected--" );
314 0           } );
315              
316 0           $statusbar->add( $serverlabel );
317              
318 0           return $statusbar;
319             }
320              
321             0x55AA;