File Coverage

blib/lib/Net/IPMessenger.pm
Criterion Covered Total %
statement 129 173 74.5
branch 30 76 39.4
condition 15 36 41.6
subroutine 20 23 86.9
pod 13 13 100.0
total 207 321 64.4


line stmt bran cond sub pod time code
1             package Net::IPMessenger;
2              
3 2     2   65715 use warnings;
  2         4  
  2         66  
4 2     2   9 use strict;
  2         4  
  2         57  
5 2     2   46 use 5.008001;
  2         9  
  2         78  
6 2     2   11 use Carp;
  2         4  
  2         230  
7 2     2   1720 use IO::Socket::INET;
  2         85873  
  2         20  
8 2     2   2929 use Net::IPMessenger::ClientData;
  2         11  
  2         23  
9 2     2   1498 use Net::IPMessenger::Encrypt;
  2         7  
  2         16  
10 2     2   68 use Net::IPMessenger::MessageCommand;
  2         5  
  2         46  
11 2     2   1423 use Net::IPMessenger::RecvEventHandler;
  2         6  
  2         74  
12 2     2   12 use base qw( Class::Accessor::Fast );
  2         4  
  2         8223  
13              
14             __PACKAGE__->mk_accessors(
15             qw(
16             packet_count sending_packet user message
17             nickname groupname username hostname
18             socket serveraddr sendretry broadcast
19             event_handler encrypt debug
20             )
21             );
22              
23             our $VERSION = '0.14';
24             my $PROTO = 'udp';
25             my $PORT = 2425;
26             my $BROADCAST = '255.255.255.255';
27             my $MAX_SOCKBUF = 65535;
28             my $SEND_RETRY = 3;
29              
30             sub new {
31 3     3 1 391 my $class = shift;
32 3         17 my %args = @_;
33              
34 3         7 my $self = {};
35 3         8 bless $self, $class;
36              
37 3         14 $self->packet_count(0);
38 3         41 $self->user( {} );
39 3         25 $self->message( [] );
40 3         27 $self->event_handler( [] );
41 3         23 $self->sending_packet( {} );
42 3         25 $self->broadcast( [] );
43              
44 3 50       31 $self->nickname( $args{NickName} ) if $args{NickName};
45 3 50       23 $self->groupname( $args{GroupName} ) if $args{GroupName};
46 3 50       25 $self->username( $args{UserName} ) if $args{UserName};
47 3 50       32 $self->hostname( $args{HostName} ) if $args{HostName};
48 3 50       21 $self->serveraddr( $args{ServerAddr} ) if $args{ServerAddr};
49 3 50       10 $self->debug( $args{Debug} ) if $args{Debug};
50 3 50       87 $self->add_broadcast( $args{BroadCast} ) if $args{BroadCast};
51 3   33     26 $self->sendretry( $args{SendRetry} || $SEND_RETRY );
52              
53             # encryption support
54 3         34 my $encrypt = Net::IPMessenger::Encrypt->new;
55             # enable only encrypt modules are available
56 3 50       8 $self->encrypt($encrypt) if $encrypt;
57              
58 3 100 33     53 my $sock = IO::Socket::INET->new(
59             Proto => $PROTO,
60             LocalPort => $args{Port} || $PORT,
61             ) or return;
62              
63 2         773 $self->socket($sock);
64 2         31 $self->add_event_handler( Net::IPMessenger::RecvEventHandler->new );
65              
66 2         18 return $self;
67             }
68              
69             sub get_connection {
70 0     0 1 0 shift->socket;
71             }
72              
73             sub add_event_handler {
74 2     2 1 4 my $self = shift;
75 2         4 push @{ $self->event_handler }, shift;
  2         14  
76             }
77              
78             sub add_broadcast {
79 1     1 1 2 my $self = shift;
80 1         2 push @{ $self->broadcast }, shift;
  1         4  
81             }
82              
83             sub recv {
84 4     4 1 1525 my $self = shift;
85 4         17 my $sock = $self->socket;
86              
87 4         236 my $msg;
88 4 50       28 $sock->recv( $msg, $MAX_SOCKBUF ) or croak "recv: $!\n";
89 4         643 my $peeraddr = inet_ntoa( $sock->peeraddr );
90 4         205 my $peerport = $sock->peerport;
91             # ignore yourself
92 4 50       1553 if ( $self->serveraddr ) {
93 0 0       0 return if ( $peeraddr eq $self->serveraddr );
94             }
95              
96 4         53 my $user = Net::IPMessenger::ClientData->new(
97             Message => $msg,
98             PeerAddr => $peeraddr,
99             PeerPort => $peerport,
100             );
101 4         16 $self->update_userlist( $user, $msg );
102              
103 4         100 my $command = $self->messagecommand( $user->command );
104 4         13 my $modename = $command->modename;
105             # invoke event handler
106 4         32 my $ev_handler = $self->event_handler;
107 4 50 33     45 if ( ref $ev_handler and ref $ev_handler eq 'ARRAY' ) {
108 4         82 for my $handler ( @{$ev_handler} ) {
  4         10  
109 4 50 33     17 if ( $self->debug and $handler->can('debug') ) {
110 0         0 $handler->debug( $self, $user );
111             }
112 4 100       348 $handler->$modename( $self, $user ) if $handler->can($modename);
113             }
114             }
115 4         187 return $user;
116             }
117              
118             sub update_userlist {
119 4     4 1 7 my $self = shift;
120 4         5 my $user = shift;
121 4         78 my $msg = shift;
122 4         14 my $key = $user->key;
123              
124             # exists in user list
125 4 50       150 if ( exists $self->user->{$key} ) {
126 0         0 $self->user->{$key}->parse($msg);
127             }
128             # new user
129             else {
130 4         35 my $command = $self->messagecommand( $user->command );
131 4         13 my $modename = $command->modename;
132 4 50 66     103 unless ( $modename eq 'SENDMSG' and $command->get_noaddlist ) {
133 4         14 $self->user->{$key} = $user;
134             }
135             }
136             }
137              
138             sub parse_anslist {
139 0     0 1 0 my $self = shift;
140 0         0 my $user = shift;
141 0         0 my $listaddr = shift;
142              
143 0         0 my @list = split( /\a/, $user->option );
144 0         0 my $title = shift(@list);
145 0         0 my $count = shift(@list);
146              
147 0         0 my %present;
148             my %new;
149 0         0 for my $key ( keys %{ $self->user } ) {
  0         0  
150 0 0 0     0 if ( defined $self->user->{$key}->listaddr
151             and $listaddr eq $self->user->{$key}->listaddr )
152             {
153 0         0 $present{$key} = 1;
154             }
155             }
156              
157 0         0 while (1) {
158 0 0       0 my $uname = shift @list or last;
159 0 0       0 my $host = shift @list or last;
160 0 0       0 my $pnum = shift @list or last;
161 0 0       0 my $addr = shift @list or last;
162 0 0       0 my $com = shift @list or last;
163 0 0       0 my $nick = shift @list or last;
164 0 0       0 my $group = shift @list or last;
165              
166 0 0       0 if ( $self->serveraddr ) {
167 0 0       0 next if ( $addr eq $self->serveraddr );
168             }
169              
170 0         0 my $newuser = Net::IPMessenger::ClientData->new(
171             Ver => 1,
172             PacketNum => $pnum,
173             User => $uname,
174             Host => $host,
175             Command => $com,
176             Nick => $nick,
177             Group => $group,
178             PeerAddr => $addr,
179             PeerPort => $PORT,
180             ListAddr => $listaddr,
181             );
182 0         0 my $newkey = $newuser->key;
183 0         0 $self->user->{$newkey} = $newuser;
184 0         0 $new{$newkey} = 1;
185             }
186              
187 0         0 my @deleted;
188 0         0 foreach my $pkey ( keys %present ) {
189 0 0       0 unless ( exists $new{$pkey} ) {
190 0         0 push @deleted, $self->user->{$pkey}->nickname;
191 0         0 delete $self->user->{$pkey};
192             }
193             }
194 0         0 return (@deleted);
195             }
196              
197             sub generate_packet {
198 4     4 1 9 my( $self, $args ) = @_;
199 4         60 my $command = $args->{command};
200 4   50     23 my $option = $args->{option} || '';
201 4   33     250 my $packet_num = $args->{packet_num} || $self->get_new_packet_num;
202 4         9 $args->{option} = $option;
203 4         8 $args->{packet_num} = $packet_num;
204              
205 4         17 my $msg = sprintf "1:%s:%s:%s:%s:%s", $packet_num, $self->username,
206             $self->hostname, $command, $option;
207 4         11 return $msg;
208             }
209              
210             sub send {
211 4     4 1 80 my( $self, $args ) = @_;
212 4         17 my $sock = $self->socket;
213 4         23 my $command = $args->{command};
214 4         18 my $msg = $self->generate_packet($args);
215 4         8 my $packet_num = $args->{packet_num};
216             # TODO check max msg length check by MAX_SOCKBUF
217              
218             # stack sendmsg packet number
219 4 50 66     15 if ( $command->modename eq 'SENDMSG'
      66        
220             and $command->get_sendcheck
221             and not exists $self->sending_packet->{$packet_num} )
222             {
223 1         13 $args->{sendretry} = $self->sendretry;
224 1         8 $self->sending_packet->{$packet_num} = $args;
225             }
226              
227 4         16 my $peerport = $args->{peerport};
228 4 100       80 if ( not defined $peerport ) {
229 2   33     8 $peerport = $sock->peerport || $PORT;
230             }
231              
232             # send broadcast packet
233 4 100       76 if ( $command->get_broadcast ) {
234 1 50       16 $sock->sockopt( SO_BROADCAST() => 1 )
235             or croak "failed sockopt : $!\n";
236              
237 1 50       36 unless ( @{ $self->broadcast } ) {
  1         6  
238 1         12 $self->add_broadcast($BROADCAST);
239             }
240 1         8 for my $broadcast_addr ( @{ $self->broadcast } ) {
  1         4  
241 1         29 my $dest = sockaddr_in( $peerport, inet_aton($broadcast_addr) );
242 1 50       20 $sock->send( $msg, 0, $dest )
243             or croak "send() failed : $!\n";
244             }
245              
246 1 50       297 $sock->sockopt( SO_BROADCAST() => 0 )
247             or croak "failed sockopt : $!\n";
248             }
249             # send packet
250             else {
251 3         91 my $peeraddr = $args->{peeraddr};
252 3 100       9 if ( not defined $peeraddr ) {
253 2         8 $peeraddr = inet_ntoa( $sock->peeraddr );
254             }
255              
256 3         63 my $dest = sockaddr_in( $peerport, inet_aton($peeraddr) );
257 3 50       115 $sock->send( $msg, 0, $dest )
258             or croak "send() failed : $!\n";
259             }
260             }
261              
262             sub flush_sendings {
263 0     0 1 0 my $self = shift;
264              
265 0         0 for my $packet_num ( keys %{ $self->sending_packet } ) {
  0         0  
266 0         0 my $args = $self->sending_packet->{$packet_num};
267 0 0       0 if ( 0 > --$args->{sendretry} ) {
268 0         0 delete $self->sending_packet->{$packet_num};
269 0         0 next;
270             }
271 0         0 $args->{packet_num} = $packet_num;
272 0         0 $self->send($args);
273             }
274             }
275              
276             sub messagecommand {
277 16     16 1 6929 my $self = shift;
278 16         65 return Net::IPMessenger::MessageCommand->new(shift);
279             }
280              
281             sub get_new_packet_num {
282 4     4 1 7 my $self = shift;
283 4         17 my $count = $self->packet_count;
284 4         30 $self->packet_count( ++$count );
285 4         40 return ( time + $count );
286             }
287              
288             sub my_info {
289 2     2 1 8 my $self = shift;
290 2   50     13 return sprintf "%s\0%s\0", $self->nickname || '', $self->groupname || '';
      50        
291             }
292              
293             1;
294             __END__