From e3e95c29f6503cbfe1724c46be725e8096644ccb Mon Sep 17 00:00:00 2001 From: Diego Waxemberg Date: Sat, 31 Jan 2015 01:57:23 -0500 Subject: [PATCH] Created http response class and added error checking --- Limelight/Network/AppManager.m | 6 +- Limelight/Network/DiscoveryManager.m | 6 +- Limelight/Network/DiscoveryWorker.m | 18 +- Limelight/Network/HttpManager.h | 10 +- Limelight/Network/HttpManager.m | 205 +--------------- Limelight/Network/HttpResponse.h | 26 ++ Limelight/Network/HttpResponse.m | 222 ++++++++++++++++++ Limelight/Network/PairManager.m | 96 +++++--- Limelight/Stream/StreamManager.m | 30 ++- .../ViewControllers/MainFrameViewController.m | 95 ++++---- 10 files changed, 401 insertions(+), 313 deletions(-) create mode 100644 Limelight/Network/HttpResponse.h create mode 100644 Limelight/Network/HttpResponse.m diff --git a/Limelight/Network/AppManager.m b/Limelight/Network/AppManager.m index f597fb6..6e24cf8 100644 --- a/Limelight/Network/AppManager.m +++ b/Limelight/Network/AppManager.m @@ -9,6 +9,7 @@ #import "AppManager.h" #import "CryptoManager.h" #import "Utils.h" +#import "HttpResponse.h" @implementation AppManager { NSOperationQueue* _opQueue; @@ -60,8 +61,9 @@ } if (appImage == nil) { HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* appAsset = [hMan executeRequestSynchronously:[hMan newAppAssetRequestWithAppId:app.appId]]; - appImage = [UIImage imageWithData:appAsset]; + HttpResponse* appAssetResp = [hMan executeRequestSynchronously:[hMan newAppAssetRequestWithAppId:app.appId]]; + + appImage = [UIImage imageWithData:appAssetResp.responseData]; app.appImage = appImage; if (appImage != nil) { @synchronized(_imageCache) { diff --git a/Limelight/Network/DiscoveryManager.m b/Limelight/Network/DiscoveryManager.m index 60ec61c..a58aebe 100644 --- a/Limelight/Network/DiscoveryManager.m +++ b/Limelight/Network/DiscoveryManager.m @@ -39,14 +39,14 @@ - (void) discoverHost:(NSString *)hostAddress withCallback:(void (^)(Host *))callback { HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + HttpResponse* serverInfoResponse = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; Host* host = nil; - if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { + if ([serverInfoResponse isStatusOk]) { DataManager* dataMan = [[DataManager alloc] init]; host = [dataMan createHost]; host.address = hostAddress; - [HttpManager populateHostFromXML:serverInfoData host:host]; + [serverInfoResponse populateHost:host]; if (![self addHostToDiscovery:host]) { [dataMan removeHost:host]; } diff --git a/Limelight/Network/DiscoveryWorker.m b/Limelight/Network/DiscoveryWorker.m index 7cf8f04..dce456c 100644 --- a/Limelight/Network/DiscoveryWorker.m +++ b/Limelight/Network/DiscoveryWorker.m @@ -43,18 +43,18 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds BOOL receivedResponse = NO; if (!self.cancelled && _host.localAddress != nil) { HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.localAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; - if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { - [HttpManager populateHostFromXML:serverInfoData host:_host]; + HttpResponse* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + if ([serverInfoResp isStatusOk]) { + [serverInfoResp populateHost:_host]; _host.address = _host.localAddress; receivedResponse = YES; } } if (!self.cancelled && !receivedResponse && _host.externalAddress != nil) { HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.externalAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; - if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { - [HttpManager populateHostFromXML:serverInfoData host:_host]; + HttpResponse* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + if ([serverInfoResp isStatusOk]) { + [serverInfoResp populateHost:_host]; _host.address = _host.externalAddress; receivedResponse = YES; } @@ -62,9 +62,9 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds if (!self.cancelled && !receivedResponse && _host.address != nil) { HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; - if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) { - [HttpManager populateHostFromXML:serverInfoData host:_host]; + HttpResponse* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + if ([serverInfoResp isStatusOk]) { + [serverInfoResp populateHost:_host]; receivedResponse = YES; } } diff --git a/Limelight/Network/HttpManager.h b/Limelight/Network/HttpManager.h index 68c2f39..77f7bb8 100644 --- a/Limelight/Network/HttpManager.h +++ b/Limelight/Network/HttpManager.h @@ -8,14 +8,10 @@ #import #import "Host.h" +#import "HttpResponse.h" @interface HttpManager : NSObject -+ (NSArray*) getAppListFromXML:(NSData*)xml; -+ (void) populateHostFromXML:(NSData*)xml host:(Host*)host; -+ (NSString*) getStringFromXML:(NSData*)xml tag:(NSString*)tag; -+ (NSString*) getStatusStringFromXML:(NSData*)xml; - - (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId deviceName:(NSString*) deviceName cert:(NSData*) cert; - (NSURLRequest*) newPairRequest:(NSData*)salt; - (NSURLRequest*) newUnpairRequest; @@ -29,7 +25,9 @@ - (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId; - (NSURLRequest*) newQuitAppRequest; - (NSURLRequest*) newAppAssetRequestWithAppId:(NSString*)appId; -- (NSData*) executeRequestSynchronously:(NSURLRequest*)request; +- (HttpResponse*) executeRequestSynchronously:(NSURLRequest*)request; +- (void) executeRequest:(NSURLRequest*)request; + @end diff --git a/Limelight/Network/HttpManager.m b/Limelight/Network/HttpManager.m index c5982c0..df72d5d 100644 --- a/Limelight/Network/HttpManager.m +++ b/Limelight/Network/HttpManager.m @@ -26,207 +26,6 @@ static const NSString* PORT = @"47984"; -+ (NSArray*) getAppListFromXML:(NSData*)xml { - xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]); - - if (docPtr == NULL) { - NSLog(@"ERROR: An error occured trying to parse xml."); - return NULL; - } - - xmlNodePtr node; - xmlNodePtr rootNode = node = xmlDocGetRootElement(docPtr); - - // Check root status_code - if (![HttpManager verifyStatus: rootNode]) { - NSLog(@"ERROR: Request returned with failure status"); - xmlFreeDoc(docPtr); - return NULL; - } - - // Skip the root node - node = node->children; - - NSMutableArray* appList = [[NSMutableArray alloc] init]; - - while (node != NULL) { - //NSLog(@"node: %s", node->name); - if (!xmlStrcmp(node->name, (const xmlChar*)"App")) { - xmlNodePtr appInfoNode = node->xmlChildrenNode; - NSString* appName; - NSString* appId; - BOOL appIsRunning = NO; - while (appInfoNode != NULL) { - //NSLog(@"appInfoNode: %s", appInfoNode->name); - if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"AppTitle")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); - appName = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } else if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"ID")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); - appId = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } else if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"IsRunning")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); - appIsRunning = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"]; - xmlFree(nodeVal); - } - appInfoNode = appInfoNode->next; - } - App* app = [[App alloc] init]; - app.appName = appName; - app.appId = appId; - app.isRunning = appIsRunning; - [appList addObject:app]; - } - node = node->next; - } - - xmlFreeDoc(docPtr); - - return appList; -} - -+ (void) populateHostFromXML:(NSData*)xml host:(Host*)host { - xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]); - - if (docPtr == NULL) { - NSLog(@"ERROR: An error occured trying to parse xml."); - return; - } - - xmlNodePtr node; - xmlNodePtr rootNode = node = xmlDocGetRootElement(docPtr); - - // Check root status_code - if (![HttpManager verifyStatus: rootNode]) { - NSLog(@"ERROR: Request returned with failure status"); - xmlFreeDoc(docPtr); - return; - } - - // Skip the root node - node = node->children; - - while (node != NULL) { - //NSLog(@"node: %s", node->name); - if (!xmlStrcmp(node->name, (const xmlChar*)"hostname")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); - host.name = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } else if (!xmlStrcmp(node->name, (const xmlChar*)"ExternalIP")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); - host.externalAddress = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } else if (!xmlStrcmp(node->name, (const xmlChar*)"LocalIP")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); - host.localAddress = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } else if (!xmlStrcmp(node->name, (const xmlChar*)"uniqueid")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); - host.uuid = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } else if (!xmlStrcmp(node->name, (const xmlChar*)"mac")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); - host.mac = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } else if (!xmlStrcmp(node->name, (const xmlChar*)"PairStatus")) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); - host.pairState = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"] ? PairStatePaired : PairStateUnpaired; - xmlFree(nodeVal); - } - - node = node->next; - } - - xmlFreeDoc(docPtr); -} - -+ (NSString*) getStatusStringFromXML:(NSData*)xml { - xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]); - - if (docPtr == NULL) { - NSLog(@"ERROR: An error occured trying to parse xml."); - return NULL; - } - - NSString* string; - xmlNodePtr rootNode = xmlDocGetRootElement(docPtr); - if (rootNode == NULL) { - NSLog(@"ERROR: No root XML element."); - xmlFreeDoc(docPtr); - return NULL; - } - - string = [HttpManager getStatusMessage: rootNode]; - - xmlFreeDoc(docPtr); - - return string; -} - -+ (NSString*) getStringFromXML:(NSData*)xml tag:(NSString*)tag { - xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]); - - if (docPtr == NULL) { - NSLog(@"ERROR: An error occured trying to parse xml."); - return NULL; - } - NSString* value; - xmlNodePtr node; - xmlNodePtr rootNode = node = xmlDocGetRootElement(docPtr); - - // Check root status_code - if (![HttpManager verifyStatus: rootNode]) { - NSLog(@"ERROR: Request returned with failure status"); - xmlFreeDoc(docPtr); - return NULL; - } - - // Skip the root node - node = node->children; - - while (node != NULL) { - //NSLog(@"node: %s", node->name); - if (!xmlStrcmp(node->name, (const xmlChar*)[tag UTF8String])) { - xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); - value = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; - xmlFree(nodeVal); - } - node = node->next; - } - //NSLog(@"xmlValue: %@", value); - - xmlFreeDoc(docPtr); - - return value; -} - -+ (NSString*) getStatusMessage:(xmlNodePtr)docRoot { - xmlChar* statusMsgXml = xmlGetProp(docRoot, (const xmlChar*)"status_message"); - NSString* statusMsg; - if (statusMsgXml != NULL) { - statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml]; - xmlFree(statusMsgXml); - } - else { - statusMsg = @"Server Error"; - } - - return statusMsg; -} - -+ (bool) verifyStatus:(xmlNodePtr)docRoot { - xmlChar* statusStr = xmlGetProp(docRoot, (const xmlChar*)"status_code"); - if (statusStr != NULL) { - int status = [[NSString stringWithUTF8String:(const char*)statusStr] intValue]; - xmlFree(statusStr); - return status == 200; - } - - return false; -} - + (NSData*) fixXmlVersion:(NSData*) xmlData { NSString* dataString = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding]; NSString* xmlString = [dataString stringByReplacingOccurrencesOfString:@"UTF-16" withString:@"UTF-8" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [dataString length])]; @@ -247,14 +46,14 @@ static const NSString* PORT = @"47984"; return self; } -- (NSData*) executeRequestSynchronously:(NSURLRequest*)request { +- (HttpResponse*) executeRequestSynchronously:(NSURLRequest*)request { NSLog(@"Making Request: %@", request); [_respData setLength:0]; dispatch_sync(dispatch_get_main_queue(), ^{ [NSURLConnection connectionWithRequest:request delegate:self]; }); dispatch_semaphore_wait(_requestLock, DISPATCH_TIME_FOREVER); - return _requestResp; + return [HttpResponse responseWithData:_requestResp]; } - (void) executeRequest:(NSURLRequest*)request { diff --git a/Limelight/Network/HttpResponse.h b/Limelight/Network/HttpResponse.h new file mode 100644 index 0000000..accef07 --- /dev/null +++ b/Limelight/Network/HttpResponse.h @@ -0,0 +1,26 @@ +// +// HttpResponse.h +// Limelight +// +// Created by Diego Waxemberg on 1/30/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import +#import "Host.h" + +@interface HttpResponse : NSObject + +@property (nonatomic) NSInteger statusCode; +@property (nonatomic) NSString* statusMessage; +@property (nonatomic) NSData* responseData; + ++ (HttpResponse*) responseWithData:(NSData*)xml; + +- (NSString*) parseStringTag:(NSString*)tag; +- (BOOL)parseIntTag:(NSString *)tag value:(NSInteger*)value; +- (BOOL) isStatusOk; +- (BOOL) populateHost:(Host*)host; +- (NSArray*) getAppList; + +@end diff --git a/Limelight/Network/HttpResponse.m b/Limelight/Network/HttpResponse.m new file mode 100644 index 0000000..11f4031 --- /dev/null +++ b/Limelight/Network/HttpResponse.m @@ -0,0 +1,222 @@ +// +// HttpResponse.m +// Limelight +// +// Created by Diego Waxemberg on 1/30/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "HttpResponse.h" +#import "App.h" +#import + +@implementation HttpResponse + ++ (HttpResponse*) responseWithData:(NSData*)xml { + HttpResponse* response = [[HttpResponse alloc] init]; + response.responseData = xml; + + xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]); + + if (docPtr == NULL) { + NSLog(@"ERROR: An error occured trying to parse xml."); + return response; + } + + xmlNodePtr rootNode = xmlDocGetRootElement(docPtr); + if (rootNode == NULL) { + NSLog(@"ERROR: No root XML element."); + xmlFreeDoc(docPtr); + return response; + } + + xmlChar* statusStr = xmlGetProp(rootNode, (const xmlChar*)"status_code"); + if (statusStr != NULL) { + int status = [[NSString stringWithUTF8String:(const char*)statusStr] intValue]; + xmlFree(statusStr); + response.statusCode = status; + } + + xmlChar* statusMsgXml = xmlGetProp(rootNode, (const xmlChar*)"status_message"); + NSString* statusMsg; + if (statusMsgXml != NULL) { + statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml]; + xmlFree(statusMsgXml); + } + else { + statusMsg = @"Server Error"; + } + response.statusMessage = statusMsg; + + + return response; +} + +- (NSString*)parseStringTag:(NSString *)tag { + xmlDocPtr docPtr = xmlParseMemory([self.responseData bytes], (int)[self.responseData length]); + + if (docPtr == NULL) { + NSLog(@"ERROR: An error occured trying to parse xml."); + return nil; + } + + xmlNodePtr node = xmlDocGetRootElement(docPtr); + + // Check root status_code + if (![self isStatusOk]) { + NSLog(@"ERROR: Request returned with failure status"); + xmlFreeDoc(docPtr); + return nil; + } + + // Skip the root node + node = node->children; + + NSString* value; + while (node != NULL) { + //NSLog(@"node: %s", node->name); + if (!xmlStrcmp(node->name, (const xmlChar*)[tag UTF8String])) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + value = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } + node = node->next; + } + //NSLog(@"xmlValue: %@", value); + + xmlFreeDoc(docPtr); + return value; +} + +- (BOOL)parseIntTag:(NSString *)tag value:(NSInteger*)value { + NSString* stringVal = [self parseStringTag:tag]; + if (stringVal != nil) { + *value = [stringVal integerValue]; + return true; + } else { + return false; + } +} + +- (BOOL) isStatusOk { + return self.statusCode == 200; +} + +- (BOOL) populateHost:(Host*)host { + xmlDocPtr docPtr = xmlParseMemory([self.responseData bytes], (int)[self.responseData length]); + + if (docPtr == NULL) { + NSLog(@"ERROR: An error occured trying to parse xml."); + return false; + } + + xmlNodePtr node = xmlDocGetRootElement(docPtr); + + // Check root status_code + if (![self isStatusOk]) { + NSLog(@"ERROR: Request returned with failure status"); + xmlFreeDoc(docPtr); + return false; + } + + // Skip the root node + node = node->children; + + while (node != NULL) { + //NSLog(@"node: %s", node->name); + if (!xmlStrcmp(node->name, (const xmlChar*)"hostname")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + host.name = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(node->name, (const xmlChar*)"ExternalIP")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + host.externalAddress = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(node->name, (const xmlChar*)"LocalIP")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + host.localAddress = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(node->name, (const xmlChar*)"uniqueid")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + host.uuid = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(node->name, (const xmlChar*)"mac")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + host.mac = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(node->name, (const xmlChar*)"PairStatus")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + host.pairState = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"] ? PairStatePaired : PairStateUnpaired; + xmlFree(nodeVal); + } + + node = node->next; + } + + xmlFreeDoc(docPtr); + + return true; +} + +- (NSArray*) getAppList { + xmlDocPtr docPtr = xmlParseMemory([self.responseData bytes], (int)[self.responseData length]); + + if (docPtr == NULL) { + NSLog(@"ERROR: An error occured trying to parse xml."); + return nil; + } + + xmlNodePtr node = xmlDocGetRootElement(docPtr); + + // Check root status_code + if (![self isStatusOk]) { + NSLog(@"ERROR: Request returned with failure status"); + xmlFreeDoc(docPtr); + return nil; + } + + // Skip the root node + node = node->children; + + NSMutableArray* appList = [[NSMutableArray alloc] init]; + + while (node != NULL) { + //NSLog(@"node: %s", node->name); + if (!xmlStrcmp(node->name, (const xmlChar*)"App")) { + xmlNodePtr appInfoNode = node->xmlChildrenNode; + NSString* appName; + NSString* appId; + BOOL appIsRunning = NO; + while (appInfoNode != NULL) { + //NSLog(@"appInfoNode: %s", appInfoNode->name); + if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"AppTitle")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); + appName = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"ID")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); + appId = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"IsRunning")) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); + appIsRunning = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"]; + xmlFree(nodeVal); + } + appInfoNode = appInfoNode->next; + } + App* app = [[App alloc] init]; + app.appName = appName; + app.appId = appId; + app.isRunning = appIsRunning; + [appList addObject:app]; + } + node = node->next; + } + + xmlFreeDoc(docPtr); + + return appList; +} + + +@end diff --git a/Limelight/Network/PairManager.m b/Limelight/Network/PairManager.m index 0f72ecf..69f8343 100644 --- a/Limelight/Network/PairManager.m +++ b/Limelight/Network/PairManager.m @@ -9,6 +9,7 @@ #import "PairManager.h" #import "CryptoManager.h" #import "Utils.h" +#import "HttpResponse.h" #include @@ -27,18 +28,19 @@ } - (void) main { - NSData* serverInfo = [_httpManager executeRequestSynchronously:[_httpManager newServerInfoRequest]]; - if (serverInfo == NULL) { + HttpResponse* serverInfo = [_httpManager executeRequestSynchronously:[_httpManager newServerInfoRequest]]; + if (serverInfo == nil) { [_callback pairFailed:@"Unable to connect to PC"]; return; } - if (![[HttpManager getStringFromXML:serverInfo tag:@"currentgame"] isEqual:@"0"]) { - [_callback pairFailed:@"You must stop streaming before attempting to pair."]; - } - else if (![[HttpManager getStringFromXML:serverInfo tag:@"PairStatus"] isEqual:@"1"]) { - [self initiatePair]; - } else { - [_callback alreadyPaired]; + if ([serverInfo isStatusOk]) { + if (![[serverInfo parseStringTag:@"currentgame"] isEqual:@"0"]) { + [_callback pairFailed:@"You must stop streaming before attempting to pair."]; + } else if (![[serverInfo parseStringTag:@"PairStatus"] isEqual:@"1"]) { + [self initiatePair]; + } else { + [_callback alreadyPaired]; + } } } @@ -48,16 +50,18 @@ NSLog(@"PIN: %@, saltedPIN: %@", PIN, salt); [_callback showPIN:PIN]; - NSData* pairResp = [_httpManager executeRequestSynchronously:[_httpManager newPairRequest:salt]]; - NSString* pairedString; - pairedString = [HttpManager getStringFromXML:pairResp tag:@"paired"]; - if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) { - [_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]]; + HttpResponse* pairResp = [_httpManager executeRequestSynchronously:[_httpManager newPairRequest:salt]]; + if (![self verifyResponseStatus:pairResp]) { + return; + } + NSInteger* pairedStatus; + if (![pairResp parseIntTag:@"paired" value:pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; [_callback pairFailed:@"Pairing was declined by the target."]; return; } - NSString* plainCert = [HttpManager getStringFromXML:pairResp tag:@"plaincert"]; + NSString* plainCert = [pairResp parseStringTag:@"plaincert"]; CryptoManager* cryptoMan = [[CryptoManager alloc] init]; NSData* aesKey = [cryptoMan createAESKeyFromSalt:salt]; @@ -65,15 +69,17 @@ NSData* randomChallenge = [Utils randomBytes:16]; NSData* encryptedChallenge = [cryptoMan aesEncrypt:randomChallenge withKey:aesKey]; - NSData* challengeResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRequest:encryptedChallenge]]; - pairedString = [HttpManager getStringFromXML:challengeResp tag:@"paired"]; - if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) { - [_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]]; + HttpResponse* challengeResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRequest:encryptedChallenge]]; + if (![self verifyResponseStatus:challengeResp]) { + return; + } + if (![challengeResp parseIntTag:@"paired" value:pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; [_callback pairFailed:@"Pairing stage #2 failed"]; return; } - NSData* encServerChallengeResp = [Utils hexToBytes:[HttpManager getStringFromXML:challengeResp tag:@"challengeresponse"]]; + NSData* encServerChallengeResp = [Utils hexToBytes:[challengeResp parseStringTag:@"challengeresponse"]]; NSData* decServerChallengeResp = [cryptoMan aesDecrypt:encServerChallengeResp withKey:aesKey]; NSData* serverResponse = [decServerChallengeResp subdataWithRange:NSMakeRange(0, 20)]; @@ -83,50 +89,70 @@ NSData* challengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:serverChallenge with:[CryptoManager getSignatureFromCert:_cert]] with:clientSecret]]; NSData* challengeRespEncrypted = [cryptoMan aesEncrypt:challengeRespHash withKey:aesKey]; - NSData* secretResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRespRequest:challengeRespEncrypted]]; - pairedString = [HttpManager getStringFromXML:secretResp tag:@"paired"]; - if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) { - [_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]]; + HttpResponse* secretResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRespRequest:challengeRespEncrypted]]; + if (![self verifyResponseStatus:secretResp]) { + return; + } + if (![secretResp parseIntTag:@"paired" value:pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; [_callback pairFailed:@"Pairing stage #3 failed"]; return; } - NSData* serverSecretResp = [Utils hexToBytes:[HttpManager getStringFromXML:secretResp tag:@"pairingsecret"]]; + NSData* serverSecretResp = [Utils hexToBytes:[secretResp parseStringTag:@"pairingsecret"]]; NSData* serverSecret = [serverSecretResp subdataWithRange:NSMakeRange(0, 16)]; NSData* serverSignature = [serverSecretResp subdataWithRange:NSMakeRange(16, 256)]; if (![cryptoMan verifySignature:serverSecret withSignature:serverSignature andCert:[Utils hexToBytes:plainCert]]) { - [_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]]; + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; [_callback pairFailed:@"Server certificate invalid"]; return; } NSData* serverChallengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:randomChallenge with:[CryptoManager getSignatureFromCert:[Utils hexToBytes:plainCert]]] with:serverSecret]]; if (![serverChallengeRespHash isEqual:serverResponse]) { - [_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]]; + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; [_callback pairFailed:@"Incorrect PIN"]; return; } NSData* clientPairingSecret = [self concatData:clientSecret with:[cryptoMan signData:clientSecret withKey:[CryptoManager readKeyFromFile]]]; - NSData* clientSecretResp = [_httpManager executeRequestSynchronously:[_httpManager newClientSecretRespRequest:[Utils bytesToHex:clientPairingSecret]]]; - pairedString = [HttpManager getStringFromXML:clientSecretResp tag:@"paired"]; - if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) { - [_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]]; + HttpResponse* clientSecretResp = [_httpManager executeRequestSynchronously:[_httpManager newClientSecretRespRequest:[Utils bytesToHex:clientPairingSecret]]]; + if (![self verifyResponseStatus:clientSecretResp]) { + return; + } + if ([clientSecretResp parseIntTag:@"paired" value:pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; [_callback pairFailed:@"Pairing stage #4 failed"]; return; } - NSData* clientPairChallenge = [_httpManager executeRequestSynchronously:[_httpManager newPairChallenge]]; - pairedString = [HttpManager getStringFromXML:clientPairChallenge tag:@"paired"]; - if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) { - [_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]]; + HttpResponse* clientPairChallengeResp = [_httpManager executeRequestSynchronously:[_httpManager newPairChallenge]]; + if (![self verifyResponseStatus:clientPairChallengeResp]) { + return; + } + if (![clientPairChallengeResp parseIntTag:@"paired" value:pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; [_callback pairFailed:@"Pairing stage #5 failed"]; return; } [_callback pairSuccessful]; } +- (BOOL) verifyResponseStatus:(HttpResponse*)resp { + if (resp == nil) { + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; + [_callback pairFailed:@"Network error occured."]; + return false; + } else if (![resp isStatusOk]) { + [_httpManager executeRequest:[_httpManager newUnpairRequest]]; + [_callback pairFailed:resp.statusMessage]; + return false; + } else { + return true; + } +} + - (NSData*) concatData:(NSData*)data with:(NSData*)moreData { NSMutableData* concatData = [[NSMutableData alloc] initWithData:data]; [concatData appendData:moreData]; diff --git a/Limelight/Stream/StreamManager.m b/Limelight/Stream/StreamManager.m index f57c6b9..94e4886 100644 --- a/Limelight/Stream/StreamManager.m +++ b/Limelight/Stream/StreamManager.m @@ -41,11 +41,11 @@ deviceName:@"roth" cert:cert]; - NSData* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; - NSString* currentGame = [HttpManager getStringFromXML:serverInfoResp tag:@"currentgame"]; - NSString* pairStatus = [HttpManager getStringFromXML:serverInfoResp tag:@"PairStatus"]; - NSString* currentClient = [HttpManager getStringFromXML:serverInfoResp tag:@"CurrentClient"]; - if (currentGame == NULL || pairStatus == NULL) { + HttpResponse* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + NSString* currentGame = [serverInfoResp parseStringTag:@"currentgame"]; + NSString* pairStatus = [serverInfoResp parseStringTag:@"PairStatus"]; + NSString* currentClient = [serverInfoResp parseStringTag:@"CurrentClient"]; + if (![serverInfoResp isStatusOk] || currentGame == NULL || pairStatus == NULL) { [_callbacks launchFailed:@"Failed to connect to PC"]; return; } @@ -102,19 +102,21 @@ } - (BOOL) launchApp:(HttpManager*)hMan { - NSData* launchResp = [hMan executeRequestSynchronously: + HttpResponse* launchResp = [hMan executeRequestSynchronously: [hMan newLaunchRequest:_config.appID width:_config.width height:_config.height refreshRate:_config.frameRate rikey:[Utils bytesToHex:_config.riKey] rikeyid:_config.riKeyId]]; - NSString *gameSession = [HttpManager getStringFromXML:launchResp tag:@"gamesession"]; - if (launchResp == NULL) { + NSString *gameSession = [launchResp parseStringTag:@"gamesession"]; + if (launchResp == NULL || ![launchResp isStatusOk]) { [_callbacks launchFailed:@"Failed to launch app"]; + NSLog(@"Failed Launch Response: %@", launchResp.statusMessage); return FALSE; } else if (gameSession == NULL || [gameSession isEqualToString:@"0"]) { - [_callbacks launchFailed:[HttpManager getStatusStringFromXML:launchResp]]; + [_callbacks launchFailed:launchResp.statusMessage]; + NSLog(@"Failed to parse game session. Code: %ld Response: %@", (long)launchResp.statusCode, launchResp.statusMessage); return FALSE; } @@ -122,15 +124,17 @@ } - (BOOL) resumeApp:(HttpManager*)hMan { - NSData* resumeResp = [hMan executeRequestSynchronously: + HttpResponse* resumeResp = [hMan executeRequestSynchronously: [hMan newResumeRequestWithRiKey:[Utils bytesToHex:_config.riKey] riKeyId:_config.riKeyId]]; - NSString *resume = [HttpManager getStringFromXML:resumeResp tag:@"resume"]; - if (resumeResp == NULL) { + NSString* resume = [resumeResp parseStringTag:@"resume"]; + if (resumeResp == NULL || ![resumeResp isStatusOk]) { [_callbacks launchFailed:@"Failed to resume app"]; + NSLog(@"Failed Resume Response: %@", resumeResp.statusMessage); return FALSE; } else if (resume == NULL || [resume isEqualToString:@"0"]) { - [_callbacks launchFailed:[HttpManager getStatusStringFromXML:resumeResp]]; + [_callbacks launchFailed:resumeResp.statusMessage]; + NSLog(@"Failed to parse resume response. Code: %ld Response: %@", (long)resumeResp.statusCode, resumeResp.statusMessage); return FALSE; } diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 2d7e7ee..e21612d 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -69,18 +69,25 @@ static StreamConfiguration* streamConfig; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ _computerNameButton.title = _selectedHost.name; - [self.navigationController.navigationBar setNeedsLayout]; + [self.navigationController.navigationBar setNeedsLayout]; }); HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* appListResp = [hMan executeRequestSynchronously:[hMan newAppListRequest]]; - appList = [HttpManager getAppListFromXML:appListResp]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self updateApps]; - }); - - [_appManager stopRetrieving]; - [_appManager retrieveAssets:appList fromHost:_selectedHost]; + HttpResponse* appListResp = [hMan executeRequestSynchronously:[hMan newAppListRequest]]; + if (appListResp == nil || ![appListResp isStatusOk]) { + NSLog(@"Failed to get applist: %@", appListResp.statusMessage); + } else { + appList = [appListResp getAppList]; + if (appList == nil) { + NSLog(@"Failed to parse applist"); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateApps]; + }); + + [_appManager stopRetrieving]; + [_appManager retrieveAssets:appList fromHost:_selectedHost]; + } + } }); } @@ -108,16 +115,20 @@ static StreamConfiguration* streamConfig; _selectedHost = host; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - NSData* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; - if ([[HttpManager getStringFromXML:serverInfoResp tag:@"PairStatus"] isEqualToString:@"1"]) { - NSLog(@"Already Paired"); - [self alreadyPaired]; + HttpResponse* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]]; + if (serverInfoResp == nil || ![serverInfoResp isStatusOk]) { + NSLog(@"Failed to get server info: %@", serverInfoResp.statusMessage); } else { - NSLog(@"Trying to pair"); - // Polling the server while pairing causes the server to screw up - [_discMan stopDiscoveryBlocking]; - PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:_cert callback:self]; - [_opQueue addOperation:pMan]; + if ([[serverInfoResp parseStringTag:@"PairStatus"] isEqualToString:@"1"]) { + NSLog(@"Already Paired"); + [self alreadyPaired]; + } else { + NSLog(@"Trying to pair"); + // Polling the server while pairing causes the server to screw up + [_discMan stopDiscoveryBlocking]; + PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:_cert callback:self]; + [_opQueue addOperation:pMan]; + } } }); } @@ -163,7 +174,7 @@ static StreamConfiguration* streamConfig; // these two lines are required for iPad support of UIAlertSheet longClickAlert.popoverPresentationController.sourceView = view; - + longClickAlert.popoverPresentationController.sourceRect = CGRectMake(view.bounds.size.width / 2.0, view.bounds.size.height / 2.0, 1.0, 1.0); // center of the view [self presentViewController:longClickAlert animated:YES completion:^{ [self updateHosts]; @@ -227,29 +238,29 @@ static StreamConfiguration* streamConfig; App* currentApp = [self findRunningApp]; if (currentApp != nil) { UIAlertController* alertController = [UIAlertController - alertControllerWithTitle: app.appName - message: [app.appId isEqualToString:currentApp.appId] ? @"" : [NSString stringWithFormat:@"%@ is currently running", currentApp.appName]preferredStyle:UIAlertControllerStyleAlert]; + alertControllerWithTitle: app.appName + message: [app.appId isEqualToString:currentApp.appId] ? @"" : [NSString stringWithFormat:@"%@ is currently running", currentApp.appName]preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction - actionWithTitle:[app.appId isEqualToString:currentApp.appId] ? @"Resume App" : @"Resume Running App" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ - NSLog(@"Resuming application: %@", currentApp.appName); - [self performSegueWithIdentifier:@"createStreamFrame" sender:nil]; - }]]; + actionWithTitle:[app.appId isEqualToString:currentApp.appId] ? @"Resume App" : @"Resume Running App" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ + NSLog(@"Resuming application: %@", currentApp.appName); + [self performSegueWithIdentifier:@"createStreamFrame" sender:nil]; + }]]; [alertController addAction:[UIAlertAction actionWithTitle: - [app.appId isEqualToString:currentApp.appId] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ - NSLog(@"Quitting application: %@", currentApp.appName); - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - [hMan executeRequestSynchronously:[hMan newQuitAppRequest]]; - // TODO: handle failure to quit app - currentApp.isRunning = NO; - - if (![app.appId isEqualToString:currentApp.appId]) { - dispatch_async(dispatch_get_main_queue(), ^{ - [self performSegueWithIdentifier:@"createStreamFrame" sender:nil]; - }); - } - }); - }]]; + [app.appId isEqualToString:currentApp.appId] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ + NSLog(@"Quitting application: %@", currentApp.appName); + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; + [hMan executeRequestSynchronously:[hMan newQuitAppRequest]]; + // TODO: handle failure to quit app + currentApp.isRunning = NO; + + if (![app.appId isEqualToString:currentApp.appId]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self performSegueWithIdentifier:@"createStreamFrame" sender:nil]; + }); + } + }); + }]]; [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [self presentViewController:alertController animated:YES completion:nil]; } else { @@ -344,7 +355,7 @@ static StreamConfiguration* streamConfig; [super viewDidDisappear:animated]; // when discovery stops, we must create a new instance because you cannot restart an NSOperation when it is finished [_discMan stopDiscovery]; - + // In case the host objects were updated in the background [[[DataManager alloc] init] saveHosts]; }