iOS 4.x と、5.x では、NSURLProtocol の認証時に注意

iOS 4.x と、5.x では、NSURLProtocol SSL認証時 / BASIC認証時の実行メソッドが異なる。

iOS 4.x では、、、
  canAuthenticateAgainstProtectionSpace → didReceiveAuthenticationChallenge

iOS 5.x では、、、
  canAuthenticateAgainstProtectionSpace → willSendRequestForAuthenticationChallenge

であるので、両方のバージョンサポートは工夫が必要


#pragma mark == NSURLProtocolオブジェクトの初期化 ==
- (id)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id <NSURLProtocolClient>)client
{
    NSMutableURLRequest* aRequest = [ request mutableCopy ];
    [ aRequest setAllHTTPHeaderFields:[ request allHTTPHeaderFields]];
    [ aRequest setValue: kFenicsBrowserHeaderValueforHTTPHeaderField: kFenicsBrowserHeader ];
    self = [ super initWithRequest: aRequest cachedResponse:cachedResponse client: client ];
    if ( self )
    {
        self.protocolRequest = aRequest;
    }
    [ aRequest release ];
    aRequest = nil;
    returnself;
}

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    NSString* strVersion = [[UIDevice currentDevice] systemVersion];
    double version = [strVersion doubleValue];
    DLog(@"## OS Version  %1.2f", version);

    return YES;
}

#pragma -
#pragma mark *** 認証の必要なページにアクセスしたとき   iOS 4,x  **********************
- ( void ) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge

   self.myChallenge = challenge; 
   [self authProcess:challenge];
}


#pragma mark *** 認証の必要なページにアクセスしたとき   iOS 5.x   **********************
static BOOL isSSL_CONFIRM;
-( void ) connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    NSString* strVersion = [[UIDevice currentDevice] systemVersion];
    double version = [strVersion doubleValue];
    DLog(@"## OS Version  %1.2f", version);
 
    self.myChallenge = challenge; 
    [self authProcess:challenge];
}

 
#pragma mark -------------- 認証用プロセス OS共通 -----------------
-(void)authProcess:(NSURLAuthenticationChallenge *)challenge
{
    isSSL_CONFIRM = YES;
    if ([challenge.protectionSpace.authenticationMethodisEqualToString:NSURLAuthenticationMethodHTTPBasic] 
        || [challenge.protectionSpace.authenticationMethodisEqualToString:NSURLAuthenticationMethodHTTPDigest]) {
        // BASIC/DIGEST認証が必要
        isSSL_CONFIRM = NO;
          
        [selfperformSelectorOnMainThread:@selector(challengeBasic) withObject:nilwaitUntilDone:YES];
        return;
    }elseif([challenge.protectionSpace.authenticationMethodisEqualToString:NSURLAuthenticationMethodServerTrust]){
        // SSL認証時
        NSLog(@"# NSURLAuthentication  URL = %@", [[self.requestURL] absoluteString] );
        NSURLResponse *checkresponse;
        NSError *error = nil;
        [ NSURLConnectionsendSynchronousRequest:self.request
                               returningResponse:&checkresponse
                                           error:&error ];
        if (error==nil) {
            // 事前チェックでエラーなし
            [challenge.senderuseCredential:[NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust] 
                 forAuthenticationChallenge:challenge];
            [challenge.sendercontinueWithoutCredentialForAuthenticationChallenge:challenge];
        }else{
            NSLog(@"#  SSL  error code = %d", [error code]);
            if ([error code]==NSURLErrorServerCertificateHasUnknownRoot){
               [selfchallengSSL:[NSStringstringWithFormat:@"%@ %@",@"SSL認証に問題があります", @"Certificate has Unknown Root"]];
              //
              // SSL エラーなどを記述
              //

            }else if ([error code]==NSURLErrorTimedOut){
                // iOS 4.x は、UIWebView の didFailLoadWithError ではなく、ここでハンドルされる。

            }else{
               // 
            }
        }
    }
 
}
 
#pragma mark --- BASIC認証ダイアログ表示 ---
-(void)challengeBasic
{
    NSString* strVersion = [[UIDevice currentDevice] systemVersion];
    double version = [strVersion doubleValue];
 
    if (version >= 5.0) {
        // iOS 5.0 ~   
        UIAlertView *dialog = [[UIAlertViewalloc] initWithTitle:@"認証が必要です"
                                                         message:@""
                                                        delegate:self 
                                               cancelButtonTitle:@"cancel"
                                               otherButtonTitles:@"OK", nil ];
        [dialog setAlertViewStyle: UIAlertViewStyleLoginAndPasswordInput];
        [dialog show];
        [dialog autorelease];
    }else {
        // iOS 4.x は、UIAlertViewStyleLoginAndPasswordInput が使えないので
        // 上とは別の方法でダイアログを表示する必要がある。

       
    }
    
}

#pragma mark --- SSL警告ダイアログ ------
-(void)challengSSL:(NSString*)message
{
    UIAlertView *dialog = [[UIAlertViewalloc] initWithTitle:nil
                                                    message:message
                                                   delegate:self 
                                          cancelButtonTitle:@"cancel"
                                          otherButtonTitles:@"done", nil ];
    [dialog show];
    [dialog autorelease];
}


#pragma mark ==== ダイアログボタン押したインデックス====

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSString* strVersion = [[UIDevice currentDevice] systemVersion];
    double version = [strVersion doubleValue];
    if (isSSL_CONFIRM){
        switch (buttonIndex) {
            case 0:  // キャンセル
                [[self.myChallengesender] cancelAuthenticationChallenge:self.myChallenge];
                if (version >= 5.0) {
                    [ self.protocolConnection cancel ];
                }
                break;
            case 1:  // GO
                [self.myChallenge.senderuseCredential:[NSURLCredentialcredentialForTrust:self.myChallenge.protectionSpace.serverTrust] 
                         forAuthenticationChallenge:self.myChallenge];
                [[self.myChallengesender] continueWithoutCredentialForAuthenticationChallenge:self.myChallenge];
                break;
        }
    }else {
        switch (buttonIndex) {
            case 0:  // キャンセル
                [[self.myChallengesender] cancelAuthenticationChallenge:self.myChallenge];
                [ self.protocolConnection cancel ];
                break;
            case 1:  // GO
                ;
                UITextField *username = [alertView textFieldAtIndex:0];
                UITextField *password = [alertView textFieldAtIndex:1];           
                NSURLCredential* credential = [NSURLCredentialcredentialWithUser:username.textpassword:password.textpersistence:NSURLCredentialPersistenceForSession];
                [[self.myChallengesender] useCredential:credential forAuthenticationChallenge:self.myChallenge];
                // webview で、stopLoading すべき!!
                break;
        }
    }
}


#pragma mark ----- リダイレクトのハンドリング -----
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
{
   NSMutableURLRequest* mrequest = [[request mutableCopy] autorelease];
   if (redirectResponse){
      NSLog(@"# Redirect  URL = %@", [request.URL absoluteString]);
      [ mrequest setValue:nilforHTTPHeaderField:kFenicsBrowserHeader];
      [[selfclient] URLProtocol:selfwasRedirectedToRequest: mrequest redirectResponse:redirectResponse];
      self.protocolRequest  = mrequest;
      self.protocolResponse = ( NSHTTPURLResponse* )redirectResponse;
      return mrequest;
   }
   self.protocolRequest  = mrequest;
   self.protocolResponse = ( NSHTTPURLResponse* )redirectResponse;
   return aRequest;
}

#pragma mark *** 通信エラーが発生したとき ***
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    [[selfclient] URLProtocol:selfdidFailWithError:error];
    self.protocolConnection = nil;
}


- ( void ) notifyResponse

    NSHTTPURLResponse* res = [ [ self.protocolResponse copy ] autorelease ];
    NSMutableDictionary* dic = [ NSMutableDictionary  dictionaryWithObject: res  forKey: @"HTTP_RESPONSE" ];
    NSNotification* n = [ NSNotificationnotificationWithName: kHttpResponseReceivedobject: selfuserInfo: dic ];
    [ [ NSNotificationCenterdefaultCenter ] postNotification: n ];
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}

- (NSString *)cachePathForRequest:(NSURLRequest *)aRequest
{
    NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    return [cachesPath stringByAppendingPathComponent:[NSStringstringWithFormat:@"%x", [[[aRequest URL] absoluteString] hash]]];
}