File Coverage

blib/lib/Net/Raccdoc.pm
Criterion Covered Total %
statement 16 234 6.8
branch 1 90 1.1
condition 2 19 10.5
subroutine 4 18 22.2
pod 14 15 93.3
total 37 376 9.8


line stmt bran cond sub pod time code
1             package Net::Raccdoc;
2             $VERSION = "1.3";
3             require IO::Socket::INET;
4 2     2   70869 use strict;
  2         5  
  2         85  
5 2     2   10 use warnings;
  2         5  
  2         57  
6 2     2   21 use Carp;
  2         7  
  2         18876  
7              
8             sub new {
9 1     1 1 15 my ( $class, %arg ) = @_;
10              
11 1   50     10 my $host = $arg{host} || "bbs.iscabbs.com";
12 1   50     10 my $port = $arg{port} || "6145";
13 1         4 my $login = $arg{login};
14 1         2 my $password = $arg{password};
15 1         2 my $loggedin = 0;
16              
17 1 50       14 my $socket = IO::Socket::INET->new(
18             PeerAddr => $host,
19             PeerPort => $port,
20             Proto => "tcp",
21             Type => IO::Socket::INET::SOCK_STREAM()
22             ) or return;
23              
24 0           my $welcome = <$socket>;
25 0 0         if ( $welcome !~ /^2/ ) {
26 0           $@ = "Server connection failed with response $welcome";
27 0           return;
28             }
29              
30 0 0 0       if ( $login && $password ) {
31 0           print $socket "LOGIN $login\t$password\n";
32 0           my $answer = <$socket>;
33 0 0         if ( $answer =~ /^2/ ) {
34 0           $loggedin = 1;
35             }
36             else {
37 0           $@ = "LOGIN command failed with response $answer";
38 0           return;
39             }
40             }
41              
42             bless {
43 0           _host => $host,
44             _port => $port,
45             _socket => $socket,
46             _loggedin => $loggedin,
47             }, $class;
48             }
49              
50             sub forums {
51 0     0 1   my $self = shift;
52 0   0       my $type = shift || "all";
53 0           my $socket = $self->{_socket};
54 0           my %forums = ();
55 0           print $socket "LIST $type\n";
56 0           my $status = <$socket>;
57 0 0         if ( $status =~ /^3/ ) {
58              
59 0           while ( my $line = <$socket> ) {
60 0           chomp($line);
61              
62 0 0         last if ( $line =~ /^\.$/ );
63 0           my @tuples = split( /\t/, $line );
64 0           my %topichash;
65 0           foreach my $pair (@tuples) {
66 0           my ( $key, $value ) = split( /:/, $pair );
67 0           $topichash{$key} = $value;
68             }
69 0           my $topicid = $topichash{topic};
70 0           $forums{$topicid} = \%topichash;
71             }
72 0           return %forums;
73             }
74             else {
75 0           $@ = "Topic listing failed with response $status";
76 0           return;
77             }
78             }
79              
80             sub jump {
81 0     0 1   my $self = shift;
82 0           my $forum = $_[0];
83              
84 0           my $socket = $self->{_socket};
85              
86 0           print $socket "TOPIC $forum\n";
87 0           my $response = <$socket>;
88 0 0         if ( $response =~ /^2/ ) {
89 0           my $forumdata = {};
90              
91 0           $response =~ s/^.*?\t//;
92 0           my @tuples = split( /\t/, $response );
93 0           foreach my $pair (@tuples) {
94 0           my ( $key, $value ) = split( /:/, $pair );
95 0 0         if ( $key eq "admin" ) {
96 0           my ( $id, $name, $hidden ) = split( /\//, $value );
97 0           $value = $name;
98             }
99 0           $forumdata->{$key} = $value;
100             }
101              
102 0           $self->{_forum} = $forumdata->{topic};
103 0           $self->{_lastnote} = $forumdata->{lastnote};
104 0           $self->{_firstnote} = $forumdata->{firstnote};
105              
106 0           return $forumdata;
107              
108             }
109             else {
110 0           $@ = "Jump to forum $forum died with error $response";
111 0           return;
112             }
113             }
114              
115             sub get_first_unread {
116 0     0 1   my $self = shift;
117 0           my $socket = $self->{_socket};
118              
119 0           print $socket "SHOW rcval\n";
120 0           chomp( my $response = <$socket> );
121 0 0         if ( $response =~ /^2.*?\t(\d+)$/ ) {
122 0           return $1;
123             }
124             else {
125 0           $@ = "SHOW rcval failed with response $response";
126 0           return;
127             }
128              
129             }
130              
131             sub read {
132 0     0 1   my ( $self, %options ) = @_;
133 0           my $socket = $self->{_socket};
134 0   0       my $message = $options{message} || $self->get_first_unread;
135 0 0         unless ($message) {
136 0           my @noteids = $self->get_forum_noteids;
137 0           $message = $noteids[0];
138             }
139 0   0       my $dammit = $options{dammit} || 0;
140 0 0         if ( $message > $self->{_lastnote} ) {
141 0           return;
142             }
143             else {
144 0           my $nextmessage = $message + 1;
145 0 0         if ($dammit) {
146 0           print $socket "READ $message DAMMIT\n";
147             }
148             else {
149 0           print $socket "READ $message\n";
150             }
151 0           chomp( my $response = <$socket> );
152 0 0         if ( $response =~ /^3/ ) {
153 0           my %message;
154 0           while (1) {
155              
156             # Get header info until we hit a blank line
157 0           chomp( my $headerline = <$socket> );
158 0 0         last if ( $headerline =~ /^$/ );
159 0           my ( $key, $value ) = split( /:\s+/, $headerline );
160 0           $key = lc($key);
161 0 0         $key = "author" if ( $key eq "from" );
162 0 0         next if ( $key eq "formal-name" );
163 0           $message{$key} = $value;
164             }
165              
166 0           my @lines;
167              
168 0           while ( chomp( $response = <$socket> ) ) {
169 0 0         last if ( $response =~ /^\.$/ );
170 0           push( @lines, $response );
171             }
172 0           my $body = join( "\n", @lines );
173              
174 0           $message{body} = $body;
175              
176 0           return \%message;
177             }
178             else {
179 0           $@ = "Read of $message failed with response $response";
180 0           return;
181             }
182             }
183              
184             }
185              
186             sub get_forum_noteids {
187 0     0 1   my @noteids;
188 0           my $self = shift;
189 0           my $socket = $self->{_socket};
190 0 0         unless ( defined $self->{_forum} ) {
191 0           $@ = "Forum not defined";
192 0           return;
193             }
194              
195 0           print $socket "XHDR\n";
196 0           my $result = <$socket>;
197 0 0         if ( $result =~ /^3/ ) {
198 0           while ( my $noteinfo = <$socket> ) {
199 0 0         last if ( $noteinfo =~ /^\./ );
200 0 0         push( @noteids, $1 ) if ( $noteinfo =~ /^noteno:(\d+)/ );
201             }
202              
203 0           return @noteids;
204             }
205             else {
206 0           $@ = "XHDR failed with response $result";
207 0           return;
208             }
209             }
210              
211             sub get_forum_headers {
212 0     0 1   my %xhdr;
213 0           my $self = shift;
214 0   0       my $range = shift || "";
215 0           my $socket = $self->{_socket};
216 0 0         return unless defined $self->{_forum};
217              
218 0           print $socket "XHDR ALL $range\n";
219 0           my $result = <$socket>;
220 0 0         if ( $result =~ /^3/ ) {
221 0           while ( my $noteinfo = <$socket> ) {
222 0 0         last if ( $noteinfo =~ /^\./ );
223 0           my $notenum;
224             my %tmphash;
225 0           chomp($noteinfo);
226 0           my @tuples = split( /\t/, $noteinfo );
227 0           foreach my $tuple (@tuples) {
228 0           my $key;
229             my $value;
230 0 0         if ( $tuple =~ /^(.*?):(.*)$/ ) {
231 0           $key = $1;
232 0           $value = $2;
233             }
234             else {
235 0           next;
236             }
237 0 0         if ( $key eq "noteno" ) {
    0          
238 0           $notenum = $value;
239             }
240             elsif ( $key eq "formal-author" ) {
241 0           my ( undef, $author, undef ) = split( /\//, $value );
242 0           $tmphash{author} = $author;
243             }
244             else {
245 0           $tmphash{$key} = $value;
246             }
247             }
248 0           $xhdr{$notenum} = \%tmphash;
249             }
250 0           return %xhdr;
251             }
252             else {
253 0           $@ = "XHDR ALL failed with response $result";
254 0           return;
255             }
256             }
257              
258             sub post {
259 0     0 1   my $self = shift;
260 0 0         my $message = shift or return;
261 0           my $socket = $self->{_socket};
262 0 0         return unless defined $self->{_forum};
263              
264 0           print $socket "POST\n";
265 0           chomp( my $post_resp = <$socket> );
266 0 0         if ( $post_resp !~ /^3/ ) {
267 0           $@ = "POST command failed with response $post_resp";
268 0           return;
269             }
270              
271 0           print $socket "$message\n";
272 0           print $socket ".\n";
273 0           chomp( my $data_resp = <$socket> );
274 0 0         if ( $data_resp =~ /^2/ ) {
275 0           $data_resp =~ / .*?(\d+)/;
276 0           return $1;
277             }
278             else {
279 0           $@ = "POST data failed with response $data_resp";
280 0           return;
281             }
282             }
283              
284             sub delete {
285 0     0 1   my $self = shift;
286 0 0         my $postid = shift or return;
287 0           my $socket = $self->{_socket};
288 0 0         return unless defined $self->{_forum};
289              
290 0           print $socket "DELETE NOTE $postid\n";
291 0           chomp( my $delete_resp = <$socket> );
292              
293 0 0         if ( $delete_resp =~ /^2/ ) {
294 0           return 1;
295             }
296             else {
297 0           $@ = "Deletion of note $postid failed with response $delete_resp";
298 0           return;
299             }
300             }
301              
302             sub set_first_unread {
303 0     0 1   my $self = shift;
304 0           my $socket = $self->{_socket};
305 0 0         my $messageid = shift or return;
306              
307 0 0 0       if ( ( $messageid >= $self->{_firstnote} )
308             && ( $messageid <= ( $self->{_lastnote} + 1 ) ) )
309             {
310 0           print $socket "SETRC $messageid\n";
311 0           my $response = <$socket>;
312 0 0         if ( $response =~ /^2/ ) {
313 0           return 1;
314             }
315             else {
316 0           $@ = "SETRC returned bad status: $response";
317 0           return;
318             }
319             }
320             else {
321 0           return;
322             }
323             }
324              
325             sub forums_with_unread {
326 0     0 1   my $self = shift;
327 0           my $socket = $self->{_socket};
328 0           my %unread;
329 0 0         if ( !$self->{_loggedin} ) {
330 0           return;
331             }
332 0           my %forums = $self->forums("todo");
333 0           foreach my $key ( sort keys %forums ) {
334 0           my $unread_count = $forums{$key}->{todo};
335 0           $unread{$key} = {
336             unread => $unread_count,
337             name => $forums{$key}->{name},
338             firstnote => $forums{$key}->{firstnote},
339             lastnote => $forums{$key}->{lastnote}
340             };
341             }
342              
343 0           return (%unread);
344             }
345              
346             sub get_fi {
347 0     0 1   my $self = shift;
348 0           my $socket = $self->{_socket};
349 0           print $socket "SHOW info\n";
350 0           my $result = <$socket>;
351 0 0         if ( $result =~ /^3/ ) {
352 0           chomp( my $fromline = <$socket> );
353 0 0         my $author = $1 if ( $fromline =~ /From: (.*)/ );
354 0           chomp( my $dateline = <$socket> );
355 0 0         my $date = $1 if ( $dateline =~ /Date: (.*)/ );
356 0           my $blankline = <$socket>;
357              
358 0           my @lines;
359              
360 0           while ( chomp( my $line = <$socket> ) ) {
361 0 0         last if ( $line =~ /^\.$/ );
362 0           push( @lines, $line );
363             }
364 0           my $body = join( "\n", @lines );
365 0           my $fi = { fi_author => $author, last_updated => $date, body => $body };
366 0           return $fi;
367             }
368             else {
369 0           $@ = "SHOW INFO failed with response $result";
370 0           return;
371             }
372             }
373              
374             sub can_post {
375 0     0 1   my $self = shift;
376 0           my $socket = $self->{_socket};
377 0 0         return unless defined $self->{_forum};
378              
379 0           print $socket "OKAY POST\n";
380 0           chomp( my $result = <$socket> );
381 0 0         if ( $result =~ /^2/ ) {
382 0           return 1;
383             }
384             else {
385 0           $@ = "OKAY POST failed with the following response: $result";
386 0           return;
387             }
388             }
389              
390             sub xmsg_add {
391 0     0 0   my $self = shift;
392 0           my $socket = $self->{_socket};
393              
394             }
395              
396             sub logout {
397 0     0 1   my $socket = $_[0]->{_socket};
398 0           print $socket "QUIT\n";
399 0 0         $socket->close() or return;
400             }
401              
402             1;
403              
404             __END__