File Coverage

blib/lib/Net/ICB.pm
Criterion Covered Total %
statement 128 163 78.5
branch 27 54 50.0
condition 12 41 29.2
subroutine 23 26 88.4
pod 10 14 71.4
total 200 298 67.1


line stmt bran cond sub pod time code
1             #!/usr/local/bin/perl -w
2             #
3             # Net::ICB ver 1.6 10/7/98
4             # John M Vinopal banshee@resort.com
5             #
6             # Copyright (C) 1996-98, John M Vinopal, All Rights Reserved.
7             # Permission is granted to copy and modify this program for
8             # non-commercial purposes, so long as this copyright notice is
9             # preserved. This software is distributed without warranty.
10             # Commercial users must contact the author for licensing terms.
11             #
12              
13             package Net::ICB;
14              
15             require 5.004;
16 1     1   886 use strict;
  1         2  
  1         13702  
17 1     1   20 use Carp;
  1         3  
  1         141  
18 1     1   1136 use IO::Socket;
  1         48613  
  1         7  
19 1     1   882 use vars qw($VERSION);
  1         2  
  1         116  
20             $VERSION = '1.6';
21              
22 1         190 use vars qw($M_LOGIN $M_LOGINOK $M_OPEN $M_PERSONAL $M_STATUS
23             $M_ERROR $M_ALERT $M_EXIT $M_COMMAND $M_CMDOUT $M_PROTO
24 1     1   6 $M_BEEP $M_PING $M_PONG $M_OOPEN $M_OPERSONAL);
  1         1  
25 1     1   6 use vars qw($DEF_HOST $DEF_PORT $DEF_GROUP $DEF_CMD $DEF_USER);
  1         2  
  1         86  
26              
27             require Exporter;
28              
29 1     1   5 use vars qw(@ISA %EXPORT_TAGS @EXPORT_OK);
  1         2  
  1         2263  
30             @ISA = qw(Exporter);
31              
32             %EXPORT_TAGS = ( client => [qw($M_LOGIN $M_LOGINOK $M_OPEN $M_PERSONAL
33             $M_STATUS $M_ERROR $M_ALERT $M_EXIT $M_COMMAND $M_CMDOUT
34             $M_PROTO $M_BEEP $M_PING)],
35             defaults => [qw($DEF_HOST $DEF_PORT $DEF_GROUP $DEF_CMD $DEF_USER)] );
36              
37             @EXPORT_OK = qw( $M_LOGIN $M_LOGINOK $M_OPEN $M_PERSONAL $M_STATUS $M_ERROR
38             $M_ALERT $M_EXIT $M_COMMAND $M_CMDOUT $M_PROTO $M_BEEP $M_PING
39             $M_PONG $M_OOPEN $M_OPERSONAL
40             $DEF_HOST $DEF_PORT $DEF_GROUP $DEF_CMD $DEF_USER);
41              
42             # Default connection values.
43             # (evolve.icb.net, empire.icb.net, cjnetworks.icb.net, swcp.icb.net)
44             $DEF_HOST = "default.icb.net";
45             $DEF_PORT = 7326;
46             $DEF_GROUP = "1";
47             $DEF_CMD = "login"; # cmds are only "login" and "w"
48             $DEF_USER = eval { getlogin() } || "user".substr(rand(), 2, 5);
49              
50             # Protocol definitions: all nice cleartext.
51             my $DEL = "\001"; # Packet argument delimiter.
52             $M_LOGIN = 'a'; # login packet
53             $M_LOGINOK = 'a'; # login response
54             $M_OPEN = 'b'; # open msg to group
55             $M_PERSONAL = 'c'; # personal message
56             $M_STATUS = 'd'; # group status update message
57             $M_ERROR = 'e'; # error message
58             $M_ALERT = 'f'; # important announcement
59             $M_EXIT = 'g'; # quit packet from server
60             $M_COMMAND = 'h'; # send a command from user
61             $M_CMDOUT = 'i'; # output from a command
62             $M_PROTO = 'j'; # protocol/version information
63             $M_BEEP = 'k'; # beeps
64             $M_PING = 'l'; # ping packet from server
65             $M_PONG = 'm'; # return for ping packet
66             # Archaic packets: some sort of echo scheme?
67             $M_OOPEN = 'n'; # for own open messages
68             $M_OPERSONAL = 'o'; # for own personal messages
69              
70             # Create a new fnet object and optionally connect to a server.
71             # keys: host port user nick group cmd passwd
72             sub new {
73 2     2 1 544 my $class = shift;
74 2         6 my $self = {};
75 2         6 bless $self, $class;
76 2 100 66     109 if (!@_ or $self->connect(@_)) {
77 1         13 return $self;
78             }
79 1         205 carp $self->error();
80 1         14 return;
81             }
82              
83             sub DESTROY {
84 2     2   338 my ($self) = @_;
85 2         12 $self->close();
86 2         884 undef $self;
87             }
88              
89             # Version Checking.
90 1     1 1 31 sub version { $VERSION; }
91              
92             # Error reporting.
93 8     8 1 145 sub error { my $self = shift; return $self->{'errstr'}; }
  8         560  
94 2     2 1 13 sub clearerr { my $self = shift; delete $self->{'errstr'}; }
  2         14  
95              
96             # Debugging.
97 4     4 1 7 sub debug { my $self = shift; my $level = shift; $self->{'debug'} = $level; }
  4         7  
  4         12  
98              
99             # Return the object's socket.
100             sub fd {
101 36     36 1 74 my ($self) = @_;
102 36         97 return $self->{'socket'};
103             }
104              
105             # Open or group wide message.
106             sub sendopen {
107 0 0 0 0 1 0 @_ == 2 && ref($_[0]) or die '$obj->sendopen(string)';
108 0         0 my ($self, $txt) = @_;
109 0         0 my ($pbuf) = "$M_OPEN$txt";
110              
111 0 0       0 if (eval { $self->_sendpacket($pbuf) }) {
  0         0  
112 0         0 return 'ok';
113             }
114 0         0 $self->{'errstr'} = $@;
115 0         0 return;
116             }
117              
118             # Private or user-directed message.
119             sub sendpriv {
120 3 50 33 3 1 7320 @_ >= 2 && ref($_[0]) or die '$obj->sendpriv(user, string)';
121 3         11 my ($self, @args) = @_;
122 3         14 return $self->sendcmd("m", @args);
123             # Any $self->{'errstr'} will be set in sendcmd().
124             }
125              
126             # Server processed command.
127             #sub sendcmd(cmd, args)
128             sub sendcmd {
129 4 50 33 4 1 617 @_ >= 2 && ref($_[0]) or die '$obj->sendpriv(cmd, [args])';
130 4         11 my ($self, $cmd, @args) = @_;
131 4         21 my $pbuf = "$M_COMMAND$cmd$DEL@args";
132              
133 4 100       8 if (eval { $self->_sendpacket($pbuf) }) {
  4         86  
134 3         49 return 'ok';
135             }
136 1         4 $self->{'errstr'} = $@;
137 1         12 return;
138             }
139              
140             # Ping reply.
141             sub sendpong {
142 0 0 0 0 0 0 @_ == 1 && ref($_[0]) or die '$obj->sendpong()';
143 0         0 my ($self) = shift;
144 0         0 my ($pbuf) = "$M_PONG";
145              
146 0 0       0 if (eval { $self->_sendpacket($pbuf) }) {
  0         0  
147 0         0 return 'ok';
148             }
149 0         0 $self->{'errstr'} = $@;
150 0         0 return;
151             }
152              
153             # Send a raw packet (ie: don't insert a packet type)
154             sub sendraw {
155 0 0 0 0 0 0 @_ == 2 && ref($_[0]) or die '$obj->sendraw()';
156 0         0 my ($self) = shift;
157 0         0 my ($pbuf) = shift;
158              
159 0 0       0 if (eval { $self->_sendpacket($pbuf) }) {
  0         0  
160 0         0 return 'ok';
161             }
162 0         0 $self->{'errstr'} = $@;
163 0         0 return;
164             }
165              
166             # Read a message from the server and break it into its fields.
167             # XXX - timeout to prevent sitting on bad socket?
168             sub readmsg {
169 30 50 33 30 0 689 @_ == 1 && ref($_[0]) or die '$obj->readmsg()';
170 30         56 my $self = shift;
171 30         44 my $msg;
172              
173             # Read the waiting packet.
174 30 50       42 if (eval { $msg = $self->_recvpacket() }) {
  30         71  
175             # Break up the message.
176 30         167 my ($type, $buf) = unpack("aa*", $msg);
177 30         172 my @split = split($DEL, $buf);
178             # Reply to a ping with a pong.
179             # XXX - let client decide about this?
180 30 50       80 $self->sendpong() if $type eq $M_PING;
181 30         211 return ($type, @split);
182             }
183 0         0 $self->{'errstr'} = $@;
184 0         0 return;
185             }
186              
187             # Connect to a server and send our login packet.
188             # keys: host port user nick group cmd passwd
189             sub connect {
190 4     4 1 37 my $self = shift;
191 4         16 my %args = @_;
192              
193 4         16 undef %$self; # Clear previous values.
194 4         15 $self->debug(0);
195 4   33     14 my $hostname = $args{'host'} || $DEF_HOST;
196 4   33     21 my $portnumber = $args{'port'} || $DEF_PORT;
197 4 100       6 if (eval { $self->_tcpopen($hostname,$portnumber) }) {
  4         16  
198 1 50       4 if (eval { $self->_sendlogin(@_) }) {
  1         12  
199 1         31 return 'ok';
200             }
201 0         0 undef %$self;
202             }
203 3         19246 $self->{'errstr'} = "connect: $@";
204 3         53 return;
205             }
206              
207             sub close {
208 2     2 0 5 my $self = shift;
209 2 100       60 if (defined $self->{'socket'}) {
210 1         89 shutdown($self->{'socket'}, 2);
211 1         45 CORE::close($self->{'socket'});
212             }
213 2         21 undef %$self;
214             }
215              
216             #### Internal Methods for Net::ICB ####
217              
218             # Sends a login packet to the server. It specifies our login name,
219             # nickname, active group, a command "login" or "w", and our passwd.
220             sub _sendlogin {
221 1     1   5 my $self = shift;
222 1         9 my %args = @_;
223 1         4 my ($user, $nick, $group, $cmd, $passwd);
224              
225 1   33     8 $user = $args{'user'} || $DEF_USER;
226 1   33     18 $nick = $args{'nick'} || $user;
227 1   33     9 $group = $args{'group'} || $DEF_GROUP;
228 1   33     10 $cmd = $args{'cmd'} || $DEF_CMD;
229 1   50     11 $passwd = $args{'passwd'} || "";
230              
231 1         4 my ($pbuf) = $M_LOGIN;
232 1         11 $pbuf .= join($DEL, ($user, $nick, $group, $cmd, $passwd));
233              
234 1 50       5 if (eval { $self->_sendpacket($pbuf) }) {
  1         5  
235 1         18 $self->{'user'} = $user;
236             # XXX - wait for protocol and loginok packets?
237 1         27 return 'ok';
238             }
239 0         0 die "sendlogin: $@";
240             }
241              
242             # Send a packet to the server.
243             sub _sendpacket {
244 5     5   15 my ($self, $packet) = @_;
245 5         30 my ($socket) = $self->fd;
246 5         14 my ($plen) = length($packet); # Size plus null.
247              
248 5 50       21 print "SEND: ",$plen+1,"b -- $packet\\0" if ($self->{'debug'});
249             # Bounds checking to MAXCHAR-1 (terminating null).
250 5 100       15 if ($plen > 254) {
251 1         13 die "send: packet > 255 bytes";
252             # XXX - truncate and send instead?
253             #$plen = 254;
254             #warn "truncated to $plen bytes\n";
255             #$packet = substr($packet, 0, $plen);
256             }
257              
258             # Add the terminating null.
259 4         11 $packet .= "\0"; $plen++;
  4         7  
260              
261             # Add the packet length (<= 255) to the packet head.
262 4         16 $packet = chr($plen).$packet; $plen++;
  4         10  
263              
264 4         1333 my $wrotelen = send($socket, $packet, 0);
265 4 50       45 if (not defined($wrotelen)) {
    50          
266 0         0 die "send: $!";
267             } elsif ($wrotelen != $plen) {
268 0         0 die "send: wrote $wrotelen of $plen: $!";
269             } else {
270 4         38 return 'ok';
271             }
272 0         0 return;
273             }
274              
275              
276             # Read a pending packet from the socket. Will block forever.
277             sub _recvpacket {
278 30     30   42 my ($self) = @_;
279 30         59 my ($socket) = $self->fd;
280 30         46 my ($slen, $buffer, $ret);
281              
282             # Read a byte of packet length.
283 30         962287 $ret = recv($socket, $slen, 1, 0);
284 30 50       130 if (not defined($ret)) {
    50          
285 0         0 die "recv size: $!";
286             } elsif (length($slen) != 1) {
287 0         0 die "recv size != 1: $!";
288             } else {
289             # Convert char to integer.
290 30         40 $slen = ord($slen);
291 30 50       101 print "RECV: reading $slen" if ($self->{'debug'} > 1);
292 30         64 while ($slen) { # Read the entire packet.
293 30         34 my $pbuf;
294 30         200 $ret = recv($socket, $pbuf, $slen, 0);
295 30 50       54 if (not defined($ret)) {
296 0         0 die "recv msg: $!";
297             } else {
298 30         53 $slen -= length($pbuf);
299 30         130 $buffer .= $pbuf;
300             }
301             }
302 30 50       82 print "RECV: ",length($buffer),"b -- $buffer\\0" if ($self->{'debug'});
303             # Remove trailing null.
304 30         53 chop($buffer);
305 30         146 return($buffer);
306             }
307 0         0 return;
308             }
309              
310             # tcpopen(hostname,portnumber);
311             # Returns a connected socket if all goes well.
312             sub _tcpopen {
313 4     4   8 my ($self,$hostname,$portnumber) = @_;
314              
315 4 100       50 my $socket = new IO::Socket::INET(
316             PeerAddr => $hostname,
317             PeerPort => "($portnumber)",
318             Proto => 'tcp') or die "_tcpopen: $@";
319              
320 1         308331 $self->{'socket'} = $socket;
321 1         8 $self->{'host'} = $hostname;
322 1         7 $self->{'port'} = $portnumber;
323 1         9 return 'ok';
324             }
325              
326             1;
327             __END__