File Coverage

blib/lib/Net/SIP/Packet.pm
Criterion Covered Total %
statement 244 312 78.2
branch 80 136 58.8
condition 21 34 61.7
subroutine 39 45 86.6
pod 21 21 100.0
total 405 548 73.9


line stmt bran cond sub pod time code
1             ###########################################################################
2             # Net::SIP::Packet
3             # parsing, creating and manipulating of SIP packets
4             ###########################################################################
5              
6 44     44   55639 use strict;
  44         84  
  44         1085  
7 44     44   186 use warnings;
  44         72  
  44         1187  
8              
9             package Net::SIP::Packet;
10              
11 44     44   529 use Net::SIP::Debug;
  44         68  
  44         323  
12 44     44   25492 use Storable;
  44         120782  
  44         2268  
13 44     44   18135 use Net::SIP::SDP;
  44         106  
  44         1347  
14 44     44   262 use Carp 'croak';
  44         85  
  44         1934  
15              
16 44     44   700 use fields qw( code method text header lines body as_string );
  44         1424  
  44         260  
17              
18             # code: numeric response code in responses
19             # method request method in requests
20             # text: response text or request URI
21             # body: scalar with body
22             # as_string: string representation
23             # lines: array-ref or [ original_header_lines, number_of_parts ]
24             # header: array-ref of Net::SIP::HeaderPair
25              
26              
27              
28              
29             ###########################################################################
30             # Constructor - Creates new object.
31             # If there are more than one argument it will forward to new_from_parts.
32             # If the only argument is a scalar it will forward to new_from_string.
33             # Otherwise it will just create the object of the given class and if
34             # there is an argument treat is as a hash to fill the new object.
35             #
36             # Apart from new there are also _new_request and _new_response.
37             # These can be overridden so that application specific classes for
38             # request and response will be used for the new object.
39             #
40             # Args: see new_from_parts(..)|new_from_string($scalar)|\%hash|none
41             # Returns: $self
42             ###########################################################################
43             sub new {
44 593     593 1 8137 my $class = shift;
45 593 100       2652 return $class->new_from_parts(@_) if @_>1;
46 407 100 66     2244 return $class->new_from_string(@_) if @_ && !ref($_[0]);
47 398         1251 my $self = fields::new($class);
48 398 50       48661 %$self = %{$_[0]} if @_;
  398         1852  
49 398         2010 return $self;
50             }
51              
52             sub _new_request {
53 190     190   398 shift;
54 190         1389 return Net::SIP::Request->new(@_);
55             }
56              
57             sub _new_response {
58 208     208   582 shift;
59 208         1259 return Net::SIP::Response->new(@_);
60             }
61              
62             ###########################################################################
63             # create new object from parts
64             # Args: ($class,$code_or_method,$text,$header,$body)
65             # $code_or_method: Response code or request method
66             # $text: Response text or request URI
67             # $header: Header representation as array or hash
68             # either [ [key1 => val2],[key2 => val2],... ] where the same
69             # key can occure multiple times
70             # or { key1 => val1, key2 => val2 } where val can be either
71             # a scalar or an array-ref (if the same key has multiple values)
72             # $body: Body as string
73             # Returns: $self
74             # Comment:
75             # the actual object will be created with _new_request and _new_response and
76             # thus will usually be a subclass of Net::SIP::Packet
77             ###########################################################################
78             sub new_from_parts {
79 186     186 1 840 my ($class,$code,$text,$header,$body) = @_;
80              
81             # header can be hash-ref or array-ref
82             # if hash-ref convert it to array-ref sorted by key
83             # (sort just to make the result predictable)
84 186 50       829 if ( UNIVERSAL::isa( $header,'HASH' )) {
85 186         371 my @hnew;
86 186         1865 foreach my $key ( sort keys %$header ) {
87 1044         1858 my $v = $header->{$key};
88 1044 100       1948 foreach my $value ( ref($v) ? @$v : ($v) ) {
89 884         2338 push @hnew,[ $key,$value ];
90             }
91             }
92 186         474 $header = \@hnew;
93             }
94              
95 186 100       1998 my $self = $code =~m{^\d}
96             ? $class->_new_response({ code => $code })
97             : $class->_new_request({ method => uc($code) });
98 186 50       798 $self->{text} = defined($text) ? $text:'';
99              
100             # $self->{header} is list of Net::SIP::HeaderPair which cares about normalized
101             # keys while maintaining the original key, so that one can restore header
102             # the elements from @$header can be either [ key,value ] or Net::SIP::HeaderPair's
103             # but have to be all from the same type
104 186         347 my @hnew;
105 186         320 my $normalized = 0;
106 186         693 for( my $i=0;$i<@$header;$i++ ) {
107 884         1272 my $h = $header->[$i];
108 884 50       2040 if ( UNIVERSAL::isa($h,'Net::SIP::HeaderPair')) {
109             # already normalized
110 0         0 $normalized = 1;
111 0         0 push @hnew,$h;
112             } else {
113 884         2032 my ($key,$value) = @$h;
114 884 50       1525 defined($value) || next;
115 884 50       1451 croak( "mix between normalized and not normalized data in header" ) if $normalized;
116 884         2455 push @hnew, Net::SIP::HeaderPair->new( $key,$value ) ;
117             }
118             }
119              
120 186         467 $self->{header} = \@hnew;
121             # as_string is still undef, it will be evaluated once we call as_string()
122              
123 186 100       523 if ( ref($body)) {
124 56 50       683 if ( !$self->get_header( 'content-type' )) {
125 56         263 my $sub = UNIVERSAL::can( $body, 'content_type' );
126 56 50       469 $self->set_header( 'content-type' => $sub->($body) ) if $sub;
127             }
128 56         306 $body = $body->as_string;
129             }
130 186         503 $self->{body} = $body;
131              
132 186         1125 return $self;
133             }
134              
135             ###########################################################################
136             # Create new packet from string
137             # Args: ($class,$string)
138             # $string: String representation of packet
139             # Returns: $self
140             # Comment:
141             # for the class of $self see comment in new_from_parts above
142             ###########################################################################
143             sub new_from_string {
144 218     218 1 1004 my ($class,$string) = @_;
145 218         1325 my $data = _string2parts($string);
146             return $data->{method}
147 212 100       1213 ? $class->_new_request($data)
148             : $class->_new_response($data);
149             }
150              
151             ###########################################################################
152             # Find out if it is a request
153             # Args: $self
154             # Returns: true if it's a request
155             ###########################################################################
156             sub is_request {
157 0     0 1 0 my $self = shift;
158 0 0       0 $self->{header} || $self->as_parts();
159 0   0     0 return $self->{method} && 1;
160             }
161              
162             ###########################################################################
163             # Find out if it is a response
164             # Args: $self
165             # Returns: true if it's a response
166             ###########################################################################
167             sub is_response {
168 0     0 1 0 my $self = shift;
169 0 0       0 $self->{header} || $self->as_parts();
170 0         0 return ! $self->{method};
171             }
172              
173              
174             ###########################################################################
175             # Return transaction Id of packet, consisting of the call-id and
176             # the CSeq num. Method is not included because ACK or CANCEL requests
177             # belong to the same transaction as the INVITE
178             # Responses have the same TID as the request
179             # Args: $self
180             # Returns: $tid
181             ###########################################################################
182             sub tid {
183 1123     1123 1 1778 my Net::SIP::Packet $self = shift;
184 1123         2341 $self->get_header( 'cseq' ) =~m{^(\d+)};
185 1123         2244 return $self->get_header( 'call-id' ).' '.$1;
186             }
187              
188             ###########################################################################
189             # Accessors for Headerelements
190             ###########################################################################
191              
192             ###########################################################################
193             # Access cseq Header
194             # Args: $self
195             # Returns: $cseq_value
196             ###########################################################################
197 396     396 1 971 sub cseq { scalar( shift->get_header('cseq')) }
198              
199             ###########################################################################
200             # Access call-id Header
201             # Args: $self
202             # Returns: $callid
203             ###########################################################################
204 196     196 1 598 sub callid { scalar( shift->get_header('call-id')) }
205              
206             ###########################################################################
207             # Access header
208             # Args: ($self; $key)
209             # $key: (optional) which headerkey to access
210             # Returns: @val|\%header
211             # @val: if key given returns all values for this key
212             # croak()s if in scalar context and I've more then one value for the key
213             # \%header: if no key given returns hash with
214             # { key1 => \@val1, key2 => \@val2,.. }
215             ###########################################################################
216             sub get_header {
217 6190     6190 1 13190 my ($self,$key) = @_;
218 6190         10982 my $hdr = ($self->as_parts)[2];
219 6190 50       11102 if ( $key ) {
220 6190         9323 $key = _normalize_hdrkey($key);
221 6190         8571 my @v;
222 6190         9678 foreach my $h (@$hdr) {
223 44769 100       75020 push @v,$h->{value} if $h->{key} eq $key;
224             }
225 6190 100       15606 return @v if wantarray;
226 4134 50       6929 if (@v>1) {
227             # looks like we have multiple headers but expect only
228             # one. Because we've seen bad client which issue multiple
229             # content-length header we try if all in @v are the same
230 0         0 my %v = map { $_ => 1 } @v;
  0         0  
231 0 0       0 return $v[0] if keys(%v) == 1; # ok, only one
232 0         0 croak( "multiple values for $key in packet:\n".$self->as_string );
233             }
234 4134         22620 return $v[0];
235             } else {
236 0         0 my %result;
237 0         0 foreach my $h (@$hdr) {
238 0         0 push @{ $result{$h->{key}} }, $h->{value};
  0         0  
239             }
240 0         0 return \%result;
241             }
242             }
243              
244             ###########################################################################
245             # get header as Net::SIP::HeaderVal
246             # like get_header, but instead of giving scalar values gives Net::SIP::HeaderVal
247             # objects which have various accessors, like extracting the parameters
248             # Args: ($self; $key)
249             # $key: (optional) which headerkey to access
250             # Returns: @val|\%header
251             # @val: if key given returns all values (Net::SIP::HeaderVal) for this key
252             # croak()s if in scalar context and I've more then one value for the key
253             # \%header: if no key given returns hash with
254             # { key1 => \@val1, key2 => \@val2,.. } where val are Net::SIP::HeaderVal
255             ###########################################################################
256             sub get_header_hashval {
257 9     9 1 17 my ($self,$key) = @_;
258 9         15 my $hdr = ($self->as_parts)[2];
259 9 50       20 if ( $key ) {
260 9         13 $key = _normalize_hdrkey($key);
261 9         11 my @v;
262 9         15 foreach my $h (@$hdr) {
263             push @v,Net::SIP::HeaderVal->new( $h )
264 59 100       99 if $h->{key} eq $key;
265             }
266 9 50       37 return @v if wantarray;
267 0 0       0 croak( "multiple values for $key" ) if @v>1;
268 0         0 return $v[0];
269             } else {
270 0         0 my %result;
271 0         0 foreach my $h (@$hdr) {
272 0         0 push @{ $result{$h->{key}} },
  0         0  
273             Net::SIP::HeaderVal->new( $h );
274             }
275 0         0 return \%result;
276             }
277             }
278              
279             ###########################################################################
280             # Add header to SIP packet, headers gets added after all other headers
281             # Args: ($self,$key,$val)
282             # $key: Header key
283             # $val: scalar or \@array which contains value(s)
284             ###########################################################################
285             sub add_header {
286 3     3 1 8 my ($self,$key,$val) = @_;
287 3         8 my $hdr = ($self->as_parts)[2];
288 3 50       10 foreach my $v ( ref($val) ? @$val:$val ) {
289             ### TODO: should add quoting to $v if necessary
290 3         8 push @$hdr, Net::SIP::HeaderPair->new( $key,$v );
291             }
292 3         6 $self->_update_string();
293             }
294              
295             ###########################################################################
296             # Add header to SIP packet, header gets added before all other headers
297             # Args: ($self,$key,$val)
298             # $key: Header key
299             # $val: scalar or \@array which contains value(s)
300             ###########################################################################
301             sub insert_header {
302 320     320 1 1040 my ($self,$key,$val) = @_;
303 320         689 my $hdr = ($self->as_parts)[2];
304 320 50       1099 foreach my $v ( ref($val) ? @$val:$val ) {
305             ### TODO: should add quoting to $v if necessary
306 320         814 unshift @$hdr, Net::SIP::HeaderPair->new( $key,$v );
307             }
308 320         765 $self->_update_string();
309             }
310              
311             ###########################################################################
312             # Delete all headers for a key
313             # Args: ($self,$key)
314             ###########################################################################
315             sub del_header {
316 0     0 1 0 my ($self,$key) = @_;
317 0         0 $key = _normalize_hdrkey($key);
318 0         0 my $hdr = ($self->as_parts)[2];
319 0         0 @$hdr = grep { $_->{key} ne $key } @$hdr;
  0         0  
320 0         0 $self->_update_string();
321             }
322              
323             ###########################################################################
324             # Set header for key to val, e.g. delete all remaining headers for key
325             # Args: ($self,$key,$val)
326             # $key: Header key
327             # $val: scalar or \@array which contains value(s)
328             ###########################################################################
329             sub set_header {
330 95     95 1 339 my ($self,$key,$val) = @_;
331 95         247 $key = _normalize_hdrkey($key);
332             # del_header
333 95         283 my $hdr = ($self->as_parts)[2];
334 95         271 @$hdr = grep { $_->{key} ne $key } @$hdr;
  695         1375  
335             # add_header
336 95 100       657 foreach my $v ( ref($val) ? @$val:$val ) {
337             ### TODO: should add quoting to $v if necessary
338 90         322 push @$hdr, Net::SIP::HeaderPair->new( $key,$v );
339             }
340 95         672 $self->_update_string();
341             }
342              
343             ###########################################################################
344             # set the body
345             # Args: ($self,$body)
346             # $body: string or object with method as_string (like Net::SIP::SDP)
347             # Returns: NONE
348             ###########################################################################
349             sub set_body {
350 0     0 1 0 my ($self,$body) = @_;
351 0 0       0 if ( ref($body)) {
352 0 0       0 if ( !$self->get_header( 'content-type' )) {
353 0         0 my $sub = UNIVERSAL::can( $body, 'content_type' );
354 0 0       0 $self->set_header( 'content-type' => $sub->($body) ) if $sub;
355             }
356 0         0 $body = $body->as_string;
357             }
358 0         0 $self->as_parts;
359 0         0 $self->{body} = $body;
360 0         0 $self->_update_string();
361             }
362              
363             ###########################################################################
364             # Iterate over all headers with sup and remove or manipulate them
365             # Args: ($self,@arg)
366             # @arg: either $key => $sub or only $sub
367             # if $key is given only headers for this key gets modified
368             # $sub is either \&code or [ \&code, @args ]
369             # code gets $pair (Net::SIP::HeaderPair) as last parameter
370             # to remove header it should call $pair->remove, if it modify
371             # header it should call $pair->set_modified
372             ###########################################################################
373             sub scan_header {
374 72     72 1 228 my Net::SIP::Packet $self = shift;
375 72 50       334 my $key = @_>1 ? _normalize_hdrkey(shift) : undef;
376 72         147 my $sub = shift;
377              
378 72 50       333 ($sub, my @args) = ref($sub) eq 'CODE' ? ($sub):@$sub;
379              
380 72         372 my $hdr = ($self->as_parts)[2];
381 72         340 foreach my $h (@$hdr) {
382 705 100 66     2275 next if $key && $h->{key} ne $key;
383             # in-place modify or delete (set key to undef)
384 73         273 $sub->(@args,$h);
385             }
386             # remove deleted entries ( !key ) from @$hdr
387 72         213 @$hdr = grep { $_->{key} } @$hdr;
  705         1208  
388 72         390 $self->_update_string();
389             }
390              
391             ###########################################################################
392             # Return packet as string
393             # tries to restore as much as possible from original packet (if created
394             # from string)
395             # Args: $self
396             # Returns: $packet_as_string
397             ###########################################################################
398             sub as_string {
399 330     330 1 611 my $self = shift;
400              
401             # check if content-length header is up-to-date
402 330   100     1651 my $body = $self->{body} || '';
403 330         805 my $cl = $self->get_header( 'content-length' );
404 330 50 33     965 if ( defined($cl) && $cl != length($body) ) {
405 0         0 $self->set_header( 'content-length',length($body))
406             }
407              
408             # return immediately if request is up to date
409 330 100       757 return $self->{as_string} if $self->{as_string};
410              
411 319         662 my $header = $self->{header};
412              
413             # check if the lines from the original packet (if created
414             # from string, see as_parts) are up-to-date
415 319         435 my @result;
416 319 50       852 if ( my $lines = $self->{lines} ) {
417 0         0 for (my $i=0;$i<@$lines;$i++ ) {
418 0 0       0 my ($line,$count) = @{ $lines->[$i] || next };
  0         0  
419              
420             # check if $count entries for line-index $i in headers
421             my @hi = grep {
422 0         0 my $line = $header->[$_]{line};
  0         0  
423 0 0 0     0 ( defined($line) && $line == $i ) ? 1:0;
424             } (0..$#$header);
425 0 0       0 if ( @hi == $count ) {
    0          
426             # assume that line wasn't changed because the count is right
427 0         0 $result[ $hi[0] ] = $line;
428             } elsif ( @hi ) {
429             # some parts from this line have been modified
430             # place remaining parts back to same line
431 0         0 my $v = join( ", ", map { $header->[$_]{value} } @hi );
  0         0  
432 0         0 $v =~s{\r?\n\s*}{\r\n }g; # \r?\n\s* -> \r\n + space for continuation lines
433 0         0 my $r = $result[ $hi[0] ] = $header->[ $hi[0] ]{orig_key}.": ".$v;
434 0         0 $lines->[$i] = [ $r,int(@hi) ]; # and update $lines
435             } else {
436             # nothing remaining from line $i, update lines
437 0         0 delete $lines->[$i];
438             }
439             }
440             }
441              
442             # all lines from $header which had a defined line index should have been
443             # handled by the code above, now care about the lines w/o defined line
444 319         1002 foreach my $hi ( grep { !defined( $header->[$_]{line} ) } (0..$#$header) ) {
  1944         5882  
445              
446 1944         2835 my $v = $header->[$hi]{value};
447 1944         2866 $v =~s{\r?\n\s*}{\r\n }g; # \r?\n\s* -> \r\n + space for continuation lines
448 1944         5060 $result[$hi] = ucfirst($header->[$hi]{key}).": ".$v;
449             }
450              
451             # (re)build packet
452             my $hdr_string = $self->{method}
453 319 100       1439 ? "$self->{method} $self->{text} SIP/2.0\r\n" # Request
454             : "SIP/2.0 $self->{code} $self->{text}\r\n"; # Response
455              
456 319         718 $hdr_string .= join( "\r\n", grep { $_ } @result )."\r\n";
  1944         3197  
457              
458             # add content-length header if there was none
459 319 50       1772 $hdr_string .= sprintf( "Content-length: %d\r\n", length( $body ))
460             if !defined($cl);
461              
462 319         2222 return ( $self->{as_string} = $hdr_string."\r\n".$body );
463             }
464              
465             ###########################################################################
466             # packet dump in long or short form, used mainly for debuging
467             # Args: ($self,?$level)
468             # $level: level of details: undef|0 -> one line, else -> as_string
469             # Returns: $dump_as_string
470             ###########################################################################
471             sub dump {
472 151     151 1 282 my Net::SIP::Packet $self = shift;
473 151         282 my $level = shift;
474 151 100       540 if ( !$level ) {
475 148 50       407 if ( $self->is_request ) {
476 148         692 my ($method,$text,$header,$body) = $self->as_parts;
477 148 100       1166 return "REQ $method $text ".( $body ? 'with body' :'' );
478             } else {
479 0         0 my ($code,$text,$header,$body) = $self->as_parts;
480 0 0       0 return "RESP $code '$text' ".( $body ? 'with body' :'' );
481             }
482             } else {
483 3         25 return $self->as_string
484             }
485             }
486              
487              
488             ###########################################################################
489             # Return parts
490             # Args: ($self)
491             # Returns: ($code_or_method,$text,$header,$body)
492             # $code_or_method: Response code or request method
493             # $text: Response text or request URI
494             # $header: Header representation as array
495             # [ [key1 => val2],[key2 => val2],... ] where the same
496             # key can occure multiple times
497             # $body: Body as string
498             # Comment:
499             # Output from this method is directly usable as input to new_from_parts
500             ###########################################################################
501             sub as_parts {
502 8291     8291 1 10731 my $self = shift;
503              
504             # if parts are up to date return immediately
505 8291 50       14997 if ( ! $self->{header} ) {
506 0         0 my $data = _string2parts( $self->{as_string} );
507 0         0 %$self = ( %$self,%$data );
508             }
509 8291 100       13607 return @{$self}{qw(method text header body)} if $self->{method};
  5287         16015  
510 3004         3964 return @{$self}{qw(code text header body)};
  3004         8350  
511             }
512              
513             {
514             my $word_rx = qr{[\w\-\.!%\*+`'~()<>:"/?{}\x1c\x1b\x1d]+};
515             my $callid_rx = qr{^$word_rx(?:\@$word_rx)?$};
516             my %key2parser = (
517              
518             # FIXME: More of these should be more strict to filter out invalid values
519             # for now they are only given here to distinguish them from the keys, which
520             # can be given multiple times either on different lines or on the same delimited
521             # by comma
522              
523             'www-authenticate' => \&_hdrkey_parse_keep,
524             'authorization' => \&_hdrkey_parse_keep,
525             'proxy-authenticate' => \&_hdrkey_parse_keep,
526             'proxy-authorization' => \&_hdrkey_parse_keep,
527             'date' => \&_hdrkey_parse_keep,
528             'content-disposition' => \&_hdrkey_parse_keep,
529             'content-type' => \&_hdrkey_parse_keep,
530             'mime-version' => \&_hdrkey_parse_keep,
531             'organization' => \&_hdrkey_parse_keep,
532             'priority' => \&_hdrkey_parse_keep,
533             'reply-to' => \&_hdrkey_parse_keep,
534             'retry-after' => \&_hdrkey_parse_keep,
535             'server' => \&_hdrkey_parse_keep,
536             'to' => \&_hdrkey_parse_keep,
537             'user-agent' => \&_hdrkey_parse_keep,
538              
539             'content-length' => \&_hdrkey_parse_num,
540             'expires' => \&_hdrkey_parse_num,
541             'max-forwards' => \&_hdrkey_parse_num,
542             'min-expires' => \&_hdrkey_parse_num,
543              
544             'via' => \&_hdrkey_parse_comma_seperated,
545             'contact' => \&_hdrkey_parse_comma_seperated,
546             'record-route' => \&_hdrkey_parse_comma_seperated,
547             'route' => \&_hdrkey_parse_comma_seperated,
548             'allow' => \&_hdrkey_parse_comma_seperated,
549             'supported' => \&_hdrkey_parse_comma_seperated,
550             'unsupported' => \&_hdrkey_parse_comma_seperated,
551              
552             'in-reply-to' => \&_hdrkey_parse_comma_seperated,
553             'accept' => \&_hdrkey_parse_comma_seperated,
554             'accept-encoding' => \&_hdrkey_parse_comma_seperated,
555             'accept-language' => \&_hdrkey_parse_comma_seperated,
556             'proxy-require' => \&_hdrkey_parse_comma_seperated,
557             'require' => \&_hdrkey_parse_comma_seperated,
558             'content-encoding' => \&_hdrkey_parse_comma_seperated,
559             'content-language' => \&_hdrkey_parse_comma_seperated,
560             'alert-info' => \&_hdrkey_parse_comma_seperated,
561             'call-info' => \&_hdrkey_parse_comma_seperated,
562             'error-info' => \&_hdrkey_parse_comma_seperated,
563             'error-info' => \&_hdrkey_parse_comma_seperated,
564             'warning' => \&_hdrkey_parse_comma_seperated,
565              
566             'call-id' => sub {
567             $_[0] =~ $callid_rx or
568             die "invalid callid, should be 'word [@ word]'\n";
569             return $_[0];
570             },
571             'cseq' => sub {
572             $_[0] =~ m{^\d+\s+\w+\s*$} or
573             die "invalid cseq, should be 'number method'\n";
574             return $_[0];
575             },
576             );
577              
578             my %once = map { ($_ => 1) }
579             qw(cseq content-type from to call-id content-length);
580             my %key2check = (
581             rsp => undef,
582             req => {
583             cseq => sub {
584             my ($v,$result) = @_;
585             $v =~ m{^\d+\s+(\w+)\s*$} or
586             die "invalid cseq, should be 'number method'\n";
587             $result->{method} eq $1 or
588             die "method in cseq does not match method of request\n";
589             },
590             }
591             );
592              
593 509     509   1003 sub _hdrkey_parse_keep { return $_[0] };
594             sub _hdrkey_parse_num {
595 262     262   670 my ($v,$k) = @_;
596 262 50       1356 $v =~m{^(\d+)\s*$} || die "invalid $k, should be number\n";
597 262         942 return $1;
598             };
599              
600             sub _hdrkey_parse_comma_seperated {
601 388     388   994 my ($v,$k) = @_;
602 388         939 my @v = ( '' );
603 388         772 my $quote = '';
604             # split on komma (but not if quoted)
605 388         531 while (1) {
606 635 100       3804 if ( $quote ) {
    100          
607 5 50       73 if ( $v =~m{\G(.*?)(\\|$quote)}gc ) {
608 5 50       13 if ( $2 eq "\\" ) {
609 0         0 $v[-1].=$1.$2.substr( $v,pos($v),1 );
610 0         0 pos($v)++;
611             } else {
612 5         31 $v[-1].=$1.$2;
613 5         13 $quote = '';
614             }
615             } else {
616             # missing end-quote
617 0         0 die "missing '$quote' in '$v'\n";
618             }
619             } elsif ( $v =~m{\G(.*?)([\\"<,])}gc ) {
620 242 100       816 if ( $2 eq "\\" ) {
    100          
621 5         18 $v[-1].=$1.$2.substr( $v,pos($v),1 );
622 5         16 pos($v)++;
623             } elsif ( $2 eq ',' ) {
624             # next item if not quoted
625 232         907 ( $v[-1].=$1 ) =~s{\s+$}{}; # strip trailing space
626 232 50       725 push @v,'' if !$quote;
627 232         663 $v =~m{\G\s+}gc; # skip space after ','
628             } else {
629 5         10 $v[-1].=$1.$2;
630 5 50       12 $quote = $2 eq '<' ? '>':$2;
631             }
632             } else {
633             # add rest to last from @v
634 388   100     2241 $v[-1].= substr($v,pos($v)||0 );
635 388         699 last;
636             }
637             }
638 388         1105 return @v;
639             }
640              
641             sub _string2parts {
642 218     218   560 my $string = shift;
643 218         1008 my %result = ( as_string => $string );
644              
645             # otherwise parse request
646 218         3093 my ($header,$body) = split( m{\r?\n\r?\n}, $string,2 );
647 218         2310 my @header = split( m{\r?\n}, $header );
648              
649 218         448 my $key2check;
650 218 100       2270 if ( $header[0] =~m{^SIP/2.0\s+(\d+)\s+(\S.*?)\s*$} ) {
    50          
651             # Response, e.g. SIP/2.0 407 Authorization required
652 138         655 $result{code} = $1;
653 138         589 $result{text} = $2;
654 138         458 $key2check = $key2check{rsp};
655             } elsif ( $header[0] =~m{^(\w+)\s+(\S.*?)\s+SIP/2\.0\s*$} ) {
656             # Request, e.g. INVITE SIP/2.0
657 80         556 $result{method} = $1;
658 80         615 $result{text} = $2;
659 80         334 $key2check = $key2check{req};
660             } else {
661 0         0 die "bad request: starts with '$header[0]'\n";
662             }
663 218         551 shift(@header);
664              
665 218         660 $result{body} = $body;
666              
667 218         897 my @hdr;
668             my @lines;
669 218         0 my @check;
670 218         0 my %check_once;
671 218         841 while (@header) {
672 1597 50       10376 my ($k,$v) = $header[0] =~m{^([^\s:]+)\s*:\s*(.*)}
673             or die "bad header line $header[0]\n";
674 1597         2761 my $line = shift(@header);
675 1597   66     7098 while ( @header && $header[0] =~m{^\s+(.*)} ) {
676             # continuation line
677 0         0 $v .= "\n$1";
678 0         0 $line .= shift(@header);
679             }
680 1597         3393 my $nk = _normalize_hdrkey($k);
681              
682 1597         3394 my $parse = $key2parser{$nk};
683 1597 100       5186 my @v = $parse ? $parse->($v,$nk) : _hdrkey_parse_keep($v,$nk);
684 1597 100       2979 if ( @v>1 ) {
685 58         225 for( my $i=0;$i<@v;$i++ ) {
686 290         672 push @hdr, Net::SIP::HeaderPair->new( $k,$v[$i],scalar(@lines),$i );
687             }
688             } else {
689 1539         3952 push @hdr, Net::SIP::HeaderPair->new( $k,$v[0],scalar(@lines) );
690             }
691 1597 100       4022 if (my $k2c = $key2check->{$nk}) {
692 81         364 push @check, [ $k2c, $_ ] for @v;
693             }
694 1597 100       3506 if ($once{$nk}) {
695             ($check_once{$nk} //= $_) eq $_ or
696             die "conflicting definition of $nk\n"
697 1151   100     5775 for @v;
      100        
698             }
699 1592         5978 push @lines, [ $line, int(@v) ];
700             }
701 213         683 $result{header} = \@hdr;
702 213         727 $result{lines} = \@lines;
703 213         562 for(@check) {
704 75         222 my ($sub,$v) = @$_;
705 75         274 $sub->($v,\%result);
706             }
707 212         919 return \%result;
708             }
709             }
710              
711             ###########################################################################
712             # return SDP body
713             # Args: $self
714             # Returns: $body
715             # $body: Net::SIP::SDP object if body exists and content-type is
716             # application/sdp (or not defined)
717             ###########################################################################
718             sub sdp_body {
719 66     66 1 207 my Net::SIP::Packet $self = shift;
720 66         199 my $ct = $self->get_header( 'content-type' );
721 66 50 66     563 return if $ct && lc($ct) ne 'application/sdp';
722 66   100     220 my $body = ($self->as_parts)[3] || return;
723 48         910 return Net::SIP::SDP->new( $body );
724             }
725              
726             ###########################################################################
727             # clone packet, so that modification does not affect the original
728             # Args: $self
729             # Returns: $clone
730             ###########################################################################
731             sub clone {
732 119     119 1 15054 return Storable::dclone( shift );
733             }
734              
735             ###########################################################################
736             # Trigger updating parts, e.g. code, method, header...
737             # done by setting header as undef if as_string is set, so the next time
738             # I'll try to access code it will be recalculated from string
739             # Args: $self
740             ###########################################################################
741             sub _update_parts {
742 0     0   0 my $self = shift;
743 0 0       0 $self->{header} = undef if $self->{as_string};
744             }
745              
746             ###########################################################################
747             # Trigger updating string
748             # done by setting as_string as undef if header is set, so the next time
749             # I'll try to access as_string it will be recalculated from the parts
750             # Args: $self
751             ###########################################################################
752             sub _update_string {
753 520     520   877 my $self = shift;
754 520 50       2020 $self->{as_string} = undef if $self->{header};
755             }
756              
757             ###########################################################################
758             # access _normalize_hdrkey function from Net::SIP::HeaderPair
759             # Args: $key
760             # Returns: $key_normalized
761             ###########################################################################
762             sub _normalize_hdrkey {
763 7963     7963   17346 goto &Net::SIP::HeaderPair::_normalize_hdrkey
764             }
765              
766             ###########################################################################
767             # Net::SIP::HeaderPair
768             # container for normalized key,value and some infos to restore
769             # string representation
770             ###########################################################################
771              
772             package Net::SIP::HeaderPair;
773 44     44   161407 use fields qw( key value orig_key line pos );
  44         121  
  44         1893  
774              
775             # key: normalized key: lower case, not compact
776             # value: value
777             # orig_key: original key: can be mixed case and compact
778             # line: index of header line within original request
779             # pos: relativ position in line (starting with 0) if multiple
780             # values are given in one line
781              
782             ###########################################################################
783             # Create new HeaderPair
784             # Args: ($class,$key,$value,$line,$pos)
785             # $key: original key
786             # $value: value
787             # $line: index of header line in original header
788             # $pos: index within header line if multiple values are in line
789             # Returns: $self
790             ###########################################################################
791             sub new {
792 3126     3126   6268 my ($class,$key,$value,$line,$pos) = @_;
793 3126         6771 my $self = fields::new( $class );
794 3126         199738 $self->{key} = _normalize_hdrkey( $key);
795 3126         5502 $self->{value} = $value;
796 3126         4414 $self->{orig_key} = $key;
797 3126         4232 $self->{line} = $line;
798 3126         4130 $self->{pos} = $pos;
799 3126         7646 return $self;
800             }
801              
802             ###########################################################################
803             # Mark HeaderPair as removed by setting key to undef
804             # used from Net::SIP:Packet::scan_header
805             # Args: $self
806             ###########################################################################
807             sub remove {
808             # mark es removed
809             shift->{key} = undef
810 1     1   3 }
811              
812             ###########################################################################
813             # Mark HeaderPair as modified by setting line to undef and thus deassociating
814             # it from the original header line
815             # Args: $self
816             ###########################################################################
817             sub set_modified {
818             # mark as modified
819 0     0   0 my $self = shift;
820 0         0 $self->{line} = $self->{pos} = undef;
821             }
822              
823              
824             {
825             my %alias = (
826             i => 'call-id',
827             m => 'contact',
828             e => 'content-encoding',
829             l => 'content-length',
830             c => 'content-type',
831             f => 'from',
832             s => 'subject',
833             k => 'supported',
834             t => 'to',
835             v => 'via',
836             );
837             sub _normalize_hdrkey {
838 11089     11089   18632 my $key = lc(shift);
839 11089   33     47156 return $alias{$key} || $key;
840             }
841             }
842              
843              
844             ###########################################################################
845             # Net::SIP::HeaderVal;
846             # gives string representation and hash representation
847             # (split by ';' or ',') of header value
848             ###########################################################################
849              
850             package Net::SIP::HeaderVal;
851 44     44   11791 use Net::SIP::Util qw(sip_hdrval2parts);
  44         87  
  44         2364  
852 44     44   357 use fields qw( data parameter );
  44         101  
  44         204  
853              
854             # WWW-Authenticate: Digest method="md5",qop="auth",...
855             # To: Bob Example ;tag=2626262;...
856             #
857             # data: the part before the first argument, e.g. "Digest" or
858             # "Bob Example "
859             # parameter: hash of parameters, e.g { method => md5, qop => auth }
860             # or { tag => 2626262, ... }
861              
862             ###########################################################################
863             # create new object from string
864             # knows which headers have ',' as delimiter and the rest uses ';'
865             # Args: ($class,$pair)
866             # $pair: Net::SIP::HeaderPair
867             # Returns: $self
868             ###########################################################################
869             sub new {
870 3     3   4 my $class = shift;
871 3         6 my Net::SIP::HeaderPair $pair = shift;
872 3         5 my $key = $pair->{key};
873 3         6 my $v = $pair->{value};
874              
875 3         9 my $self = fields::new($class);
876 3         161 ($self->{data}, $self->{parameter}) = sip_hdrval2parts( $key,$v );
877              
878 3         8 return $self;
879             }
880              
881              
882              
883              
884             1;