File Coverage

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