File Coverage

blib/lib/Net/ICAP/Message.pm
Criterion Covered Total %
statement 366 408 89.7
branch 116 168 69.0
condition 43 72 59.7
subroutine 30 30 100.0
pod 11 11 100.0
total 566 689 82.1


line stmt bran cond sub pod time code
1             # Net::ICAP::Message -- Message object for ICAP
2             #
3             # (c) 2012, Arthur Corliss
4             #
5             # $Revision: 0.03 $
6             #
7             # This software is licensed under the same terms as Perl, itself.
8             # Please see http://dev.perl.org/licenses/ for more information.
9             #
10             #####################################################################
11              
12             #####################################################################
13             #
14             # Environment definitions
15             #
16             #####################################################################
17              
18             package Net::ICAP::Message;
19              
20 4     4   2150 use 5.008003;
  4         14  
  4         183  
21              
22 4     4   21 use strict;
  4         8  
  4         117  
23 4     4   22 use warnings;
  4         5  
  4         138  
24 4     4   23 use vars qw($VERSION @ISA @_properties @_methods);
  4         7  
  4         297  
25 4     4   2301 use Class::EHierarchy qw(:all);
  4         30348  
  4         749  
26 4     4   3751 use Paranoid::Debug;
  4         13590  
  4         333  
27 4     4   32 use Net::ICAP::Common qw(:std :debug);
  4         10  
  4         728  
28 4     4   7482 use HTTP::Date;
  4         17594  
  4         409  
29              
30             ($VERSION) = ( q$Revision: 0.03 $ =~ /(\d+(?:\.(\d+))+)/sm );
31              
32             @ISA = qw(Class::EHierarchy);
33              
34 4     4   29 use constant DEF_CHUNK => 1024;
  4         7  
  4         26477  
35              
36             @_properties = (
37             [ CEH_RESTR | CEH_ARRAY, '_errors' ],
38             [ CEH_RESTR | CEH_SCALAR, '_version', ICAP_VERSION ],
39             [ CEH_RESTR | CEH_SCALAR, '_start' ],
40             [ CEH_RESTR | CEH_HASH, '_headers' ],
41             [ CEH_RESTR | CEH_SCALAR, '_req-hdr' ],
42             [ CEH_RESTR | CEH_SCALAR, '_res-hdr' ],
43             [ CEH_RESTR | CEH_SCALAR, '_body' ],
44             [ CEH_RESTR | CEH_SCALAR, '_body_type' ],
45             [ CEH_RESTR | CEH_SCALAR, '_trailer' ],
46             );
47              
48             @_methods = (
49             [ CEH_RESTR, '_getLine' ],
50             [ CEH_RESTR, '_putLine' ],
51             [ CEH_RESTR, '_parseHeaders' ],
52             [ CEH_RESTR, '_genHeaders' ],
53             [ CEH_RESTR, '_readChunked' ],
54             [ CEH_RESTR, '_writeChunked' ],
55             [ CEH_RESTR, '_parseEncap' ],
56             [ CEH_RESTR, '_genEncap' ],
57             [ CEH_RESTR, '_validHeaders' ],
58             );
59              
60             #####################################################################
61             #
62             # Module code follows
63             #
64             #####################################################################
65              
66             sub _initialize ($;@) {
67              
68             # Purpose: Does nothing, base class
69             # Returns: Boolean
70             # Usage: $rv = $obj->_initialization;
71              
72 26     26   26442 my $obj = shift;
73 26         70 my %args = @_;
74 26         47 my $rv = 1;
75              
76 26         87 pdebug( "entering w/$obj and @{[ keys %args ]}", ICAPDEBUG1 );
  26         142  
77 26         320 pIn();
78              
79             # Set internal state if args were passed
80 26 100       219 $rv = $obj->version( $args{version} ) if exists $args{version};
81 26 100 66     112 $rv = $obj->setHeaders( %{ $args{headers} } )
  3         19  
82             if exists $args{headers} and $rv;
83 26 100 66     98 $rv = $obj->reqhdr( $args{reqhdr} ) if exists $args{reqhdr} and $rv;
84 26 50 33     86 $rv = $obj->reshdr( $args{reshdr} ) if exists $args{reshdr} and $rv;
85 26 100 66     422 $rv = $obj->body( @args{qw(body_type body)} )
      66        
86             if $rv
87             and exists $args{body_type}
88             and exists $args{body};
89 26 100 66     85 $rv = $obj->trailer( $args{trailer} ) if exists $args{trailer} and $rv;
90              
91 26         71 pOut();
92 26         209 pdebug( "leaving w/rv: $rv", ICAPDEBUG1 );
93              
94 26         280 return $rv;
95             }
96              
97             sub _validHeaders ($) {
98              
99             # Purpose: Returns a list of valid ICAP headers
100             # Returns: Array
101             # Usage: @val = $obj->_validHeaders;
102              
103 99     99   1016 my $obj = shift;
104              
105 99         596 return qw(Cache-Control Connection Date Expires
106             Pragma Trailer Upgrade Encapsulated);
107             }
108              
109             sub error ($;$) {
110              
111             # Purpose: Returns errors or logs a new one
112             # Returns: Array
113             # Usage: $obj->error($msg);
114             # Usage: @errors = $obj->error;
115              
116 48     48 1 82 my $obj = shift;
117 48         194 my $msg = shift;
118 48         55 my @rv;
119              
120 48 100       121 if ( defined $msg ) {
121 25         91 $obj->push( '_errors', $msg );
122 25         1022 pdebug( $msg, ICAPDEBUG1 );
123             }
124 48         314 @rv = $obj->property('_errors');
125              
126 48         1887 return @rv;
127             }
128              
129             sub reqhdr ($;$) {
130              
131             # Purpose: Gets/sets the request header
132             # Returns: Boolean/String
133             # Usage: $rv = $obj->reqhdr($text);
134             # Usage: $header = $obj->reqhdr;
135              
136 15     15 1 747 my $obj = shift;
137 15         26 my ($header) = @_;
138 15         18 my $rv;
139              
140 15 100       63 $rv =
141             scalar @_
142             ? $obj->property( '_req-hdr', $header )
143             : $obj->property('_req-hdr');
144              
145 15         576 return $rv;
146             }
147              
148             sub reshdr ($;$) {
149              
150             # Purpose: Gets/sets the response header
151             # Returns: Boolean/String
152             # Usage: $rv = $obj->reshdr($text);
153             # Usage: $header = $obj->reshdr;
154              
155 13     13 1 22 my $obj = shift;
156 13         20 my ($header) = @_;
157 13         18 my $rv;
158              
159 13 50       65 $rv =
160             scalar @_
161             ? $obj->property( '_res-hdr', $header )
162             : $obj->property('_res-hdr');
163              
164 13         468 return $rv;
165             }
166              
167             sub trailer ($;$) {
168              
169             # Purpose: Gets/sets the trailer
170             # Returns: Boolean/String
171             # Usage: $rv = $obj->trailer($text);
172             # Usage: $trailer = $obj->trailer;
173              
174 1     1 1 2 my $obj = shift;
175 1         3 my ($trailer) = @_;
176 1         3 my $rv;
177              
178 1 50       17 $rv =
179             scalar @_
180             ? $obj->property( '_trailer', $trailer )
181             : $obj->property('_trailer');
182              
183 1         49 return $rv;
184             }
185              
186             sub body ($;$$) {
187              
188             # Purpose: Gets/sets the body type and content
189             # Returns: Array
190             # Usage: ($type, $body) = $obj->body;
191             # Usage: $rv = $obj->body($type, $body);
192              
193 28     28 1 469 my $obj = shift;
194 28         39 my ( $type, $body ) = @_;
195 28         29 my $rv;
196              
197 28 100       65 if (@_) {
198 2   33     8 $rv = $obj->property( '_body_type', $type )
199             && $obj->property( '_body', $body );
200             } else {
201 26         75 $rv = [ $obj->property('_body_type'), $obj->property('_body') ];
202             }
203              
204 28 100       2084 return ref $rv eq 'ARRAY' ? @$rv : $rv;
205             }
206              
207             sub version ($;$) {
208              
209             # Purpose: Gets/sets version
210             # Returns: Boolean/String
211             # Usage: $rv = $obj->version($version);
212             # Usage: $method = $obj->version;
213              
214 21     21 1 37 my $obj = shift;
215 21         35 my $version = shift;
216 21 100       52 my $v = defined $version ? $version : 'undef';
217 21         25 my ( $r, $rv );
218              
219 21         82 pdebug( "entering w/$v", ICAPDEBUG1 );
220 21         189 pIn();
221              
222 21 100       197 if ( defined $version ) {
223              
224             # Write mode
225 11 50       31 if ( $version eq ICAP_VERSION ) {
226 11         34 $rv = $obj->property( '_version', $version );
227             } else {
228 0         0 pdebug( "invalid version passed: $version", ICAPDEBUG1 );
229 0         0 $rv = 0;
230             }
231              
232             } else {
233              
234             # Read mode
235 10         36 $rv = $obj->property('_version');
236             }
237              
238 21 50       962 $r = defined $rv ? $rv : 'undef';
239 21         58 pOut();
240 21         159 pdebug( "leaving w/rv: $r", ICAPDEBUG1 );
241              
242 21         247 return $rv;
243             }
244              
245             sub _getLine ($$) {
246              
247             # Purpose: Retrieves input from passed string ref/object/file handle
248             # Returns: String
249             # Usage: $line = _getLine($fh);
250             # Usage: $line = _getLine(\$text);
251             # Usage: $line = _getLine($io_handle);
252              
253 233     233   2154 my $obj = shift;
254 233         285 my $ref = shift;
255 233         247 my ( $line, @lines, $rv );
256              
257 233         862 pdebug( "entering w/$ref", ICAPDEBUG4 );
258 233         2034 pIn();
259              
260 233 50       1479 if ( defined $ref ) {
261 233 100       1112 if ( ref $ref eq 'SCALAR' ) {
    100          
    50          
262 5         98 ( $line, @lines ) = split /\r\n/sm, $$ref;
263 5         52 $$ref = join "\r\n", @lines;
264 5         189 $line .= "\r\n";
265             } elsif ( ref $ref eq 'GLOB' ) {
266 5         61 $line = <$ref>;
267             } elsif ( $ref->isa('IO::Handle') ) {
268 223         5579 $line = $ref->getline;
269             } else {
270 0         0 $obj->error("don't know what to do with ref $ref");
271             }
272             } else {
273 0         0 $obj->error('undefined value passed for reference');
274             }
275              
276 233 50       7363 $rv = defined $line ? $line : 'undef';
277              
278 233         623 pOut();
279 233         2010 pdebug( "leaving w/rv: $rv", ICAPDEBUG4 );
280              
281 233         2933 return $line;
282             }
283              
284             sub _putLine ($$@) {
285              
286             # Purpose: Writes strings to passed string ref/object/file handle
287             # Returns: Boolean
288             # Usage: $rv = _putLine($fh, @lines);
289             # Usage: $rv = _putLine(\$text, @lines);
290             # Usage: $rv = _putLine($io_handle, @lines);
291              
292 48     48   432 my $obj = shift;
293 48         61 my $ref = shift;
294 48         113 my @lines = splice @_;
295 48         75 my $rv = 0;
296              
297 48         123 pdebug( "entering w/$ref, @{[ scalar @lines ]} line(s)", ICAPDEBUG4 );
  48         213  
298 48         445 pIn();
299              
300 48 50       295 if ( defined $ref ) {
301 48 50       114 if ( ref $ref eq 'SCALAR' ) {
    0          
    0          
302 48         54 $rv = 1;
303 48         153 $$ref .= join '', @lines;
304             } elsif ( ref $ref eq 'GLOB' ) {
305 0         0 $rv = print $ref join '', @lines;
306             } elsif ( $ref->isa('IO::Handle') ) {
307 0         0 $rv = $ref->print( join '', @lines );
308             } else {
309 0         0 $obj->error("don't know what to do with ref $ref");
310             }
311             } else {
312 0         0 $obj->error(
313             pdebug( 'undefined value passed for reference', ICAPDEBUG1 ) );
314             }
315              
316 48         109 pOut();
317 48         338 pdebug( "leaving w/rv: $rv", ICAPDEBUG4 );
318              
319 48         448 return $rv;
320             }
321              
322             sub _parseHeaders (@) {
323              
324             # Purpose: Parses lines of text to extract headers
325             # Returns: Hash
326             # Usage: %headers = _parseHeaders(@lines);
327              
328 23     23   280 my @lines = splice @_;
329 23         34 my ( $text, $line, $k, $v, %headers );
330              
331 23         42 pdebug( "entering w/@{[ scalar @lines ]} line(s) of text", ICAPDEBUG3 );
  23         135  
332 23         252 pIn();
333              
334 23 50       163 if ( scalar @lines ) {
335 23         116 $text = join "\r\n", @lines;
336              
337             # Fold header continuation lines
338 23         104 $text =~ s/\r\n\s+/ /smg;
339              
340             # Get new set of lines, each one a different header
341 23         188 @lines = split /\r\n/sm, $text;
342              
343 23         60 foreach $line (@lines) {
344 105         752 ( $k, $v ) = ( $line =~ m/^(\S+):\s*(.*?)\s*$/sm );
345 105 50 33     491 last unless defined $k and defined $v;
346 105 50       448 $headers{$k} = exists $headers{$k} ? "$headers{$k},$v" : $v;
347             }
348             }
349              
350 23         74 pOut();
351 23         134 pdebug( "leaving w/@{[ scalar keys %headers ]} headers", ICAPDEBUG3 );
  23         121  
352              
353 23         356 return %headers;
354             }
355              
356             sub _genHeaders ($) {
357              
358             # Purpose: Returns header block
359             # Returns: String
360             # Usage: $headers = $obj->_genHeaders;
361              
362 13     13   194 my $obj = shift;
363 13         42 my %headers = $obj->getHeaders;
364 13         516 my @valid = $obj->_validHeaders;
365 13         33 my $text = '';
366 13         17 my ( $h, $v );
367              
368 13         28 foreach $h (@valid) {
369 224 100       455 if ( exists $headers{$h} ) {
370 58         87 $v = $headers{$h};
371 58 50 33     481 $text .= "$h: $v\r\n" if defined $v and length $v;
372             }
373             }
374              
375 13         89 return $text;
376             }
377              
378             sub setHeaders ($@) {
379              
380             # Purpose: Sets all valid headers
381             # Returns: Boolean
382             # Usage: $rv = $obj->setHeaders(%headers);
383              
384 26     26 1 45 my $obj = shift;
385 26         101 my %headers = splice @_;
386 26         103 my @valid = $obj->_validHeaders;
387 26         369 my $rv = 1;
388 26         34 my $k;
389              
390 26         71 pdebug( 'entering', ICAPDEBUG2 );
391 26         338 pIn();
392              
393             # Validate headers
394 26         351 foreach $k ( keys %headers ) {
395 112 100 100     366 if ( $k =~ /^X-\w+[\w-]*/sm or grep { $_ eq $k } @valid ) {
  1888         2959  
396              
397             # Chomp header value
398 87         241 $headers{$k} =~ s/\r\n$//sm;
399              
400             } else {
401              
402 25         32 $rv = 0;
403 25         90 $obj->error("ignoring invalid header: $k");
404 25         75 delete $headers{$k};
405             }
406             }
407              
408             # Store anything left
409 26 50       166 $obj->property( '_headers', %headers )
410             if scalar keys %headers;
411              
412 26         1270 pOut();
413 26         201 pdebug( "leaving w/rv: $rv", ICAPDEBUG2 );
414              
415 26         350 return $rv;
416             }
417              
418             sub getHeaders ($) {
419              
420             # Purpose: Gets all headers
421             # Returns: Hash
422             # Usage: %headers = $obj->getHeaders;
423              
424 16     16 1 10019 my $obj = shift;
425 16         54 return $obj->property('_headers');
426             }
427              
428             sub header ($$;$) {
429              
430             # Purpose: Gets/sets the requested header
431             # Returns: Boolean/String
432             # Usage: $value = $obj->header($name);
433             # Usage: $rv = $obj->header($name, $value);
434              
435 60     60 1 1149 my $obj = shift;
436 60         90 my $header = shift;
437 60 100       128 my $v = @_ ? $_[0] : '(omitted)';
438 60         177 my @valid = $obj->_validHeaders;
439 60         116 my ( $value, $rv, $r );
440              
441 60         276 pdebug( "entering w/$obj, $header, $v", ICAPDEBUG1 );
442 60         574 pIn();
443              
444 60 50 66     475 if ( $header =~ /^X-\w+[\w-]*/sm or grep { $_ eq $header } @valid ) {
  1009         1583  
445              
446             # Valid header requested
447 60 100       107 if (@_) {
448              
449             # Write mode
450 9         94 $value = shift @_;
451 9 50       23 if ( defined $value ) {
452              
453             # Set mode
454 9         46 $obj->store( '_headers', $header, $value );
455 9         345 $rv = 1;
456              
457             } else {
458              
459             # Delete mode
460 0         0 $obj->remove( '_headers', $header );
461 0         0 $rv = 1;
462             }
463              
464             } else {
465              
466             # Read mode
467 51         187 $rv = $obj->retrieve( '_headers', $header );
468             }
469             } else {
470 0         0 $obj->error("invalid header requested: $header");
471             }
472              
473 60 100       1786 $r = defined $rv ? $rv : 'undef';
474 60         155 pOut();
475 60         437 pdebug( "leaving w/rv: $r", ICAPDEBUG1 );
476              
477 60         2092 return $rv;
478             }
479              
480             sub _readChunked ($$) {
481              
482             # Purpose: Reads chunked-encoded text
483             # Returns: String
484             # Usage: $text = $obj->-readChunked($input);
485              
486 5     5   54 my $obj = shift;
487 5         9 my $input = shift;
488 5         9 my $text = '';
489 5         9 my ( $line, $chunk, $c );
490              
491 5         32 pdebug( "entering w/$obj, $input", ICAPDEBUG2 );
492 5         48 pIn();
493              
494 5 50       48 if ( defined( $line = $obj->_getLine($input) ) ) {
495              
496             # Get initial chunk size
497 5         46 ($c) = ( $line =~ /^([0-9a-fA-F]+)\r\n$/sm );
498 5         12 $c = hex $c;
499              
500 5         18 OUTER: while ($c) {
501              
502             # Read lines until chunk size is met
503 5         14 $chunk = '';
504 5         20 while ( length $chunk <= $c ) {
505 6         25 $line = $obj->_getLine($input);
506              
507 6 50       27 unless ( defined $line ) {
508 0         0 $obj->error('ran out of text while reading chunks');
509 0         0 last OUTER;
510             }
511              
512 6         37 $chunk .= $line;
513             }
514              
515             # Trim line separator appended to chunk
516 5         28 $chunk =~ s/\r\n$//sm;
517              
518             # Check for chunk size accuracy
519 5 50       18 if ( length $chunk == $c ) {
520              
521             # Save chunk
522 5         18 $text .= $chunk;
523              
524             # Get next chunk size
525 5         21 $line = $obj->_getLine($input);
526 5 50       29 if ( defined $line ) {
527 5         31 ($c) = ( $line =~ /^([0-9a-fA-F]+)\r\n$/sm );
528 5         23 $c = hex $c;
529             } else {
530 0         0 $c = 0;
531 0         0 $obj->error('missing next chunk header');
532             }
533              
534             } else {
535 0         0 $obj->error( "chunk size mismatch: expected $c "
536 0         0 . "recieved @{[ length $chunk ]}" );
537 0         0 last;
538             }
539             }
540             }
541              
542 5         17 pOut();
543 5         30 pdebug( "leaving w/@{[ length $text ]} characters of text", ICAPDEBUG2 );
  5         42  
544              
545 5         60 return $text;
546             }
547              
548             sub _writeChunked ($) {
549              
550             # Purpose: Writes the body in chunked encoding
551             # Returns: String
552             # Usage: $chunked = $obj->_writeChunked;
553              
554 6     6   63 my $obj = shift;
555 6         21 my $body = $obj->property('_body');
556 6         210 my ( @segments, $r, $rv );
557              
558 6         19 pdebug( 'entering', ICAPDEBUG2 );
559 6         56 pIn();
560              
561 6 50       54 if ( defined $body ) {
562 6   66     80 while ( defined $body and length $body ) {
563 6         70 push @segments, substr $body, 0, DEF_CHUNK, '';
564             }
565 6         11 $rv = '';
566 6         15 foreach (@segments) {
567 6         39 $rv .= sprintf "%x\r\n", length $_;
568              
569             # the following should probably be in the above sprintf,
570             # but I'm a little leery of some of the sprintf bugs in the
571             # past with binary data...
572 6         28 $rv .= "$_\r\n";
573             }
574 6         22 $rv .= "0\r\n";
575             }
576              
577 6 50       20 $r = defined $rv ? "@{[ length $rv ]} characters" : 'undef';
  6         37  
578 6         23 pOut();
579 6         53 pdebug( "leaving w/rv: $r", ICAPDEBUG2 );
580              
581 6         69 return $rv;
582             }
583              
584             sub _parseEncap ($$) {
585              
586             # Purpose: Parses message body as per rules in parseEncap
587             # Returns: Boolean
588             # Usage: $rv = $obj->parseEncap($input);
589              
590 10     10   95 my $obj = shift;
591 10         15 my $input = shift;
592 10         16 my $rv = 1;
593 10         34 my @ventitites = qw(rep-hdr req-hdr res-body req-body null-body opt-body);
594 10         14 my ( $encap, @entities, $t, $l, $n, $offset, $line, $text );
595              
596 10         28 pdebug( 'entering', ICAPDEBUG2 );
597 10         88 pIn();
598              
599 10         97 $encap = $obj->header('Encapsulated');
600 10 50       27 if ( defined $encap ) {
601 10         81 @entities = split /\s*,\s*/sm, $encap;
602              
603             # Sanity tests:
604             #
605             # 1) there must be one (and only one) *-body tag as last entity
606 10         56 $n = scalar grep /^\w+-body=\d+$/sm, @entities;
607 10 50       32 unless ( $n == 1 ) {
608 0         0 $rv = 0;
609 0         0 $obj->error(
610             "invalid number of body entities in Encapsulated: $encap");
611             }
612 10 50       54 unless ( $entities[$#entities] =~ /^\w+-body=\d+$/sm ) {
613 0         0 $rv = 0;
614 0         0 $obj->error( 'last entity must be a body entities in '
615             . "Encapsulated: $encap" );
616             }
617              
618             # 2) only one req-hdr and/or resp-hdr allowed, but are optional
619 10         43 $n = scalar grep /^req-hdr=\d+$/sm, @entities;
620 10 50       29 unless ( $n <= 1 ) {
621 0         0 $rv = 0;
622 0         0 $obj->error("too many req-hedr entities in Encapsulated: $encap");
623             }
624 10         35 $n = scalar grep /^res-hdr=\d+$/sm, @entities;
625 10 50       30 unless ( $n <= 1 ) {
626 0         0 $rv = 0;
627 0         0 $obj->error("too many res-hedr entities in Encapsulated: $encap");
628             }
629              
630             # 3) offsets are monotonically increasing
631 10         13 $n = undef;
632 10         18 foreach ( map {m/=(\d+)$/sm} @entities ) {
  19         97  
633 19 50 66     82 unless ( !defined $n or $_ > $n ) {
634 0         0 $rv = 0;
635 0         0 $obj->error( 'Encapsulated offsets aren\'t monotonically '
636             . "ordered: $encap" );
637 0         0 last;
638             }
639 19 100       44 unless ( defined $n ) {
640 10 50       35 unless ( $_ == 0 ) {
641 0         0 $rv = 0;
642 0         0 $obj->error( 'Encapsulated offsets don\'t start at '
643             . "0: $encap" );
644 0         0 last;
645             }
646             }
647 19         41 $n = $_;
648             }
649              
650             # 4) no unknown entity types
651 10 50       83 if ( scalar grep !m/^(?:re[qs]-hdr|(?:opt|null|re[qs])-body)=\d+$/sm,
652             @entities ) {
653 0         0 $rv = 0;
654 0         0 $obj->error("invalid entities in Encapsulated: $encap");
655             }
656              
657             # Read data
658 10 50       26 if ($rv) {
659 10         12 $offset = 0;
660 10         27 while (@entities) {
661 19         60 ( $t, $l ) = split /=/sm, shift @entities;
662 19         36 ( $line, $text ) = ( '', '' );
663              
664 19 100       116 if ( $t =~ /-hdr$/sm ) {
    50          
665              
666             # Read headers
667 9         35 while ( defined( $line = $obj->_getLine($input) ) ) {
668 61 100       229 last if $line eq "\r\n";
669 52         259 $text .= $line;
670             }
671              
672             # Store the headers
673 9         39 $obj->property( "_$t", $text );
674              
675             } elsif ( $t =~ /-body$/sm ) {
676 10 100       31 unless ( $t eq 'null-body' ) {
677 5         28 $text = $obj->_readChunked($input);
678 5         28 $obj->property( '_body', $text );
679 5         258 $obj->property( '_body_type', $t );
680             }
681             }
682              
683             # Check the intermediate length
684 19 100       706 if (@entities) {
685 9         51 ($offset) = ( $entities[0] =~ /=(\d+)$/sm );
686 9 100       42 $l = $l == 0 ? $offset - 2 : $offset - $l - 2;
687 9 50       48 unless ( length $text == $l ) {
688 0         0 $rv = 0;
689 0         0 $obj->error( "$t length mismatch: expected $l "
690             . 'characters, recieved '
691             . length $text );
692             }
693             }
694             }
695             }
696              
697             # Check for trailers for all message bodies
698 10 100       62 if ( grep /\b(?:res|req|opt)-body=/sm, $encap ) {
699 5         20 $line = $obj->_getLine($input);
700 5 50 33     48 if ( defined $line and $line ne "\r\n" ) {
701 0         0 $text = $line;
702 0         0 while ( defined( $line = $obj->_getLine($input) ) ) {
703 0 0       0 last if $line eq "\r\n";
704 0         0 $text .= $line;
705             }
706 0         0 $obj->property( '_trailer', $text );
707             }
708             }
709              
710             } else {
711 0         0 pdebug( 'no Encapsulated header found', ICAPDEBUG2 );
712             }
713              
714 10         29 pOut();
715 10         79 pdebug( "leaving w/rv: $rv", ICAPDEBUG2 );
716              
717 10         125 return $rv;
718             }
719              
720             sub _genEncap ($) {
721              
722             # Purpose: Returns a string Encapsulated value based on
723             # stored data
724             # Returns: String
725             # Usage: $encap = $obj->_genEncap;
726              
727 13     13   223 my $obj = shift;
728 13         23 my $encap = '';
729 13         17 my $offset = 0;
730 13         17 my ( $t, $tt );
731              
732 13         40 pdebug( 'entering', ICAPDEBUG2 );
733 13         114 pIn();
734              
735             # Check for req-hdr
736 13         110 $t = $obj->reqhdr;
737 13 100 66     92 if ( defined $t and length $t ) {
738 7         18 $encap = "req-hdr=$offset";
739 7         20 $offset = length($t) + 2;
740             }
741              
742             # Check for res-hdr
743 13         1800 $t = $obj->reshdr;
744 13 100 66     75 if ( defined $t and length $t ) {
745 3 100       15 $encap .= ', ' if length $encap;
746 3         9 $encap .= "res-hdr=$offset";
747 3         8 $offset = length($t) + 2;
748             }
749              
750             # Check for body
751 13         56 ( $tt, $t ) = $obj->body;
752 13 100 66     142 if ( defined $tt and length $tt and defined $t and length $t ) {
      66        
      33        
753 6 50       29 $encap .= ', ' if length $encap;
754 6         22 $encap .= "$tt=$offset";
755             } else {
756 7 100       21 $encap .= ', ' if length $encap;
757 7         26 $encap .= "null-body=$offset";
758             }
759              
760 13         40 pOut();
761 13         109 pdebug( "leaving w/rv: $encap", ICAPDEBUG2 );
762              
763 13         160 return $encap;
764             }
765              
766             sub parse ($$) {
767              
768             # Purpose: Reads request/response from input
769             # Returns: Boolean
770             # Usage: $rv = $obj->parse($input);
771              
772 23     23 1 872 my $obj = shift;
773 23         31 my $input = shift;
774 23         55 my ( $start, @headers, $line, $icap_msg );
775 23         29 my $rv = 0;
776              
777 23         116 pdebug( "entering w/$obj, $input", ICAPDEBUG1 );
778 23         204 pIn();
779              
780             # Purge internal state
781 23         191 $obj->purge('_errors');
782 23         811 $obj->purge('_headers');
783 23         777 $obj->property( '_start', undef );
784 23         995 $obj->property( '_req-hdr', undef );
785 23         1179 $obj->property( '_res-hdr', undef );
786 23         1087 $obj->property( '_body', undef );
787 23         1011 $obj->property( '_trailer', undef );
788              
789             # Read the transaction
790 23         1131 while ( defined( $line = $obj->_getLine($input) ) ) {
791 151 100       550 last if $line eq "\r\n";
792 128         711 $icap_msg .= $line;
793             }
794              
795             # Process $icap_msg
796 23 50       98 if ( length $icap_msg ) {
797              
798             # Strip any trailing line terminations
799 23         156 $icap_msg =~ s/\r\n$//sm;
800              
801             # Separate start line from headers
802 23         211 ( $start, @headers ) = split /\r\n/sm, $icap_msg;
803              
804             # Store the start line, headers, and parse Encap data
805 23         128 $obj->property( '_start', $start );
806 23   66     1141 $rv = $obj->setHeaders( _parseHeaders(@headers) )
807             && $obj->_parseEncap($input);
808             }
809              
810 23         83 pOut();
811 23         168 pdebug( "leaving w/rv: $rv", ICAPDEBUG1 );
812              
813 23         283 return $rv;
814             }
815              
816             sub generate ($$) {
817              
818             # Purpose: Writes output to the passed reference
819             # Returns: Boolean
820             # Usage: $rv = $obj->generate($ref);
821              
822 13     13 1 26 my $obj = shift;
823 13         103 my $ref = shift;
824 13         18 my $rv = 1;
825 13         22 my ( $d, $t, $tt, $l );
826              
827 13         60 pdebug( "entering w/$ref", ICAPDEBUG1 );
828 13         125 pIn();
829              
830             # Update Date Header if missing
831 13         85 $d = $obj->header('Date');
832 13 100 66     122 $obj->header( 'Date', time2str(time) ) unless defined $d and length $d;
833              
834             # Print Start/Status line
835 13         45 $t = $obj->property('_start') . "\r\n";
836 13 50       508 $rv = defined $t and length $t ? $obj->_putLine( $ref, $t ) : 0;
    50          
837              
838             # Print ICAP headers
839 13 50       35 if ($rv) {
840 13         61 $obj->store( qw(_headers Encapsulated), $obj->_genEncap );
841 13         769 $l = $t = $obj->_genHeaders . "\r\n";
842 13 50       105 $rv = defined $t and length $t ? $obj->_putLine( $ref, $t ) : 0;
    50          
843             }
844              
845             # Print req-hdr
846 13 50       38 if ($rv) {
847 13         40 $t = $obj->property('_req-hdr');
848 13 100 66     490 if ( defined $t and length $t ) {
849 7         36 while ( $t !~ /\r\n\r\n$/sm ) { $t .= "\r\n" }
  7         44  
850 7         16 $l = $t;
851 7         31 $rv = $obj->_putLine( $ref, $t );
852             }
853             }
854              
855             # Print res-hdr
856 13 50       46 if ($rv) {
857 13         45 $t = $obj->property('_res-hdr');
858 13 100 66     465 if ( defined $t and length $t ) {
859 3         17 while ( $t !~ /\r\n\r\n$/sm ) { $t .= "\r\n" }
  3         16  
860 3         7 $l = $t;
861 3         12 $rv = $obj->_putLine( $ref, $t );
862             }
863             }
864              
865             # Print body
866 13 50       38 if ($rv) {
867 13         32 ( $tt, $t ) = $obj->body;
868 13 100 66     101 if ( defined $t and length $t ) {
869 6         45 $l = $t = $obj->_writeChunked;
870 6         32 $rv = $obj->_putLine( $ref, $t );
871             }
872             }
873              
874             # Print end of message termination
875 13 50       41 if ($rv) {
876 13         61 while ( $l !~ /\r\n\r\n/sm ) {
877 6         15 $l .= "\r\n";
878 6         24 $rv = $obj->_putLine( $ref, "\r\n" );
879             }
880             }
881              
882 13         37 pOut();
883 13         98 pdebug( "leaving w/rv: $rv", ICAPDEBUG1 );
884              
885 13         140 return $rv;
886             }
887              
888             1;
889              
890             __END__