File Coverage

blib/lib/FTN/Crypt/Msg.pm
Criterion Covered Total %
statement 181 217 83.4
branch 32 54 59.2
condition 11 24 45.8
subroutine 34 35 97.1
pod 11 11 100.0
total 269 341 78.8


line stmt bran cond sub pod time code
1             # FTN::Crypt::Msg - Message parsing for the FTN::Crypt module
2             #
3             # Copyright (C) 2019 by Petr Antonov
4             #
5             # This library is free software; you can redistribute it and/or modify it
6             # under the same terms as Perl 5.10.0. For more details, see the full text
7             # of the licenses at https://opensource.org/licenses/Artistic-1.0, and
8             # http://www.gnu.org/licenses/gpl-2.0.html.
9             #
10             # This package is provided "as is" and without any express or implied
11             # warranties, including, without limitation, the implied warranties of
12             # merchantability and fitness for a particular purpose.
13             #
14             package FTN::Crypt::Msg::MsgChunk;
15              
16 3     3   70597 use strict;
  3         15  
  3         85  
17 3     3   13 use warnings;
  3         6  
  3         67  
18 3     3   28 use v5.10.1;
  3         10  
19              
20             sub new {
21 15     15   48 my $class = shift;
22              
23 15         54 my $self = {
24             data => [],
25             };
26            
27 15         172 bless $self, $class;
28             }
29              
30             sub get {
31 12     12   24 my $self = shift;
32            
33 12         28 return $self->{data};
34             }
35              
36             sub set {
37 74     74   107 my $self = shift;
38 74         204 my ($line) = @_;
39            
40 74         101 push @{$self->{data}}, $line;
  74         226  
41             }
42              
43             #----------------------------------------------------------------------#
44              
45             package FTN::Crypt::Msg::KludgeChunk;
46              
47 3     3   20 use strict;
  3         11  
  3         71  
48 3     3   14 use warnings;
  3         6  
  3         124  
49 3     3   27 use v5.10.1;
  3         9  
50              
51 3     3   18 use base qw/FTN::Crypt::Msg::MsgChunk/;
  3         5  
  3         1301  
52              
53             sub remove {
54 2     2   11 my $self = shift;
55 2         7 my ($kludge) = @_;
56            
57 2 50 33     22 if (defined $kludge && $kludge ne '') {
58 2         60 @{$self->{data}} = grep { !/^${kludge}(?::?\s.+)*$/ }
  14         105  
59 2         9 @{$self->{data}};
  2         16  
60             } else {
61 0         0 return;
62             }
63              
64 2         28 return 1;
65             }
66              
67             #----------------------------------------------------------------------#
68              
69             package FTN::Crypt::Msg::TextChunk;
70              
71 3     3   56 use strict;
  3         12  
  3         124  
72 3     3   19 use warnings;
  3         7  
  3         85  
73 3     3   27 use v5.10.1;
  3         10  
74              
75 3     3   18 use base qw/FTN::Crypt::Msg::MsgChunk/;
  3         10  
  3         941  
76              
77             sub get {
78 7     7   40 my $self = shift;
79 7         47 my ($ftn_ready) = @_;
80              
81 7 100       62 my $sep = $ftn_ready ? "\r" : "\n";
82              
83 7         20 return join $sep, @{$self->{data}};
  7         61  
84             }
85              
86             #----------------------------------------------------------------------#
87              
88             package FTN::Crypt::Msg;
89              
90 3     3   23 use strict;
  3         6  
  3         85  
91 3     3   17 use warnings;
  3         5  
  3         86  
92 3     3   40 use v5.10.1;
  3         10  
93              
94 3     3   18 use base qw/FTN::Crypt::Error/;
  3         5  
  3         646  
95              
96             #----------------------------------------------------------------------#
97              
98             =head1 NAME
99              
100             FTN::Crypt::Msg - Message parsing for the L<FTN::Crypt> module.
101              
102             =head1 SYNOPSIS
103              
104             use FTN::Crypt::Msg;
105              
106             my $obj = FTN::Crypt::Msg->new(
107             Address => $ftn_address,
108             Message => $msg,
109             );
110             $obj->add_kludge('ENC: PGP5');
111             $obj->remove_kludge('ENC');
112             my $text = $obj->get_text;
113             my $kludges = $obj->get_kludges;
114             my $msg = $obj->get_message;
115              
116             =cut
117              
118             #----------------------------------------------------------------------#
119              
120 3     3   1365 use FTN::Address;
  3         6547  
  3         5056  
121              
122             #----------------------------------------------------------------------#
123              
124             my $SOH = chr(1);
125              
126             my %PREDEFINED_INDEX = (
127             'TOP' => 0,
128             'BOTTOM' => -1,
129             );
130             my %DEFAULT_INDEX = (
131             KLUDGE => 'TOP',
132             TEXT => 'BOTTOM',
133             );
134              
135             #----------------------------------------------------------------------#
136              
137             =head1 METHODS
138              
139             =cut
140              
141             #----------------------------------------------------------------------#
142              
143             =head2 new()
144              
145             Constructor.
146              
147             =head3 Parameters:
148              
149             =over 4
150              
151             =item * C<Address>: Recipient's FTN address.
152              
153             =item * C<Message>: FTN message text with kludges.
154              
155             =back
156              
157             =head3 Returns:
158              
159             Created object or error in C<FTN::Crypt::Msg-E<gt>error>.
160              
161             Sample:
162              
163             my $obj = FTN::Crypt::Msg->new(
164             Address => $ftn_address,
165             Message => $msg,
166             ) or die FTN::Crypt::Msg->error;
167              
168             =cut
169              
170             sub new {
171 3     3 1 1118 my $class = shift;
172 3         14 my (%opts) = @_;
173              
174 3 50       17 unless (%opts) {
175 0         0 $class->set_error('No options specified');
176 0         0 return;
177             }
178 3 50       13 unless ($opts{Address}) {
179 0         0 $class->set_error('No address specified');
180 0         0 return;
181             }
182 3 50       11 unless ($opts{Message}) {
183 0         0 $class->set_error('No message specified');
184 0         0 return;
185             }
186              
187 3         35 my $self = {
188             msg => [],
189             idx => {
190             KLUDGE => [],
191             TEXT => [],
192             },
193             };
194              
195 3         11 $self = bless $self, $class;
196              
197 3 50       13 unless ($self->set_address($opts{Address})) {
198 0         0 $class->set_error($self->error);
199 0         0 return;
200             }
201 3 50       19 unless ($self->set_message($opts{Message})) {
202 0         0 $class->set_error($self->error);
203 0         0 return;
204             }
205              
206 3         15 return $self;
207             }
208              
209             #----------------------------------------------------------------------#
210              
211             sub _check_kludge {
212 4     4   8 my $self = shift;
213 4         14 my ($kludge) = @_;
214              
215 4 50 33     60 unless (defined $kludge && $kludge ne '') {
216 0         0 $self->set_error('Kludge is empty');
217 0         0 return;
218             }
219              
220 4         13 return $kludge;
221             }
222              
223             #----------------------------------------------------------------------#
224              
225             sub _check_text {
226 3     3   6 my $self = shift;
227 3         9 my ($text) = @_;
228              
229 3 50 33     67 unless (defined $text && $text ne '') {
230 0         0 $self->set_error('Text is empty');
231 0         0 return;
232             }
233              
234 3         21 return $text;
235             }
236              
237             #----------------------------------------------------------------------#
238              
239             sub _check_idx {
240 11     11   60 my $self = shift;
241 11         59 my ($type, $idx) = @_;
242              
243 11 50       37 unless (grep /^$type$/, keys %{$self->{idx}}) {
  11         425  
244 0         0 $self->set_error("Invalid message area type (`$type')");
245 0         0 return;
246             }
247              
248 11 50       88 $idx = $DEFAULT_INDEX{$type} unless defined $idx;
249              
250 11 50       75 $idx = $PREDEFINED_INDEX{$idx} if defined $PREDEFINED_INDEX{$idx};
251            
252 11 50       109 unless ($idx =~ /^-?\d+$/) {
253 0         0 $self->set_error("Invalid chunk index (`$idx')");
254 0         0 return;
255             }
256              
257 11 50       60 unless (defined $self->{idx}->{$type}->[$idx]) {
258 0         0 $self->set_error("Invalid chunk index (`$idx')");
259 0         0 return;
260             }
261              
262 11         41 return $idx;
263             }
264              
265             #----------------------------------------------------------------------#
266              
267             =head2 add_kludge()
268              
269             Add kludge to the message.
270              
271             =head3 Parameters:
272              
273             =over 4
274              
275             =item * Kludge string.
276              
277             =item * B<Optional> C<[TOP|BOTTOM|<index>]> Kludges block, defaults to TOP.
278              
279             =back
280              
281             =head3 Returns:
282              
283             True or error in C<$obj-E<gt>error>.
284              
285             Sample:
286              
287             $obj->add_kludge('ENC: PGP5') or die $obj->error;
288              
289             =cut
290              
291             sub add_kludge {
292 2     2 1 6 my $self = shift;
293 2         6 my ($kludge, $idx) = @_;
294              
295 2         10 $kludge = $self->_check_kludge($kludge);
296 2         17 $idx = $self->_check_idx('KLUDGE', $idx);
297              
298 2 50 33     20 if (defined $kludge && defined $idx) {
299 2         51 $self->{msg}->[$self->{idx}->{KLUDGE}->[$idx]]->set($kludge);
300             } else {
301 0         0 return;
302             }
303              
304 2         10 return 1;
305             }
306              
307             #----------------------------------------------------------------------#
308              
309             =head2 remove_kludge()
310              
311             Remove kludge from the message.
312              
313             =head3 Parameters:
314              
315             =over 4
316              
317             =item * Kludge string, may be only the first part of the composite kludge.
318              
319             =item * B<Optional> C<[TOP|BOTTOM|<index>]> Kludges block, defaults to TOP.
320              
321             =back
322              
323             =head3 Returns:
324              
325             True or error in C<$obj-E<gt>error>.
326              
327             Sample:
328              
329             $obj->remove_kludge('ENC') or die $obj->error;
330              
331             =cut
332              
333             sub remove_kludge {
334 2     2 1 5792 my $self = shift;
335 2         14 my ($kludge, $idx) = @_;
336              
337 2         21 $kludge = $self->_check_kludge($kludge);
338 2         108 $idx = $self->_check_idx('KLUDGE', $idx);
339              
340 2 50 33     24 if (defined $kludge && defined $idx) {
341 2         19 $self->{msg}->[$self->{idx}->{KLUDGE}->[$idx]]->remove($kludge);
342             } else {
343 0         0 return;
344             }
345              
346 2         15 return 1;
347             }
348              
349             #----------------------------------------------------------------------#
350              
351             =head2 get_kludges()
352              
353             Get message kludges.
354              
355             =head3 Parameters:
356              
357             None.
358              
359             =head3 Returns:
360              
361             Arrayref with kludges list or error in C<$obj-E<gt>error>.
362              
363             Sample:
364              
365             $obj->get_kludges() or die $obj->error;
366              
367             =cut
368              
369             sub get_kludges {
370 3     3 1 25 my $self = shift;
371              
372 3         8 my $kludges = [];
373 3         6 foreach my $c (@{$self->{msg}}) {
  3         13  
374 9 100       58 push @{$kludges}, $c->get if $c->isa('FTN::Crypt::Msg::KludgeChunk');
  6         24  
375             }
376              
377 3         15 return $kludges;
378             }
379              
380             #----------------------------------------------------------------------#
381              
382             =head2 get_address()
383              
384             Get recipient's FTN address.
385              
386             =head3 Parameters:
387              
388             None.
389              
390             =head3 Returns:
391              
392             Recipient's FTN address or error in C<$obj-E<gt>error>.
393              
394             Sample:
395              
396             my $ftn_address = $obj->get_address() or die $obj->error;
397              
398             =cut
399              
400             sub get_address {
401 4     4 1 1019 my $self = shift;
402              
403 4         22 my $addr = $self->{addr}->get;
404 4 50       39 unless ($addr) {
405 0         0 $self->set_error($@);
406 0         0 return;
407             }
408              
409 4         33 return $addr;
410             }
411              
412             #----------------------------------------------------------------------#
413              
414             =head2 set_address()
415              
416             Set recipient's FTN address.
417              
418             =head3 Parameters:
419              
420             =over 4
421              
422             =item * Recipient's FTN address.
423              
424             =back
425              
426             =head3 Returns:
427              
428             True or error in C<$obj-E<gt>error>.
429              
430             Sample:
431              
432             $obj->set_address($ftn_address)
433              
434             =cut
435              
436             sub set_address {
437 4     4 1 9 my $self = shift;
438 4         9 my ($addr) = @_;
439              
440 4         38 $self->{addr} = FTN::Address->new($addr);
441 4 50       243 unless ($self->{addr}) {
442 0         0 $self->set_error($@);
443 0         0 return;
444             }
445              
446 4         16 return 1;
447             }
448              
449             #----------------------------------------------------------------------#
450              
451             =head2 get_text()
452              
453             Get text part of the message.
454              
455             =head3 Parameters:
456              
457             =over 4
458              
459             =item * B<Optional> C<[TOP|BOTTOM|<index>]> Text block, defaults to BOTTOM.
460              
461             =back
462              
463             =head3 Returns:
464              
465             Text part of the message or error in C<$obj-E<gt>error>.
466              
467             Sample:
468              
469             my $text = $obj->get_text() or die $obj->error;
470              
471             =cut
472              
473             sub get_text {
474 4     4 1 5200 my $self = shift;
475 4         41 my ($idx) = @_;
476              
477 4         59 $idx = $self->_check_idx('TEXT', $idx);
478              
479 4         39 my $text = '';
480 4 50       61 if (defined $idx) {
481 4         86 $text = $self->{msg}->[$self->{idx}->{TEXT}->[$idx]]->get;
482             } else {
483 0         0 return;
484             }
485              
486 4         110 return $text;
487             }
488              
489             #----------------------------------------------------------------------#
490              
491             =head2 get_all_text()
492              
493             Get all text parts of the message.
494              
495             =head3 Parameters:
496              
497             None.
498              
499             =head3 Returns:
500              
501             Arrayref with text parts of the message or error in C<$obj-E<gt>error>.
502              
503             Sample:
504              
505             my $text = $obj->get_all_text() or die $obj->error;
506              
507             =cut
508              
509             sub get_all_text {
510 0     0 1 0 my $self = shift;
511              
512 0         0 my $text = [];
513 0         0 foreach my $c (@{$self->{msg}}) {
  0         0  
514 0 0       0 push @{$text}, $c->get if $c->isa('FTN::Crypt::Msg::TextChunk');
  0         0  
515             }
516              
517 0         0 return $text;
518             }
519              
520             #----------------------------------------------------------------------#
521              
522             =head2 set_text()
523              
524             Set text part of the message.
525              
526             =head3 Parameters:
527              
528             =over 4
529              
530             =item * Text part of the message.
531              
532             =item * B<Optional> C<[TOP|BOTTOM|<index>]> Text block, defaults to BOTTOM.
533              
534             =back
535              
536             =head3 Returns:
537              
538             True or error in C<$obj-E<gt>error>.
539              
540             Sample:
541              
542             $obj->set_text($text) or die $obj->error;
543              
544             =cut
545              
546             sub set_text {
547 3     3 1 710 my $self = shift;
548 3         24 my ($text, $idx) = @_;
549              
550 3         22 $text = $self->_check_text($text);
551 3         12 $idx = $self->_check_idx('TEXT', $idx);
552              
553 3 50 33     81 if (defined $text && defined $idx) {
554 3         82 $self->{msg}->[$self->{idx}->{TEXT}->[$idx]] = FTN::Crypt::Msg::TextChunk->new;
555 3         20 $text =~ s/\r\n/\r/g;
556 3         41 $text =~ s/\n/\r/g;
557 3         45 my @text_lines = split /\r/, $text;
558 3         10 foreach my $l (@text_lines) {
559 20         84 $self->{msg}->[$self->{idx}->{TEXT}->[$idx]]->set($l);
560             }
561             } else {
562 0         0 return;
563             }
564              
565 3         18 return 1;
566             }
567              
568             #----------------------------------------------------------------------#
569              
570             =head2 get_message()
571              
572             Get FTN message text with kludges.
573              
574             =head3 Parameters:
575              
576             None.
577              
578             =head3 Returns:
579              
580             FTN message text with kludges or error in C<$obj-E<gt>error>.
581              
582             Sample:
583              
584             my $msg = $obj->get_message() or die $obj->error;
585              
586             =cut
587              
588             sub get_message {
589 3     3 1 8 my $self = shift;
590              
591 3         6 my @msg;
592 3         6 foreach my $c (@{$self->{msg}}) {
  3         20  
593 9 100       72 if ($c->isa('FTN::Crypt::Msg::KludgeChunk')) {
    50          
594 6         11 push @msg, join "\r", map { "${SOH}$_" } @{$c->get};
  22         84  
  6         26  
595             } elsif ($c->isa('FTN::Crypt::Msg::TextChunk')) {
596 3         11 push @msg, $c->get(1);
597             }
598             }
599              
600 3         12 my $msg_out = join "\r", @msg;
601            
602 3         23 return $msg_out;
603             }
604              
605             #----------------------------------------------------------------------#
606              
607             =head2 set_message()
608              
609             Set FTN message text with kludges.
610              
611             =head3 Parameters:
612              
613             =over 4
614              
615             =item * FTN message text with kludges.
616              
617             =back
618              
619             =head3 Returns:
620              
621             True or error in C<$obj-E<gt>error>.
622              
623             Sample:
624              
625             $obj->set_message($msg) or die $obj->error;
626              
627             =cut
628              
629             sub set_message {
630 4     4 1 706 my $self = shift;
631 4         11 my ($msg) = @_;
632              
633 4         20 $self->{msg} = [];
634 4         7 foreach my $a (keys %{$self->{idx}}) {
  4         23  
635 8         21 $self->{idx}->{$a} = [];
636             }
637              
638 4         19 $msg =~ s/\r\n/\r/g;
639 4         15 $msg =~ s/\n/\r/g;
640 4         45 my @msg_lines = split /\r/, $msg;
641              
642 4         14 my $is_kludge;
643 4         10 foreach my $l (@msg_lines) {
644 52 100       246 if ($l =~ s/^${SOH}//) {
645 29 100 100     132 if (!defined $is_kludge || !$is_kludge) {
646 8         15 push @{$self->{msg}}, FTN::Crypt::Msg::KludgeChunk->new;
  8         47  
647 8         16 push @{$self->{idx}->{KLUDGE}}, $#{$self->{msg}};
  8         14  
  8         54  
648 8         16 $is_kludge = 1;
649             }
650             } else {
651 23 100 66     90 if (!defined $is_kludge || $is_kludge) {
652 4         10 push @{$self->{msg}}, FTN::Crypt::Msg::TextChunk->new;
  4         36  
653 4         8 push @{$self->{idx}->{TEXT}}, $#{$self->{msg}};
  4         9  
  4         10  
654 4         16 $is_kludge = 0;
655             }
656             }
657 52         118 $self->{msg}->[-1]->set($l);
658             }
659              
660 4         19 return 1;
661             }
662              
663             1;
664             __END__
665              
666             =head1 AUTHOR
667              
668             Petr Antonov, E<lt>pietro@cpan.orgE<gt>
669              
670             =head1 COPYRIGHT AND LICENSE
671              
672             Copyright (C) 2019 by Petr Antonov
673              
674             This library is free software; you can redistribute it and/or modify it
675             under the same terms as Perl 5.10.0. For more details, see the full text
676             of the licenses at L<https://opensource.org/licenses/Artistic-1.0>, and
677             L<http://www.gnu.org/licenses/gpl-2.0.html>.
678              
679             This package is provided "as is" and without any express or implied
680             warranties, including, without limitation, the implied warranties of
681             merchantability and fitness for a particular purpose.
682              
683             =cut