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   26 use strict;
  4         12  
  4         171  
8 4     4   21 use warnings;
  4         7  
  4         145  
9              
10 4     4   24 use base qw( Tangence::Object Circle::WindowItem Circle::Ruleable );
  4         10  
  4         719  
11             __PACKAGE__->APPLY_Ruleable;
12              
13 4     4   30 use constant NETTYPE => 'raw';
  4         8  
  4         296  
14              
15 4     4   23 use base qw( Circle::Rule::Store ); # for the attributes
  4         7  
  4         535  
16              
17 4     4   24 use Text::Balanced qw( extract_delimited );
  4         15  
  4         272  
18              
19 4     4   21 use Circle::TaggedString;
  4         15  
  4         113  
20              
21 4     4   22 use Circle::Widget::Box;
  4         8  
  4         110  
22 4     4   21 use Circle::Widget::Label;
  4         7  
  4         1568  
23              
24             sub new
25             {
26 2     2 0 102 my $class = shift;
27 2         13 my %args = @_;
28              
29 2         26 my $self = $class->SUPER::new( %args );
30              
31 2         115 $self->{loop} = $args{loop};
32 2         7 $self->{root} = $args{root};
33              
34 2         21 $self->set_prop_tag( $args{tag} );
35              
36 2         27 $self->{host} = undef;
37 2         6 $self->{port} = undef;
38 2         5 $self->{echo} = 1;
39              
40 2         28 my $rulestore = $self->init_rulestore( parent => $args{root}->{rulestore} );
41              
42 2         12 $rulestore->register_action( "sendline" => $self );
43              
44 2         11 $rulestore->new_chain( "input" );
45 2         8 $rulestore->new_chain( "output" );
46 2         7 $rulestore->new_chain( "connected" );
47              
48 2         8 return $self;
49             }
50              
51             sub describe
52             {
53 2     2 1 218 my $self = shift;
54 2         33 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 4     4   33 }
  4         25  
  4         35  
74              
75             sub deparse_action_sendline
76             {
77 0     0 0 0 my $self = shift;
78 0         0 my ( $text ) = @_;
79              
80 0         0 $text =~ s/([\\"])/\\$1/g;
81 0         0 return qq{"$text"};
82             }
83              
84             sub eval_action_sendline
85             {
86 0     0 0 0 my $self = shift;
87 0         0 my ( $event, $results, $text ) = @_;
88              
89 0 0       0 if( my $conn = $self->{conn} ) {
90 0         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 1     1 0 2 my $self = shift;
100 1         3 my ( $host, $port, $cinv ) = @_;
101              
102 1   33     3 $host ||= $self->{host};
103 1   33     4 $port ||= $self->{port}; # 0 is not a valid TCP port
104              
105 1 50       4 defined $host or return $cinv->responderr( "Cannot connect - no host defined" );
106 1 50       4 defined $port or return $cinv->responderr( "Cannot connect - no port defined" );
107              
108 1         3 my $loop = $self->{loop};
109             $loop->connect(
110             host => $host,
111             service => $port,
112             socktype => 'stream',
113              
114             on_connected => sub {
115 1     1   2055069 my ( $sock ) = @_;
116              
117 1         58 $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 2         9531 my ( undef, $buffref, $closed ) = @_;
123 2 50       32 return 0 unless $$buffref =~ s/^([^\r\n]*)\r?\n//;
124              
125 2         14 $self->incoming_text( $1 );
126              
127 2         380 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 1         38 );
138              
139 1         183 $loop->add( $conn );
140              
141 1         210 $self->run_rulechain( "connected" );
142              
143 1         16 $self->set_prop_connected(1);
144 1         35 $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 1         30 );
155              
156 1         49881 return;
157 4     4   3558 }
  4         9  
  4         23  
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 4     4   2165 }
  4         8  
  4         29  
177              
178             sub connected
179             {
180 0     0 0 0 my $self = shift;
181 0         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 4     4   1270 }
  4         9  
  4         20  
196              
197             sub do_send
198             {
199 1     1 0 2 my $self = shift;
200 1         3 my ( $text ) = @_;
201              
202             # TODO: Line separator
203              
204 1 50       12 if( my $conn = $self->{conn} ) {
205 1         66 my $event = {
206             text => Circle::TaggedString->new( $text ),
207             };
208              
209 1         44 $self->run_rulechain( "output", $event );
210              
211 1         31 my $str = $event->{text}->str;
212 1         119 $conn->write( "$str\r\n" );
213              
214 1 50       195 $self->push_displayevent( "text", { text => $event->{text} } ) if $self->{echo};
215             }
216             else {
217 0         0 $self->responderr( "Not connected" );
218             }
219             }
220              
221             sub enter_text
222             {
223 1     1 0 9 my $self = shift;
224 1         2 my ( $text ) = @_;
225              
226 1         5 $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 0 my $self = shift;
234 0         0 my ( $text, $cinv ) = @_;
235              
236 0         0 $self->do_send( $text );
237 4     4   2979 }
  4         10  
  4         185  
238              
239             sub incoming_text
240             {
241 2     2 0 14 my $self = shift;
242 2         7 my ( $text ) = @_;
243              
244 2         18 my $event = {
245             text => Circle::TaggedString->new( $text ),
246             level => 2,
247             };
248              
249 2         63 $self->run_rulechain( "input", $event );
250              
251 2         28 $self->push_displayevent( "text", { text => $event->{text} } );
252 2 50       17 $self->bump_level( $event->{level} ) if defined $event->{level};
253             }
254              
255             sub commandable_parent
256             {
257 2     2 0 6 my $self = shift;
258 2         16 return $self->{root};
259             }
260              
261             sub enumerable_name
262             {
263 0     0 0 0 my $self = shift;
264 0         0 return $self->get_prop_tag;
265             }
266              
267             sub parent
268             {
269 4     4 0 23 my $self = shift;
270 4         51 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 1     1 0 2 my $self = shift;
295              
296 1         10 my $registry = $self->{registry};
297              
298 1         5 my $statusbar = $registry->construct(
299             "Circle::Widget::Box",
300             classes => [qw( status )],
301             orientation => "horizontal",
302             );
303              
304 1         5 my $serverlabel = $registry->construct(
305             "Circle::Widget::Label",
306             classes => [qw( label )],
307             );
308             $self->subscribe_event( connected => sub {
309 1     1   523 my ( $self, $host, $port ) = @_;
310 1         7 $serverlabel->set_prop_text( "$host:$port" );
311 1         8 } );
312             $self->subscribe_event( disconnected => sub {
313 0     0   0 $serverlabel->set_prop_text( "--unconnected--" );
314 1         109 } );
315              
316 1         153 $statusbar->add( $serverlabel );
317              
318 1         22 return $statusbar;
319             }
320              
321             0x55AA;