3 * AudioscrobblerController
4 * Audioscrobbler Support Class
6 * Original Author : Kent Sutherland <kent.sutherland@ithinksw.com>
7 * Responsibility : Kent Sutherland <kent.sutherland@ithinksw.com>
9 * Copyright (c) 2005 iThink Software.
14 #import "AudioscrobblerController.h"
15 #import <openssl/evp.h>
17 static AudioscrobblerController *_sharedController = nil;
19 @implementation AudioscrobblerController
23 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
24 [[AudioscrobblerController sharedController] submitTrack:@"Stairway To Heaven" artist:@"Led Zeppelin" album:@"Led Zeppelin IV" length:483];
28 + (AudioscrobblerController *)sharedController
30 if (!_sharedController) {
31 _sharedController = [[AudioscrobblerController alloc] init];
33 return _sharedController;
38 if ( (self = [super init]) ) {
39 _handshakeCompleted = NO;
43 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAudioscrobblerNotification:) name:nil object:self];
50 [_md5Challenge release];
52 [_responseData release];
56 - (void)attemptHandshake
58 NSString *version = [[[NSBundle bundleWithPath:[[NSWorkspace sharedWorkspace] fullPathForApplication:@"iTunes.app"]] infoDictionary] objectForKey:@"CFBundleVersion"], *user = @"Tristrex";
59 NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://post.audioscrobbler.com/?hs=true&p=1.1&c=tst&v=%@&u=%@", version, user]];
60 NSURLConnection *connection;
62 _currentStatus = AudioscrobblerRequestingHandshakeStatus;
63 _responseData = [[NSMutableData alloc] init];
64 connection = [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30] delegate:self];
67 - (BOOL)handshakeCompleted
69 return _handshakeCompleted;
72 - (void)submitTrack:(NSString *)title artist:(NSString *)artist album:(NSString *)album length:(int)length
74 if (!_handshakeCompleted) {
75 [self attemptHandshake];
79 //What we eventually want is a submission list that sends backlogs also
80 NSMutableURLRequest *request = [[NSURLRequest requestWithURL:_postURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30] mutableCopy];
81 NSString *responseHash = @"", *requestBody;
83 char *pass = "waffles";
84 unsigned char *buffer;
88 buffer = malloc(EVP_MD_size(EVP_md5()));
90 EVP_DigestInit(&ctx, EVP_md5());
91 EVP_DigestUpdate(&ctx, pass, strlen(pass));
92 EVP_DigestFinal(&ctx, buffer, NULL);
94 for (i = 0; i < 16; i++) {
95 responseHash = [responseHash stringByAppendingFormat:@"%0.2x", buffer[i]];
99 buffer = malloc(EVP_MD_size(EVP_md5()));
100 char *cat = (char *)[[responseHash stringByAppendingString:_md5Challenge] UTF8String];
101 EVP_DigestInit(&ctx, EVP_md5());
102 EVP_DigestUpdate(&ctx, cat, strlen(cat));
103 EVP_DigestFinal(&ctx, buffer, NULL);
106 for (i = 0; i < 16; i++) {
107 responseHash = [responseHash stringByAppendingFormat:@"%0.2x", buffer[i]];
111 requestBody = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)[NSString stringWithFormat:@"u=%@&s=%@&a[0]=%@&t[0]=%@&b[0]=%@&m[0]=&l[0]=%i&i[0]=%@", @"Tristrex", responseHash, artist, title, album, length, [[NSDate date] descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S" timeZone:nil locale:nil]], NULL, NULL, kCFStringEncodingUTF8);
112 [request setHTTPMethod:@"POST"];
113 [request setHTTPBody:[requestBody dataUsingEncoding:NSUTF8StringEncoding]];
114 _currentStatus = AudioscrobblerSubmittingTrackStatus;
115 _responseData = [[NSMutableData alloc] init];
116 [NSURLConnection connectionWithRequest:request delegate:self];
117 CFRelease(requestBody);
121 - (void)handleAudioscrobblerNotification:(NSNotification *)note
123 if ([[note name] isEqualToString:@"AudioscrobblerHandshakeComplete"]) {
124 [[AudioscrobblerController sharedController] submitTrack:@"Good Times Bad Times" artist:@"Led Zeppelin" album:@"Led Zeppelin I" length:166];
130 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
132 NSLog(@"Failed with an error: %@", error);
135 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
137 [_responseData appendData:data];
140 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
142 NSString *string = [[NSString alloc] initWithData:_responseData encoding:NSASCIIStringEncoding];
144 if (_currentStatus == AudioscrobblerRequestingHandshakeStatus) {
145 NSArray *lines = [string componentsSeparatedByString:@"\n"];
146 NSString *responseAction;
147 if ([lines count] < 2) {
150 responseAction = [lines objectAtIndex:0];
151 if ([responseAction isEqualToString:@"UPTODATE"]) {
152 if ([lines count] >= 4) {
153 _md5Challenge = [[lines objectAtIndex:1] retain];
154 _postURL = [[NSURL alloc] initWithString:[lines objectAtIndex:2]];
155 _handshakeCompleted = YES;
156 [[NSNotificationCenter defaultCenter] postNotificationName:@"AudioscrobblerHandshakeComplete" object:self];
161 } else if (([responseAction length] > 6) && [[responseAction substringToIndex:5] isEqualToString:@"UPDATE"]) {
162 //Something plus update action
163 } else if (([responseAction length] > 6) && [[responseAction substringToIndex:5] isEqualToString:@"FAILED"]) {
165 } else if ([responseAction isEqualToString:@"BADUSER"]) {
170 } else if (_currentStatus == AudioscrobblerSubmittingTrackStatus) {