GeckoReporter/Classes/Framework/MGMSender.m

386 lines
20 KiB
Mathematica
Raw Normal View History

2010-01-06 18:11:16 -06:00
//
// MGMSender.h
// GeckoReporter
//
// Created by Mr. Gecko on 12/28/09.
// Copyright 2010 by Mr. Gecko's Media (James Coleman). All rights reserved. http://mrgeckosmedia.com/
//
#import "MGMSender.h"
#import "MGMReporter.h"
#import "MGMSystemInfo.h"
#import "MGMLocalized.h"
#import "MGMLog.h"
NSString * const MGMDefaultTimeZone = @"GMT";
NSString * const MGMDefaultTimeFormat = @"%a, %m/%d/%y %H:%M:%S %Z";
NSString * const MGMDefaultCrashEmail = @"crashreports@mrgeckosmedia.com";
NSString * const MGMDefaultBugsEmail = @"bugs@mrgeckosmedia.com";
NSString * const MGMDefaultContactEmail = @"support@mrgeckosmedia.com";
NSString * const MGMDefaultURL = @"https://mrgeckosmedia.com/sendreport.php";
NSString * const MGMGRTimeZone = @"MGMGRTimeZone";
NSString * const MGMGRTimeFormat = @"MGMGRTimeFormat";
NSString * const MGMGRReportFileAttached = @"MGMGRReportFileAttached";
NSString * const MGMGRCrashEmail = @"MGMGRCrashEmail";
NSString * const MGMGRBugsEmail = @"MGMGRBugsEmail";
NSString * const MGMGRContactEmail = @"MGMGRContactEmail";
NSString * const MGMGRURL = @"MGMGRURL";
NSString * const MGMGRLogFiles = @"MGMGRLogFiles";
@interface MGMSender (MGMPrivate)
- (NSData *)buildBodyWithObjects:(NSDictionary *)theObjects boundary:(NSString *)theBoundary;
- (NSDictionary *)defaultObjects;
@end
@implementation MGMSender
- (void)dealloc {
#if releaseDebug
MGMLog(@"%s Releasing", __PRETTY_FUNCTION__);
#endif
if (theConnection!=nil)
[theConnection release];
if (receivedData!=nil)
[receivedData release];
[super dealloc];
}
- (NSData *)buildBodyWithObjects:(NSDictionary *)theObjects boundary:(NSString *)theBoundary {
NSMutableData *data = [NSMutableData data];
NSArray *keys = [theObjects allKeys];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSFileManager *manager = [NSFileManager defaultManager];
NSString *timeZone, *timeFormat;
if ([userDefaults objectForKey:MGMGRTimeZone]!=nil && ![[userDefaults objectForKey:MGMGRTimeZone] isEqualToString:@""]) {
timeZone = [userDefaults objectForKey:MGMGRTimeZone];
} else if ([infoDictionary objectForKey:MGMGRTimeZone]!=nil && ![[infoDictionary objectForKey:MGMGRTimeZone] isEqualToString:@""]) {
timeZone = [infoDictionary objectForKey:MGMGRTimeZone];
} else {
timeZone = MGMDefaultTimeZone;
}
if ([userDefaults objectForKey:MGMGRTimeFormat]!=nil && ![[userDefaults objectForKey:MGMGRTimeFormat] isEqualToString:@""]) {
timeFormat = [userDefaults objectForKey:MGMGRTimeFormat];
} else if ([infoDictionary objectForKey:MGMGRTimeFormat]!=nil && ![[infoDictionary objectForKey:MGMGRTimeFormat] isEqualToString:@""]) {
timeFormat = [infoDictionary objectForKey:MGMGRTimeFormat];
} else {
timeFormat = MGMDefaultTimeFormat;
}
for (int i=0; i<[keys count]; i++) {
NSString *key = [keys objectAtIndex:i];
id object = [theObjects objectForKey:key];
[data appendData:[[NSString stringWithFormat:@"--%@\r\n", theBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
if ([object isKindOfClass:[NSString class]]) {
[data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[object dataUsingEncoding:NSUTF8StringEncoding]];
} else if ([object isKindOfClass:[NSNumber class]]) {
[data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[[object stringValue] dataUsingEncoding:NSUTF8StringEncoding]];
} else if ([object isKindOfClass:[NSData class]]) {
[data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:object];
} else if ([object isKindOfClass:[NSDate class]]) {
[data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
NSString *date;
if ([timeZone length]==3) {
date = [object descriptionWithCalendarFormat:timeFormat timeZone:[NSTimeZone timeZoneWithAbbreviation:timeZone] locale:nil];
} else {
date = [object descriptionWithCalendarFormat:timeFormat timeZone:[NSTimeZone timeZoneWithName:timeZone] locale:nil];
}
[data appendData:[date dataUsingEncoding:NSUTF8StringEncoding]];
} else if ([object isKindOfClass:[NSURL class]]) {
if ([object isFileURL]) {
NSString *objectPath = [object path];
if ([manager fileExistsAtPath:objectPath] && [manager isReadableFileAtPath:objectPath]) {
[data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, [objectPath lastPathComponent]] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[@"Content-Type: plain/text\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[@"Content-Transfer-Encoding: binary\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[NSData dataWithContentsOfFile:objectPath]];
}
} else {
[data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[[object absoluteString] dataUsingEncoding:NSUTF8StringEncoding]];
}
}
[data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[data appendData:[[NSString stringWithFormat:@"--%@--", theBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
return data;
}
- (NSDictionary *)defaultObjects {
MGMSystemInfo *sysInfo = [[MGMSystemInfo new] autorelease];
NSMutableDictionary *objects = [NSMutableDictionary dictionary];
2010-01-06 18:11:16 -06:00
[objects setObject:[sysInfo frameworkVersion] forKey:@"GRVersion"];
[objects setObject:[sysInfo applicationName] forKey:@"Application"];
[objects setObject:[sysInfo applicationIdentifier] forKey:@"Application Identifier"];
[objects setObject:[sysInfo applicationVersion] forKey:@"Application Version"];
[objects setObject:[sysInfo language] forKey:@"Language"];
[objects setObject:[NSString stringWithFormat:@"%@ %@", [sysInfo architecture], [sysInfo CPUFamily]] forKey:@"System Architecture"];
[objects setObject:[NSNumber numberWithInt:[sysInfo CPUCount]] forKey:@"CPU Count"];
[objects setObject:[sysInfo modelName] forKey:@"Model Name"];
[objects setObject:[sysInfo model] forKey:@"Model"];
[objects setObject:[NSString stringWithFormat:@"%@ %@", [sysInfo OSVersion], [sysInfo OSVersionName]] forKey:@"System Version"];
double CPUMHz = (double)[sysInfo CPUMHz];
NSString *CPUFreq = @"MHz";
if (CPUMHz>=1000) {
CPUFreq = @"GHz";
CPUMHz /= 1000;
if (CPUMHz>=1000) {
CPUFreq = @"THz";
CPUMHz /= 1000;
}
}
[objects setObject:[NSString stringWithFormat:@"%.2f %@", CPUMHz, CPUFreq] forKey:@"CPU Speed"];
double RAMSize = (double)[sysInfo RAMSize];
NSString *RAMType = @"MB";
if (RAMSize>=1024) {
RAMType = @"GB";
RAMSize /= 1024;
if (RAMSize>=1024) {
RAMType = @"TB";
RAMSize /= 1024;
}
}
[objects setObject:[NSString stringWithFormat:@"%.2f %@", RAMSize, RAMType] forKey:@"RAM Size"];
return objects;
}
- (void)sendReport:(NSString *)theReportPath reportDate:(NSDate *)theReportDate userReport:(NSString *)theUserReport delegate:(id)theDelegate {
if (theDelegate!=nil)
delegate = theDelegate;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
MGMSystemInfo *sysInfo = [[MGMSystemInfo new] autorelease];
2010-01-06 18:11:16 -06:00
NSString *email = nil, *url = nil, *userEmail = nil, *logFiles = nil;
BOOL reportAttached = NO;
if ([userDefaults objectForKey:MGMGRCrashEmail]!=nil && ![[userDefaults objectForKey:MGMGRCrashEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRCrashEmail];
} else if ([infoDictionary objectForKey:MGMGRCrashEmail]!=nil && ![[infoDictionary objectForKey:MGMGRCrashEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRCrashEmail];
} else if ([userDefaults objectForKey:MGMGRBugsEmail]!=nil && ![[userDefaults objectForKey:MGMGRBugsEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRBugsEmail];
} else if ([infoDictionary objectForKey:MGMGRBugsEmail]!=nil && ![[infoDictionary objectForKey:MGMGRBugsEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRBugsEmail];
} else if ([userDefaults objectForKey:MGMGRContactEmail]!=nil && ![[userDefaults objectForKey:MGMGRContactEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRContactEmail];
} else if ([infoDictionary objectForKey:MGMGRContactEmail]!=nil && ![[infoDictionary objectForKey:MGMGRContactEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRContactEmail];
} else {
email = MGMDefaultCrashEmail;
}
if ([userDefaults objectForKey:MGMGRURL]!=nil && ![[userDefaults objectForKey:MGMGRURL] isEqualToString:@""]) {
url = [userDefaults objectForKey:MGMGRURL];
} else if ([infoDictionary objectForKey:MGMGRURL]!=nil && ![[infoDictionary objectForKey:MGMGRURL] isEqualToString:@""]) {
url = [infoDictionary objectForKey:MGMGRURL];
} else {
url = MGMDefaultURL;
}
if ([userDefaults objectForKey:MGMGRUserEmail]!=nil && ![[userDefaults objectForKey:MGMGRUserEmail] isEqualToString:@""]) {
userEmail = [userDefaults objectForKey:MGMGRUserEmail];
}
if ([userDefaults objectForKey:MGMGRReportFileAttached]!=nil) {
reportAttached = [[userDefaults objectForKey:MGMGRReportFileAttached] boolValue];
} else if ([infoDictionary objectForKey:MGMGRReportFileAttached]!=nil) {
reportAttached = [[infoDictionary objectForKey:MGMGRReportFileAttached] boolValue];
}
if ([userDefaults objectForKey:MGMGRLogFiles]!=nil && ![[userDefaults objectForKey:MGMGRLogFiles] isEqualToString:@""]) {
logFiles = [userDefaults objectForKey:MGMGRLogFiles];
} else if ([infoDictionary objectForKey:MGMGRLogFiles]!=nil && ![[infoDictionary objectForKey:MGMGRLogFiles] isEqualToString:@""]) {
logFiles = [infoDictionary objectForKey:MGMGRLogFiles];
}
srandomdev();
NSString *boundary = [NSString stringWithFormat:@"----Boundary+%d", random()%100000];
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
[postRequest setHTTPMethod:@"POST"];
[postRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary, nil] forHTTPHeaderField:@"Content-Type"];
NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithDictionary:[self defaultObjects]];
[objects setObject:@"crash" forKey:@"GRType"];
if (email!=nil)
[objects setObject:email forKey:@"GREmail"];
[objects setObject:[NSString stringWithFormat:@"Crash Report for %@", [sysInfo applicationName]] forKey:@"GRSubject"];
[objects setObject:(reportAttached ? @"YES" : @"NO") forKey:@"GRReportAttached"];
if (theUserReport!=nil)
[objects setObject:theUserReport forKey:@"GRUserReport"];
if (userEmail!=nil)
[objects setObject:userEmail forKey:@"User Email Address"];
if (theReportDate!=nil)
[objects setObject:theReportDate forKey:@"Crash Report Date"];
if (theReportPath!=nil && ![theReportPath isEqualToString:@""])
[objects setObject:[NSURL fileURLWithPath:theReportPath] forKey:@"reportFile"];
if (logFiles!=nil && ![logFiles isEqualToString:@""]) {
NSArray *logs = [logFiles componentsSeparatedByString:@" "];
for (int i=0; i<[logs count]; i++) {
[objects setObject:[NSURL fileURLWithPath:[[logs objectAtIndex:i] stringByExpandingTildeInPath]] forKey:[NSString stringWithFormat:@"logFile%d", i]];
}
}
[postRequest setHTTPBody:[self buildBodyWithObjects:objects boundary:boundary]];
theConnection = [[NSURLConnection connectionWithRequest:postRequest delegate:self] retain];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
}
- (void)sendBug:(NSString *)theBug reproduce:(NSString *)theReproduce delegate:(id)theDelegate {
if (theDelegate!=nil)
delegate = theDelegate;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
MGMSystemInfo *sysInfo = [[MGMSystemInfo new] autorelease];
2010-01-06 18:11:16 -06:00
NSString *email = nil, *url = nil, *userEmail = nil;
if ([userDefaults objectForKey:MGMGRBugsEmail]!=nil && ![[userDefaults objectForKey:MGMGRBugsEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRBugsEmail];
} else if ([infoDictionary objectForKey:MGMGRBugsEmail]!=nil && ![[infoDictionary objectForKey:MGMGRBugsEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRBugsEmail];
} else if ([userDefaults objectForKey:MGMGRCrashEmail]!=nil && ![[userDefaults objectForKey:MGMGRCrashEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRCrashEmail];
} else if ([infoDictionary objectForKey:MGMGRCrashEmail]!=nil && ![[infoDictionary objectForKey:MGMGRCrashEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRCrashEmail];
} else if ([userDefaults objectForKey:MGMGRContactEmail]!=nil && ![[userDefaults objectForKey:MGMGRContactEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRContactEmail];
} else if ([infoDictionary objectForKey:MGMGRContactEmail]!=nil && ![[infoDictionary objectForKey:MGMGRContactEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRContactEmail];
} else {
email = MGMDefaultBugsEmail;
}
if ([userDefaults objectForKey:MGMGRURL]!=nil && ![[userDefaults objectForKey:MGMGRURL] isEqualToString:@""]) {
url = [userDefaults objectForKey:MGMGRURL];
} else if ([infoDictionary objectForKey:MGMGRURL]!=nil && ![[infoDictionary objectForKey:MGMGRURL] isEqualToString:@""]) {
url = [infoDictionary objectForKey:MGMGRURL];
} else {
url = MGMDefaultURL;
}
if ([userDefaults objectForKey:MGMGRUserEmail]!=nil && ![[userDefaults objectForKey:MGMGRUserEmail] isEqualToString:@""]) {
userEmail = [userDefaults objectForKey:MGMGRUserEmail];
}
srandomdev();
NSString *boundary = [NSString stringWithFormat:@"----Boundary+%d", random()%100000];
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
[postRequest setHTTPMethod:@"POST"];
[postRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary, nil] forHTTPHeaderField:@"Content-Type"];
NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithDictionary:[self defaultObjects]];
[objects setObject:@"bug" forKey:@"GRType"];
if (email!=nil)
[objects setObject:email forKey:@"GREmail"];
[objects setObject:[NSString stringWithFormat:@"Bug Report for %@", [sysInfo applicationName]] forKey:@"GRSubject"];
if (userEmail!=nil)
[objects setObject:userEmail forKey:@"User Email Address"];
if (theBug!=nil)
[objects setObject:theBug forKey:@"GRBug"];
if (theReproduce!=nil)
[objects setObject:theReproduce forKey:@"GRReproduce"];
[postRequest setHTTPBody:[self buildBodyWithObjects:objects boundary:boundary]];
theConnection = [[NSURLConnection connectionWithRequest:postRequest delegate:self] retain];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
}
- (void)sendMessage:(NSString *)theMessage subject:(NSString *)theSubject delegate:(id)theDelegate {
if (theDelegate!=nil)
delegate = theDelegate;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
MGMSystemInfo *sysInfo = [[MGMSystemInfo new] autorelease];
2010-01-06 18:11:16 -06:00
NSString *email = nil, *url = nil, *userEmail = nil, *userName = nil;
if ([userDefaults objectForKey:MGMGRContactEmail]!=nil && ![[userDefaults objectForKey:MGMGRContactEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRContactEmail];
} else if ([infoDictionary objectForKey:MGMGRContactEmail]!=nil && ![[infoDictionary objectForKey:MGMGRContactEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRContactEmail];
} else if ([userDefaults objectForKey:MGMGRBugsEmail]!=nil && ![[userDefaults objectForKey:MGMGRBugsEmail] isEqualToString:@""]) {
2010-01-06 18:11:16 -06:00
email = [userDefaults objectForKey:MGMGRBugsEmail];
} else if ([infoDictionary objectForKey:MGMGRBugsEmail]!=nil && ![[infoDictionary objectForKey:MGMGRBugsEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRBugsEmail];
} else if ([userDefaults objectForKey:MGMGRCrashEmail]!=nil && ![[userDefaults objectForKey:MGMGRCrashEmail] isEqualToString:@""]) {
email = [userDefaults objectForKey:MGMGRCrashEmail];
} else if ([infoDictionary objectForKey:MGMGRCrashEmail]!=nil && ![[infoDictionary objectForKey:MGMGRCrashEmail] isEqualToString:@""]) {
email = [infoDictionary objectForKey:MGMGRCrashEmail];
} else {
email = MGMDefaultBugsEmail;
}
if ([userDefaults objectForKey:MGMGRURL]!=nil && ![[userDefaults objectForKey:MGMGRURL] isEqualToString:@""]) {
url = [userDefaults objectForKey:MGMGRURL];
} else if ([infoDictionary objectForKey:MGMGRURL]!=nil && ![[infoDictionary objectForKey:MGMGRURL] isEqualToString:@""]) {
url = [infoDictionary objectForKey:MGMGRURL];
} else {
url = MGMDefaultURL;
}
if ([userDefaults objectForKey:MGMGRUserEmail]!=nil && ![[userDefaults objectForKey:MGMGRUserEmail] isEqualToString:@""]) {
userEmail = [userDefaults objectForKey:MGMGRUserEmail];
}
if ([userDefaults objectForKey:MGMGRUserName]!=nil && ![[userDefaults objectForKey:MGMGRUserName] isEqualToString:@""]) {
userName = [userDefaults objectForKey:MGMGRUserName];
}
srandomdev();
NSString *boundary = [NSString stringWithFormat:@"----Boundary+%d", random()%100000];
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
[postRequest setHTTPMethod:@"POST"];
[postRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary, nil] forHTTPHeaderField:@"Content-Type"];
NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithDictionary:[self defaultObjects]];
[objects setObject:@"contact" forKey:@"GRType"];
if (email!=nil)
[objects setObject:email forKey:@"GREmail"];
[objects setObject:[NSString stringWithFormat:@"%@ for %@", theSubject, [sysInfo applicationName]] forKey:@"GRSubject"];
if (userEmail!=nil)
[objects setObject:userEmail forKey:@"User Email Address"];
if (userEmail!=nil)
[objects setObject:userName forKey:@"User Name"];
if (theMessage!=nil)
[objects setObject:theMessage forKey:@"GRMessage"];
[postRequest setHTTPBody:[self buildBodyWithObjects:objects boundary:boundary]];
theConnection = [[NSURLConnection connectionWithRequest:postRequest delegate:self] retain];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
if ([response statusCode]!=200) {
[connection cancel];
NSError *error = [NSError errorWithDomain:@"com.MrGeckosMedia.GRStatus" code:1 userInfo:
[NSDictionary dictionaryWithObject:[NSString stringWithFormat:MGMLocalized(@"Status Code Returned %d", nil), [response statusCode]]
forKey:NSLocalizedDescriptionKey]];
MGMLog(@"%@", [error localizedDescription]);
if (delegate!=nil && [delegate respondsToSelector:@selector(sendError:)])
[delegate sendError:error];
} else {
[receivedData setLength:0];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
MGMLog(@"Connection failed! Error - %@ %@", [error localizedDescription], [[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
if (delegate!=nil && [delegate respondsToSelector:@selector(sendError:)])
[delegate sendError:error];
[self release];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (receivedData!=nil) {
NSString *receivedString = [[[NSString alloc] initWithData:[NSData dataWithData:receivedData] encoding:NSUTF8StringEncoding] autorelease];
if (delegate!=nil && [delegate respondsToSelector:@selector(sendFinished:)])
[delegate sendFinished:receivedString];
}
}
@end