cog/AudioScrobbler/AudioScrobblerClient.m

190 lines
4.8 KiB
Objective-C

/*
* $Id: AudioScrobblerClient.m 362 2007-02-13 05:30:49Z stephen_booth $
*
* Copyright (C) 2006 - 2007 Stephen F. Booth <me@sbooth.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This is a port of the BlockingClient client class from
* the Last.fm ScrobSub library by sharevari
*/
#import "AudioScrobblerClient.h"
#define kBufferSize 1024
#define kPortsToStep 5
static in_addr_t
addressForHost(NSString *hostname)
{
NSCParameterAssert(nil != hostname);
in_addr_t address;
struct hostent *hostinfo;
address = inet_addr([hostname cStringUsingEncoding:NSASCIIStringEncoding]);
if(INADDR_NONE == address) {
hostinfo = gethostbyname([hostname cStringUsingEncoding:NSASCIIStringEncoding]);
if(NULL == hostinfo) {
NSLog(@"Unable to resolve address for \"%@\".", hostname);
return INADDR_NONE;
}
address = *((in_addr_t *)hostinfo->h_addr_list[0]);
}
return address;
}
@interface AudioScrobblerClient (Private)
- (void) connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port;
@end
@implementation AudioScrobblerClient
- (id) init
{
if((self = [super init])) {
_socket = -1;
_lastPort = -1;
_doPortStepping = YES;
}
return self;
}
- (in_port_t) connectToHost:(NSString *)hostname port:(in_port_t)port
{
in_addr_t remoteAddress = addressForHost(hostname);
[self connectToSocket:remoteAddress port:port];
return _lastPort;
}
- (void) send:(NSString *)data
{
const char *utf8data = [data UTF8String];
unsigned len = strlen(utf8data);
unsigned bytesToSend = len;
unsigned totalBytesSent = 0;
ssize_t bytesSent = 0;
while(totalBytesSent < bytesToSend && -1 != bytesSent) {
bytesSent = send(_socket, utf8data + totalBytesSent, bytesToSend - totalBytesSent, 0);
if(-1 == bytesSent || 0 == bytesSent) {
NSLog(@"Unable to send data through socket");
}
totalBytesSent += bytesSent;
}
}
- (NSString *) receive
{
char buffer [ kBufferSize ];
int readSize = kBufferSize - 1;
ssize_t bytesRead = 0;
BOOL keepGoing = YES;
NSString *result = nil;
do {
bytesRead = recv(_socket, buffer, readSize, 0);
NSAssert1(-1 != bytesRead && 0 < bytesRead, @"Unable to receive data through socket (%s).", strerror(errno));
if('\n' == buffer[bytesRead - 1]) {
--bytesRead;
keepGoing = NO;
}
buffer[bytesRead] = '\0';
result = [[NSString alloc] initWithUTF8String:buffer];
} while(keepGoing);
return [result autorelease];
}
- (void) shutdown
{
int result;
char buffer [ kBufferSize ];
ssize_t bytesRead;
if(-1 == _socket) {
return;
}
result = shutdown(_socket, SHUT_WR);
NSAssert1(-1 != result, @"Socket shutdown failed (%s).", strerror(errno));
for(;;) {
bytesRead = recv(_socket, buffer, kBufferSize, 0);
NSAssert1(-1 != bytesRead, @"Waiting for shutdown confirmation failed (%s).", strerror(errno));
if(0 != bytesRead) {
NSLog(@"Received unexpected bytes during shutdown: %@.", [[[NSString alloc] initWithBytes:buffer length:bytesRead encoding:NSUTF8StringEncoding] autorelease]);
}
else {
break;
}
}
result = close(_socket);
NSAssert1(-1 != result, @"Couldn't close socket (%s).", strerror(errno));
_socket = -1;
}
@end
@implementation AudioScrobblerClient (Private)
- (void) connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port
{
struct sockaddr_in socketAddress;
int result;
_socket = socket(AF_INET, SOCK_STREAM, 0);
NSAssert1(-1 != _socket, @"Unable to create socket (%s).", strerror(errno));
_lastPort = port;
socketAddress.sin_family = AF_INET;
socketAddress.sin_addr.s_addr = remoteAddress;
socketAddress.sin_port = htons(_lastPort);
result = connect(_socket, (const struct sockaddr *)&socketAddress, sizeof(struct sockaddr_in));
if(_doPortStepping) {
while(-1 == result && _lastPort <= (port + kPortsToStep)) {
socketAddress.sin_port = htons(++_lastPort);
result = connect(_socket, (const struct sockaddr *)&socketAddress, sizeof(struct sockaddr_in));
}
}
if(-1 == result) {
_doPortStepping = NO;
close(_socket);
_socket = -1;
NSAssert1(-1 != result, @"Couldn't connect to server (%s).", strerror(errno));
}
}
@end