Merge branch 'master' of git://github.com/ksuther/MenuTunes
[MenuTunes.git] / AudioscrobblerController.m
index 47b4a8b..ee0fad5 100644 (file)
@@ -1,16 +1,3 @@
-/*
- *     MenuTunes
- *  AudioscrobblerController
- *    Audioscrobbler Support Class
- *
- *  Original Author : Kent Sutherland <kent.sutherland@ithinksw.com>
- *   Responsibility : Kent Sutherland <kent.sutherland@ithinksw.com>
- *
- *  Copyright (c) 2005 iThink Software.
- *  All Rights Reserved
- *
- */
-
 #import "AudioscrobblerController.h"
 #import "PreferencesController.h"
 #import <openssl/evp.h>
@@ -43,7 +30,7 @@ static AudioscrobblerController *_sharedController = nil;
                _postURL = [NSURL URLWithString:@"http://audioscrobbler.com/"];*/
                
                _delayDate = [[NSDate date] retain];
-               _responseData = nil;
+               _responseData = [[NSMutableData alloc] init];
                _tracks = [[NSMutableArray alloc] init];
                _submitTracks = [[NSMutableArray alloc] init];
                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAudioscrobblerNotification:) name:@"AudioscrobblerHandshakeComplete" object:self];
@@ -79,6 +66,26 @@ static AudioscrobblerController *_sharedController = nil;
                return;
        }
        
+       //If we've already tried to handshake three times in a row unsuccessfully, set the attempt count to -3
+       if (_handshakeAttempts > 3) {
+               ITDebugLog(@"Audioscrobbler: Maximum handshake limit reached (3). Retrying when handshake attempts reach zero.");
+               _handshakeAttempts = -3;
+               
+               //Remove any tracks we were trying to submit, just to be safe
+               [_submitTracks removeAllObjects];
+               
+               return;
+       }
+       
+       //Increment the number of times we've tried to handshake
+       _handshakeAttempts++;
+       
+       //We're still on our self-imposed cooldown time.
+       if (_handshakeAttempts < 0) {
+               ITDebugLog(@"Audioscrobbler: Handshake timeout. Retrying when handshake attempts reach zero.");
+               return;
+       }
+       
        //Delay if we haven't met the interval time limit
        NSTimeInterval interval = [_delayDate timeIntervalSinceNow];
        if (interval > 0) {
@@ -91,8 +98,12 @@ static AudioscrobblerController *_sharedController = nil;
        if (!_handshakeCompleted && user) {
                NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://post.audioscrobbler.com/?hs=true&p=1.1&c=%@&v=%@&u=%@", AUDIOSCROBBLER_ID, AUDIOSCROBBLER_VERSION, user]];
                
+               [_lastStatus release];
+               _lastStatus = [NSLocalizedString(@"audioscrobbler_handshaking", @"Attempting to handshake with server") retain];
+               [[NSNotificationCenter defaultCenter] postNotificationName:@"AudioscrobblerStatusChanged" object:nil userInfo:[NSDictionary dictionaryWithObject:_lastStatus forKey:@"StatusString"]];
+               
                _currentStatus = AudioscrobblerRequestingHandshakeStatus;
-               _responseData = [[NSMutableData alloc] init];
+               //_responseData = [[NSMutableData alloc] init];
                [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15] delegate:self];
        }
 }
@@ -127,6 +138,13 @@ static AudioscrobblerController *_sharedController = nil;
                return;
        }
        
+       ITDebugLog(@"Audioscrobbler: Submitting queued tracks");
+       
+       if ([_tracks count] == 0) {
+               ITDebugLog(@"Audioscrobbler: No queued tracks to submit.");
+               return;
+       }
+       
        NSString *user = [[NSUserDefaults standardUserDefaults] stringForKey:@"audioscrobblerUser"], *passString = [PreferencesController getKeychainItemPasswordForUser:user];
        char *pass = (char *)[passString UTF8String];
        
@@ -148,13 +166,6 @@ static AudioscrobblerController *_sharedController = nil;
        unsigned char *buffer;
        EVP_MD_CTX ctx;
        
-       ITDebugLog(@"Audioscrobbler: Submitting queued tracks");
-       
-       if ([_tracks count] == 0) {
-               ITDebugLog(@"Audioscrobbler: No queued tracks to submit.");
-               return;
-       }
-       
        //Build the MD5 response string we send along with the request
        buffer = malloc(EVP_MD_size(EVP_md5()));
        EVP_DigestInit(&ctx, EVP_md5());
@@ -210,14 +221,17 @@ static AudioscrobblerController *_sharedController = nil;
        }
        
        ITDebugLog(@"Audioscrobbler: Sending track submission request");
+       [_lastStatus release];
+       _lastStatus = [NSLocalizedString(@"audioscrobbler_submitting", @"Submitting tracks to server") retain];
+       [[NSNotificationCenter defaultCenter] postNotificationName:@"AudioscrobblerStatusChanged" object:nil userInfo:[NSDictionary dictionaryWithObject:_lastStatus forKey:@"StatusString"]];
        
        //Create and send the request
        NSMutableURLRequest *request = [[NSURLRequest requestWithURL:_postURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15] mutableCopy];
-       NSLog(@"Posting Audioscrobbler URL request: %@", requestString);
        [request setHTTPMethod:@"POST"];
        [request setHTTPBody:[requestString dataUsingEncoding:NSUTF8StringEncoding]];
        _currentStatus = AudioscrobblerSubmittingTracksStatus;
-       _responseData = [[NSMutableData alloc] init];
+       //_responseData = [[NSMutableData alloc] init];
+       [_responseData setData:nil];
        [NSURLConnection connectionWithRequest:request delegate:self];
        [requestString release];
        [request release];
@@ -225,7 +239,7 @@ static AudioscrobblerController *_sharedController = nil;
        //For now we're not going to cache results, as it is less of a headache
        //[_tracks removeObjectsInArray:_submitTracks];
        [_tracks removeAllObjects];
-       [_submitTracks removeAllObjects];
+       //[_submitTracks removeAllObjects];
        
        //If we have tracks left, submit again after the interval seconds
 }
@@ -241,7 +255,7 @@ static AudioscrobblerController *_sharedController = nil;
 
 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
 {
-       [_responseData release];
+       [_responseData setData:nil];
        [_lastStatus release];
        _lastStatus = [[NSString stringWithFormat:NSLocalizedString(@"audioscrobbler_error", @"Error - %@"), [error localizedDescription]] retain];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"AudioscrobblerStatusChanged" object:self userInfo:[NSDictionary dictionaryWithObject:_lastStatus forKey:@"StatusString"]];
@@ -263,7 +277,6 @@ static AudioscrobblerController *_sharedController = nil;
                responseAction = [lines objectAtIndex:0];
        }
        ITDebugLog(@"Audioscrobbler: Response %@", string);
-       NSLog(@"Audioscrobbler: Response %@", string);
        if (_currentStatus == AudioscrobblerRequestingHandshakeStatus) {
                if ([lines count] < 2) {
                        //We have a protocol error
@@ -276,6 +289,7 @@ static AudioscrobblerController *_sharedController = nil;
                                [[NSNotificationCenter defaultCenter] postNotificationName:@"AudioscrobblerHandshakeComplete" object:self];
                                key = @"audioscrobbler_handshake_complete";
                                comment = @"Handshake complete";
+                               _handshakeAttempts = 0;
                        } else {
                                //We have a protocol error
                        }
@@ -289,6 +303,9 @@ static AudioscrobblerController *_sharedController = nil;
                        key = @"audioscrobbler_bad_user";
                        comment = @"Handshake failed - invalid user name";
                        //We have a bad user
+                       
+                       //Don't count this as a bad handshake attempt
+                       _handshakeAttempts = 0;
                } else {
                        ITDebugLog(@"Audioscrobbler: Handshake failed, protocol error");
                        key = @"audioscrobbler_protocol_error";
@@ -300,6 +317,7 @@ static AudioscrobblerController *_sharedController = nil;
                        ITDebugLog(@"Audioscrobbler: Submission successful, clearing queue.");
                        /*[_tracks removeObjectsInArray:_submitTracks];
                        [_submitTracks removeAllObjects];*/
+                       [_submitTracks removeAllObjects];
                        if ([_tracks count] > 0) {
                                ITDebugLog(@"Audioscrobbler: Tracks remaining in queue, submitting remaining tracks");
                                [self performSelector:@selector(submitTracks) withObject:nil afterDelay:2];
@@ -311,12 +329,24 @@ static AudioscrobblerController *_sharedController = nil;
                        key = @"audioscrobbler_bad_password";
                        comment = @"Last track submission failed - invalid password";
                        //Bad auth
+                       
+                       //Add the tracks we were trying to submit back into the submission queue
+                       [_tracks addObjectsFromArray:_submitTracks];
+                       
+                       _handshakeCompleted = NO;
+                       
+                       //If we were previously valid with the same login name, try reauthenticating and sending again
+                       [self attemptHandshake:YES];
                } else if (([responseAction length] > 5) && [[responseAction substringToIndex:5] isEqualToString:@"FAILED"]) {
                        ITDebugLog(@"Audioscrobbler: Submission failed (%@)", [responseAction substringFromIndex:6]);
-                       NSLog(@"Audioscrobbler: Submission failed (%@)", [responseAction substringFromIndex:6]);
                        key = @"audioscrobbler_submission_failed";
                        comment = @"Last track submission failed - see console for error";
                        //Failed
+                       
+                       //We got an unknown error. To be safe we're going to remove the tracks we tried to submit
+                       [_submitTracks removeAllObjects];
+                       
+                       _handshakeCompleted = NO;
                }
        }
        
@@ -334,7 +364,13 @@ static AudioscrobblerController *_sharedController = nil;
        _lastStatus = [NSLocalizedString(key, comment) retain];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"AudioscrobblerStatusChanged" object:nil userInfo:[NSDictionary dictionaryWithObject:_lastStatus forKey:@"StatusString"]];
        [string release];
-       [_responseData release];
+       [_responseData setData:nil];
+}
+
+-(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
+{
+       //Don't cache any Audioscrobbler communication
+       return nil;
 }
 
 @end