diff --git a/Builds/Limelight_0_3_2.ipa b/Builds/Limelight_0_3_2.ipa new file mode 100644 index 00000000..fdec6d5c Binary files /dev/null and b/Builds/Limelight_0_3_2.ipa differ diff --git a/Limelight.xcodeproj/project.pbxproj b/Limelight.xcodeproj/project.pbxproj index a8e10cc3..e215a719 100644 --- a/Limelight.xcodeproj/project.pbxproj +++ b/Limelight.xcodeproj/project.pbxproj @@ -58,7 +58,13 @@ FB8946EB19F6AFE100339C8A /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946E019F6AFB800339C8A /* libcrypto.a */; }; FB8946EC19F6AFE400339C8A /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946E119F6AFB800339C8A /* libssl.a */; }; FB8946ED19F6AFE800339C8A /* libopus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946EA19F6AFB800339C8A /* libopus.a */; }; - FBD3494319FC9C04002D2A60 /* AppManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494219FC9C04002D2A60 /* AppManager.m */; }; + FB9AFD281A7C84ED00872C98 /* HttpResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD271A7C84ED00872C98 /* HttpResponse.m */; }; + FB9AFD321A7D867C00872C98 /* AppAssetRetriever.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD311A7D867C00872C98 /* AppAssetRetriever.m */; }; + FB9AFD371A7E02DB00872C98 /* HttpRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD361A7E02DB00872C98 /* HttpRequest.m */; }; + FB9AFD3A1A7E05CE00872C98 /* ServerInfoResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD391A7E05CE00872C98 /* ServerInfoResponse.m */; }; + FB9AFD3D1A7E111600872C98 /* AppAssetResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD3C1A7E111600872C98 /* AppAssetResponse.m */; }; + FB9AFD401A7E127D00872C98 /* AppListResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD3F1A7E127D00872C98 /* AppListResponse.m */; }; + FBD3494319FC9C04002D2A60 /* AppAssetManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494219FC9C04002D2A60 /* AppAssetManager.m */; }; FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494F19FF2174002D2A60 /* SettingsViewController.m */; }; FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */; }; FBD3495B1A004411002D2A60 /* Host.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495A1A004411002D2A60 /* Host.m */; }; @@ -247,8 +253,20 @@ FB8946E719F6AFB800339C8A /* opus_multistream.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = opus_multistream.h; sourceTree = ""; }; FB8946E819F6AFB800339C8A /* opus_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = opus_types.h; sourceTree = ""; }; FB8946EA19F6AFB800339C8A /* libopus.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libopus.a; sourceTree = ""; }; - FBD3494119FC9C04002D2A60 /* AppManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppManager.h; sourceTree = ""; }; - FBD3494219FC9C04002D2A60 /* AppManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppManager.m; sourceTree = ""; }; + FB9AFD261A7C84ED00872C98 /* HttpResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpResponse.h; sourceTree = ""; }; + FB9AFD271A7C84ED00872C98 /* HttpResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HttpResponse.m; sourceTree = ""; }; + FB9AFD301A7D867C00872C98 /* AppAssetRetriever.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAssetRetriever.h; sourceTree = ""; }; + FB9AFD311A7D867C00872C98 /* AppAssetRetriever.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAssetRetriever.m; sourceTree = ""; }; + FB9AFD351A7E02DB00872C98 /* HttpRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpRequest.h; sourceTree = ""; }; + FB9AFD361A7E02DB00872C98 /* HttpRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HttpRequest.m; sourceTree = ""; }; + FB9AFD381A7E05CE00872C98 /* ServerInfoResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerInfoResponse.h; sourceTree = ""; }; + FB9AFD391A7E05CE00872C98 /* ServerInfoResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServerInfoResponse.m; sourceTree = ""; }; + FB9AFD3B1A7E111600872C98 /* AppAssetResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAssetResponse.h; sourceTree = ""; }; + FB9AFD3C1A7E111600872C98 /* AppAssetResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAssetResponse.m; sourceTree = ""; }; + FB9AFD3E1A7E127D00872C98 /* AppListResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppListResponse.h; sourceTree = ""; }; + FB9AFD3F1A7E127D00872C98 /* AppListResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppListResponse.m; sourceTree = ""; }; + FBD3494119FC9C04002D2A60 /* AppAssetManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAssetManager.h; sourceTree = ""; }; + FBD3494219FC9C04002D2A60 /* AppAssetManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAssetManager.m; sourceTree = ""; }; FBD3494E19FF2174002D2A60 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; FBD3494F19FF2174002D2A60 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; FBD3495119FF36FB002D2A60 /* SWRevealViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWRevealViewController.h; sourceTree = ""; }; @@ -453,20 +471,13 @@ FB89460E19F646E200339C8A /* Network */ = { isa = PBXGroup; children = ( - FB89460F19F646E200339C8A /* HttpManager.h */, - FB89461019F646E200339C8A /* HttpManager.m */, - FB89461119F646E200339C8A /* MDNSManager.h */, - FB89461219F646E200339C8A /* MDNSManager.m */, + FB9AFD2F1A7C979700872C98 /* Http */, + FB9AFD341A7D877B00872C98 /* Discovery */, + FB9AFD331A7D876F00872C98 /* AppAsset */, FB89461319F646E200339C8A /* PairManager.h */, FB89461419F646E200339C8A /* PairManager.m */, - FBD3494119FC9C04002D2A60 /* AppManager.h */, - FBD3494219FC9C04002D2A60 /* AppManager.m */, FB4678FD1A565DAC00377732 /* WakeOnLanManager.h */, FB4678FE1A565DAC00377732 /* WakeOnLanManager.m */, - FB4678F81A55FFAD00377732 /* DiscoveryManager.h */, - FB4678F91A55FFAD00377732 /* DiscoveryManager.m */, - FB6549541A57907E001C8F39 /* DiscoveryWorker.h */, - FB6549551A57907E001C8F39 /* DiscoveryWorker.m */, ); path = Network; sourceTree = ""; @@ -673,6 +684,49 @@ path = lib; sourceTree = ""; }; + FB9AFD2F1A7C979700872C98 /* Http */ = { + isa = PBXGroup; + children = ( + FB89460F19F646E200339C8A /* HttpManager.h */, + FB89461019F646E200339C8A /* HttpManager.m */, + FB9AFD351A7E02DB00872C98 /* HttpRequest.h */, + FB9AFD361A7E02DB00872C98 /* HttpRequest.m */, + FB9AFD261A7C84ED00872C98 /* HttpResponse.h */, + FB9AFD271A7C84ED00872C98 /* HttpResponse.m */, + FB9AFD381A7E05CE00872C98 /* ServerInfoResponse.h */, + FB9AFD391A7E05CE00872C98 /* ServerInfoResponse.m */, + FB9AFD3B1A7E111600872C98 /* AppAssetResponse.h */, + FB9AFD3C1A7E111600872C98 /* AppAssetResponse.m */, + FB9AFD3E1A7E127D00872C98 /* AppListResponse.h */, + FB9AFD3F1A7E127D00872C98 /* AppListResponse.m */, + ); + name = Http; + sourceTree = ""; + }; + FB9AFD331A7D876F00872C98 /* AppAsset */ = { + isa = PBXGroup; + children = ( + FBD3494119FC9C04002D2A60 /* AppAssetManager.h */, + FBD3494219FC9C04002D2A60 /* AppAssetManager.m */, + FB9AFD301A7D867C00872C98 /* AppAssetRetriever.h */, + FB9AFD311A7D867C00872C98 /* AppAssetRetriever.m */, + ); + name = AppAsset; + sourceTree = ""; + }; + FB9AFD341A7D877B00872C98 /* Discovery */ = { + isa = PBXGroup; + children = ( + FB4678F81A55FFAD00377732 /* DiscoveryManager.h */, + FB4678F91A55FFAD00377732 /* DiscoveryManager.m */, + FB6549541A57907E001C8F39 /* DiscoveryWorker.h */, + FB6549551A57907E001C8F39 /* DiscoveryWorker.m */, + FB89461119F646E200339C8A /* MDNSManager.h */, + FB89461219F646E200339C8A /* MDNSManager.m */, + ); + name = Discovery; + sourceTree = ""; + }; FBD3495F1A004453002D2A60 /* Database */ = { isa = PBXGroup; children = ( @@ -815,11 +869,14 @@ FB89463219F646E200339C8A /* VideoDecoderRenderer.m in Sources */, FBD3495E1A004412002D2A60 /* Settings.m in Sources */, FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */, + FB9AFD401A7E127D00872C98 /* AppListResponse.m in Sources */, FB89463419F646E200339C8A /* Utils.m in Sources */, FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */, FB89462F19F646E200339C8A /* Connection.m in Sources */, + FB9AFD321A7D867C00872C98 /* AppAssetRetriever.m in Sources */, FB4678FF1A565DAC00377732 /* WakeOnLanManager.m in Sources */, FB89462919F646E200339C8A /* mkcert.c in Sources */, + FB9AFD281A7C84ED00872C98 /* HttpResponse.m in Sources */, FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */, FBDE86E919F82315001C18A8 /* App.m in Sources */, FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */, @@ -834,12 +891,15 @@ FB89463619F646E200339C8A /* StreamFrameViewController.m in Sources */, FB89462819F646E200339C8A /* CryptoManager.m in Sources */, FB89462E19F646E200339C8A /* PairManager.m in Sources */, + FB9AFD371A7E02DB00872C98 /* HttpRequest.m in Sources */, FB4678ED1A50C40900377732 /* OnScreenControls.m in Sources */, FB290D0019B2C406004C83CF /* main.m in Sources */, - FBD3494319FC9C04002D2A60 /* AppManager.m in Sources */, + FBD3494319FC9C04002D2A60 /* AppAssetManager.m in Sources */, FB6549561A57907E001C8F39 /* DiscoveryWorker.m in Sources */, FB89462A19F646E200339C8A /* ControllerSupport.m in Sources */, + FB9AFD3D1A7E111600872C98 /* AppAssetResponse.m in Sources */, FBD349621A0089F6002D2A60 /* DataManager.m in Sources */, + FB9AFD3A1A7E05CE00872C98 /* ServerInfoResponse.m in Sources */, FB89463119F646E200339C8A /* StreamManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Limelight/Limelight-Info.plist b/Limelight/Limelight-Info.plist index 2facde8c..87781286 100644 --- a/Limelight/Limelight-Info.plist +++ b/Limelight/Limelight-Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.3.1 + 0.3.2 CFBundleSignature ???? CFBundleVersion diff --git a/Limelight/Network/AppManager.h b/Limelight/Network/AppAssetManager.h similarity index 92% rename from Limelight/Network/AppManager.h rename to Limelight/Network/AppAssetManager.h index e0d4fbaa..736d444d 100644 --- a/Limelight/Network/AppManager.h +++ b/Limelight/Network/AppAssetManager.h @@ -17,7 +17,7 @@ @end -@interface AppManager : NSObject +@interface AppAssetManager : NSObject - (id) initWithCallback:(id)callback; - (void) retrieveAssets:(NSArray*)appList fromHost:(Host*)host; diff --git a/Limelight/Network/AppAssetManager.m b/Limelight/Network/AppAssetManager.m new file mode 100644 index 00000000..d7510a0b --- /dev/null +++ b/Limelight/Network/AppAssetManager.m @@ -0,0 +1,57 @@ +// +// AppManager.m +// Limelight +// +// Created by Diego Waxemberg on 10/25/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import "AppAssetManager.h" +#import "CryptoManager.h" +#import "Utils.h" +#import "HttpResponse.h" +#import "AppAssetRetriever.h" + +@implementation AppAssetManager { + NSOperationQueue* _opQueue; + id _callback; + Host* _host; + NSMutableDictionary* _imageCache; +} + +- (id) initWithCallback:(id)callback { + self = [super init]; + _callback = callback; + _opQueue = [[NSOperationQueue alloc] init]; + _imageCache = [[NSMutableDictionary alloc] init]; + + return self; +} + +- (void) retrieveAssets:(NSArray*)appList fromHost:(Host*)host { + Host* oldHost = _host; + _host = host; + BOOL useCache = [oldHost.uuid isEqualToString:_host.uuid]; + NSLog(@"Using cached app images: %d", useCache); + if (!useCache) { + [_imageCache removeAllObjects]; + } + for (App* app in appList) { + AppAssetRetriever* retriever = [[AppAssetRetriever alloc] init]; + retriever.app = app; + retriever.host = _host; + retriever.callback = _callback; + retriever.cache = useCache ? _imageCache : nil; + [_opQueue addOperation:retriever]; + } +} + +- (void) stopRetrieving { + [_opQueue cancelAllOperations]; +} + +- (void) sendCallBackForApp:(App*)app { + [_callback receivedAssetForApp:app]; +} + +@end diff --git a/Limelight/Network/AppAssetResponse.h b/Limelight/Network/AppAssetResponse.h new file mode 100644 index 00000000..e4fead48 --- /dev/null +++ b/Limelight/Network/AppAssetResponse.h @@ -0,0 +1,16 @@ +// +// AppAssetResponse.h +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import +#import "HttpResponse.h" + +@interface AppAssetResponse : NSObject + + + +@end diff --git a/Limelight/Network/AppAssetResponse.m b/Limelight/Network/AppAssetResponse.m new file mode 100644 index 00000000..94a2f1e2 --- /dev/null +++ b/Limelight/Network/AppAssetResponse.m @@ -0,0 +1,25 @@ +// +// AppAssetResponse.m +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "AppAssetResponse.h" + +@implementation AppAssetResponse +@synthesize data, statusCode, statusMessage; + +- (void)populateWithData:(NSData *)imageData { + self.data = imageData; + self.statusMessage = @"App asset has no status message"; + self.statusCode = -1; +} + +- (UIImage*) getImage { + UIImage* appImage = [[UIImage alloc] initWithData:self.data]; + return appImage; +} + +@end diff --git a/Limelight/Network/AppAssetRetriever.h b/Limelight/Network/AppAssetRetriever.h new file mode 100644 index 00000000..25301e1a --- /dev/null +++ b/Limelight/Network/AppAssetRetriever.h @@ -0,0 +1,21 @@ +// +// AppAssetRetriever.h +// Limelight +// +// Created by Diego Waxemberg on 1/31/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import +#import "Host.h" +#import "App.h" +#import "AppAssetManager.h" + +@interface AppAssetRetriever : NSOperation + +@property (nonatomic) Host* host; +@property (nonatomic) App* app; +@property (nonatomic) NSMutableDictionary* cache; +@property (nonatomic) id callback; + +@end diff --git a/Limelight/Network/AppAssetRetriever.m b/Limelight/Network/AppAssetRetriever.m new file mode 100644 index 00000000..3a220eab --- /dev/null +++ b/Limelight/Network/AppAssetRetriever.m @@ -0,0 +1,54 @@ +// +// AppAssetRetriever.m +// Limelight +// +// Created by Diego Waxemberg on 1/31/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "AppAssetRetriever.h" +#import "HttpManager.h" +#import "CryptoManager.h" +#import "AppAssetResponse.h" +#import "HttpRequest.h" + +@implementation AppAssetRetriever +static const double RETRY_DELAY = 1; // seconds + + +- (void) main { + UIImage* appImage = nil; + while (![self isCancelled] && appImage == nil) { + if (self.cache) { + @synchronized(self.cache) { + UIImage* cachedImage = [self.cache objectForKey:self.app.appId]; + if (cachedImage != nil) { + appImage = cachedImage; + self.app.appImage = appImage; + } + } + } + if (appImage == nil) { + HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.address uniqueId:[CryptoManager getUniqueID] deviceName:deviceName cert:[CryptoManager readCertFromFile]]; + AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.appId]]]; + + appImage = [UIImage imageWithData:appAssetResp.data]; + self.app.appImage = appImage; + if (appImage != nil) { + @synchronized(self.cache) { + [self.cache setObject:appImage forKey:self.app.appId]; + } + } + } + + [NSThread sleepForTimeInterval:RETRY_DELAY]; + } + [self performSelectorOnMainThread:@selector(sendCallbackForApp:) withObject:self.app waitUntilDone:NO]; +} + +- (void) sendCallbackForApp:(App*)app { + [self.callback receivedAssetForApp:app]; +} + +@end diff --git a/Limelight/Network/AppListResponse.h b/Limelight/Network/AppListResponse.h new file mode 100644 index 00000000..8c953feb --- /dev/null +++ b/Limelight/Network/AppListResponse.h @@ -0,0 +1,17 @@ +// +// AppListResponse.h +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "HttpResponse.h" + +@interface AppListResponse : NSObject + +- (void)populateWithData:(NSData *)data; +- (NSArray*) getAppList; +- (BOOL) isStatusOk; + +@end diff --git a/Limelight/Network/AppListResponse.m b/Limelight/Network/AppListResponse.m new file mode 100644 index 00000000..5c8e929e --- /dev/null +++ b/Limelight/Network/AppListResponse.m @@ -0,0 +1,106 @@ +// +// AppListResponse.m +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "AppListResponse.h" +#import "App.h" +#import + +@implementation AppListResponse { + NSMutableArray* _appList; +} +@synthesize data, statusCode, statusMessage; + +static const char* TAG_APP = "App"; +static const char* TAG_APP_TITLE = "AppTitle"; +static const char* TAG_APP_ID = "ID"; +static const char* TAG_APP_IS_RUNNING = "IsRunning"; + +- (void)populateWithData:(NSData *)xml { + self.data = xml; + _appList = [[NSMutableArray alloc] init]; + [self parseData]; +} + +- (void) parseData { + xmlDocPtr docPtr = xmlParseMemory([self.data bytes], (int)[self.data length]); + if (docPtr == NULL) { + NSLog(@"ERROR: An error occured trying to parse xml."); + return; + } + + xmlNodePtr node = xmlDocGetRootElement(docPtr); + if (node == NULL) { + NSLog(@"ERROR: No root XML element."); + xmlFreeDoc(docPtr); + return; + } + + xmlChar* statusStr = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_CODE UTF8String]); + if (statusStr != NULL) { + int status = [[NSString stringWithUTF8String:(const char*)statusStr] intValue]; + xmlFree(statusStr); + self.statusCode = status; + } + + xmlChar* statusMsgXml = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_MESSAGE UTF8String]); + NSString* statusMsg; + if (statusMsgXml != NULL) { + statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml]; + xmlFree(statusMsgXml); + } + else { + statusMsg = @"Server Error"; + } + self.statusMessage = statusMsg; + + node = node->children; + + while (node != NULL) { + //NSLog(@"node: %s", node->name); + if (!xmlStrcmp(node->name, (xmlChar*)TAG_APP)) { + xmlNodePtr appInfoNode = node->xmlChildrenNode; + NSString* appName; + NSString* appId; + BOOL appIsRunning = NO; + while (appInfoNode != NULL) { + if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_TITLE)) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); + appName = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_ID)) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1); + appId = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + xmlFree(nodeVal); + } else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_IS_RUNNING)) { + 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); +} + +- (NSArray*) getAppList { + return _appList; +} + +- (BOOL) isStatusOk { + return self.statusCode == 200; +} + +@end diff --git a/Limelight/Network/AppManager.m b/Limelight/Network/AppManager.m deleted file mode 100644 index f597fb61..00000000 --- a/Limelight/Network/AppManager.m +++ /dev/null @@ -1,79 +0,0 @@ -// -// AppManager.m -// Limelight -// -// Created by Diego Waxemberg on 10/25/14. -// Copyright (c) 2014 Limelight Stream. All rights reserved. -// - -#import "AppManager.h" -#import "CryptoManager.h" -#import "Utils.h" - -@implementation AppManager { - NSOperationQueue* _opQueue; - id _callback; - Host* _host; - NSString* _uniqueId; - NSData* _cert; - NSMutableDictionary* _imageCache; -} - -- (id) initWithCallback:(id)callback { - self = [super init]; - _callback = callback; - _opQueue = [[NSOperationQueue alloc] init]; - _imageCache = [[NSMutableDictionary alloc] init]; - _uniqueId = [CryptoManager getUniqueID]; - return self; -} - -- (void) retrieveAssets:(NSArray*)appList fromHost:(Host*)host { - Host* oldHost = _host; - _host = host; - BOOL useCache = [oldHost.uuid isEqualToString:_host.uuid]; - NSLog(@"Using cached app images: %d", useCache); - if (!useCache) { - [_imageCache removeAllObjects]; - } - for (App* app in appList) { - [_opQueue addOperationWithBlock:^{ - [self retrieveAssetForApp:app useCache:useCache]; - }]; - } -} - -- (void) stopRetrieving { - [_opQueue cancelAllOperations]; -} - -- (void) retrieveAssetForApp:(App*)app useCache:(BOOL)useCache { - UIImage* appImage = nil; - if (useCache) { - @synchronized(_imageCache) { - UIImage* cachedImage = [_imageCache objectForKey:app.appId]; - if (cachedImage != nil) { - appImage = cachedImage; - app.appImage = appImage; - } - } - } - 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]; - app.appImage = appImage; - if (appImage != nil) { - @synchronized(_imageCache) { - [_imageCache setObject:appImage forKey:app.appId]; - } - } - } - [self performSelectorOnMainThread:@selector(sendCallBackForApp:) withObject:app waitUntilDone:NO]; -} - -- (void) sendCallBackForApp:(App*)app { - [_callback receivedAssetForApp:app]; -} - -@end diff --git a/Limelight/Network/DiscoveryManager.m b/Limelight/Network/DiscoveryManager.m index 60ec61c8..48c2fb7e 100644 --- a/Limelight/Network/DiscoveryManager.m +++ b/Limelight/Network/DiscoveryManager.m @@ -12,6 +12,7 @@ #import "Utils.h" #import "DataManager.h" #import "DiscoveryWorker.h" +#import "ServerInfoResponse.h" @implementation DiscoveryManager { NSMutableArray* _hostQueue; @@ -39,14 +40,16 @@ - (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]]; + ServerInfoResponse* serverInfoResponse = [[ServerInfoResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[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]; + host.online = YES; + [serverInfoResponse populateHost:host]; if (![self addHostToDiscovery:host]) { [dataMan removeHost:host]; } diff --git a/Limelight/Network/DiscoveryWorker.m b/Limelight/Network/DiscoveryWorker.m index 7cf8f047..53ac5755 100644 --- a/Limelight/Network/DiscoveryWorker.m +++ b/Limelight/Network/DiscoveryWorker.m @@ -9,6 +9,8 @@ #import "DiscoveryWorker.h" #import "Utils.h" #import "HttpManager.h" +#import "ServerInfoResponse.h" +#import "HttpRequest.h" @implementation DiscoveryWorker { Host* _host; @@ -43,18 +45,21 @@ 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]; + + ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[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]; + ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc]init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]]]; + if ([serverInfoResp isStatusOk]) { + [serverInfoResp populateHost:_host]; _host.address = _host.externalAddress; receivedResponse = YES; } @@ -62,18 +67,16 @@ 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]; + ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]]]; + if ([serverInfoResp isStatusOk]) { + [serverInfoResp populateHost:_host]; receivedResponse = YES; } } _host.online = receivedResponse; if (receivedResponse) { - NSLog(@"Received response from: %@\n{\n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n}", _host.name, _host.address, _host.localAddress, _host.externalAddress, _host.uuid, _host.mac, _host.pairState, _host.online); - } else { - // If the host is not online, we do not know the pairstate - _host.pairState = PairStateUnknown; + NSLog(@"Received response from: %@\n{\n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n}", _host.name, _host.address, _host.localAddress, _host.externalAddress, _host.uuid, _host.mac, _host.pairState, _host.online); } } diff --git a/Limelight/Network/HttpManager.h b/Limelight/Network/HttpManager.h index 68c2f390..c7cba953 100644 --- a/Limelight/Network/HttpManager.h +++ b/Limelight/Network/HttpManager.h @@ -8,14 +8,11 @@ #import #import "Host.h" +#import "HttpResponse.h" +#import "HttpRequest.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 +26,9 @@ - (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId; - (NSURLRequest*) newQuitAppRequest; - (NSURLRequest*) newAppAssetRequestWithAppId:(NSString*)appId; -- (NSData*) executeRequestSynchronously:(NSURLRequest*)request; +- (void) executeRequestSynchronously:(HttpRequest*)request; +- (void) executeRequest:(HttpRequest*)request; + @end diff --git a/Limelight/Network/HttpManager.m b/Limelight/Network/HttpManager.m index c5982c03..a3d1a9b7 100644 --- a/Limelight/Network/HttpManager.m +++ b/Limelight/Network/HttpManager.m @@ -7,6 +7,7 @@ // #import "HttpManager.h" +#import "HttpRequest.h" #import "CryptoManager.h" #import "App.h" @@ -26,207 +27,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,18 +47,20 @@ static const NSString* PORT = @"47984"; return self; } -- (NSData*) executeRequestSynchronously:(NSURLRequest*)request { +- (void) executeRequestSynchronously:(HttpRequest*)request { NSLog(@"Making Request: %@", request); [_respData setLength:0]; dispatch_sync(dispatch_get_main_queue(), ^{ - [NSURLConnection connectionWithRequest:request delegate:self]; + [NSURLConnection connectionWithRequest:request.request delegate:self]; }); dispatch_semaphore_wait(_requestLock, DISPATCH_TIME_FOREVER); - return _requestResp; + if (request.response) { + [request.response populateWithData:_requestResp]; + } } -- (void) executeRequest:(NSURLRequest*)request { - [NSURLConnection connectionWithRequest:request delegate:self]; +- (void) executeRequest:(HttpRequest*)request { + [NSURLConnection connectionWithRequest:request.request delegate:self]; } - (NSURLRequest*) createRequestFromString:(NSString*) urlString enableTimeout:(BOOL)normalTimeout { diff --git a/Limelight/Network/HttpRequest.h b/Limelight/Network/HttpRequest.h new file mode 100644 index 00000000..6cb71583 --- /dev/null +++ b/Limelight/Network/HttpRequest.h @@ -0,0 +1,20 @@ +// +// HttpRequest.h +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import +#import "HttpResponse.h" + +@interface HttpRequest : NSObject + +@property (nonatomic) id response; +@property (nonatomic) NSURLRequest* request; + ++ (instancetype) requestForResponse:(id)response withUrlRequest:(NSURLRequest*)req; ++ (instancetype) requestWithUrlRequest:(NSURLRequest*)req; + +@end diff --git a/Limelight/Network/HttpRequest.m b/Limelight/Network/HttpRequest.m new file mode 100644 index 00000000..e07954b7 --- /dev/null +++ b/Limelight/Network/HttpRequest.m @@ -0,0 +1,28 @@ +// +// HttpRequest.m +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "HttpRequest.h" +#import "HttpResponse.h" +#import "HttpManager.h" + +@implementation HttpRequest + ++ (HttpRequest*) requestForResponse:(id)response withUrlRequest:(NSURLRequest*)req { + HttpRequest* request = [[HttpRequest alloc] init]; + request.request = req; + request.response = response; + return request; +} + ++ (HttpRequest*) requestWithUrlRequest:(NSURLRequest*)req { + HttpRequest* request = [[HttpRequest alloc] init]; + request.request = req; + return request; +} + +@end diff --git a/Limelight/Network/HttpResponse.h b/Limelight/Network/HttpResponse.h new file mode 100644 index 00000000..d5a2612d --- /dev/null +++ b/Limelight/Network/HttpResponse.h @@ -0,0 +1,33 @@ +// +// HttpResponse.h +// Limelight +// +// Created by Diego Waxemberg on 1/30/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import +#import "Host.h" + +static NSString* TAG_STATUS_CODE = @"status_code"; +static NSString* TAG_STATUS_MESSAGE = @"status_message"; + +@protocol Response + +- (void) populateWithData:(NSData*)data; + +@property (nonatomic) NSInteger statusCode; +@property (nonatomic) NSString* statusMessage; +@property (nonatomic) NSData* data; + +@end + +@interface HttpResponse : NSObject + +- (void) populateWithData:(NSData*)data; +- (void) parseData; +- (NSString*) getStringTag:(NSString*)tag; +- (BOOL) getIntTag:(NSString *)tag value:(NSInteger*)value; +- (BOOL) isStatusOk; + +@end diff --git a/Limelight/Network/HttpResponse.m b/Limelight/Network/HttpResponse.m new file mode 100644 index 00000000..f2d2a1c3 --- /dev/null +++ b/Limelight/Network/HttpResponse.m @@ -0,0 +1,94 @@ +// +// 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 { + NSMutableDictionary* _elements; +} +@synthesize data, statusCode, statusMessage; + +- (void) populateWithData:(NSData*)xml { + self.data = xml; + [self parseData]; +} + +- (NSString*) getStringTag:(NSString*)tag { + return [_elements objectForKey:tag]; +} + +- (BOOL) getIntTag:(NSString *)tag value:(NSInteger*)value { + NSString* stringVal = [self getStringTag:tag]; + if (stringVal != nil) { + *value = [stringVal integerValue]; + return true; + } else { + return false; + } +} + +- (BOOL) isStatusOk { + return self.statusCode == 200; +} + +- (void) parseData { + _elements = [[NSMutableDictionary alloc] init]; + xmlDocPtr docPtr = xmlParseMemory([self.data bytes], (int)[self.data length]); + if (docPtr == NULL) { + NSLog(@"ERROR: An error occured trying to parse xml."); + return; + } + + xmlNodePtr node = xmlDocGetRootElement(docPtr); + if (node == NULL) { + NSLog(@"ERROR: No root XML element."); + xmlFreeDoc(docPtr); + return; + } + + xmlChar* statusStr = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_CODE UTF8String]); + if (statusStr != NULL) { + int status = [[NSString stringWithUTF8String:(const char*)statusStr] intValue]; + xmlFree(statusStr); + self.statusCode = status; + } + + xmlChar* statusMsgXml = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_MESSAGE UTF8String]); + NSString* statusMsg; + if (statusMsgXml != NULL) { + statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml]; + xmlFree(statusMsgXml); + } + else { + statusMsg = @"Server Error"; + } + self.statusMessage = statusMsg; + + node = node->children; + + while (node != NULL) { + xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1); + + NSString* value; + if (nodeVal == NULL) { + value = @""; + } else { + value = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding]; + } + NSString* key = [[NSString alloc] initWithCString:(const char*)node->name encoding:NSUTF8StringEncoding]; + [_elements setObject:value forKey:key]; + xmlFree(nodeVal); + node = node->next; + } + + xmlFreeDoc(docPtr); +} + +@end diff --git a/Limelight/Network/PairManager.m b/Limelight/Network/PairManager.m index 0f72ecfe..b462486c 100644 --- a/Limelight/Network/PairManager.m +++ b/Limelight/Network/PairManager.m @@ -9,6 +9,9 @@ #import "PairManager.h" #import "CryptoManager.h" #import "Utils.h" +#import "HttpResponse.h" +#import "HttpRequest.h" +#import "ServerInfoResponse.h" #include @@ -27,18 +30,20 @@ } - (void) main { - NSData* serverInfo = [_httpManager executeRequestSynchronously:[_httpManager newServerInfoRequest]]; - if (serverInfo == NULL) { + ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; + [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[_httpManager newServerInfoRequest]]]; + if (serverInfoResp == 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 ([serverInfoResp isStatusOk]) { + if (![[serverInfoResp getStringTag:@"currentgame"] isEqual:@"0"]) { + [_callback pairFailed:@"You must stop streaming before attempting to pair."]; + } else if (![[serverInfoResp getStringTag:@"PairStatus"] isEqual:@"1"]) { + [self initiatePair]; + } else { + [_callback alreadyPaired]; + } } } @@ -48,16 +53,19 @@ 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 = [[HttpResponse alloc] init]; + [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:pairResp withUrlRequest:[_httpManager newPairRequest:salt]]]; + if (![self verifyResponseStatus:pairResp]) { + return; + } + NSInteger pairedStatus; + if (![pairResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing was declined by the target."]; return; } - NSString* plainCert = [HttpManager getStringFromXML:pairResp tag:@"plaincert"]; + NSString* plainCert = [pairResp getStringTag:@"plaincert"]; CryptoManager* cryptoMan = [[CryptoManager alloc] init]; NSData* aesKey = [cryptoMan createAESKeyFromSalt:salt]; @@ -65,15 +73,18 @@ 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 = [[HttpResponse alloc] init]; + [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:challengeResp withUrlRequest:[_httpManager newChallengeRequest:encryptedChallenge]]]; + if (![self verifyResponseStatus:challengeResp]) { + return; + } + if (![challengeResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing stage #2 failed"]; return; } - NSData* encServerChallengeResp = [Utils hexToBytes:[HttpManager getStringFromXML:challengeResp tag:@"challengeresponse"]]; + NSData* encServerChallengeResp = [Utils hexToBytes:[challengeResp getStringTag:@"challengeresponse"]]; NSData* decServerChallengeResp = [cryptoMan aesDecrypt:encServerChallengeResp withKey:aesKey]; NSData* serverResponse = [decServerChallengeResp subdataWithRange:NSMakeRange(0, 20)]; @@ -83,50 +94,73 @@ 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 = [[HttpResponse alloc] init]; + [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:secretResp withUrlRequest:[_httpManager newChallengeRespRequest:challengeRespEncrypted]]]; + if (![self verifyResponseStatus:secretResp]) { + return; + } + if (![secretResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing stage #3 failed"]; return; } - NSData* serverSecretResp = [Utils hexToBytes:[HttpManager getStringFromXML:secretResp tag:@"pairingsecret"]]; + NSData* serverSecretResp = [Utils hexToBytes:[secretResp getStringTag:@"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:[HttpRequest requestWithUrlRequest:[_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:[HttpRequest requestWithUrlRequest:[_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 = [[HttpResponse alloc] init]; + [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:clientSecretResp withUrlRequest:[_httpManager newClientSecretRespRequest:[Utils bytesToHex:clientPairingSecret]]]]; + if (![self verifyResponseStatus:clientSecretResp]) { + return; + } + if (![clientSecretResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_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 = [[HttpResponse alloc] init]; + [_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:clientPairChallengeResp withUrlRequest:[_httpManager newPairChallenge]]]; + if (![self verifyResponseStatus:clientPairChallengeResp]) { + return; + } + if (![clientPairChallengeResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) { + [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Pairing stage #5 failed"]; return; } [_callback pairSuccessful]; } +- (BOOL) verifyResponseStatus:(HttpResponse*)resp { + if (resp == nil) { + [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; + [_callback pairFailed:@"Network error occured."]; + return false; + } else if (![resp isStatusOk]) { + [_httpManager executeRequest:[HttpRequest requestWithUrlRequest:[_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/Network/ServerInfoResponse.h b/Limelight/Network/ServerInfoResponse.h new file mode 100644 index 00000000..bae71d0e --- /dev/null +++ b/Limelight/Network/ServerInfoResponse.h @@ -0,0 +1,16 @@ +// +// ServerInfoResponse.h +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "HttpResponse.h" + +@interface ServerInfoResponse : HttpResponse + +- (void) populateWithData:(NSData *)data; +- (void) populateHost:(Host*)host; + +@end diff --git a/Limelight/Network/ServerInfoResponse.m b/Limelight/Network/ServerInfoResponse.m new file mode 100644 index 00000000..5d074613 --- /dev/null +++ b/Limelight/Network/ServerInfoResponse.m @@ -0,0 +1,42 @@ +// +// ServerInfoResponse.m +// Limelight +// +// Created by Diego Waxemberg on 2/1/15. +// Copyright (c) 2015 Limelight Stream. All rights reserved. +// + +#import "ServerInfoResponse.h" +#import + +@implementation ServerInfoResponse +@synthesize data, statusCode, statusMessage; + +static NSString* TAG_HOSTNAME = @"hostname"; +static NSString* TAG_EXTERNAL_IP = @"ExternalIP"; +static NSString* TAG_LOCAL_IP = @"LocalIP"; +static NSString* TAG_UNIQUE_ID = @"uniqueid"; +static NSString* TAG_MAC_ADDRESS = @"mac"; +static NSString* TAG_PAIR_STATUS = @"PairStatus"; + +- (void) populateWithData:(NSData *)xml { + self.data = xml; + [super parseData]; +} + +- (void) populateHost:(Host*)host { + host.name = [self getStringTag:TAG_HOSTNAME]; + host.externalAddress = [self getStringTag:TAG_EXTERNAL_IP]; + host.localAddress = [self getStringTag:TAG_LOCAL_IP]; + host.uuid = [self getStringTag:TAG_UNIQUE_ID]; + host.mac = [self getStringTag:TAG_MAC_ADDRESS]; + + NSInteger pairStatus; + if ([self getIntTag:TAG_PAIR_STATUS value:&pairStatus]) { + host.pairState = pairStatus ? PairStatePaired : PairStateUnpaired; + } else { + host.pairState = PairStateUnknown; + } +} + +@end diff --git a/Limelight/Stream/StreamManager.m b/Limelight/Stream/StreamManager.m index f57c6b99..dc77bce9 100644 --- a/Limelight/Stream/StreamManager.m +++ b/Limelight/Stream/StreamManager.m @@ -12,6 +12,9 @@ #import "Utils.h" #import "OnScreenControls.h" #import "StreamView.h" +#import "ServerInfoResponse.h" +#import "HttpResponse.h" +#import "HttpRequest.h" @implementation StreamManager { StreamConfiguration* _config; @@ -41,11 +44,12 @@ 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) { + ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]]]; + NSString* currentGame = [serverInfoResp getStringTag:@"currentgame"]; + NSString* pairStatus = [serverInfoResp getStringTag:@"PairStatus"]; + NSString* currentClient = [serverInfoResp getStringTag:@"CurrentClient"]; + if (![serverInfoResp isStatusOk] || currentGame == NULL || pairStatus == NULL) { [_callbacks launchFailed:@"Failed to connect to PC"]; return; } @@ -102,19 +106,22 @@ } - (BOOL) launchApp:(HttpManager*)hMan { - NSData* launchResp = [hMan executeRequestSynchronously: + HttpResponse* launchResp = [[HttpResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:launchResp withUrlRequest: [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) { + rikeyid:_config.riKeyId]]]; + NSString *gameSession = [launchResp getStringTag:@"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 +129,18 @@ } - (BOOL) resumeApp:(HttpManager*)hMan { - NSData* resumeResp = [hMan executeRequestSynchronously: + HttpResponse* resumeResp = [[HttpResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:resumeResp withUrlRequest: [hMan newResumeRequestWithRiKey:[Utils bytesToHex:_config.riKey] - riKeyId:_config.riKeyId]]; - NSString *resume = [HttpManager getStringFromXML:resumeResp tag:@"resume"]; - if (resumeResp == NULL) { + riKeyId:_config.riKeyId]]]; + NSString* resume = [resumeResp getStringTag:@"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.h b/Limelight/ViewControllers/MainFrameViewController.h index 48ab399a..560e0070 100644 --- a/Limelight/ViewControllers/MainFrameViewController.h +++ b/Limelight/ViewControllers/MainFrameViewController.h @@ -12,13 +12,12 @@ #import "StreamConfiguration.h" #import "UIComputerView.h" #import "UIAppView.h" -#import "AppManager.h" +#import "AppAssetManager.h" #import "SWRevealViewController.h" @interface MainFrameViewController : UICollectionViewController + @property (strong, nonatomic) IBOutlet UIButton *limelightLogoButton; @property (weak, nonatomic) IBOutlet UIBarButtonItem *computerNameButton; -+ (StreamConfiguration*) getStreamConfiguration; - @end diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 2d7e7eea..24ec1807 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -9,7 +9,6 @@ #import "CryptoManager.h" #import "HttpManager.h" #import "Connection.h" -#import "VideoDecoderRenderer.h" #import "StreamManager.h" #import "Utils.h" #import "UIComputerView.h" @@ -19,6 +18,9 @@ #import "DataManager.h" #import "Settings.h" #import "WakeOnLanManager.h" +#import "AppListResponse.h" +#import "ServerInfoResponse.h" +#import "StreamFrameViewController.h" @implementation MainFrameViewController { NSOperationQueue* _opQueue; @@ -27,18 +29,14 @@ NSData* _cert; NSString* _currentGame; DiscoveryManager* _discMan; - AppManager* _appManager; + AppAssetManager* _appManager; + StreamConfiguration* _streamConfig; UIAlertView* _pairAlert; UIScrollView* hostScrollView; int currentPosition; } static NSMutableSet* hostList; static NSArray* appList; -static StreamConfiguration* streamConfig; - -+ (StreamConfiguration*) getStreamConfiguration { - return streamConfig; -} - (void)showPIN:(NSString *)PIN { dispatch_sync(dispatch_get_main_queue(), ^{ @@ -69,18 +67,27 @@ 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]; + AppListResponse* appListResp = [[AppListResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:appListResp withUrlRequest:[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,22 @@ 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]; + ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; + [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[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]; + NSLog(@"server info pair status: %@", [serverInfoResp getStringTag:@"PairStatus"]); + if ([[serverInfoResp getStringTag:@"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]; + } } }); } @@ -129,7 +142,7 @@ static StreamConfiguration* streamConfig; [longClickAlert addAction:[UIAlertAction actionWithTitle:@"Unpair" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert]; - [hMan executeRequestSynchronously:[hMan newUnpairRequest]]; + [hMan executeRequest:[HttpRequest requestWithUrlRequest:[hMan newUnpairRequest]]]; }); }]]; } else { @@ -163,7 +176,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]; @@ -202,11 +215,11 @@ static StreamConfiguration* streamConfig; - (void) appClicked:(App *)app { NSLog(@"Clicked app: %@", app.appName); - streamConfig = [[StreamConfiguration alloc] init]; - streamConfig.host = _selectedHost.address; - streamConfig.hostAddr = [Utils resolveHost:_selectedHost.address]; - streamConfig.appID = app.appId; - if (streamConfig.hostAddr == 0) { + _streamConfig = [[StreamConfiguration alloc] init]; + _streamConfig.host = _selectedHost.address; + _streamConfig.hostAddr = [Utils resolveHost:_selectedHost.address]; + _streamConfig.appID = app.appId; + if (_streamConfig.hostAddr == 0) { [self displayDnsFailedDialog]; return; } @@ -214,10 +227,10 @@ static StreamConfiguration* streamConfig; DataManager* dataMan = [[DataManager alloc] init]; Settings* streamSettings = [dataMan retrieveSettings]; - streamConfig.frameRate = [streamSettings.framerate intValue]; - streamConfig.bitRate = [streamSettings.bitrate intValue]; - streamConfig.height = [streamSettings.height intValue]; - streamConfig.width = [streamSettings.width intValue]; + _streamConfig.frameRate = [streamSettings.framerate intValue]; + _streamConfig.bitRate = [streamSettings.bitrate intValue]; + _streamConfig.height = [streamSettings.height intValue]; + _streamConfig.width = [streamSettings.width intValue]; if (currentPosition != FrontViewPositionLeft) { @@ -227,29 +240,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:[HttpRequest requestWithUrlRequest:[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 { @@ -275,14 +288,21 @@ static StreamConfiguration* streamConfig; currentPosition = position; } +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + if ([segue.destinationViewController isKindOfClass:[StreamFrameViewController class]]) { + StreamFrameViewController* streamFrame = segue.destinationViewController; + streamFrame.streamConfig = _streamConfig; + } +} + - (void)viewDidLoad { [super viewDidLoad]; - // Set the side bar button action. When it's tapped, it'll show up the sidebar. + // Set the side bar button action. When it's tapped, it'll show the sidebar. [_limelightLogoButton addTarget:self.revealViewController action:@selector(revealToggle:) forControlEvents:UIControlEventTouchDown]; - // Set the host name button action. When it's tapped, it'll show up the host selection view. + // Set the host name button action. When it's tapped, it'll show the host selection view. [_computerNameButton setTarget:self]; [_computerNameButton setAction:@selector(showHostSelectionView)]; @@ -296,12 +316,12 @@ static StreamConfiguration* streamConfig; currentPosition = FrontViewPositionLeft; // Set up crypto - _opQueue = [[NSOperationQueue alloc] init]; [CryptoManager generateKeyPairUsingSSl]; _uniqueId = [CryptoManager getUniqueID]; _cert = [CryptoManager readCertFromFile]; - - _appManager = [[AppManager alloc] initWithCallback:self]; + + _appManager = [[AppAssetManager alloc] initWithCallback:self]; + _opQueue = [[NSOperationQueue alloc] init]; // Only initialize the host picker list once if (hostList == nil) { @@ -344,7 +364,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]; } @@ -450,4 +470,5 @@ static StreamConfiguration* streamConfig; - (BOOL)shouldAutorotate { return YES; } + @end diff --git a/Limelight/ViewControllers/StreamFrameViewController.h b/Limelight/ViewControllers/StreamFrameViewController.h index 35d0dc43..68cba510 100644 --- a/Limelight/ViewControllers/StreamFrameViewController.h +++ b/Limelight/ViewControllers/StreamFrameViewController.h @@ -7,11 +7,13 @@ // #import "Connection.h" +#import "StreamConfiguration.h" #import @interface StreamFrameViewController : UIViewController @property (strong, nonatomic) IBOutlet UILabel *stageLabel; @property (strong, nonatomic) IBOutlet UIActivityIndicatorView *spinner; +@property (nonatomic) StreamConfiguration* streamConfig; @end diff --git a/Limelight/ViewControllers/StreamFrameViewController.m b/Limelight/ViewControllers/StreamFrameViewController.m index c4dd47ad..fbbfcf63 100644 --- a/Limelight/ViewControllers/StreamFrameViewController.m +++ b/Limelight/ViewControllers/StreamFrameViewController.m @@ -36,7 +36,7 @@ _controllerSupport = [[ControllerSupport alloc] init]; - _streamMan = [[StreamManager alloc] initWithConfig:[MainFrameViewController getStreamConfiguration] + _streamMan = [[StreamManager alloc] initWithConfig:self.streamConfig renderView:self.view connectionCallbacks:self]; NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];