| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ########################################################################### | 
| 2 |  |  |  |  |  |  | # package Net::SIP::Request | 
| 3 |  |  |  |  |  |  | # subclass from Net::SIP::Packet for managing the request packets | 
| 4 |  |  |  |  |  |  | # has methods for creating ACK, CANCEL based on the request (and response) | 
| 5 |  |  |  |  |  |  | # and for adding Digest authorization (md5+qop=auth only) to the | 
| 6 |  |  |  |  |  |  | # request based on the requirements in the response | 
| 7 |  |  |  |  |  |  | ########################################################################### | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 44 |  |  | 44 |  | 657 | use strict; | 
|  | 44 |  |  |  |  | 86 |  | 
|  | 44 |  |  |  |  | 1178 |  | 
| 10 | 44 |  |  | 44 |  | 189 | use warnings; | 
|  | 44 |  |  |  |  | 73 |  | 
|  | 44 |  |  |  |  | 1460 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | package Net::SIP::Request; | 
| 13 | 44 |  |  | 44 |  | 194 | use base 'Net::SIP::Packet'; | 
|  | 44 |  |  |  |  | 71 |  | 
|  | 44 |  |  |  |  | 8603 |  | 
| 14 | 44 |  |  | 44 |  | 298 | use Net::SIP::Debug; | 
|  | 44 |  |  |  |  | 102 |  | 
|  | 44 |  |  |  |  | 273 |  | 
| 15 | 44 |  |  | 44 |  | 265 | use Net::SIP::Util 'invoke_callback'; | 
|  | 44 |  |  |  |  | 131 |  | 
|  | 44 |  |  |  |  | 2131 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 44 |  |  | 44 |  | 240 | use Digest::MD5 'md5_hex'; | 
|  | 44 |  |  |  |  | 84 |  | 
|  | 44 |  |  |  |  | 43300 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | my %ResponseCode = ( | 
| 20 |  |  |  |  |  |  | # Informational | 
| 21 |  |  |  |  |  |  | 100 => 'Trying', | 
| 22 |  |  |  |  |  |  | 180 => 'Ringing', | 
| 23 |  |  |  |  |  |  | 181 => 'Call Is Being Forwarded', | 
| 24 |  |  |  |  |  |  | 182 => 'Queued', | 
| 25 |  |  |  |  |  |  | 183 => 'Session Progress', | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | # Success | 
| 28 |  |  |  |  |  |  | 200 => 'OK', | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | # Redirection | 
| 31 |  |  |  |  |  |  | 300 => 'Multiple Choices', | 
| 32 |  |  |  |  |  |  | 301 => 'Moved Permanently', | 
| 33 |  |  |  |  |  |  | 302 => 'Moved Temporarily', | 
| 34 |  |  |  |  |  |  | 305 => 'Use Proxy', | 
| 35 |  |  |  |  |  |  | 380 => 'Alternative Service', | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | # Client-Error | 
| 38 |  |  |  |  |  |  | 400 => 'Bad Request', | 
| 39 |  |  |  |  |  |  | 401 => 'Unauthorized', | 
| 40 |  |  |  |  |  |  | 402 => 'Payment Required', | 
| 41 |  |  |  |  |  |  | 403 => 'Forbidden', | 
| 42 |  |  |  |  |  |  | 404 => 'Not Found', | 
| 43 |  |  |  |  |  |  | 405 => 'Method Not Allowed', | 
| 44 |  |  |  |  |  |  | 406 => 'Not Acceptable', | 
| 45 |  |  |  |  |  |  | 407 => 'Proxy Authentication Required', | 
| 46 |  |  |  |  |  |  | 408 => 'Request Timeout', | 
| 47 |  |  |  |  |  |  | 410 => 'Gone', | 
| 48 |  |  |  |  |  |  | 413 => 'Request Entity Too Large', | 
| 49 |  |  |  |  |  |  | 414 => 'Request-URI Too Large', | 
| 50 |  |  |  |  |  |  | 415 => 'Unsupported Media Type', | 
| 51 |  |  |  |  |  |  | 416 => 'Unsupported URI Scheme', | 
| 52 |  |  |  |  |  |  | 420 => 'Bad Extension', | 
| 53 |  |  |  |  |  |  | 421 => 'Extension Required', | 
| 54 |  |  |  |  |  |  | 423 => 'Interval Too Brief', | 
| 55 |  |  |  |  |  |  | 480 => 'Temporarily not available', | 
| 56 |  |  |  |  |  |  | 481 => 'Call Leg/Transaction Does Not Exist', | 
| 57 |  |  |  |  |  |  | 482 => 'Loop Detected', | 
| 58 |  |  |  |  |  |  | 483 => 'Too Many Hops', | 
| 59 |  |  |  |  |  |  | 484 => 'Address Incomplete', | 
| 60 |  |  |  |  |  |  | 485 => 'Ambiguous', | 
| 61 |  |  |  |  |  |  | 486 => 'Busy Here', | 
| 62 |  |  |  |  |  |  | 487 => 'Request Terminated', | 
| 63 |  |  |  |  |  |  | 488 => 'Not Acceptable Here', | 
| 64 |  |  |  |  |  |  | 491 => 'Request Pending', | 
| 65 |  |  |  |  |  |  | 493 => 'Undecipherable', | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | # Server-Error | 
| 68 |  |  |  |  |  |  | 500 => 'Internal Server Error', | 
| 69 |  |  |  |  |  |  | 501 => 'Not Implemented', | 
| 70 |  |  |  |  |  |  | 502 => 'Bad Gateway', | 
| 71 |  |  |  |  |  |  | 503 => 'Service Unavailable', | 
| 72 |  |  |  |  |  |  | 504 => 'Server Time-out', | 
| 73 |  |  |  |  |  |  | 505 => 'SIP Version not supported', | 
| 74 |  |  |  |  |  |  | 513 => 'Message Too Large', | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | # Global-Failure | 
| 77 |  |  |  |  |  |  | 600 => 'Busy Everywhere', | 
| 78 |  |  |  |  |  |  | 603 => 'Decline', | 
| 79 |  |  |  |  |  |  | 604 => 'Does not exist anywhere', | 
| 80 |  |  |  |  |  |  | 606 => 'Not Acceptable', | 
| 81 |  |  |  |  |  |  | ); | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | ########################################################################### | 
| 84 |  |  |  |  |  |  | # Redefine methods from Net::SIP::Packet, no need to find out dynamically | 
| 85 |  |  |  |  |  |  | ########################################################################### | 
| 86 | 528 |  |  | 528 | 1 | 1714 | sub is_request  {1} | 
| 87 | 334 |  |  | 334 | 1 | 1710 | sub is_response {0} | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | ########################################################################### | 
| 90 |  |  |  |  |  |  | # Accessors for method and URI | 
| 91 |  |  |  |  |  |  | ########################################################################### | 
| 92 | 754 |  |  | 754 | 1 | 2167 | sub method      { return (shift->as_parts())[0] } | 
| 93 | 184 |  |  | 184 | 1 | 567 | sub uri         { return (shift->as_parts())[1] } | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | sub set_uri { | 
| 96 | 30 |  |  | 30 | 1 | 69 | my Net::SIP::Request $self = shift; | 
| 97 | 30 |  |  |  |  | 117 | $self->_update_string; | 
| 98 | 30 |  |  |  |  | 80 | $self->{text} = shift; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | ########################################################################### | 
| 102 |  |  |  |  |  |  | # set cseq | 
| 103 |  |  |  |  |  |  | # Args: ($self,$number) | 
| 104 |  |  |  |  |  |  | #   $number: new cseq number | 
| 105 |  |  |  |  |  |  | # Returns: $self | 
| 106 |  |  |  |  |  |  | ########################################################################### | 
| 107 |  |  |  |  |  |  | sub set_cseq { | 
| 108 | 0 |  |  | 0 | 1 | 0 | my Net::SIP::Request $self = shift; | 
| 109 | 0 |  |  |  |  | 0 | my $cseq = shift; | 
| 110 | 0 |  |  |  |  | 0 | $self->set_header( cseq => "$cseq ".$self->method ); | 
| 111 | 0 |  |  |  |  | 0 | return $self; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | ########################################################################### | 
| 115 |  |  |  |  |  |  | # create ack to response based on original request | 
| 116 |  |  |  |  |  |  | # see RFC3261 "17.1.1.3 Construction of the ACK Request" | 
| 117 |  |  |  |  |  |  | # Args: ($self,$response) | 
| 118 |  |  |  |  |  |  | #  $response: Net::SIP::Response object for request $self | 
| 119 |  |  |  |  |  |  | # Returns: $cancel | 
| 120 |  |  |  |  |  |  | #  $ack: Net::SIP::Request object for ACK method | 
| 121 |  |  |  |  |  |  | ########################################################################### | 
| 122 |  |  |  |  |  |  | sub create_ack { | 
| 123 | 39 |  |  | 39 | 1 | 129 | my Net::SIP::Request $self = shift; | 
| 124 | 39 |  |  |  |  | 79 | my $response = shift; | 
| 125 |  |  |  |  |  |  | # ACK uses cseq from request | 
| 126 | 39 |  |  |  |  | 213 | $self->cseq =~m{(\d+)}; | 
| 127 | 39 |  |  |  |  | 194 | my $cseq = "$1 ACK"; | 
| 128 | 39 |  |  |  |  | 77 | my %auth; | 
| 129 | 39 |  |  |  |  | 113 | for (qw(authorization proxy-authorization)) { | 
| 130 | 78 | 100 |  |  |  | 212 | my $v = scalar($self->get_header($_)) or next; | 
| 131 | 2 |  |  |  |  | 16 | $auth{$_} = $v; | 
| 132 |  |  |  |  |  |  | } | 
| 133 | 39 |  |  |  |  | 115 | my $header = { | 
| 134 |  |  |  |  |  |  | 'call-id' => scalar($self->get_header('call-id')), | 
| 135 |  |  |  |  |  |  | from      => scalar($self->get_header('from')), | 
| 136 |  |  |  |  |  |  | # unlike CANCEL the 'to' header is from the response | 
| 137 |  |  |  |  |  |  | to        => [ $response->get_header('to') ], | 
| 138 |  |  |  |  |  |  | via       => [ ($self->get_header( 'via' ))[0] ], | 
| 139 |  |  |  |  |  |  | route     => [ $self->get_header( 'route' ) ], | 
| 140 |  |  |  |  |  |  | cseq      => $cseq, | 
| 141 |  |  |  |  |  |  | %auth, | 
| 142 |  |  |  |  |  |  | }; | 
| 143 | 39 |  |  |  |  | 352 | return Net::SIP::Request->new( 'ACK',$self->uri,$header ); | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | ########################################################################### | 
| 147 |  |  |  |  |  |  | # Create cancel for request | 
| 148 |  |  |  |  |  |  | # Args: $self | 
| 149 |  |  |  |  |  |  | # Returns: $cancel | 
| 150 |  |  |  |  |  |  | #   $cancel: Net::SIP::Request containing CANCEL for $self | 
| 151 |  |  |  |  |  |  | ########################################################################### | 
| 152 |  |  |  |  |  |  | sub create_cancel { | 
| 153 | 6 |  |  | 6 | 1 | 49 | my Net::SIP::Request $self = shift; | 
| 154 |  |  |  |  |  |  | # CANCEL uses cseq from request | 
| 155 | 6 |  |  |  |  | 54 | $self->cseq =~m{(\d+)}; | 
| 156 | 6 |  |  |  |  | 26 | my $cseq = "$1 CANCEL"; | 
| 157 | 6 |  |  |  |  | 12 | my %auth; | 
| 158 | 6 |  |  |  |  | 13 | for (qw(authorization proxy-authorization)) { | 
| 159 | 12 | 50 |  |  |  | 30 | my $v = scalar($self->get_header($_)) or next; | 
| 160 | 0 |  |  |  |  | 0 | $auth{$_} = $v; | 
| 161 |  |  |  |  |  |  | } | 
| 162 | 6 |  |  |  |  | 21 | my $header = { | 
| 163 |  |  |  |  |  |  | 'call-id' => scalar($self->get_header('call-id')), | 
| 164 |  |  |  |  |  |  | from      => scalar($self->get_header('from')), | 
| 165 |  |  |  |  |  |  | # unlike ACK the 'to' header is from the original request | 
| 166 |  |  |  |  |  |  | to        => [ $self->get_header('to') ], | 
| 167 |  |  |  |  |  |  | via       => [ ($self->get_header( 'via' ))[0] ], | 
| 168 |  |  |  |  |  |  | route     => [ $self->get_header( 'route' ) ], | 
| 169 |  |  |  |  |  |  | cseq      => $cseq, | 
| 170 |  |  |  |  |  |  | %auth | 
| 171 |  |  |  |  |  |  | }; | 
| 172 | 6 |  |  |  |  | 30 | return Net::SIP::Request->new( 'CANCEL',$self->uri,$header ); | 
| 173 |  |  |  |  |  |  | } | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | ########################################################################### | 
| 176 |  |  |  |  |  |  | # Create response to request | 
| 177 |  |  |  |  |  |  | # Args: ($self,$code,[$msg],[$args,$body]) | 
| 178 |  |  |  |  |  |  | #   $code: numerical response code | 
| 179 |  |  |  |  |  |  | #   $msg: msg for code, if arg not given it will be used from %ResponseCode | 
| 180 |  |  |  |  |  |  | #   $args: additional args for SIP header | 
| 181 |  |  |  |  |  |  | #   $body: body as string | 
| 182 |  |  |  |  |  |  | # Returns: $response | 
| 183 |  |  |  |  |  |  | #   $response: Net::SIP::Response | 
| 184 |  |  |  |  |  |  | ########################################################################### | 
| 185 |  |  |  |  |  |  | sub create_response { | 
| 186 | 70 |  |  | 70 | 1 | 13306 | my Net::SIP::Request $self = shift; | 
| 187 | 70 |  |  |  |  | 287 | my $code = shift; | 
| 188 | 70 | 50 | 66 |  |  | 654 | my ($msg,$args,$body) = ( defined $_[0] && ref($_[0]) ) ? (undef,@_):@_; | 
| 189 | 70 | 100 |  |  |  | 3823 | $msg = $ResponseCode{$code} if ! defined $msg; | 
| 190 | 70 | 100 |  |  |  | 324 | my %header = ( | 
| 191 |  |  |  |  |  |  | cseq      => scalar($self->get_header('cseq')), | 
| 192 |  |  |  |  |  |  | 'call-id' => scalar($self->get_header('call-id')), | 
| 193 |  |  |  |  |  |  | from      => scalar($self->get_header('from')), | 
| 194 |  |  |  |  |  |  | to        => [ $self->get_header('to') ], | 
| 195 |  |  |  |  |  |  | 'record-route'  => [ $self->get_header( 'record-route' ) ], | 
| 196 |  |  |  |  |  |  | via       => [ $self->get_header( 'via' ) ], | 
| 197 |  |  |  |  |  |  | $args ? %$args : () | 
| 198 |  |  |  |  |  |  | ); | 
| 199 | 70 |  |  |  |  | 716 | return Net::SIP::Response->new($code,$msg,\%header,$body); | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | ########################################################################### | 
| 204 |  |  |  |  |  |  | # Authorize Request based on credentials in response using | 
| 205 |  |  |  |  |  |  | # Digest Authorization specified in RFC2617 | 
| 206 |  |  |  |  |  |  | # Args: ($self,$response,@args) | 
| 207 |  |  |  |  |  |  | #   $response: Net::SIP::Response for $self which has code 401 or 407 | 
| 208 |  |  |  |  |  |  | #   @args: either [ $user,$pass ] if there is one user+pass for all realms | 
| 209 |  |  |  |  |  |  | #       or { realm1 => [ $user,$pass ], realm2 => [...].. } | 
| 210 |  |  |  |  |  |  | #       for different user,pass in different realms | 
| 211 |  |  |  |  |  |  | #       or callback(realm)->[ user,pass ] | 
| 212 |  |  |  |  |  |  | # Returns:  0|1 | 
| 213 |  |  |  |  |  |  | #    1: if (proxy-)=authorization headers were added to $self | 
| 214 |  |  |  |  |  |  | #    0: if $self was not modified, e.g. no usable authenticate | 
| 215 |  |  |  |  |  |  | #       headers were found | 
| 216 |  |  |  |  |  |  | ########################################################################### | 
| 217 |  |  |  |  |  |  | sub authorize { | 
| 218 | 3 |  |  | 3 | 1 | 9 | my Net::SIP::Request $self = shift; | 
| 219 | 3 |  |  |  |  | 7 | my ($response,$user2pass) = @_; | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | # find out format of user2pass | 
| 222 | 3 |  |  |  |  | 8 | my ($default_upw,$realm2upw,$cb_upw); | 
| 223 | 3 | 50 | 33 |  |  | 30 | if ( ref($user2pass) eq 'ARRAY' && ! ref( $user2pass->[0] )) { | 
|  |  | 0 |  |  |  |  |  | 
| 224 | 3 |  |  |  |  | 7 | $default_upw = $user2pass; | 
| 225 |  |  |  |  |  |  | } elsif ( ref($user2pass) eq 'HASH' ) { | 
| 226 | 0 |  |  |  |  | 0 | $realm2upw = %$user2pass; | 
| 227 |  |  |  |  |  |  | } else { | 
| 228 | 0 |  |  |  |  | 0 | $cb_upw = $user2pass; | 
| 229 |  |  |  |  |  |  | } | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  |  | 
| 232 | 3 |  |  |  |  | 3 | my $auth = 0; | 
| 233 | 3 |  |  |  |  | 32 | my %auth_map = ( | 
| 234 |  |  |  |  |  |  | 'proxy-authenticate' => 'proxy-authorization', | 
| 235 |  |  |  |  |  |  | 'www-authenticate' => 'authorization', | 
| 236 |  |  |  |  |  |  | ); | 
| 237 |  |  |  |  |  |  |  | 
| 238 | 3 |  |  |  |  | 27 | while ( my ($req,$resp) = each %auth_map ) { | 
| 239 | 6 |  |  |  |  | 9 | my $existing_auth; | 
| 240 | 6 | 100 |  |  |  | 28 | if ( my @auth = $response->get_header_hashval( $req ) ) { | 
| 241 | 3 |  |  |  |  | 7 | foreach my $a (@auth) { | 
| 242 | 3 |  |  |  |  | 6 | my $h = $a->{parameter}; | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | # check if we already have an authorize header for this realm/opaque | 
| 245 | 3 | 50 |  |  |  | 9 | if ( ! $existing_auth ) { | 
| 246 | 3 |  |  |  |  | 6 | $existing_auth = {}; | 
| 247 | 3 |  |  |  |  | 13 | foreach my $hdr ( $self->get_header_hashval( $resp )) { | 
| 248 | 0 |  |  |  |  | 0 | my @auth = grep { defined } map { $hdr->{parameter}{$_} }qw( realm opaque ); | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 249 | 0 |  |  |  |  | 0 | $existing_auth->{ join( "\0",@auth ) } = 1; | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 3 |  |  |  |  | 7 | my @auth = grep { defined } map { $h->{$_} }qw( realm opaque ); | 
|  | 6 |  |  |  |  | 14 |  | 
|  | 6 |  |  |  |  | 14 |  | 
| 254 | 3 | 50 |  |  |  | 55 | if ( $existing_auth->{ join( "\0",@auth ) } ) { | 
| 255 |  |  |  |  |  |  | # we have this auth header already, don't repeat | 
| 256 | 0 |  |  |  |  | 0 | next; | 
| 257 |  |  |  |  |  |  | } | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | # RFC2617 | 
| 260 |  |  |  |  |  |  | # we support only md5 (not md5-sess or other) | 
| 261 |  |  |  |  |  |  | # and only empty qop or qop=auth (not auth-int or other) | 
| 262 |  |  |  |  |  |  |  | 
| 263 | 3 | 50 | 33 |  |  | 46 | if ( lc($a->{data}) ne 'digest' | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 264 |  |  |  |  |  |  | || $h->{algorithm} && lc($h->{algorithm}) ne 'md5' | 
| 265 |  |  |  |  |  |  | || $h->{qop} && $h->{qop} !~ m{(?:^|,\s*)auth(?:$|,)}i ) { | 
| 266 | 44 |  |  | 44 |  | 570 | no warnings; | 
|  | 44 |  |  |  |  | 144 |  | 
|  | 44 |  |  |  |  | 29631 |  | 
| 267 | 0 |  |  |  |  | 0 | DEBUG(10,"unsupported authorization method $a->{data} method=$h->{method} qop=$h->{qop}"); | 
| 268 | 0 |  |  |  |  | 0 | next; | 
| 269 |  |  |  |  |  |  | } | 
| 270 | 3 |  |  |  |  | 8 | my $realm = $h->{realm}; | 
| 271 |  |  |  |  |  |  | my $upw = | 
| 272 |  |  |  |  |  |  | $cb_upw      ? invoke_callback( $cb_upw, $realm ) : | 
| 273 | 3 | 50 |  |  |  | 11 | $realm2upw   ? $realm2upw->{$realm} : | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | $default_upw ? $default_upw : | 
| 275 |  |  |  |  |  |  | next; | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | # for meaning of a1,a2... and for the full algorithm see RFC2617, 3.2.2 | 
| 278 | 3 |  |  |  |  | 11 | my $a1 = join(':',$upw->[0],$realm,$upw->[1] ); # 3.2.2.2 | 
| 279 | 3 |  |  |  |  | 10 | my $a2 = join(':',$self->method,$self->uri );   # 3.2.2.3, qop == auth|undef | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | my %digest = ( | 
| 282 |  |  |  |  |  |  | username => $upw->[0], | 
| 283 |  |  |  |  |  |  | realm => $realm, | 
| 284 |  |  |  |  |  |  | nonce => $h->{nonce}, | 
| 285 | 3 |  |  |  |  | 11 | uri => $self->uri, | 
| 286 |  |  |  |  |  |  | ); | 
| 287 | 3 | 50 |  |  |  | 12 | $digest{opaque} = $h->{opaque} if defined $h->{opaque}; | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | # 3.2.2.1 | 
| 290 | 3 | 50 |  |  |  | 9 | if ( $h->{qop} ) { | 
| 291 | 0 |  |  |  |  | 0 | $h->{qop} = 'auth'; # in case it was 'auth,auth-int' | 
| 292 | 0 |  |  |  |  | 0 | my $nc = $digest{nc} = '00000001'; | 
| 293 | 0 |  |  |  |  | 0 | my $cnonce = $digest{cnonce} = sprintf("%08x",rand(2**32)); | 
| 294 | 0 |  |  |  |  | 0 | $digest{qop} = $h->{qop}; | 
| 295 |  |  |  |  |  |  | $digest{response} = md5_hex( join(':', | 
| 296 |  |  |  |  |  |  | md5_hex($a1), | 
| 297 |  |  |  |  |  |  | $h->{nonce}, | 
| 298 |  |  |  |  |  |  | $nc, | 
| 299 |  |  |  |  |  |  | $cnonce, | 
| 300 |  |  |  |  |  |  | $h->{qop}, | 
| 301 | 0 |  |  |  |  | 0 | md5_hex($a2) | 
| 302 |  |  |  |  |  |  | )); | 
| 303 |  |  |  |  |  |  | } else { | 
| 304 |  |  |  |  |  |  | # 3.2.2.1 compability with RFC2069 | 
| 305 |  |  |  |  |  |  | $digest{response} = md5_hex( join(':', | 
| 306 |  |  |  |  |  |  | md5_hex($a1), | 
| 307 |  |  |  |  |  |  | $h->{nonce}, | 
| 308 | 3 |  |  |  |  | 32 | md5_hex($a2), | 
| 309 |  |  |  |  |  |  | )); | 
| 310 |  |  |  |  |  |  | } | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | # RFC2617 has it's specific ideas what should be quoted and what not | 
| 313 |  |  |  |  |  |  | # so we assemble it manually | 
| 314 | 3 |  |  |  |  | 20 | my $header = qq[Digest username="$digest{username}",realm="$digest{realm}",]. | 
| 315 |  |  |  |  |  |  | qq[nonce="$digest{nonce}",uri="$digest{uri}",response="$digest{response}"]; | 
| 316 | 3 | 50 |  |  |  | 8 | $header.= qq[,opaque="$digest{opaque}"] if defined $digest{opaque}; | 
| 317 | 3 | 50 |  |  |  | 10 | $header.= qq[,cnonce="$digest{cnonce}"] if defined $digest{cnonce}; | 
| 318 | 3 | 50 |  |  |  | 7 | $header.= qq[,qop=$digest{qop}] if defined $digest{qop}; | 
| 319 | 3 | 50 |  |  |  | 8 | $header.= qq[,nc=$digest{nc}] if defined $digest{nc}; | 
| 320 |  |  |  |  |  |  | # Echo back the algorithm if specifically set in response | 
| 321 | 3 | 50 |  |  |  | 13 | $header.= qq[,algorithm=$h->{algorithm}] if defined $h->{algorithm}; | 
| 322 | 3 |  |  |  |  | 16 | $self->add_header( $resp, $header ); | 
| 323 | 3 |  |  |  |  | 23 | $auth++; | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 | 3 | 50 |  |  |  | 9 | return if !$auth; # no usable authenticate headers found | 
| 329 |  |  |  |  |  |  |  | 
| 330 | 3 |  |  |  |  | 10 | my ($rseq) = $response->cseq =~m{^(\d+)}; | 
| 331 | 3 |  |  |  |  | 11 | $self->cseq =~m{^(\d+)(.*)}; | 
| 332 | 3 | 50 | 33 |  |  | 21 | if ( defined $1 and $1 <= $rseq ) { | 
| 333 |  |  |  |  |  |  | # increase cseq, because this will be a new request, not a retransmit | 
| 334 | 3 |  |  |  |  | 61 | $self->set_header( cseq => ($rseq+1).$2 ); | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 3 |  |  |  |  | 15 | return 1; | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | 1; |