├── .gitignore ├── Images ├── Icon.png ├── logo.png ├── Default.png ├── Icon@2x.png ├── app-off.png ├── app-on.png ├── logo-type.png ├── Default@2x.png ├── Icon-white.png ├── Icon_Large.png ├── Icon-white@2x.png ├── Icon-white_Large.png └── logo-type-white.png ├── Libraries ├── JSONFramework │ ├── JSONFramework.h │ ├── NSString+SBJSON.h │ ├── NSString+SBJSON.m │ ├── NSObject+SBJSON.m │ ├── NSObject+SBJSON.h │ ├── SBJSON.h │ └── SBJSON.m └── ASI │ ├── NSHTTPCookieAdditions.h │ ├── ASINSStringAdditions.h │ ├── ASIInputStream.h │ ├── ASINSStringAdditions.m │ ├── NSHTTPCookieAdditions.m │ ├── ASIAuthenticationDialog.h │ ├── ASIFormDataRequest.h │ ├── ASIInputStream.m │ ├── ASINetworkQueue.h │ ├── Reachability.h │ ├── ASIFormDataRequest.m │ ├── ASIAuthenticationDialog.m │ ├── ASINetworkQueue.m │ ├── Reachability.m │ └── ASIHTTPRequest.h ├── iNodejitsu_Prefix.pch ├── main.m ├── Classes ├── LogInfoViewController.h ├── AppsViewController.h ├── LoginViewController.h ├── AppDetailViewController.h ├── LogsListViewController.h ├── iNodejitsuAppDelegate.h ├── Nodejitsu.h ├── LogInfoViewController.m ├── LoginViewController.m ├── KeychainItemWrapper.h ├── LogsListViewController.m ├── iNodejitsuAppDelegate.m ├── AppDetailViewController.m ├── AppsViewController.m ├── Nodejitsu.m └── KeychainItemWrapper.m ├── iNodejitsu-Info.plist └── MainWindow.xib /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build 3 | .DS_Store 4 | *.pbxuser 5 | *.perspectivev3 -------------------------------------------------------------------------------- /Images/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Icon.png -------------------------------------------------------------------------------- /Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/logo.png -------------------------------------------------------------------------------- /Images/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Default.png -------------------------------------------------------------------------------- /Images/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Icon@2x.png -------------------------------------------------------------------------------- /Images/app-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/app-off.png -------------------------------------------------------------------------------- /Images/app-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/app-on.png -------------------------------------------------------------------------------- /Images/logo-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/logo-type.png -------------------------------------------------------------------------------- /Images/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Default@2x.png -------------------------------------------------------------------------------- /Images/Icon-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Icon-white.png -------------------------------------------------------------------------------- /Images/Icon_Large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Icon_Large.png -------------------------------------------------------------------------------- /Images/Icon-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Icon-white@2x.png -------------------------------------------------------------------------------- /Images/Icon-white_Large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/Icon-white_Large.png -------------------------------------------------------------------------------- /Images/logo-type-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/corymsmith/Nodejitsu-iOS/master/Images/logo-type-white.png -------------------------------------------------------------------------------- /Libraries/JSONFramework/JSONFramework.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONFramework.h 3 | // iphoneAndRails1 4 | // 5 | // Created by vickeryj on 12/11/08. 6 | // Copyright 2008 Joshua Vickery. All rights reserved. 7 | // 8 | 9 | #import "SBJSON.h" 10 | #import "NSObject+SBJSON.h" 11 | #import "NSString+SBJSON.h" -------------------------------------------------------------------------------- /iNodejitsu_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'iNodejitsu' target in the 'iNodejitsu' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | 10 | #import "JSONFramework.h" 11 | #import "Nodejitsu.h" 12 | 13 | #define DLog(fmt, ...) NSLog((@"%s:%d| " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | int main(int argc, char *argv[]) { 10 | 11 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 12 | int retVal = UIApplicationMain(argc, argv, nil, nil); 13 | [pool release]; 14 | return retVal; 15 | } 16 | -------------------------------------------------------------------------------- /Libraries/ASI/NSHTTPCookieAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSHTTPCookieAdditions.h 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 12/09/2008. 6 | // Copyright 2008 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSHTTPCookie (ValueEncodingAdditions) 12 | 13 | - (NSString *)encodedValue; 14 | - (NSString *)decodedValue; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Classes/LogInfoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LogInfoViewController.h 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-06. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface LogInfoViewController : UIViewController { 13 | NSDictionary *logEntry; 14 | } 15 | - (id)initWithLogEntry:(NSDictionary *)theLogEntry; 16 | 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Libraries/ASI/ASINSStringAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASINSStringAdditions.h 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 12/09/2008. 6 | // Copyright 2008 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (CookieValueEncodingAdditions) 12 | 13 | - (NSString *)encodedCookieValue; 14 | - (NSString *)decodedCookieValue; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Classes/AppsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppsViewController.h 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface AppsViewController : UIViewController { 13 | UITableView *appsTableView; 14 | Nodejitsu *nodejitsu; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Classes/LoginViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.h 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Nodejitsu.h" 11 | 12 | 13 | @interface LoginViewController : UIViewController { 14 | Nodejitsu *nodejitsu; 15 | UITextField *usernameTextField; 16 | UITextField *passwordTextField; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /Classes/AppDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDetailViewController.h 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface AppDetailViewController : UIViewController { 13 | NSDictionary *app; 14 | Nodejitsu *nodejitsu; 15 | UITableView *appTableView; 16 | } 17 | - (id)initWithApp:(NSDictionary *)theApp; 18 | 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /Classes/LogsListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LogsListViewController.h 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-04. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface LogsListViewController : UIViewController { 13 | NSDictionary *app; 14 | NSDictionary *logs; 15 | Nodejitsu *nodejitsu; 16 | UITableView *logsTableView; 17 | } 18 | - (id)initWithApp:(NSDictionary *)theApp; 19 | 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Classes/iNodejitsuAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // iNodejitsuAppDelegate.h 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppsViewController.h" 11 | 12 | @interface iNodejitsuAppDelegate : NSObject { 13 | UIWindow *window; 14 | AppsViewController *appsList; 15 | UINavigationController *appsNavController; 16 | } 17 | 18 | @property (nonatomic, retain) IBOutlet UIWindow *window; 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /Libraries/ASI/ASIInputStream.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASIInputStream.h 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 10/08/2009. 6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | // This is a wrapper for NSInputStream that pretends to be an NSInputStream itself 12 | // Subclassing NSInputStream seems to be tricky, and may involve overriding undocumented methods, so we'll cheat instead. 13 | // It is used by ASIHTTPRequest whenever we have a request body, and handles measuring and throttling the bandwidth used for uploading 14 | 15 | @interface ASIInputStream : NSObject { 16 | NSInputStream *stream; 17 | } 18 | + (id)inputStreamWithFileAtPath:(NSString *)path; 19 | + (id)inputStreamWithData:(NSData *)data; 20 | 21 | @property (retain) NSInputStream *stream; 22 | @end 23 | -------------------------------------------------------------------------------- /Libraries/ASI/ASINSStringAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASINSStringAdditions.m 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 12/09/2008. 6 | // Copyright 2008 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import "ASINSStringAdditions.h" 10 | 11 | @implementation NSString (CookieValueEncodingAdditions) 12 | 13 | - (NSString *)decodedCookieValue 14 | { 15 | NSMutableString *s = [NSMutableString stringWithString:[self stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; 16 | //Also swap plus signs for spaces 17 | [s replaceOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [s length])]; 18 | return [NSString stringWithString:s]; 19 | } 20 | 21 | - (NSString *)encodedCookieValue 22 | { 23 | return [self stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 24 | } 25 | 26 | @end 27 | 28 | 29 | -------------------------------------------------------------------------------- /Libraries/ASI/NSHTTPCookieAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSHTTPCookieAdditions.m 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 12/09/2008. 6 | // Copyright 2008 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import "NSHTTPCookieAdditions.h" 10 | 11 | @implementation NSHTTPCookie (ValueEncodingAdditions) 12 | 13 | - (NSString *)decodedValue 14 | { 15 | NSMutableString *s = [NSMutableString stringWithString:[[self value] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; 16 | //Also swap plus signs for spaces 17 | [s replaceOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [s length])]; 18 | return [NSString stringWithString:s]; 19 | } 20 | 21 | - (NSString *)encodedValue 22 | { 23 | return [[self value] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 24 | } 25 | 26 | @end 27 | 28 | 29 | -------------------------------------------------------------------------------- /Libraries/ASI/ASIAuthenticationDialog.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASIAuthenticationDialog.h 3 | // iPhone 4 | // 5 | // Created by Ben Copsey on 21/08/2009. 6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import 10 | @class ASIHTTPRequest; 11 | 12 | typedef enum _ASIAuthenticationType { 13 | ASIStandardAuthenticationType = 0, 14 | ASIProxyAuthenticationType = 1 15 | } ASIAuthenticationType; 16 | 17 | @interface ASIAuthenticationDialog : NSObject { 18 | ASIHTTPRequest *request; 19 | UIActionSheet *loginDialog; 20 | ASIAuthenticationType type; 21 | } 22 | + (void)presentAuthenticationDialogForRequest:(ASIHTTPRequest *)request; 23 | + (void)presentProxyAuthenticationDialogForRequest:(ASIHTTPRequest *)request; 24 | 25 | @property (retain) ASIHTTPRequest *request; 26 | @property (retain) UIActionSheet *loginDialog; 27 | @property (assign) ASIAuthenticationType type; 28 | @end 29 | -------------------------------------------------------------------------------- /iNodejitsu-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | Icon-white.png 14 | Icon-white@2x.png 15 | 16 | CFBundleIdentifier 17 | ca.assn.nodejitsu 18 | CFBundleInfoDictionaryVersion 19 | 6.0 20 | CFBundleName 21 | ${PRODUCT_NAME} 22 | CFBundlePackageType 23 | APPL 24 | CFBundleSignature 25 | ???? 26 | CFBundleVersion 27 | 1.0 28 | LSRequiresIPhoneOS 29 | 30 | NSMainNibFile 31 | MainWindow 32 | UIPrerenderedIcon 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Classes/Nodejitsu.h: -------------------------------------------------------------------------------- 1 | // 2 | // Nodejitsu.h 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol NodejitsuDelegate 12 | 13 | @optional 14 | - (void)loadedApplications; 15 | - (void)loadedLogsForApplication:(NSDictionary *)logsDictionary; 16 | 17 | @end 18 | 19 | @protocol NodejitsuLoginDelegate 20 | 21 | @optional 22 | - (void)loginSucceeded; 23 | - (void)loginFailed:(NSString *)message; 24 | 25 | @end 26 | 27 | 28 | @interface Nodejitsu : NSObject { 29 | id delegate; 30 | id loginDelegate; 31 | 32 | NSArray *apps; 33 | NSString *_username; 34 | NSString *_password; 35 | } 36 | 37 | @property (assign) id delegate; 38 | @property (assign) id loginDelegate; 39 | 40 | - (void)loginWithUsername:(NSString *)username password:(NSString *)password; 41 | 42 | - (void)startApplication:(NSString *)appId; 43 | 44 | - (void)stopApplication:(NSString *)appId; 45 | 46 | - (void)restartApplication:(NSString *)appId; 47 | 48 | - (BOOL)isAuthenticated; 49 | 50 | - (void)getApplications; 51 | 52 | - (NSArray *)allApplications; 53 | 54 | - (void)getLogsForApplication:(NSString *)appId; 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Libraries/ASI/ASIFormDataRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASIFormDataRequest.h 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 07/11/2008. 6 | // Copyright 2008-2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ASIHTTPRequest.h" 11 | 12 | typedef enum _ASIPostFormat { 13 | ASIMultipartFormDataPostFormat = 0, 14 | ASIURLEncodedPostFormat = 1 15 | 16 | } ASIPostFormat; 17 | 18 | @interface ASIFormDataRequest : ASIHTTPRequest { 19 | 20 | // Parameters that will be POSTed to the url 21 | NSMutableDictionary *postData; 22 | 23 | // Files that will be POSTed to the url 24 | NSMutableDictionary *fileData; 25 | 26 | ASIPostFormat postFormat; 27 | 28 | NSStringEncoding stringEncoding; 29 | } 30 | 31 | #pragma mark utilities 32 | - (NSString*)encodeURL:(NSString *)string; 33 | 34 | #pragma mark setup request 35 | 36 | // Add a POST variable to the request 37 | - (void)setPostValue:(id )value forKey:(NSString *)key; 38 | 39 | // Add the contents of a local file to the request 40 | - (void)setFile:(NSString *)filePath forKey:(NSString *)key; 41 | 42 | // Same as above, but you can specify the content-type and file name 43 | - (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; 44 | 45 | // Add the contents of an NSData object to the request 46 | - (void)setData:(NSData *)data forKey:(NSString *)key; 47 | 48 | // Same as above, but you can specify the content-type and file name 49 | - (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key; 50 | 51 | 52 | @property (assign) ASIPostFormat postFormat; 53 | @property (assign) NSStringEncoding stringEncoding; 54 | @end 55 | -------------------------------------------------------------------------------- /Libraries/JSONFramework/NSString+SBJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | /// Adds JSON parsing to NSString 33 | @interface NSString (NSString_SBJSON) 34 | 35 | /// Returns the object represented in the receiver, or nil on error. 36 | - (id)JSONFragmentValue; 37 | 38 | /// Returns the dictionary or array represented in the receiver, or nil on error. 39 | - (id)JSONValue; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /Libraries/JSONFramework/NSString+SBJSON.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "NSString+SBJSON.h" 31 | #import "SBJSON.h" 32 | 33 | 34 | @implementation NSString (NSString_SBJSON) 35 | 36 | - (id)JSONFragmentValue 37 | { 38 | SBJSON *json = [[SBJSON new] autorelease]; 39 | 40 | NSError *error; 41 | id o = [json fragmentWithString:self error:&error]; 42 | 43 | if (!o) 44 | NSLog(@"%@", error); 45 | return o; 46 | } 47 | 48 | - (id)JSONValue 49 | { 50 | SBJSON *json = [[SBJSON new] autorelease]; 51 | 52 | NSError *error; 53 | id o = [json objectWithString:self error:&error]; 54 | 55 | if (!o) 56 | NSLog(@"%@", error); 57 | return o; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Libraries/JSONFramework/NSObject+SBJSON.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "NSObject+SBJSON.h" 31 | #import "SBJSON.h" 32 | 33 | @implementation NSObject (NSObject_SBJSON) 34 | 35 | - (NSString *)JSONFragment { 36 | SBJSON *generator = [[SBJSON new] autorelease]; 37 | 38 | NSError *error; 39 | NSString *json = [generator stringWithFragment:self error:&error]; 40 | 41 | if (!json) 42 | NSLog(@"%@", error); 43 | return json; 44 | } 45 | 46 | - (NSString *)JSONRepresentation { 47 | SBJSON *generator = [[SBJSON new] autorelease]; 48 | 49 | NSError *error; 50 | NSString *json = [generator stringWithObject:self error:&error]; 51 | 52 | if (!json) 53 | NSLog(@"%@", error); 54 | return json; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Libraries/JSONFramework/NSObject+SBJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | 33 | /// Adds JSON generation to NSObject subclasses 34 | @interface NSObject (NSObject_SBJSON) 35 | 36 | /** 37 | @brief Returns a string containing the receiver encoded as a JSON fragment. 38 | 39 | This method is added as a category on NSObject but is only actually 40 | supported for the following objects: 41 | @li NSDictionary 42 | @li NSArray 43 | @li NSString 44 | @li NSNumber (also used for booleans) 45 | @li NSNull 46 | */ 47 | - (NSString *)JSONFragment; 48 | 49 | /** 50 | @brief Returns a string containing the receiver encoded in JSON. 51 | 52 | This method is added as a category on NSObject but is only actually 53 | supported for the following objects: 54 | @li NSDictionary 55 | @li NSArray 56 | */ 57 | - (NSString *)JSONRepresentation; 58 | 59 | @end 60 | 61 | -------------------------------------------------------------------------------- /Classes/LogInfoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LogInfoViewController.m 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-06. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import "LogInfoViewController.h" 10 | 11 | 12 | @implementation LogInfoViewController 13 | 14 | - (id)initWithLogEntry:(NSDictionary *)theLogEntry { 15 | self = [super init]; 16 | if (self) { 17 | // Custom initialization. 18 | logEntry = [theLogEntry retain]; 19 | 20 | } 21 | return self; 22 | } 23 | 24 | - (void)loadView { 25 | [super loadView]; 26 | 27 | UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10,10, 200, 20)]; 28 | titleLabel.contentMode = UIViewContentModeTop; 29 | titleLabel.numberOfLines = 1; 30 | titleLabel.font = [UIFont boldSystemFontOfSize:16]; 31 | titleLabel.tag = 1111; 32 | titleLabel.text = [[logEntry valueForKey:@"json"] valueForKey:@"level"]; 33 | 34 | if([[[logEntry valueForKey:@"json"] valueForKey:@"level"] isEqualToString:@"error"]) 35 | titleLabel.textColor = [UIColor redColor]; 36 | else { 37 | titleLabel.textColor = [UIColor grayColor]; 38 | } 39 | 40 | [self.view addSubview:titleLabel]; 41 | [titleLabel release]; 42 | 43 | UILabel *dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 240, 15)]; 44 | dateLabel.textColor = [UIColor darkGrayColor]; 45 | dateLabel.textAlignment = UITextAlignmentLeft; 46 | //dateLabel.backgroundColor = [UIColor redColor]; 47 | dateLabel.numberOfLines = 1; 48 | dateLabel.font = [UIFont systemFontOfSize:12]; 49 | dateLabel.tag = 2222; 50 | dateLabel.text = [logEntry valueForKey:@"timestamp"]; 51 | [self.view addSubview:dateLabel]; 52 | [dateLabel release]; 53 | 54 | UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(10,50,300,400)]; 55 | textView.text = [[logEntry valueForKey:@"json"] valueForKey:@"desc"]; 56 | textView.font = [UIFont systemFontOfSize:16]; 57 | textView.editable = NO; 58 | [self.view addSubview:textView]; 59 | 60 | //CGRect frame = textView.frame; 61 | //frame.size.height = textView.contentSize.height; 62 | //textView.frame = frame; 63 | 64 | [textView release]; 65 | } 66 | 67 | - (void)didReceiveMemoryWarning { 68 | // Releases the view if it doesn't have a superview. 69 | [super didReceiveMemoryWarning]; 70 | 71 | // Release any cached data, images, etc. that aren't in use. 72 | } 73 | 74 | - (void)viewDidUnload { 75 | [super viewDidUnload]; 76 | // Release any retained subviews of the main view. 77 | // e.g. self.myOutlet = nil; 78 | } 79 | 80 | 81 | - (void)dealloc { 82 | [logEntry release]; 83 | [super dealloc]; 84 | } 85 | 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /Classes/LoginViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.m 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import "LoginViewController.h" 10 | 11 | 12 | @implementation LoginViewController 13 | 14 | - (void)loadView { 15 | [super loadView]; 16 | 17 | self.view.backgroundColor = [UIColor whiteColor]; 18 | self.navigationController.navigationBar.tintColor = [UIColor grayColor]; 19 | UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"logo-type-white.png"]]; 20 | [logo setFrame:CGRectMake(0,0,119,25)]; 21 | self.navigationItem.titleView = logo; 22 | [logo release]; 23 | 24 | usernameTextField = [[UITextField alloc] initWithFrame:CGRectMake(20,25,280,40)]; 25 | usernameTextField.placeholder = @"username"; 26 | usernameTextField.font = [UIFont systemFontOfSize:26]; 27 | usernameTextField.borderStyle = UITextBorderStyleRoundedRect; 28 | [self.view addSubview:usernameTextField]; 29 | 30 | passwordTextField = [[UITextField alloc] initWithFrame:CGRectMake(20,80,280,40)]; 31 | passwordTextField.placeholder = @"password"; 32 | passwordTextField.font = [UIFont systemFontOfSize:26]; 33 | passwordTextField.borderStyle = UITextBorderStyleRoundedRect; 34 | passwordTextField.returnKeyType = UIReturnKeyGo; 35 | passwordTextField.secureTextEntry = YES; 36 | [self.view addSubview:passwordTextField]; 37 | 38 | self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Login" 39 | style:UIBarButtonItemStyleBordered 40 | target:self 41 | action:@selector(login)] autorelease]; 42 | nodejitsu = [[Nodejitsu alloc] init]; 43 | nodejitsu.loginDelegate = self; 44 | } 45 | 46 | - (void)login { 47 | [nodejitsu loginWithUsername:[usernameTextField.text lowercaseString] password:passwordTextField.text]; 48 | } 49 | 50 | - (void)loginSucceeded { 51 | DLog(@""); 52 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadApplications" object:nil]; 53 | [self dismissModalViewControllerAnimated:YES]; 54 | } 55 | 56 | - (void)loginFailed:(NSString *)message { 57 | DLog(@"%@", message); 58 | UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:@"Login Failed" message:message delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil] autorelease]; 59 | [alertView show]; 60 | } 61 | 62 | - (void)didReceiveMemoryWarning { 63 | // Releases the view if it doesn't have a superview. 64 | [super didReceiveMemoryWarning]; 65 | 66 | // Release any cached data, images, etc. that aren't in use. 67 | } 68 | 69 | - (void)viewDidUnload { 70 | [super viewDidUnload]; 71 | // Release any retained subviews of the main view. 72 | // e.g. self.myOutlet = nil; 73 | } 74 | 75 | 76 | - (void)dealloc { 77 | [nodejitsu release]; 78 | [usernameTextField release]; 79 | [passwordTextField release]; 80 | [super dealloc]; 81 | } 82 | 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Classes/KeychainItemWrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | File: KeychainItemWrapper.h 3 | Abstract: 4 | Objective-C wrapper for accessing a single keychain item. 5 | 6 | Version: 1.2 7 | 8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 9 | Inc. ("Apple") in consideration of your agreement to the following 10 | terms, and your use, installation, modification or redistribution of 11 | this Apple software constitutes acceptance of these terms. If you do 12 | not agree with these terms, please do not use, install, modify or 13 | redistribute this Apple software. 14 | 15 | In consideration of your agreement to abide by the following terms, and 16 | subject to these terms, Apple grants you a personal, non-exclusive 17 | license, under Apple's copyrights in this original Apple software (the 18 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 19 | Software, with or without modifications, in source and/or binary forms; 20 | provided that if you redistribute the Apple Software in its entirety and 21 | without modifications, you must retain this notice and the following 22 | text and disclaimers in all such redistributions of the Apple Software. 23 | Neither the name, trademarks, service marks or logos of Apple Inc. may 24 | be used to endorse or promote products derived from the Apple Software 25 | without specific prior written permission from Apple. Except as 26 | expressly stated in this notice, no other rights or licenses, express or 27 | implied, are granted by Apple herein, including but not limited to any 28 | patent rights that may be infringed by your derivative works or by other 29 | works in which the Apple Software may be incorporated. 30 | 31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 32 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 33 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 34 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 35 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 36 | 37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 38 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 40 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 41 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 42 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 43 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 44 | POSSIBILITY OF SUCH DAMAGE. 45 | 46 | Copyright (C) 2010 Apple Inc. All Rights Reserved. 47 | 48 | */ 49 | 50 | #import 51 | 52 | /* 53 | The KeychainItemWrapper class is an abstraction layer for the iPhone Keychain communication. It is merely a 54 | simple wrapper to provide a distinct barrier between all the idiosyncracies involved with the Keychain 55 | CF/NS container objects. 56 | */ 57 | @interface KeychainItemWrapper : NSObject 58 | { 59 | NSMutableDictionary *keychainItemData; // The actual keychain item data backing store. 60 | NSMutableDictionary *genericPasswordQuery; // A placeholder for the generic keychain item query used to locate the item. 61 | } 62 | 63 | @property (nonatomic, retain) NSMutableDictionary *keychainItemData; 64 | @property (nonatomic, retain) NSMutableDictionary *genericPasswordQuery; 65 | 66 | // Designated initializer. 67 | - (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup; 68 | - (void)setObject:(id)inObject forKey:(id)key; 69 | - (id)objectForKey:(id)key; 70 | 71 | // Initializes and resets the default generic keychain item data. 72 | - (void)resetKeychainItem; 73 | 74 | @end -------------------------------------------------------------------------------- /Libraries/ASI/ASIInputStream.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASIInputStream.m 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 10/08/2009. 6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import "ASIInputStream.h" 10 | #import "ASIHTTPRequest.h" 11 | 12 | // Used to ensure only one request can read data at once 13 | static NSLock *readLock = nil; 14 | 15 | @implementation ASIInputStream 16 | 17 | + (void)initialize 18 | { 19 | if (self == [ASIInputStream class]) { 20 | readLock = [[NSLock alloc] init]; 21 | } 22 | } 23 | 24 | + (id)inputStreamWithFileAtPath:(NSString *)path 25 | { 26 | ASIInputStream *stream = [[[self alloc] init] autorelease]; 27 | [stream setStream:[NSInputStream inputStreamWithFileAtPath:path]]; 28 | return stream; 29 | } 30 | 31 | + (id)inputStreamWithData:(NSData *)data 32 | { 33 | ASIInputStream *stream = [[[self alloc] init] autorelease]; 34 | [stream setStream:[NSInputStream inputStreamWithData:data]]; 35 | return stream; 36 | } 37 | 38 | - (void)dealloc 39 | { 40 | [stream release]; 41 | [super dealloc]; 42 | } 43 | 44 | 45 | // Ok, so this works, but I don't really understand why. 46 | // Ideally, we'd just return the stream's hasBytesAvailable, but CFNetwork seems to want to monopolise our run loop until (presumably) its buffer is full, which will cause timeouts if we're throttling the bandwidth 47 | // We return NO when we shouldn't be uploading any more data because our bandwidth limit has run out (for now) 48 | // The call to maxUploadReadLength will recognise that we've run out of our allotted bandwidth limit, and sleep this thread for the rest of the measurement period 49 | // This method will be called again, but we'll almost certainly return YES the next time around, because we'll have more limit to use up 50 | // The NO returns seem to snap CFNetwork out of its reverie, and return control to the main loop in loadRequest, so that we can manage timeouts and progress delegate updates 51 | - (BOOL)hasBytesAvailable 52 | { 53 | 54 | if ([ASIHTTPRequest isBandwidthThrottled]) { 55 | [readLock lock]; 56 | if ([ASIHTTPRequest maxUploadReadLength] == 0) { 57 | [readLock unlock]; 58 | return NO; 59 | } 60 | [readLock unlock]; 61 | } 62 | return [[self stream] hasBytesAvailable]; 63 | 64 | } 65 | 66 | // Called when CFNetwork wants to read more of our request body 67 | // When throttling is on, we ask ASIHTTPRequest for the maximum amount of data we can read 68 | - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len 69 | { 70 | [readLock lock]; 71 | unsigned long toRead = len; 72 | if ([ASIHTTPRequest isBandwidthThrottled]) { 73 | toRead = [ASIHTTPRequest maxUploadReadLength]; 74 | if (toRead > len) { 75 | toRead = len; 76 | 77 | // Hopefully this won't happen because hasBytesAvailable will have returned NO, but just in case - we need to read at least 1 byte, or bad things might happen 78 | } else if (toRead == 0) { 79 | toRead = 1; 80 | } 81 | //NSLog(@"Throttled read %u",toRead); 82 | } else { 83 | //NSLog(@"Unthrottled read %u",toRead); 84 | } 85 | [ASIHTTPRequest incrementBandwidthUsedInLastSecond:toRead]; 86 | [readLock unlock]; 87 | return [[self stream] read:buffer maxLength:toRead]; 88 | } 89 | 90 | // If we get asked to perform a method we don't have (which is almost all of them), we'll just forward the message to our stream 91 | 92 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 93 | { 94 | return [[self stream] methodSignatureForSelector:aSelector]; 95 | } 96 | 97 | - (void)forwardInvocation:(NSInvocation *)anInvocation 98 | { 99 | [anInvocation invokeWithTarget:[self stream]]; 100 | } 101 | 102 | @synthesize stream; 103 | @end 104 | -------------------------------------------------------------------------------- /Classes/LogsListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LogsListViewController.m 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-04. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import "LogsListViewController.h" 10 | #import "LogInfoViewController.h" 11 | 12 | @implementation LogsListViewController 13 | 14 | - (id)initWithApp:(NSDictionary *)theApp { 15 | self = [super init]; 16 | if (self) { 17 | // Custom initialization. 18 | app = [theApp retain]; 19 | logs = [[NSDictionary dictionary] retain]; 20 | } 21 | return self; 22 | } 23 | 24 | 25 | // Implement loadView to create a view hierarchy programmatically, without using a nib. 26 | - (void)loadView { 27 | [super loadView]; 28 | 29 | self.title = @"Logs"; 30 | logsTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 416)]; 31 | logsTableView.delegate = self; 32 | logsTableView.dataSource = self; 33 | [self.view addSubview:logsTableView]; 34 | 35 | nodejitsu = [[Nodejitsu alloc] init]; 36 | nodejitsu.delegate = self; 37 | [nodejitsu getLogsForApplication:[app valueForKey:@"id"]]; 38 | 39 | self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 40 | target:self 41 | action:@selector(refresh)] autorelease]; 42 | } 43 | 44 | - (void)loadedLogsForApplication:(NSDictionary *)logsDictionary { 45 | [logs release]; 46 | logs = [logsDictionary retain]; 47 | [logsTableView reloadData]; 48 | } 49 | 50 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 51 | { 52 | return 1; 53 | } 54 | 55 | 56 | // Customize the number of rows in the table view. 57 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 58 | { 59 | return [[logs objectForKey:@"data"] count]; 60 | } 61 | 62 | 63 | // Customize the appearance of table view cells. 64 | - (UITableViewCell *)tableView:(UITableView *)tableView 65 | cellForRowAtIndexPath:(NSIndexPath *)indexPath 66 | { 67 | static NSString *CellIdentifier = @"Cell"; 68 | 69 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 70 | if (cell == nil) 71 | { 72 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease]; 73 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 74 | cell.selectionStyle = UITableViewCellSelectionStyleGray; 75 | } 76 | 77 | NSString *level = [[[[logs objectForKey:@"data"] objectAtIndex:indexPath.row] valueForKey:@"json"] valueForKey:@"level"]; 78 | NSString *description = [[[[logs objectForKey:@"data"] objectAtIndex:indexPath.row] valueForKey:@"json"] valueForKey:@"desc"]; 79 | 80 | cell.textLabel.text = description; 81 | cell.detailTextLabel.text = level; 82 | 83 | if([level isEqualToString:@"error"]) 84 | cell.detailTextLabel.textColor = [UIColor redColor]; 85 | else { 86 | cell.detailTextLabel.textColor = [UIColor grayColor]; 87 | } 88 | 89 | return cell; 90 | } 91 | 92 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 93 | NSDictionary *logEntry = [[logs objectForKey:@"data"] objectAtIndex:indexPath.row]; 94 | LogInfoViewController *logInfoViewController = [[LogInfoViewController alloc] initWithLogEntry:logEntry]; 95 | [logsTableView deselectRowAtIndexPath:indexPath animated:YES]; 96 | [self.navigationController pushViewController:logInfoViewController animated:YES]; 97 | [logInfoViewController release]; 98 | } 99 | 100 | - (void)refresh { 101 | [nodejitsu getLogsForApplication:[app valueForKey:@"id"]]; 102 | } 103 | 104 | - (void)didReceiveMemoryWarning { 105 | // Releases the view if it doesn't have a superview. 106 | [super didReceiveMemoryWarning]; 107 | 108 | // Release any cached data, images, etc. that aren't in use. 109 | } 110 | 111 | - (void)viewDidUnload { 112 | [super viewDidUnload]; 113 | // Release any retained subviews of the main view. 114 | // e.g. self.myOutlet = nil; 115 | } 116 | 117 | 118 | - (void)dealloc { 119 | [app release]; 120 | [logs release]; 121 | [logsTableView release]; 122 | [nodejitsu release]; 123 | [super dealloc]; 124 | } 125 | 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /Classes/iNodejitsuAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // iNodejitsuAppDelegate.m 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import "iNodejitsuAppDelegate.h" 10 | #import "LoginViewController.h" 11 | 12 | @interface iNodejitsuAppDelegate () 13 | - (void)showLogin; 14 | 15 | @end 16 | 17 | @implementation iNodejitsuAppDelegate 18 | 19 | @synthesize window; 20 | 21 | 22 | #pragma mark - 23 | #pragma mark Application lifecycle 24 | 25 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 26 | 27 | appsList = [[AppsViewController alloc] init]; 28 | 29 | [[NSNotificationCenter defaultCenter] addObserver:self 30 | selector:@selector(logout) 31 | name:@"Logout" 32 | object:nil]; 33 | 34 | appsNavController = [[UINavigationController alloc] initWithRootViewController:appsList]; 35 | appsList.navigationController.navigationBar.tintColor = [UIColor grayColor]; 36 | // Override point for customization after application launch. 37 | 38 | [window addSubview:appsNavController.view]; 39 | 40 | //TODO: Don't check user defaults, check keychain 41 | if(![[NSUserDefaults standardUserDefaults] objectForKey:@"username"]) { 42 | [self showLogin]; 43 | } else { 44 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadApplications" object:nil]; 45 | } 46 | 47 | [self.window makeKeyAndVisible]; 48 | return YES; 49 | } 50 | 51 | - (void)logout { 52 | 53 | //TODO: Remove apps data from keychain 54 | [self showLogin]; 55 | } 56 | 57 | - (void)showLogin { 58 | LoginViewController *lvc = [[LoginViewController alloc] init]; 59 | UINavigationController *navController2 = [[UINavigationController alloc] initWithRootViewController:lvc]; 60 | lvc.navigationController.navigationBar.tintColor = [UIColor grayColor]; 61 | [appsNavController presentModalViewController:navController2 animated:YES]; 62 | [lvc release]; 63 | [navController2 release]; 64 | } 65 | 66 | - (void)applicationWillResignActive:(UIApplication *)application { 67 | /* 68 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 69 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 70 | */ 71 | } 72 | 73 | 74 | - (void)applicationDidEnterBackground:(UIApplication *)application { 75 | /* 76 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 77 | If your application supports background execution, called instead of applicationWillTerminate: when the user quits. 78 | */ 79 | } 80 | 81 | 82 | - (void)applicationWillEnterForeground:(UIApplication *)application { 83 | /* 84 | Called as part of transition from the background to the inactive state: here you can undo many of the changes made on entering the background. 85 | */ 86 | } 87 | 88 | 89 | - (void)applicationDidBecomeActive:(UIApplication *)application { 90 | /* 91 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 92 | */ 93 | } 94 | 95 | 96 | - (void)applicationWillTerminate:(UIApplication *)application { 97 | /* 98 | Called when the application is about to terminate. 99 | See also applicationDidEnterBackground:. 100 | */ 101 | } 102 | 103 | 104 | #pragma mark - 105 | #pragma mark Memory management 106 | 107 | - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { 108 | /* 109 | Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later. 110 | */ 111 | } 112 | 113 | 114 | - (void)dealloc { 115 | [window release]; 116 | [appsList release]; 117 | [appsNavController release]; 118 | [super dealloc]; 119 | } 120 | 121 | 122 | @end 123 | -------------------------------------------------------------------------------- /Libraries/ASI/ASINetworkQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASINetworkQueue.h 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 07/11/2008. 6 | // Copyright 2008-2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ASINetworkQueue : NSOperationQueue { 12 | 13 | // Delegate will get didFail + didFinish messages (if set) 14 | id delegate; 15 | 16 | // Will be called when a request completes with the request as the argument 17 | SEL requestDidFinishSelector; 18 | 19 | // Will be called when a request fails with the request as the argument 20 | SEL requestDidFailSelector; 21 | 22 | // Will be called when the queue finishes with the queue as the argument 23 | SEL queueDidFinishSelector; 24 | 25 | // Upload progress indicator, probably an NSProgressIndicator or UIProgressView 26 | id uploadProgressDelegate; 27 | 28 | // Total amount uploaded so far for all requests in this queue 29 | unsigned long long uploadProgressBytes; 30 | 31 | // Total amount to be uploaded for all requests in this queue - requests add to this figure as they work out how much data they have to transmit 32 | unsigned long long uploadProgressTotalBytes; 33 | 34 | // Download progress indicator, probably an NSProgressIndicator or UIProgressView 35 | id downloadProgressDelegate; 36 | 37 | // Total amount downloaded so far for all requests in this queue 38 | unsigned long long downloadProgressBytes; 39 | 40 | // Total amount to be downloaded for all requests in this queue - requests add to this figure as they receive Content-Length headers 41 | unsigned long long downloadProgressTotalBytes; 42 | 43 | // When YES, the queue will cancel all requests when a request fails. Default is YES 44 | BOOL shouldCancelAllRequestsOnFailure; 45 | 46 | //Number of real requests (excludes HEAD requests created to manage showAccurateProgress) 47 | int requestsCount; 48 | 49 | // When NO, this request will only update the progress indicator when it completes 50 | // When YES, this request will update the progress indicator according to how much data it has recieved so far 51 | // When YES, the queue will first perform HEAD requests for all GET requests in the queue, so it can calculate the total download size before it starts 52 | // NO means better performance, because it skips this step for GET requests, and it won't waste time updating the progress indicator until a request completes 53 | // Set to YES if the size of a requests in the queue varies greatly for much more accurate results 54 | // Default for requests in the queue is NO 55 | BOOL showAccurateProgress; 56 | 57 | 58 | } 59 | 60 | // Convenience constructor 61 | + (id)queue; 62 | 63 | // Used internally to manage HEAD requests when showAccurateProgress is YES, do not use! 64 | - (void)addHEADOperation:(NSOperation *)operation; 65 | 66 | // Called at the start of a request to add on the size of this upload to the total 67 | - (void)incrementUploadSizeBy:(unsigned long long)bytes; 68 | 69 | // Called during a request when data is written to the upload stream to increment the progress indicator 70 | - (void)incrementUploadProgressBy:(unsigned long long)bytes; 71 | 72 | // Called at the start of a request to add on the size of this download to the total 73 | - (void)incrementDownloadSizeBy:(unsigned long long)bytes; 74 | 75 | // Called during a request when data is received to increment the progress indicator 76 | - (void)incrementDownloadProgressBy:(unsigned long long)bytes; 77 | 78 | // Called during a request when authorisation fails to cancel any progress so far 79 | - (void)decrementUploadProgressBy:(unsigned long long)bytes; 80 | 81 | // Called when the first chunk of data is written to the upload buffer 82 | // We ignore the first part chunk when tracking upload progress, as kCFStreamPropertyHTTPRequestBytesWrittenCount reports the amount of data written to the buffer, not the amount sent 83 | // This is to workaround the first 128KB of data appearing in an upload progress delegate immediately 84 | - (void)setUploadBufferSize:(unsigned long long)bytes; 85 | 86 | // All ASINetworkQueues are paused when created so that total size can be calculated before the queue starts 87 | // This method will start the queue 88 | - (void)go; 89 | 90 | // Used on iPhone platform to show / hide the network activity indicator (in the status bar) 91 | // On mac, you could subclass to do something else 92 | - (void)updateNetworkActivityIndicator; 93 | 94 | // Returns YES if the queue is in progress 95 | - (BOOL)isNetworkActive; 96 | 97 | 98 | @property (assign,setter=setUploadProgressDelegate:) id uploadProgressDelegate; 99 | @property (assign,setter=setDownloadProgressDelegate:) id downloadProgressDelegate; 100 | 101 | @property (assign) SEL requestDidFinishSelector; 102 | @property (assign) SEL requestDidFailSelector; 103 | @property (assign) SEL queueDidFinishSelector; 104 | @property (assign) BOOL shouldCancelAllRequestsOnFailure; 105 | @property (assign) id delegate; 106 | @property (assign) BOOL showAccurateProgress; 107 | @property (assign, readonly) int requestsCount; 108 | @end 109 | -------------------------------------------------------------------------------- /Libraries/ASI/Reachability.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: Reachability.h 4 | Abstract: SystemConfiguration framework wrapper. 5 | 6 | Version: 1.5 7 | 8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. 9 | ("Apple") in consideration of your agreement to the following terms, and your 10 | use, installation, modification or redistribution of this Apple software 11 | constitutes acceptance of these terms. If you do not agree with these terms, 12 | please do not use, install, modify or redistribute this Apple software. 13 | 14 | In consideration of your agreement to abide by the following terms, and subject 15 | to these terms, Apple grants you a personal, non-exclusive license, under 16 | Apple's copyrights in this original Apple software (the "Apple Software"), to 17 | use, reproduce, modify and redistribute the Apple Software, with or without 18 | modifications, in source and/or binary forms; provided that if you redistribute 19 | the Apple Software in its entirety and without modifications, you must retain 20 | this notice and the following text and disclaimers in all such redistributions 21 | of the Apple Software. 22 | Neither the name, trademarks, service marks or logos of Apple Inc. may be used 23 | to endorse or promote products derived from the Apple Software without specific 24 | prior written permission from Apple. Except as expressly stated in this notice, 25 | no other rights or licenses, express or implied, are granted by Apple herein, 26 | including but not limited to any patent rights that may be infringed by your 27 | derivative works or by other works in which the Apple Software may be 28 | incorporated. 29 | 30 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO 31 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED 32 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 33 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN 34 | COMBINATION WITH YOUR PRODUCTS. 35 | 36 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR 37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 38 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR 40 | DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF 41 | CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF 42 | APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2008 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import 49 | #import 50 | 51 | @class Reachability; 52 | 53 | @interface Reachability : NSObject { 54 | 55 | @private 56 | BOOL _networkStatusNotificationsEnabled; 57 | 58 | NSString *_hostName; 59 | NSString *_address; 60 | 61 | NSMutableDictionary *_reachabilityQueries; 62 | } 63 | 64 | /* 65 | An enumeration that defines the return values of the network state 66 | of the device. 67 | */ 68 | typedef enum { 69 | NotReachable = 0, 70 | ReachableViaCarrierDataNetwork, 71 | ReachableViaWiFiNetwork 72 | } NetworkStatus; 73 | 74 | 75 | // Set to YES to register for changes in network status. Otherwise reachability queries 76 | // will be handled synchronously. 77 | @property BOOL networkStatusNotificationsEnabled; 78 | // The remote host whose reachability will be queried. 79 | // Either this or 'addressName' must be set. 80 | @property (nonatomic, retain) NSString *hostName; 81 | // The IP address of the remote host whose reachability will be queried. 82 | // Either this or 'hostName' must be set. 83 | @property (nonatomic, retain) NSString *address; 84 | // A cache of ReachabilityQuery objects, which encapsulate a SCNetworkReachabilityRef, a host or address, and a run loop. The keys are host names or addresses. 85 | @property (nonatomic, assign) NSMutableDictionary *reachabilityQueries; 86 | 87 | // This class is intended to be used as a singleton. 88 | + (Reachability *)sharedReachability; 89 | 90 | // Is self.hostName is not nil, determines its reachability. 91 | // If self.hostName is nil and self.address is not nil, determines the reachability of self.address. 92 | - (NetworkStatus)remoteHostStatus; 93 | // Is the device able to communicate with Internet hosts? If so, through which network interface? 94 | - (NetworkStatus)internetConnectionStatus; 95 | // Is the device able to communicate with hosts on the local WiFi network? (Typically these are Bonjour hosts). 96 | - (NetworkStatus)localWiFiConnectionStatus; 97 | 98 | /* 99 | When reachability change notifications are posted, the callback method 'ReachabilityCallback' is called 100 | and posts a notification that the client application can observe to learn about changes. 101 | */ 102 | static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info); 103 | 104 | @end 105 | 106 | @interface ReachabilityQuery : NSObject 107 | { 108 | @private 109 | SCNetworkReachabilityRef _reachabilityRef; 110 | CFMutableArrayRef _runLoops; 111 | NSString *_hostNameOrAddress; 112 | } 113 | // Keep around each network reachability query object so that we can 114 | // register for updates from those objects. 115 | @property (nonatomic) SCNetworkReachabilityRef reachabilityRef; 116 | @property (nonatomic, retain) NSString *hostNameOrAddress; 117 | @property (nonatomic) CFMutableArrayRef runLoops; 118 | 119 | - (void)scheduleOnRunLoop:(NSRunLoop *)inRunLoop; 120 | 121 | @end 122 | 123 | -------------------------------------------------------------------------------- /Libraries/JSONFramework/SBJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2008 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import 31 | 32 | extern NSString * SBJSONErrorDomain; 33 | 34 | enum { 35 | EUNSUPPORTED = 1, 36 | EPARSENUM, 37 | EPARSE, 38 | EFRAGMENT, 39 | ECTRL, 40 | EUNICODE, 41 | EDEPTH, 42 | EESCAPE, 43 | ETRAILCOMMA, 44 | ETRAILGARBAGE, 45 | EEOF, 46 | EINPUT 47 | }; 48 | 49 | /** 50 | @brief A strict JSON parser and generator 51 | 52 | This is the parser and generator underlying the categories added to 53 | NSString and various other objects. 54 | 55 | Objective-C types are mapped to JSON types and back in the following way: 56 | 57 | @li NSNull -> Null -> NSNull 58 | @li NSString -> String -> NSMutableString 59 | @li NSArray -> Array -> NSMutableArray 60 | @li NSDictionary -> Object -> NSMutableDictionary 61 | @li NSNumber (-initWithBool:) -> Boolean -> NSNumber -initWithBool: 62 | @li NSNumber -> Number -> NSDecimalNumber 63 | 64 | In JSON the keys of an object must be strings. NSDictionary keys need 65 | not be, but attempting to convert an NSDictionary with non-string keys 66 | into JSON will throw an exception. 67 | 68 | NSNumber instances created with the +numberWithBool: method are 69 | converted into the JSON boolean "true" and "false" values, and vice 70 | versa. Any other NSNumber instances are converted to a JSON number the 71 | way you would expect. JSON numbers turn into NSDecimalNumber instances, 72 | as we can thus avoid any loss of precision. 73 | 74 | Strictly speaking correctly formed JSON text must have exactly 75 | one top-level container. (Either an Array or an Object.) Scalars, 76 | i.e. nulls, numbers, booleans and strings, are not valid JSON on their own. 77 | It can be quite convenient to pretend that such fragments are valid 78 | JSON however, and this class lets you do so. 79 | 80 | This class does its best to be as strict as possible, both in what it 81 | accepts and what it generates. (Other than the above mentioned support 82 | for JSON fragments.) For example, it does not support trailing commas 83 | in arrays or objects. Nor does it support embedded comments, or 84 | anything else not in the JSON specification. 85 | 86 | */ 87 | @interface SBJSON : NSObject { 88 | BOOL humanReadable; 89 | BOOL sortKeys; 90 | NSUInteger maxDepth; 91 | 92 | @private 93 | // Used temporarily during scanning/generation 94 | NSUInteger depth; 95 | const char *c; 96 | } 97 | 98 | /// Whether we are generating human-readable (multiline) JSON 99 | /** 100 | Set whether or not to generate human-readable JSON. The default is NO, which produces 101 | JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable 102 | JSON with linebreaks after each array value and dictionary key/value pair, indented two 103 | spaces per nesting level. 104 | */ 105 | @property BOOL humanReadable; 106 | 107 | /// Whether or not to sort the dictionary keys in the output 108 | /** The default is to not sort the keys. */ 109 | @property BOOL sortKeys; 110 | 111 | /// The maximum depth the parser will go to 112 | /** Defaults to 512. */ 113 | @property NSUInteger maxDepth; 114 | 115 | /// Return JSON representation of an array or dictionary 116 | - (NSString*)stringWithObject:(id)value error:(NSError**)error; 117 | 118 | /// Return JSON representation of any legal JSON value 119 | - (NSString*)stringWithFragment:(id)value error:(NSError**)error; 120 | 121 | /// Return the object represented by the given string 122 | - (id)objectWithString:(NSString*)jsonrep error:(NSError**)error; 123 | 124 | /// Return the fragment represented by the given string 125 | - (id)fragmentWithString:(NSString*)jsonrep error:(NSError**)error; 126 | 127 | /// Return JSON representation (or fragment) for the given object 128 | - (NSString*)stringWithObject:(id)value 129 | allowScalar:(BOOL)x 130 | error:(NSError**)error; 131 | 132 | /// Parse the string and return the represented object (or scalar) 133 | - (id)objectWithString:(id)value 134 | allowScalar:(BOOL)x 135 | error:(NSError**)error; 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /Classes/AppDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDetailViewController.m 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import "AppDetailViewController.h" 10 | #import "LogsListViewController.h" 11 | 12 | @interface AppDetailViewController () 13 | - (void)updateView; 14 | 15 | @end 16 | 17 | @implementation AppDetailViewController 18 | 19 | // The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. 20 | 21 | - (id)initWithApp:(NSDictionary *)theApp { 22 | self = [super init]; 23 | if (self) { 24 | // Custom initialization. 25 | app = [theApp retain]; 26 | } 27 | return self; 28 | } 29 | 30 | // Implement loadView to create a view hierarchy programmatically, without using a nib. 31 | - (void)loadView { 32 | [super loadView]; 33 | 34 | NSString *notificationName = [NSString stringWithFormat:@"APP_UPDATED_%@", [app valueForKey:@"id"]]; 35 | [[NSNotificationCenter defaultCenter] addObserver:self 36 | selector:@selector(reloadedApp:) 37 | name:notificationName 38 | object:nil]; 39 | 40 | 41 | appTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 416) style:UITableViewStyleGrouped]; 42 | appTableView.backgroundColor = [UIColor whiteColor]; 43 | appTableView.delegate = self; 44 | appTableView.dataSource = self; 45 | [self.view addSubview:appTableView]; 46 | 47 | nodejitsu = [[Nodejitsu alloc] init]; 48 | nodejitsu.delegate = self; 49 | [self updateView]; 50 | } 51 | 52 | - (void)updateView { 53 | self.title = [app valueForKey:@"name"]; 54 | [appTableView reloadData]; 55 | } 56 | 57 | - (void)reloadedApp:(NSNotification *)notification { 58 | [app release]; 59 | app = [notification.userInfo retain]; 60 | //DLog(@"%@", [notification.userInfo description]); 61 | [self updateView]; 62 | } 63 | 64 | - (void)toggleState { 65 | // Only show the stop and restart buttons when started 66 | if ([[app valueForKey:@"state"] isEqualToString:@"started"]) { 67 | UIActionSheet *popupQuery = [[UIActionSheet alloc] initWithTitle:@"Change State?" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Stop", @"Restart", nil]; 68 | popupQuery.actionSheetStyle = UIActionSheetStyleBlackOpaque; 69 | [popupQuery showInView:self.view]; 70 | [popupQuery release]; 71 | } 72 | else { 73 | UIActionSheet *popupQuery = [[UIActionSheet alloc] initWithTitle:@"Change State?" delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Start", nil]; 74 | popupQuery.actionSheetStyle = UIActionSheetStyleBlackOpaque; 75 | [popupQuery showInView:self.view]; 76 | [popupQuery release]; 77 | } 78 | } 79 | 80 | 81 | - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { 82 | BOOL showSpinner = NO; 83 | NSString *buttonTitle = [actionSheet buttonTitleAtIndex:buttonIndex]; 84 | UITableViewCell *theCell = [appTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; 85 | 86 | if ([buttonTitle isEqualToString:@"Start"]) { 87 | theCell.textLabel.text = @"starting..."; 88 | showSpinner = YES; 89 | [nodejitsu startApplication:[app valueForKey:@"id"]]; 90 | } else if ([buttonTitle isEqualToString:@"Stop"]) { 91 | theCell.textLabel.text = @"stopping..."; 92 | showSpinner = YES; 93 | [nodejitsu stopApplication:[app valueForKey:@"id"]]; 94 | } else if ([buttonTitle isEqualToString:@"Restart"]) { 95 | theCell.textLabel.text = @"restarting..."; 96 | showSpinner = YES; 97 | [nodejitsu restartApplication:[app valueForKey:@"id"]]; 98 | } else if ([buttonTitle isEqualToString:@"Delete"]) { 99 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Delete Application" 100 | message:[NSString stringWithFormat:@"Are you sure you want to delete '%@'?", [app valueForKey:@"name"]] 101 | delegate:self 102 | cancelButtonTitle:@"Cancel" 103 | otherButtonTitles:@"Yes, delete it!", nil]; 104 | [alert show]; 105 | [alert release]; 106 | } 107 | 108 | if (showSpinner) { 109 | theCell.accessoryView = nil; 110 | theCell.textLabel.textColor = [UIColor grayColor]; 111 | UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 112 | [spinner setFrame:CGRectMake(280, 12, 20, 20)]; 113 | [spinner startAnimating]; 114 | [spinner setHidesWhenStopped:YES]; 115 | [spinner setTag:1345]; 116 | [theCell addSubview:spinner]; 117 | [spinner release]; 118 | } 119 | } 120 | 121 | - (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { 122 | 123 | if (buttonIndex == 0) { 124 | // Delete the application 125 | DLog(@"Cancel"); 126 | } 127 | else { 128 | NSLog(@"Delete"); 129 | } 130 | } 131 | 132 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 133 | return 1; 134 | } 135 | 136 | 137 | // Customize the number of rows in the table view. 138 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 139 | return 2; 140 | } 141 | 142 | 143 | // Customize the appearance of table view cells. 144 | - (UITableViewCell *)tableView:(UITableView *)tableView 145 | cellForRowAtIndexPath:(NSIndexPath *)indexPath { 146 | static NSString *CellIdentifier = @"Cell"; 147 | 148 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 149 | if (cell == nil) { 150 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 151 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 152 | cell.selectionStyle = UITableViewCellSelectionStyleGray; 153 | } 154 | 155 | if ([cell viewWithTag:1345]) 156 | [[cell viewWithTag:1345] removeFromSuperview]; 157 | 158 | NSString *imageName = @"app-off.png"; 159 | if ([[app valueForKey:@"state"] isEqualToString:@"started"]) 160 | imageName = @"app-on.png"; 161 | 162 | if (indexPath.row == 0) { 163 | cell.textLabel.textColor = [UIColor blackColor]; 164 | cell.textLabel.text = [app valueForKey:@"state"]; 165 | [cell setAccessoryView:[[[UIImageView alloc] initWithImage:[UIImage imageNamed:imageName]] autorelease]]; 166 | cell.accessoryType = UITableViewCellAccessoryNone; 167 | } 168 | 169 | if (indexPath.row == 1) { 170 | 171 | cell.textLabel.text = @"Logs"; 172 | } 173 | 174 | return cell; 175 | } 176 | 177 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 178 | if (indexPath.row == 0) { 179 | [self toggleState]; 180 | } 181 | if (indexPath.row == 1) { 182 | LogsListViewController *logsViewController = [[LogsListViewController alloc] initWithApp:app]; 183 | [self.navigationController pushViewController:logsViewController animated:YES]; 184 | [logsViewController release]; 185 | } 186 | [appTableView deselectRowAtIndexPath:indexPath animated:NO]; 187 | } 188 | 189 | - (void)didReceiveMemoryWarning { 190 | // Releases the view if it doesn't have a superview. 191 | [super didReceiveMemoryWarning]; 192 | 193 | // Release any cached data, images, etc. that aren't in use. 194 | } 195 | 196 | - (void)viewDidUnload { 197 | [super viewDidUnload]; 198 | // Release any retained subviews of the main view. 199 | // e.g. self.myOutlet = nil; 200 | } 201 | 202 | 203 | - (void)dealloc { 204 | [app release]; 205 | [appTableView release]; 206 | [nodejitsu release]; 207 | [super dealloc]; 208 | } 209 | 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /Libraries/ASI/ASIFormDataRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASIFormDataRequest.m 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 07/11/2008. 6 | // Copyright 2008-2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import "ASIFormDataRequest.h" 10 | 11 | 12 | // Private stuff 13 | @interface ASIFormDataRequest () 14 | - (void)buildMultipartFormDataPostBody; 15 | - (void)buildURLEncodedPostBody; 16 | @property (retain) NSMutableDictionary *postData; 17 | @property (retain) NSMutableDictionary *fileData; 18 | @end 19 | 20 | @implementation ASIFormDataRequest 21 | 22 | #pragma mark utilities 23 | - (NSString*)encodeURL:(NSString *)string 24 | { 25 | NSString *newString = [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), CFStringConvertNSStringEncodingToEncoding([self stringEncoding])) autorelease]; 26 | if (newString) { 27 | return newString; 28 | } 29 | return @""; 30 | } 31 | 32 | #pragma mark init / dealloc 33 | 34 | + (id)requestWithURL:(NSURL *)newURL 35 | { 36 | return [[[self alloc] initWithURL:newURL] autorelease]; 37 | } 38 | 39 | - (id)initWithURL:(NSURL *)newURL 40 | { 41 | self = [super initWithURL:newURL]; 42 | [self setPostFormat:ASIURLEncodedPostFormat]; 43 | [self setStringEncoding:NSUTF8StringEncoding]; 44 | return self; 45 | } 46 | 47 | - (void)dealloc 48 | { 49 | [postData release]; 50 | [fileData release]; 51 | [super dealloc]; 52 | } 53 | 54 | #pragma mark setup request 55 | 56 | - (void)setPostValue:(id )value forKey:(NSString *)key 57 | { 58 | if (![self postData]) { 59 | [self setPostData:[NSMutableDictionary dictionary]]; 60 | } 61 | [[self postData] setValue:[value description] forKey:key]; 62 | [self setRequestMethod:@"POST"]; 63 | } 64 | 65 | - (void)setFile:(NSString *)filePath forKey:(NSString *)key 66 | { 67 | [self setFile:filePath withFileName:nil andContentType:nil forKey:key]; 68 | } 69 | 70 | - (void)setFile:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key 71 | { 72 | if (![self fileData]) { 73 | [self setFileData:[NSMutableDictionary dictionary]]; 74 | } 75 | 76 | // If data is a path to a local file 77 | if ([data isKindOfClass:[NSString class]]) { 78 | BOOL isDirectory = NO; 79 | BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:(NSString *)data isDirectory:&isDirectory]; 80 | if (!fileExists || isDirectory) { 81 | [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"No file exists at %@",data],NSLocalizedDescriptionKey,nil]]]; 82 | } 83 | 84 | // If the caller didn't specify a custom file name, we'll use the file name of the file we were passed 85 | if (!fileName) { 86 | fileName = [(NSString *)data lastPathComponent]; 87 | } 88 | 89 | // If we were given the path to a file, and the user didn't specify a mime type, we can detect it (currently only on Mac OS) 90 | // Will return 'application/octet-stream' on iPhone, or if the mime type cannot be determined 91 | if (!contentType) { 92 | contentType = [ASIHTTPRequest mimeTypeForFileAtPath:data]; 93 | } 94 | } 95 | 96 | NSDictionary *fileInfo = [NSDictionary dictionaryWithObjectsAndKeys:data, @"data", contentType, @"contentType", fileName, @"fileName", nil]; 97 | [[self fileData] setObject:fileInfo forKey:key]; 98 | [self setRequestMethod: @"POST"]; 99 | } 100 | 101 | - (void)setData:(NSData *)data forKey:(NSString *)key 102 | { 103 | [self setData:data withFileName:@"file" andContentType:nil forKey:key]; 104 | } 105 | 106 | - (void)setData:(id)data withFileName:(NSString *)fileName andContentType:(NSString *)contentType forKey:(NSString *)key 107 | { 108 | if (![self fileData]) { 109 | [self setFileData:[NSMutableDictionary dictionary]]; 110 | } 111 | if (!contentType) { 112 | contentType = @"application/octet-stream"; 113 | } 114 | 115 | NSDictionary *fileInfo = [NSDictionary dictionaryWithObjectsAndKeys:data, @"data", contentType, @"contentType", fileName, @"fileName", nil]; 116 | [[self fileData] setObject:fileInfo forKey:key]; 117 | [self setRequestMethod: @"POST"]; 118 | } 119 | 120 | - (void)buildPostBody 121 | { 122 | if (![self postData] && ![self fileData]) { 123 | [super buildPostBody]; 124 | return; 125 | } 126 | if ([[self fileData] count] > 0) { 127 | [self setShouldStreamPostDataFromDisk:YES]; 128 | } 129 | 130 | if ([self postFormat] == ASIURLEncodedPostFormat) { 131 | [self buildURLEncodedPostBody]; 132 | } else { 133 | [self buildMultipartFormDataPostBody]; 134 | } 135 | 136 | [super buildPostBody]; 137 | } 138 | 139 | 140 | - (void)buildMultipartFormDataPostBody 141 | { 142 | NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding([self stringEncoding])); 143 | 144 | // Set your own boundary string only if really obsessive. We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does. 145 | NSString *stringBoundary = @"0xKhTmLbOuNdArY"; 146 | 147 | [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"multipart/form-data; charset=%@; boundary=%@", charset, stringBoundary]]; 148 | 149 | [self appendPostData:[[NSString stringWithFormat:@"--%@\r\n",stringBoundary] dataUsingEncoding:[self stringEncoding]]]; 150 | 151 | // Adds post data 152 | NSData *endItemBoundary = [[NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary] dataUsingEncoding:[self stringEncoding]]; 153 | NSEnumerator *e = [[self postData] keyEnumerator]; 154 | NSString *key; 155 | int i=0; 156 | while (key = [e nextObject]) { 157 | [self appendPostData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key] dataUsingEncoding:[self stringEncoding]]]; 158 | [self appendPostData:[[[self postData] objectForKey:key] dataUsingEncoding:[self stringEncoding]]]; 159 | i++; 160 | if (i != [[self postData] count] || [[self fileData] count] > 0) { //Only add the boundary if this is not the last item in the post body 161 | [self appendPostData:endItemBoundary]; 162 | } 163 | } 164 | 165 | // Adds files to upload 166 | e = [fileData keyEnumerator]; 167 | i=0; 168 | while (key = [e nextObject]) { 169 | NSDictionary *fileInfo = [[self fileData] objectForKey:key]; 170 | id file = [fileInfo objectForKey:@"data"]; 171 | NSString *contentType = [fileInfo objectForKey:@"contentType"]; 172 | NSString *fileName = [fileInfo objectForKey:@"fileName"]; 173 | 174 | [self appendPostData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", key, fileName] dataUsingEncoding:[self stringEncoding]]]; 175 | [self appendPostData:[[NSString stringWithFormat:@"Content-Type: %@; charset=%@\r\n\r\n", contentType, charset] dataUsingEncoding:[self stringEncoding]]]; 176 | 177 | if ([file isKindOfClass:[NSString class]]) { 178 | [self appendPostDataFromFile:file]; 179 | } else { 180 | [self appendPostData:file]; 181 | } 182 | i++; 183 | // Only add the boundary if this is not the last item in the post body 184 | if (i != [[self fileData] count]) { 185 | [self appendPostData:endItemBoundary]; 186 | } 187 | } 188 | 189 | [self appendPostData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary] dataUsingEncoding:[self stringEncoding]]]; 190 | 191 | } 192 | 193 | - (void)buildURLEncodedPostBody 194 | { 195 | // We can't post binary data using application/x-www-form-urlencoded 196 | if ([[self fileData] count] > 0) { 197 | [self setPostFormat:ASIMultipartFormDataPostFormat]; 198 | [self buildMultipartFormDataPostBody]; 199 | return; 200 | } 201 | NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding([self stringEncoding])); 202 | 203 | [self addRequestHeader:@"Content-Type" value:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@",charset]]; 204 | 205 | 206 | NSEnumerator *e = [[self postData] keyEnumerator]; 207 | NSString *key; 208 | int i=0; 209 | int count = [[self postData] count]-1; 210 | while (key = [e nextObject]) { 211 | NSString *data = [NSString stringWithFormat:@"%@=%@%@", [self encodeURL:key], [self encodeURL:[[self postData] objectForKey:key]],(i 2 | 3 | 4 | 800 5 | 10D540 6 | 760 7 | 1038.29 8 | 460.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 81 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | IBCocoaTouchFramework 42 | 43 | 44 | 45 | 1316 46 | 47 | {320, 480} 48 | 49 | 50 | 1 51 | MSAxIDEAA 52 | 53 | NO 54 | NO 55 | 56 | IBCocoaTouchFramework 57 | YES 58 | 59 | 60 | 61 | 62 | YES 63 | 64 | 65 | delegate 66 | 67 | 68 | 69 | 4 70 | 71 | 72 | 73 | window 74 | 75 | 76 | 77 | 5 78 | 79 | 80 | 81 | 82 | YES 83 | 84 | 0 85 | 86 | 87 | 88 | 89 | 90 | 2 91 | 92 | 93 | YES 94 | 95 | 96 | 97 | 98 | -1 99 | 100 | 101 | File's Owner 102 | 103 | 104 | 3 105 | 106 | 107 | 108 | 109 | -2 110 | 111 | 112 | 113 | 114 | 115 | 116 | YES 117 | 118 | YES 119 | -1.CustomClassName 120 | -2.CustomClassName 121 | 2.IBAttributePlaceholdersKey 122 | 2.IBEditorWindowLastContentRect 123 | 2.IBPluginDependency 124 | 3.CustomClassName 125 | 3.IBPluginDependency 126 | 127 | 128 | YES 129 | UIApplication 130 | UIResponder 131 | 132 | YES 133 | 134 | 135 | YES 136 | 137 | 138 | {{198, 376}, {320, 480}} 139 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 140 | iNodejitsuAppDelegate 141 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 142 | 143 | 144 | 145 | YES 146 | 147 | 148 | YES 149 | 150 | 151 | 152 | 153 | YES 154 | 155 | 156 | YES 157 | 158 | 159 | 160 | 9 161 | 162 | 163 | 164 | YES 165 | 166 | iNodejitsuAppDelegate 167 | NSObject 168 | 169 | window 170 | UIWindow 171 | 172 | 173 | IBProjectSource 174 | Classes/iNodejitsuAppDelegate.h 175 | 176 | 177 | 178 | iNodejitsuAppDelegate 179 | NSObject 180 | 181 | IBUserSource 182 | 183 | 184 | 185 | 186 | 187 | 0 188 | IBCocoaTouchFramework 189 | 190 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 191 | 192 | 193 | YES 194 | iNodejitsu.xcodeproj 195 | 3 196 | 81 197 | 198 | 199 | -------------------------------------------------------------------------------- /Classes/Nodejitsu.m: -------------------------------------------------------------------------------- 1 | // 2 | // Nodejitsu.m 3 | // iNodejitsu 4 | // 5 | // Created by Cory Smith on 11-09-02. 6 | // Copyright 2011 Leading Lines Design. All rights reserved. 7 | // 8 | 9 | #import "Nodejitsu.h" 10 | #import "ASIHTTPRequest.h" 11 | #import "KeychainItemWrapper.h" 12 | 13 | @implementation Nodejitsu 14 | @synthesize delegate, loginDelegate; 15 | 16 | - (id)init { 17 | self = [super init]; 18 | if (self != nil) { 19 | apps = [[NSArray array] retain]; 20 | KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"TestAppLoginData" accessGroup:nil]; 21 | _username = [[keychain objectForKey:(id) kSecAttrAccount] retain]; 22 | _password = [[keychain objectForKey:(id) kSecValueData] retain]; 23 | [keychain release]; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)loginWithUsername:(NSString *)username password:(NSString *)password { 29 | _username = [username retain]; 30 | _password = [password retain]; 31 | NSString *urlString = [NSString stringWithFormat:@"http://api.nodejitsu.com/apps/%@", _username]; 32 | NSURL *url = [NSURL URLWithString:urlString]; 33 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 34 | [request setUsername:_username]; 35 | [request setPassword:_password]; 36 | DLog(@"%@/%@", _username, _password); 37 | [request setDelegate:self]; 38 | [request setDidFinishSelector:@selector(loginFinished:)]; 39 | [request setDidFailSelector:@selector(loginFailed:)]; 40 | [request startAsynchronous]; 41 | } 42 | 43 | - (void)loginFinished:(ASIHTTPRequest *)request { 44 | DLog(@""); 45 | NSDictionary *dict = [[request responseString] JSONValue]; 46 | DLog(@"%@", [dict description]); 47 | DLog(@"%d", request.responseStatusCode); 48 | DLog(@"%@", [[request error] description]); 49 | DLog(@"%@", [request responseString]); 50 | DLog(@"%@", [[request url] description]); 51 | DLog(@"%@/%@", _username, _password); 52 | if (request.responseStatusCode == 200) { 53 | KeychainItemWrapper *keychain = [[KeychainItemWrapper alloc] initWithIdentifier:@"TestAppLoginData" accessGroup:nil]; 54 | [keychain setObject:_username forKey:(id) kSecAttrAccount]; 55 | [keychain setObject:_password forKey:(id) kSecValueData]; 56 | [keychain release]; 57 | [loginDelegate loginSucceeded]; 58 | } 59 | else { 60 | [loginDelegate loginFailed:[dict valueForKey:@"error"]]; 61 | } 62 | } 63 | 64 | - (BOOL)isAuthenticated { 65 | return _username != nil && _password != nil; 66 | } 67 | 68 | - (void)loginFailed:(ASIHTTPRequest *)request { 69 | DLog(@"%d", request.responseStatusCode); 70 | DLog(@"%@", [[request error] description]); 71 | DLog(@"%@", [request responseString]); 72 | 73 | [loginDelegate loginFailed:@"Please double check your username / password"]; 74 | } 75 | 76 | - (void)getApplications { 77 | DLog(@"%@/%@", _username, _password); 78 | NSString *urlString = [NSString stringWithFormat:@"http://api.nodejitsu.com/apps/%@", _username]; 79 | 80 | NSURL *url = [NSURL URLWithString:urlString]; 81 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 82 | [request setUsername:_username]; 83 | [request setPassword:_password]; 84 | [request setDelegate:self]; 85 | [request setDidFinishSelector:@selector(getApplicationsFinished:)]; 86 | [request setDidFailSelector:@selector(getApplicationsFailed:)]; 87 | [request startAsynchronous]; 88 | } 89 | 90 | - (void)getApplicationsFinished:(ASIHTTPRequest *)request { 91 | DLog(@"%@", [[request url] description]); 92 | DLog(@"%d", request.responseStatusCode); 93 | DLog(@"%@", [[request error] description]); 94 | DLog(@"%@", [request responseString]); 95 | NSDictionary *dict = [[request responseString] JSONValue]; 96 | DLog(@"%@", [dict description]); 97 | [apps release]; 98 | apps = [[dict objectForKey:@"apps"] retain]; 99 | 100 | for (NSDictionary *dict in apps) { 101 | NSString *notificationName = [NSString stringWithFormat:@"APP_UPDATED_%@", [dict valueForKey:@"id"]]; 102 | [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil userInfo:dict]; 103 | } 104 | 105 | [delegate loadedApplications]; 106 | } 107 | 108 | - (NSArray *)allApplications { 109 | return apps; 110 | } 111 | 112 | - (void)getApplicationsFailed:(ASIHTTPRequest *)request { 113 | DLog(@"%d", request.responseStatusCode); 114 | } 115 | 116 | - (void)getLogsForApplication:(NSString *)appId { 117 | DLog(@""); 118 | NSString *urlString = [NSString stringWithFormat:@"http://api.nodejitsu.com/logs/%@/%@/", _username, appId]; 119 | NSURL *url = [NSURL URLWithString:urlString]; 120 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 121 | [request setRequestMethod:@"POST"]; 122 | [request setUsername:_username]; 123 | [request setPassword:_password]; 124 | [request setDelegate:self]; 125 | [request setDidFinishSelector:@selector(getLogsForApplicationFinished:)]; 126 | [request setDidFailSelector:@selector(getLogsForApplicationFailed:)]; 127 | [request startAsynchronous]; 128 | } 129 | 130 | - (void)getLogsForApplicationFinished:(ASIHTTPRequest *)request { 131 | DLog(@""); 132 | NSArray *array = [[request responseString] JSONValue]; 133 | [delegate loadedLogsForApplication:array]; 134 | DLog(@"%@", [array description]); 135 | } 136 | 137 | - (void)getLogsForApplicationFailed:(ASIHTTPRequest *)request { 138 | DLog(@"%d", request.responseStatusCode); 139 | } 140 | 141 | //Start an Application 142 | //POST /apps/:user-id/:app-id/start 143 | - (void)startApplication:(NSString *)appId { 144 | NSString *urlString = [NSString stringWithFormat:@"http://api.nodejitsu.com/apps/%@/%@/start", _username, appId]; 145 | NSURL *url = [NSURL URLWithString:urlString]; 146 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 147 | [request setTimeOutSeconds:15]; 148 | [request setRequestMethod:@"POST"]; 149 | [request setUsername:_username]; 150 | [request setPassword:_password]; 151 | [request setDelegate:self]; 152 | [request setDidFinishSelector:@selector(startApplicationFinished:)]; 153 | [request setDidFailSelector:@selector(startApplicationFailed:)]; 154 | [request startAsynchronous]; 155 | } 156 | 157 | - (void)startApplicationFinished:(ASIHTTPRequest *)request { 158 | DLog(@"%@", [request responseString]); 159 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadApplications" object:nil]; 160 | } 161 | 162 | - (void)startApplicationFailed:(ASIHTTPRequest *)request { 163 | DLog(@"%d", request.responseStatusCode); 164 | DLog(@"%@", [[request error] description]); 165 | DLog(@"%@", [request responseString]); 166 | } 167 | 168 | //Stop an Application 169 | //POST /apps/:user-id/:app-id/stop 170 | - (void)stopApplication:(NSString *)appId { 171 | NSString *urlString = [NSString stringWithFormat:@"http://api.nodejitsu.com/apps/%@/%@/stop", _username, appId]; 172 | NSURL *url = [NSURL URLWithString:urlString]; 173 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 174 | [request setTimeOutSeconds:15]; 175 | [request setRequestMethod:@"POST"]; 176 | [request setUsername:_username]; 177 | [request setPassword:_password]; 178 | [request setDelegate:self]; 179 | [request setDidFinishSelector:@selector(stopApplicationFinished:)]; 180 | [request setDidFailSelector:@selector(stopApplicationFailed:)]; 181 | [request startAsynchronous]; 182 | } 183 | 184 | - (void)stopApplicationFinished:(ASIHTTPRequest *)request { 185 | DLog(@"%@", [request responseString]); 186 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadApplications" object:nil]; 187 | } 188 | 189 | - (void)stopApplicationFailed:(ASIHTTPRequest *)request { 190 | DLog(@"%d", request.responseStatusCode); 191 | DLog(@"%@", [request responseString]); 192 | } 193 | 194 | //Restart an Application 195 | //POST /apps/:user-id/:app-id/restart 196 | - (void)restartApplication:(NSString *)appId { 197 | NSString *urlString = [NSString stringWithFormat:@"http://api.nodejitsu.com/apps/%@/%@/restart", _username, appId]; 198 | NSURL *url = [NSURL URLWithString:urlString]; 199 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 200 | [request setTimeOutSeconds:15]; 201 | [request setRequestMethod:@"POST"]; 202 | [request setUsername:_username]; 203 | [request setPassword:_password]; 204 | [request setDelegate:self]; 205 | [request setDidFinishSelector:@selector(restartApplicationFinished:)]; 206 | [request setDidFailSelector:@selector(restartApplicationFailed:)]; 207 | [request startAsynchronous]; 208 | } 209 | 210 | - (void)restartApplicationFinished:(ASIHTTPRequest *)request { 211 | DLog(@"%@", [request responseString]); 212 | [[NSNotificationCenter defaultCenter] postNotificationName:@"ReloadApplications" object:nil]; 213 | } 214 | 215 | - (void)restartApplicationFailed:(ASIHTTPRequest *)request { 216 | DLog(@"%d", request.responseStatusCode); 217 | DLog(@"%@", [request responseString]); 218 | } 219 | 220 | - (void)dealloc { 221 | if (_username) 222 | [_username release]; 223 | if (_password) 224 | [_password release]; 225 | [apps release]; 226 | [super dealloc]; 227 | } 228 | 229 | @end 230 | -------------------------------------------------------------------------------- /Libraries/ASI/ASIAuthenticationDialog.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASIAuthenticationDialog.m 3 | // iPhone 4 | // 5 | // Created by Ben Copsey on 21/08/2009. 6 | // Copyright 2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import "ASIAuthenticationDialog.h" 10 | #import "ASIHTTPRequest.h" 11 | 12 | ASIAuthenticationDialog *sharedDialog = nil; 13 | NSLock *dialogLock = nil; 14 | 15 | @interface ASIAuthenticationDialog () 16 | - (void)show; 17 | @end 18 | 19 | @implementation ASIAuthenticationDialog 20 | 21 | + (void)initialize 22 | { 23 | if (self == [ASIAuthenticationDialog class]) { 24 | dialogLock = [[NSLock alloc] init]; 25 | } 26 | } 27 | 28 | + (void)presentProxyAuthenticationDialogForRequest:(ASIHTTPRequest *)request 29 | { 30 | [dialogLock lock]; 31 | [sharedDialog release]; 32 | sharedDialog = [[self alloc] init]; 33 | [sharedDialog setRequest:request]; 34 | [sharedDialog setType:ASIProxyAuthenticationType]; 35 | [sharedDialog show]; 36 | [dialogLock unlock]; 37 | } 38 | 39 | + (void)presentAuthenticationDialogForRequest:(ASIHTTPRequest *)request 40 | { 41 | [dialogLock lock]; 42 | [sharedDialog release]; 43 | sharedDialog = [[self alloc] init]; 44 | [sharedDialog setRequest:request]; 45 | [sharedDialog show]; 46 | [dialogLock unlock]; 47 | 48 | } 49 | 50 | - (void)show 51 | { 52 | // Create an action sheet to show the login dialog 53 | [self setLoginDialog:[[[UIActionSheet alloc] init] autorelease]]; 54 | [[self loginDialog] setActionSheetStyle:UIActionSheetStyleBlackOpaque]; 55 | [[self loginDialog] setDelegate:self]; 56 | 57 | // We show the login form in a table view, similar to Safari's authentication dialog 58 | UITableView *table = [[[UITableView alloc] initWithFrame:CGRectMake(0,80,320,480) style:UITableViewStyleGrouped] autorelease]; 59 | [table setDelegate:self]; 60 | [table setDataSource:self]; 61 | [[self loginDialog] addSubview:table]; 62 | [[self loginDialog] showInView:[[[UIApplication sharedApplication] windows] objectAtIndex:0]]; 63 | [[self loginDialog] setFrame:CGRectMake(0,0,320,480)]; 64 | 65 | // Setup the title (Couldn't figure out how to put this in the same toolbar as the buttons) 66 | UIToolbar *titleBar = [[[UIToolbar alloc] initWithFrame:CGRectMake(0,0,320,30)] autorelease]; 67 | UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10,0,300,30)] autorelease]; 68 | if ([self type] == ASIProxyAuthenticationType) { 69 | [label setText:@"Login to this secure proxy server."]; 70 | } else { 71 | [label setText:@"Login to this secure server."]; 72 | } 73 | [label setTextColor:[UIColor blackColor]]; 74 | [label setFont:[UIFont systemFontOfSize:13.0]]; 75 | [label setShadowColor:[UIColor colorWithRed:1 green:1 blue:1 alpha:0.5]]; 76 | [label setShadowOffset:CGSizeMake(0, 1.0)]; 77 | [label setOpaque:NO]; 78 | [label setBackgroundColor:nil]; 79 | [label setTextAlignment:UITextAlignmentCenter]; 80 | 81 | [titleBar addSubview:label]; 82 | [[self loginDialog] addSubview:titleBar]; 83 | 84 | // Setup the toolbar 85 | UIToolbar *toolbar = [[[UIToolbar alloc] initWithFrame:CGRectMake(0,30,320,50)] autorelease]; 86 | 87 | NSMutableArray *items = [[[NSMutableArray alloc] init] autorelease]; 88 | UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAuthenticationFromDialog:)] autorelease]; 89 | [items addObject:backButton]; 90 | 91 | label = [[[UILabel alloc] initWithFrame:CGRectMake(0,0,170,50)] autorelease]; 92 | if ([self type] == ASIProxyAuthenticationType) { 93 | [label setText:[[self request] proxyHost]]; 94 | } else { 95 | [label setText:[[[self request] url] host]]; 96 | } 97 | [label setTextColor:[UIColor whiteColor]]; 98 | [label setFont:[UIFont boldSystemFontOfSize:22.0]]; 99 | [label setShadowColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]]; 100 | [label setShadowOffset:CGSizeMake(0, -1.0)]; 101 | [label setOpaque:NO]; 102 | [label setBackgroundColor:nil]; 103 | [label setTextAlignment:UITextAlignmentCenter]; 104 | 105 | [toolbar addSubview:label]; 106 | 107 | UIBarButtonItem *labelButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:nil action:nil] autorelease]; 108 | [labelButton setCustomView:label]; 109 | [items addObject:labelButton]; 110 | [items addObject:[[[UIBarButtonItem alloc] initWithTitle:@"Login" style:UIBarButtonItemStyleDone target:self action:@selector(loginWithCredentialsFromDialog:)] autorelease]]; 111 | [toolbar setItems:items]; 112 | 113 | [[self loginDialog] addSubview:toolbar]; 114 | 115 | // Force reload the table content, and focus the first field to show the keyboard 116 | [table reloadData]; 117 | [[[[table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] subviews] objectAtIndex:2] becomeFirstResponder]; 118 | 119 | } 120 | 121 | - (void)cancelAuthenticationFromDialog:(id)sender 122 | { 123 | [[self request] cancelAuthentication]; 124 | [[self loginDialog] dismissWithClickedButtonIndex:0 animated:YES]; 125 | } 126 | 127 | - (void)loginWithCredentialsFromDialog:(id)sender 128 | { 129 | NSString *username = [[[[[[[self loginDialog] subviews] objectAtIndex:0] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] subviews] objectAtIndex:2] text]; 130 | NSString *password = [[[[[[[self loginDialog] subviews] objectAtIndex:0] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] subviews] objectAtIndex:2] text]; 131 | 132 | if ([self type] == ASIProxyAuthenticationType) { 133 | [[self request] setProxyUsername:username]; 134 | [[self request] setProxyPassword:password]; 135 | } else { 136 | [[self request] setUsername:username]; 137 | [[self request] setPassword:password]; 138 | } 139 | 140 | // Handle NTLM domains 141 | NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme]; 142 | if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) { 143 | NSString *domain = [[[[[[[self loginDialog] subviews] objectAtIndex:0] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:2]] subviews] objectAtIndex:2] text]; 144 | if ([self type] == ASIProxyAuthenticationType) { 145 | [[self request] setProxyDomain:domain]; 146 | } else { 147 | [[self request] setDomain:domain]; 148 | } 149 | } 150 | 151 | [[self loginDialog] dismissWithClickedButtonIndex:1 animated:YES]; 152 | [[self request] retryUsingSuppliedCredentials]; 153 | } 154 | 155 | 156 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 157 | { 158 | NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme]; 159 | if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) { 160 | return 3; 161 | } 162 | return 2; 163 | } 164 | 165 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 166 | { 167 | if (section == [self numberOfSectionsInTableView:tableView]-1) { 168 | return 30; 169 | } 170 | return 0; 171 | } 172 | 173 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 174 | { 175 | if (section == 0) { 176 | return 30; 177 | } 178 | return 0; 179 | } 180 | 181 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 182 | { 183 | if (section == 0) { 184 | return [[self request] authenticationRealm]; 185 | } 186 | return nil; 187 | } 188 | 189 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 190 | { 191 | #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_0 192 | UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:nil] autorelease]; 193 | #else 194 | UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease]; 195 | #endif 196 | 197 | [cell setSelectionStyle:UITableViewCellSelectionStyleNone]; 198 | UITextField *textField = [[[UITextField alloc] initWithFrame:CGRectMake(20,12,260,25)] autorelease]; 199 | [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone]; 200 | if ([indexPath section] == 0) { 201 | [textField setPlaceholder:@"User"]; 202 | } else if ([indexPath section] == 1) { 203 | [textField setPlaceholder:@"Password"]; 204 | [textField setSecureTextEntry:YES]; 205 | } else if ([indexPath section] == 2) { 206 | [textField setPlaceholder:@"Domain"]; 207 | } 208 | [cell addSubview:textField]; 209 | 210 | return cell; 211 | } 212 | 213 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 214 | { 215 | return 1; 216 | } 217 | 218 | 219 | - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section 220 | { 221 | if (section == [self numberOfSectionsInTableView:tableView]-1) { 222 | // If we're using Basic authentication and the connection is not using SSL, we'll show the plain text message 223 | if ([[[self request] authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic] && ![[[[self request] url] scheme] isEqualToString:@"https"]) { 224 | return @"Password will be sent in the clear."; 225 | // We are using Digest, NTLM, or any scheme over SSL 226 | } else { 227 | return @"Password will be sent securely."; 228 | } 229 | } 230 | return nil; 231 | } 232 | 233 | @synthesize request; 234 | @synthesize loginDialog; 235 | @synthesize type; 236 | @end 237 | -------------------------------------------------------------------------------- /Libraries/ASI/ASINetworkQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASINetworkQueue.m 3 | // asi-http-request 4 | // 5 | // Created by Ben Copsey on 07/11/2008. 6 | // Copyright 2008-2009 All-Seeing Interactive. All rights reserved. 7 | // 8 | 9 | #import "ASINetworkQueue.h" 10 | #import "ASIHTTPRequest.h" 11 | 12 | // Private stuff 13 | @interface ASINetworkQueue () 14 | @property (assign) int requestsCount; 15 | @property (assign) unsigned long long uploadProgressBytes; 16 | @property (assign) unsigned long long uploadProgressTotalBytes; 17 | @property (assign) unsigned long long downloadProgressBytes; 18 | @property (assign) unsigned long long downloadProgressTotalBytes; 19 | @end 20 | 21 | @implementation ASINetworkQueue 22 | 23 | - (id)init 24 | { 25 | self = [super init]; 26 | [self setShouldCancelAllRequestsOnFailure:YES]; 27 | [self setMaxConcurrentOperationCount:4]; 28 | [self setSuspended:YES]; 29 | 30 | return self; 31 | } 32 | 33 | + (id)queue 34 | { 35 | return [[[self alloc] init] autorelease]; 36 | } 37 | 38 | - (void)dealloc 39 | { 40 | //We need to clear the delegate on any requests that haven't got around to cleaning up yet, as otherwise they'll try to let us know if something goes wrong, and we'll be long gone by then 41 | for (ASIHTTPRequest *request in [self operations]) { 42 | [request setDelegate:nil]; 43 | } 44 | [super dealloc]; 45 | } 46 | 47 | - (BOOL)isNetworkActive 48 | { 49 | return ([self requestsCount] > 0 && ![self isSuspended]); 50 | } 51 | 52 | - (void)updateNetworkActivityIndicator 53 | { 54 | #if TARGET_OS_IPHONE 55 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActive]]; 56 | #endif 57 | } 58 | 59 | - (void)setSuspended:(BOOL)suspend 60 | { 61 | [super setSuspended:suspend]; 62 | [self updateNetworkActivityIndicator]; 63 | } 64 | 65 | 66 | - (void)go 67 | { 68 | if (![self showAccurateProgress]) { 69 | if ([self downloadProgressDelegate]) { 70 | [self incrementDownloadSizeBy:[self requestsCount]]; 71 | } 72 | if ([self uploadProgressDelegate]) { 73 | [self incrementUploadSizeBy:[self requestsCount]]; 74 | } 75 | } 76 | [self setSuspended:NO]; 77 | } 78 | 79 | - (void)cancelAllOperations 80 | { 81 | [self setRequestsCount:0]; 82 | [self setUploadProgressBytes:0]; 83 | [self setUploadProgressTotalBytes:0]; 84 | [self setDownloadProgressBytes:0]; 85 | [self setDownloadProgressTotalBytes:0]; 86 | [super cancelAllOperations]; 87 | [self updateNetworkActivityIndicator]; 88 | } 89 | 90 | - (void)setUploadProgressDelegate:(id)newDelegate 91 | { 92 | uploadProgressDelegate = newDelegate; 93 | 94 | // If the uploadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews 95 | SEL selector = @selector(setMaxValue:); 96 | if ([[self uploadProgressDelegate] respondsToSelector:selector]) { 97 | double max = 1.0; 98 | NSMethodSignature *signature = [[[self uploadProgressDelegate] class] instanceMethodSignatureForSelector:selector]; 99 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 100 | [invocation setSelector:selector]; 101 | [invocation setArgument:&max atIndex:2]; 102 | [invocation invokeWithTarget:[self uploadProgressDelegate]]; 103 | } 104 | } 105 | 106 | 107 | - (void)setDownloadProgressDelegate:(id)newDelegate 108 | { 109 | downloadProgressDelegate = newDelegate; 110 | 111 | // If the downloadProgressDelegate is an NSProgressIndicator, we set it's MaxValue to 1.0 so we can treat it similarly to UIProgressViews 112 | SEL selector = @selector(setMaxValue:); 113 | if ([[self downloadProgressDelegate] respondsToSelector:selector]) { 114 | double max = 1.0; 115 | NSMethodSignature *signature = [[[self downloadProgressDelegate] class] instanceMethodSignatureForSelector:selector]; 116 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 117 | [invocation setSelector:@selector(setMaxValue:)]; 118 | [invocation setArgument:&max atIndex:2]; 119 | [invocation invokeWithTarget:[self downloadProgressDelegate]]; 120 | } 121 | } 122 | 123 | - (void)addHEADOperation:(NSOperation *)operation 124 | { 125 | if ([operation isKindOfClass:[ASIHTTPRequest class]]) { 126 | 127 | ASIHTTPRequest *request = (ASIHTTPRequest *)operation; 128 | [request setRequestMethod:@"HEAD"]; 129 | [request setQueuePriority:10]; 130 | [request setShowAccurateProgress:YES]; 131 | [request setQueue:self]; 132 | 133 | // Important - we are calling NSOperation's add method - we don't want to add this as a normal request! 134 | [super addOperation:request]; 135 | } 136 | } 137 | 138 | // Only add ASIHTTPRequests to this queue!! 139 | - (void)addOperation:(NSOperation *)operation 140 | { 141 | if (![operation isKindOfClass:[ASIHTTPRequest class]]) { 142 | [NSException raise:@"AttemptToAddInvalidRequest" format:@"Attempted to add an object that was not an ASIHTTPRequest to an ASINetworkQueue"]; 143 | } 144 | 145 | [self setRequestsCount:[self requestsCount]+1]; 146 | 147 | ASIHTTPRequest *request = (ASIHTTPRequest *)operation; 148 | 149 | if ([self showAccurateProgress]) { 150 | 151 | // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length 152 | if ([[request requestMethod] isEqualToString:@"GET"]) { 153 | ASIHTTPRequest *HEADRequest = [[[ASIHTTPRequest alloc] initWithURL:[request url]] autorelease]; 154 | [HEADRequest setMainRequest:request]; 155 | [self addHEADOperation:HEADRequest]; 156 | 157 | //Tell the request not to reset the progress indicator when it gets a content-length, as we will get the length from the HEAD request 158 | [request setShouldResetProgressIndicators:NO]; 159 | 160 | [request addDependency:HEADRequest]; 161 | 162 | // If we want to track uploading for this request accurately, we need to add the size of the post content to the total 163 | } else if (uploadProgressDelegate) { 164 | [request buildPostBody]; 165 | [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes]+[request postLength]]; 166 | } 167 | } 168 | [request setShowAccurateProgress:[self showAccurateProgress]]; 169 | 170 | 171 | [request setQueue:self]; 172 | [super addOperation:request]; 173 | [self updateNetworkActivityIndicator]; 174 | 175 | } 176 | 177 | - (void)requestDidFail:(ASIHTTPRequest *)request 178 | { 179 | [self setRequestsCount:[self requestsCount]-1]; 180 | [self updateNetworkActivityIndicator]; 181 | if ([self requestDidFailSelector]) { 182 | [[self delegate] performSelector:[self requestDidFailSelector] withObject:request]; 183 | } 184 | if ([self shouldCancelAllRequestsOnFailure] && [self requestsCount] > 0) { 185 | [self cancelAllOperations]; 186 | } 187 | if ([self requestsCount] == 0) { 188 | if ([self queueDidFinishSelector]) { 189 | [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self]; 190 | } 191 | } 192 | } 193 | 194 | - (void)requestDidFinish:(ASIHTTPRequest *)request 195 | { 196 | [self setRequestsCount:[self requestsCount]-1]; 197 | [self updateNetworkActivityIndicator]; 198 | if ([self requestDidFinishSelector]) { 199 | [[self delegate] performSelector:[self requestDidFinishSelector] withObject:request]; 200 | } 201 | if ([self requestsCount] == 0) { 202 | if ([self queueDidFinishSelector]) { 203 | [[self delegate] performSelector:[self queueDidFinishSelector] withObject:self]; 204 | } 205 | } 206 | } 207 | 208 | 209 | - (void)setUploadBufferSize:(unsigned long long)bytes 210 | { 211 | if (![self uploadProgressDelegate]) { 212 | return; 213 | } 214 | [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] - bytes]; 215 | [self incrementUploadProgressBy:0]; 216 | } 217 | 218 | - (void)incrementUploadSizeBy:(unsigned long long)bytes 219 | { 220 | if (![self uploadProgressDelegate]) { 221 | return; 222 | } 223 | [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] + bytes]; 224 | [self incrementUploadProgressBy:0]; 225 | } 226 | 227 | - (void)decrementUploadProgressBy:(unsigned long long)bytes 228 | { 229 | if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) { 230 | return; 231 | } 232 | [self setUploadProgressBytes:[self uploadProgressBytes] - bytes]; 233 | 234 | 235 | double progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0); 236 | [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]]; 237 | } 238 | 239 | 240 | - (void)incrementUploadProgressBy:(unsigned long long)bytes 241 | { 242 | if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) { 243 | return; 244 | } 245 | [self setUploadProgressBytes:[self uploadProgressBytes] + bytes]; 246 | 247 | double progress; 248 | //Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0 249 | if ([ASIHTTPRequest isiPhoneOS2]) { 250 | progress = [[NSNumber numberWithUnsignedLongLong:[self uploadProgressBytes]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self uploadProgressTotalBytes]] doubleValue]; 251 | } else { 252 | progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0); 253 | } 254 | [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]]; 255 | 256 | } 257 | 258 | - (void)incrementDownloadSizeBy:(unsigned long long)bytes 259 | { 260 | if (![self downloadProgressDelegate]) { 261 | return; 262 | } 263 | [self setDownloadProgressTotalBytes:[self downloadProgressTotalBytes] + bytes]; 264 | [self incrementDownloadProgressBy:0]; 265 | } 266 | 267 | - (void)incrementDownloadProgressBy:(unsigned long long)bytes 268 | { 269 | if (![self downloadProgressDelegate] || [self downloadProgressTotalBytes] == 0) { 270 | return; 271 | } 272 | [self setDownloadProgressBytes:[self downloadProgressBytes] + bytes]; 273 | 274 | double progress; 275 | //Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0 276 | if ([ASIHTTPRequest isiPhoneOS2]) { 277 | progress = [[NSNumber numberWithUnsignedLongLong:[self downloadProgressBytes]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self downloadProgressTotalBytes]] doubleValue]; 278 | } else { 279 | progress = ([self downloadProgressBytes]*1.0)/([self downloadProgressTotalBytes]*1.0); 280 | } 281 | [ASIHTTPRequest setProgress:progress forProgressIndicator:[self downloadProgressDelegate]]; 282 | } 283 | 284 | 285 | // Since this queue takes over as the delegate for all requests it contains, it should forward authorisation requests to its own delegate 286 | - (void)authenticationNeededForRequest:(ASIHTTPRequest *)request 287 | { 288 | if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) { 289 | [[self delegate] performSelector:@selector(authenticationNeededForRequest:) withObject:request]; 290 | } 291 | } 292 | 293 | - (void)proxyAuthenticationNeededForRequest:(ASIHTTPRequest *)request 294 | { 295 | if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) { 296 | [[self delegate] performSelector:@selector(proxyAuthenticationNeededForRequest:) withObject:request]; 297 | } 298 | } 299 | 300 | 301 | - (BOOL)respondsToSelector:(SEL)selector 302 | { 303 | if (selector == @selector(authenticationNeededForRequest:)) { 304 | if ([[self delegate] respondsToSelector:@selector(authenticationNeededForRequest:)]) { 305 | return YES; 306 | } 307 | return NO; 308 | } else if (selector == @selector(proxyAuthenticationNeededForRequest:)) { 309 | if ([[self delegate] respondsToSelector:@selector(proxyAuthenticationNeededForRequest:)]) { 310 | return YES; 311 | } 312 | return NO; 313 | } 314 | return [super respondsToSelector:selector]; 315 | } 316 | 317 | 318 | @synthesize requestsCount; 319 | @synthesize uploadProgressBytes; 320 | @synthesize uploadProgressTotalBytes; 321 | @synthesize downloadProgressBytes; 322 | @synthesize downloadProgressTotalBytes; 323 | @synthesize shouldCancelAllRequestsOnFailure; 324 | @synthesize uploadProgressDelegate; 325 | @synthesize downloadProgressDelegate; 326 | @synthesize requestDidFinishSelector; 327 | @synthesize requestDidFailSelector; 328 | @synthesize queueDidFinishSelector; 329 | @synthesize delegate; 330 | @synthesize showAccurateProgress; 331 | 332 | @end 333 | -------------------------------------------------------------------------------- /Classes/KeychainItemWrapper.m: -------------------------------------------------------------------------------- 1 | /* 2 | File: KeychainItemWrapper.m 3 | Abstract: 4 | Objective-C wrapper for accessing a single keychain item. 5 | 6 | Version: 1.2 7 | 8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 9 | Inc. ("Apple") in consideration of your agreement to the following 10 | terms, and your use, installation, modification or redistribution of 11 | this Apple software constitutes acceptance of these terms. If you do 12 | not agree with these terms, please do not use, install, modify or 13 | redistribute this Apple software. 14 | 15 | In consideration of your agreement to abide by the following terms, and 16 | subject to these terms, Apple grants you a personal, non-exclusive 17 | license, under Apple's copyrights in this original Apple software (the 18 | "Apple Software"), to use, reproduce, modify and redistribute the Apple 19 | Software, with or without modifications, in source and/or binary forms; 20 | provided that if you redistribute the Apple Software in its entirety and 21 | without modifications, you must retain this notice and the following 22 | text and disclaimers in all such redistributions of the Apple Software. 23 | Neither the name, trademarks, service marks or logos of Apple Inc. may 24 | be used to endorse or promote products derived from the Apple Software 25 | without specific prior written permission from Apple. Except as 26 | expressly stated in this notice, no other rights or licenses, express or 27 | implied, are granted by Apple herein, including but not limited to any 28 | patent rights that may be infringed by your derivative works or by other 29 | works in which the Apple Software may be incorporated. 30 | 31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE 32 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 33 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 34 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 35 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 36 | 37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 38 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 39 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 40 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 41 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 42 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 43 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 44 | POSSIBILITY OF SUCH DAMAGE. 45 | 46 | Copyright (C) 2010 Apple Inc. All Rights Reserved. 47 | 48 | */ 49 | 50 | #import "KeychainItemWrapper.h" 51 | #import 52 | 53 | /* 54 | 55 | These are the default constants and their respective types, 56 | available for the kSecClassGenericPassword Keychain Item class: 57 | 58 | kSecAttrAccessGroup - CFStringRef 59 | kSecAttrCreationDate - CFDateRef 60 | kSecAttrModificationDate - CFDateRef 61 | kSecAttrDescription - CFStringRef 62 | kSecAttrComment - CFStringRef 63 | kSecAttrCreator - CFNumberRef 64 | kSecAttrType - CFNumberRef 65 | kSecAttrLabel - CFStringRef 66 | kSecAttrIsInvisible - CFBooleanRef 67 | kSecAttrIsNegative - CFBooleanRef 68 | kSecAttrAccount - CFStringRef 69 | kSecAttrService - CFStringRef 70 | kSecAttrGeneric - CFDataRef 71 | 72 | See the header file Security/SecItem.h for more details. 73 | 74 | */ 75 | 76 | @interface KeychainItemWrapper (PrivateMethods) 77 | /* 78 | The decision behind the following two methods (secItemFormatToDictionary and dictionaryToSecItemFormat) was 79 | to encapsulate the transition between what the detail view controller was expecting (NSString *) and what the 80 | Keychain API expects as a validly constructed container class. 81 | */ 82 | - (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert; 83 | - (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert; 84 | 85 | // Updates the item in the keychain, or adds it if it doesn't exist. 86 | - (void)writeToKeychain; 87 | 88 | @end 89 | 90 | @implementation KeychainItemWrapper 91 | 92 | @synthesize keychainItemData, genericPasswordQuery; 93 | 94 | - (id)initWithIdentifier: (NSString *)identifier accessGroup:(NSString *) accessGroup; 95 | { 96 | if ((self = [super init])) 97 | { 98 | // Begin Keychain search setup. The genericPasswordQuery leverages the special user 99 | // defined attribute kSecAttrGeneric to distinguish itself between other generic Keychain 100 | // items which may be included by the same application. 101 | genericPasswordQuery = [[NSMutableDictionary alloc] init]; 102 | 103 | [genericPasswordQuery setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; 104 | [genericPasswordQuery setObject:identifier forKey:(id)kSecAttrGeneric]; 105 | 106 | // The keychain access group attribute determines if this item can be shared 107 | // amongst multiple apps whose code signing entitlements contain the same keychain access group. 108 | if (accessGroup != nil) 109 | { 110 | #if TARGET_IPHONE_SIMULATOR 111 | // Ignore the access group if running on the iPhone simulator. 112 | // 113 | // Apps that are built for the simulator aren't signed, so there's no keychain access group 114 | // for the simulator to check. This means that all apps can see all keychain items when run 115 | // on the simulator. 116 | // 117 | // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the 118 | // simulator will return -25243 (errSecNoAccessForItem). 119 | #else 120 | [genericPasswordQuery setObject:accessGroup forKey:(id)kSecAttrAccessGroup]; 121 | #endif 122 | } 123 | 124 | // Use the proper search constants, return only the attributes of the first match. 125 | [genericPasswordQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; 126 | [genericPasswordQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes]; 127 | 128 | NSDictionary *tempQuery = [NSDictionary dictionaryWithDictionary:genericPasswordQuery]; 129 | 130 | NSMutableDictionary *outDictionary = nil; 131 | 132 | if (! SecItemCopyMatching((CFDictionaryRef)tempQuery, (CFTypeRef *)&outDictionary) == noErr) 133 | { 134 | // Stick these default values into keychain item if nothing found. 135 | [self resetKeychainItem]; 136 | 137 | // Add the generic attribute and the keychain access group. 138 | [keychainItemData setObject:identifier forKey:(id)kSecAttrGeneric]; 139 | if (accessGroup != nil) 140 | { 141 | #if TARGET_IPHONE_SIMULATOR 142 | // Ignore the access group if running on the iPhone simulator. 143 | // 144 | // Apps that are built for the simulator aren't signed, so there's no keychain access group 145 | // for the simulator to check. This means that all apps can see all keychain items when run 146 | // on the simulator. 147 | // 148 | // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the 149 | // simulator will return -25243 (errSecNoAccessForItem). 150 | #else 151 | [keychainItemData setObject:accessGroup forKey:(id)kSecAttrAccessGroup]; 152 | #endif 153 | } 154 | } 155 | else 156 | { 157 | // load the saved data from Keychain. 158 | self.keychainItemData = [self secItemFormatToDictionary:outDictionary]; 159 | } 160 | 161 | [outDictionary release]; 162 | } 163 | 164 | return self; 165 | } 166 | 167 | - (void)dealloc 168 | { 169 | [keychainItemData release]; 170 | [genericPasswordQuery release]; 171 | 172 | [super dealloc]; 173 | } 174 | 175 | - (void)setObject:(id)inObject forKey:(id)key 176 | { 177 | if (inObject == nil) return; 178 | id currentObject = [keychainItemData objectForKey:key]; 179 | if (![currentObject isEqual:inObject]) 180 | { 181 | [keychainItemData setObject:inObject forKey:key]; 182 | [self writeToKeychain]; 183 | } 184 | } 185 | 186 | - (id)objectForKey:(id)key 187 | { 188 | return [keychainItemData objectForKey:key]; 189 | } 190 | 191 | - (void)resetKeychainItem 192 | { 193 | OSStatus junk = noErr; 194 | if (!keychainItemData) 195 | { 196 | self.keychainItemData = [[NSMutableDictionary alloc] init]; 197 | } 198 | else if (keychainItemData) 199 | { 200 | NSMutableDictionary *tempDictionary = [self dictionaryToSecItemFormat:keychainItemData]; 201 | junk = SecItemDelete((CFDictionaryRef)tempDictionary); 202 | NSAssert( junk == noErr || junk == errSecItemNotFound, @"Problem deleting current dictionary." ); 203 | } 204 | 205 | // Default attributes for keychain item. 206 | [keychainItemData setObject:@"" forKey:(id)kSecAttrAccount]; 207 | [keychainItemData setObject:@"" forKey:(id)kSecAttrLabel]; 208 | [keychainItemData setObject:@"" forKey:(id)kSecAttrDescription]; 209 | 210 | // Default data for keychain item. 211 | [keychainItemData setObject:@"" forKey:(id)kSecValueData]; 212 | } 213 | 214 | - (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert 215 | { 216 | // The assumption is that this method will be called with a properly populated dictionary 217 | // containing all the right key/value pairs for a SecItem. 218 | 219 | // Create a dictionary to return populated with the attributes and data. 220 | NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; 221 | 222 | // Add the Generic Password keychain item class attribute. 223 | [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; 224 | 225 | // Convert the NSString to NSData to meet the requirements for the value type kSecValueData. 226 | // This is where to store sensitive data that should be encrypted. 227 | NSString *passwordString = [dictionaryToConvert objectForKey:(id)kSecValueData]; 228 | [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] forKey:(id)kSecValueData]; 229 | 230 | return returnDictionary; 231 | } 232 | 233 | - (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert 234 | { 235 | // The assumption is that this method will be called with a properly populated dictionary 236 | // containing all the right key/value pairs for the UI element. 237 | 238 | // Create a dictionary to return populated with the attributes and data. 239 | NSMutableDictionary *returnDictionary = [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; 240 | 241 | // Add the proper search key and class attribute. 242 | [returnDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; 243 | [returnDictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; 244 | 245 | // Acquire the password data from the attributes. 246 | NSData *passwordData = NULL; 247 | if (SecItemCopyMatching((CFDictionaryRef)returnDictionary, (CFTypeRef *)&passwordData) == noErr) 248 | { 249 | // Remove the search, class, and identifier key/value, we don't need them anymore. 250 | [returnDictionary removeObjectForKey:(id)kSecReturnData]; 251 | 252 | // Add the password to the dictionary, converting from NSData to NSString. 253 | NSString *password = [[[NSString alloc] initWithBytes:[passwordData bytes] length:[passwordData length] 254 | encoding:NSUTF8StringEncoding] autorelease]; 255 | [returnDictionary setObject:password forKey:(id)kSecValueData]; 256 | } 257 | else 258 | { 259 | // Don't do anything if nothing is found. 260 | NSAssert(NO, @"Serious error, no matching item found in the keychain.\n"); 261 | } 262 | 263 | [passwordData release]; 264 | 265 | return returnDictionary; 266 | } 267 | 268 | - (void)writeToKeychain 269 | { 270 | NSDictionary *attributes = NULL; 271 | NSMutableDictionary *updateItem = NULL; 272 | OSStatus result; 273 | 274 | if (SecItemCopyMatching((CFDictionaryRef)genericPasswordQuery, (CFTypeRef *)&attributes) == noErr) 275 | { 276 | // First we need the attributes from the Keychain. 277 | updateItem = [NSMutableDictionary dictionaryWithDictionary:attributes]; 278 | // Second we need to add the appropriate search key/values. 279 | [updateItem setObject:[genericPasswordQuery objectForKey:(id)kSecClass] forKey:(id)kSecClass]; 280 | 281 | // Lastly, we need to set up the updated attribute list being careful to remove the class. 282 | NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:keychainItemData]; 283 | [tempCheck removeObjectForKey:(id)kSecClass]; 284 | 285 | #if TARGET_IPHONE_SIMULATOR 286 | // Remove the access group if running on the iPhone simulator. 287 | // 288 | // Apps that are built for the simulator aren't signed, so there's no keychain access group 289 | // for the simulator to check. This means that all apps can see all keychain items when run 290 | // on the simulator. 291 | // 292 | // If a SecItem contains an access group attribute, SecItemAdd and SecItemUpdate on the 293 | // simulator will return -25243 (errSecNoAccessForItem). 294 | // 295 | // The access group attribute will be included in items returned by SecItemCopyMatching, 296 | // which is why we need to remove it before updating the item. 297 | [tempCheck removeObjectForKey:(id)kSecAttrAccessGroup]; 298 | #endif 299 | 300 | // An implicit assumption is that you can only update a single item at a time. 301 | 302 | result = SecItemUpdate((CFDictionaryRef)updateItem, (CFDictionaryRef)tempCheck); 303 | NSAssert( result == noErr, @"Couldn't update the Keychain Item." ); 304 | } 305 | else 306 | { 307 | // No previous item found; add the new one. 308 | result = SecItemAdd((CFDictionaryRef)[self dictionaryToSecItemFormat:keychainItemData], NULL); 309 | NSAssert( result == noErr, @"Couldn't add the Keychain Item." ); 310 | } 311 | } 312 | 313 | @end 314 | -------------------------------------------------------------------------------- /Libraries/ASI/Reachability.m: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | File: Reachability.m 4 | Abstract: SystemConfiguration framework wrapper. 5 | 6 | Version: 1.5 7 | 8 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. 9 | ("Apple") in consideration of your agreement to the following terms, and your 10 | use, installation, modification or redistribution of this Apple software 11 | constitutes acceptance of these terms. If you do not agree with these terms, 12 | please do not use, install, modify or redistribute this Apple software. 13 | 14 | In consideration of your agreement to abide by the following terms, and subject 15 | to these terms, Apple grants you a personal, non-exclusive license, under 16 | Apple's copyrights in this original Apple software (the "Apple Software"), to 17 | use, reproduce, modify and redistribute the Apple Software, with or without 18 | modifications, in source and/or binary forms; provided that if you redistribute 19 | the Apple Software in its entirety and without modifications, you must retain 20 | this notice and the following text and disclaimers in all such redistributions 21 | of the Apple Software. 22 | Neither the name, trademarks, service marks or logos of Apple Inc. may be used 23 | to endorse or promote products derived from the Apple Software without specific 24 | prior written permission from Apple. Except as expressly stated in this notice, 25 | no other rights or licenses, express or implied, are granted by Apple herein, 26 | including but not limited to any patent rights that may be infringed by your 27 | derivative works or by other works in which the Apple Software may be 28 | incorporated. 29 | 30 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO 31 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED 32 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR 33 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN 34 | COMBINATION WITH YOUR PRODUCTS. 35 | 36 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR 37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 38 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR 40 | DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF 41 | CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF 42 | APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | 44 | Copyright (C) 2008 Apple Inc. All Rights Reserved. 45 | 46 | */ 47 | 48 | #import 49 | #import 50 | #import 51 | #import 52 | #import 53 | #include 54 | 55 | #import "Reachability.h" 56 | #import 57 | 58 | static NSString *kLinkLocalAddressKey = @"169.254.0.0"; 59 | static NSString *kDefaultRouteKey = @"0.0.0.0"; 60 | 61 | static Reachability *_sharedReachability; 62 | 63 | // A class extension that declares internal methods for this class. 64 | @interface Reachability() 65 | - (BOOL)isAdHocWiFiNetworkAvailableFlags:(SCNetworkReachabilityFlags *)outFlags; 66 | - (BOOL)isNetworkAvailableFlags:(SCNetworkReachabilityFlags *)outFlags; 67 | - (BOOL)isReachableWithoutRequiringConnection:(SCNetworkReachabilityFlags)flags; 68 | - (SCNetworkReachabilityRef)reachabilityRefForHostName:(NSString *)hostName; 69 | - (SCNetworkReachabilityRef)reachabilityRefForAddress:(NSString *)address; 70 | - (BOOL)addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)outAddress; 71 | - (void)stopListeningForReachabilityChanges; 72 | @end 73 | 74 | @implementation Reachability 75 | 76 | @synthesize networkStatusNotificationsEnabled = _networkStatusNotificationsEnabled; 77 | @synthesize hostName = _hostName; 78 | @synthesize address = _address; 79 | @synthesize reachabilityQueries = _reachabilityQueries; 80 | 81 | + (Reachability *)sharedReachability 82 | { 83 | if (!_sharedReachability) { 84 | _sharedReachability = [[Reachability alloc] init]; 85 | // Clients of Reachability will typically call [[Reachability sharedReachability] setHostName:] 86 | // before calling one of the status methods. 87 | _sharedReachability.hostName = nil; 88 | _sharedReachability.address = nil; 89 | _sharedReachability.networkStatusNotificationsEnabled = NO; 90 | _sharedReachability.reachabilityQueries = [[NSMutableDictionary alloc] init]; 91 | } 92 | return _sharedReachability; 93 | } 94 | 95 | - (void) dealloc 96 | { 97 | [self stopListeningForReachabilityChanges]; 98 | 99 | [_sharedReachability.reachabilityQueries release]; 100 | [_sharedReachability release]; 101 | [super dealloc]; 102 | } 103 | 104 | - (BOOL)isReachableWithoutRequiringConnection:(SCNetworkReachabilityFlags)flags 105 | { 106 | // kSCNetworkReachabilityFlagsReachable indicates that the specified nodename or address can 107 | // be reached using the current network configuration. 108 | BOOL isReachable = flags & kSCNetworkReachabilityFlagsReachable; 109 | 110 | // This flag indicates that the specified nodename or address can 111 | // be reached using the current network configuration, but a 112 | // connection must first be established. 113 | // 114 | // If the flag is false, we don't have a connection. But because CFNetwork 115 | // automatically attempts to bring up a WWAN connection, if the WWAN reachability 116 | // flag is present, a connection is not required. 117 | BOOL noConnectionRequired = !(flags & kSCNetworkReachabilityFlagsConnectionRequired); 118 | if ((flags & kSCNetworkReachabilityFlagsIsWWAN)) { 119 | noConnectionRequired = YES; 120 | } 121 | 122 | return (isReachable && noConnectionRequired) ? YES : NO; 123 | } 124 | 125 | // Returns whether or not the current host name is reachable with the current network configuration. 126 | - (BOOL)isHostReachable:(NSString *)host 127 | { 128 | if (!host || ![host length]) { 129 | return NO; 130 | } 131 | 132 | SCNetworkReachabilityFlags flags; 133 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [host UTF8String]); 134 | BOOL gotFlags = SCNetworkReachabilityGetFlags(reachability, &flags); 135 | 136 | CFRelease(reachability); 137 | 138 | if (!gotFlags) { 139 | return NO; 140 | } 141 | 142 | return [self isReachableWithoutRequiringConnection:flags]; 143 | } 144 | 145 | // This returns YES if the address 169.254.0.0 is reachable without requiring a connection. 146 | - (BOOL)isAdHocWiFiNetworkAvailableFlags:(SCNetworkReachabilityFlags *)outFlags 147 | { 148 | // Look in the cache of reachability queries for one that matches this query. 149 | ReachabilityQuery *query = [self.reachabilityQueries objectForKey:kLinkLocalAddressKey]; 150 | SCNetworkReachabilityRef adHocWiFiNetworkReachability = query.reachabilityRef; 151 | 152 | // If a cached reachability query was not found, create one. 153 | if (!adHocWiFiNetworkReachability) { 154 | 155 | // Build a sockaddr_in that we can pass to the address reachability query. 156 | struct sockaddr_in sin; 157 | 158 | bzero(&sin, sizeof(sin)); 159 | sin.sin_len = sizeof(sin); 160 | sin.sin_family = AF_INET; 161 | // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 162 | sin.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); 163 | 164 | adHocWiFiNetworkReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&sin); 165 | 166 | query = [[[ReachabilityQuery alloc] init] autorelease]; 167 | query.hostNameOrAddress = kLinkLocalAddressKey; 168 | query.reachabilityRef = adHocWiFiNetworkReachability; 169 | 170 | // Add the reachability query to the cache. 171 | [self.reachabilityQueries setObject:query forKey:kLinkLocalAddressKey]; 172 | } 173 | 174 | // If necessary, register for notifcations for the SCNetworkReachabilityRef on the current run loop. 175 | // If an existing SCNetworkReachabilityRef was found in the cache, we can reuse it and register 176 | // to receive notifications from it in the current run loop, which may be different than the run loop 177 | // that was previously used when registering the SCNetworkReachabilityRef for notifications. 178 | // -scheduleOnRunLoop: will schedule only if network status notifications are enabled in the Reachability instance. 179 | // By default, they are not enabled. 180 | [query scheduleOnRunLoop:[NSRunLoop currentRunLoop]]; 181 | 182 | SCNetworkReachabilityFlags addressReachabilityFlags; 183 | BOOL gotFlags = SCNetworkReachabilityGetFlags(adHocWiFiNetworkReachability, &addressReachabilityFlags); 184 | if (!gotFlags) { 185 | // There was an error getting the reachability flags. 186 | return NO; 187 | } 188 | 189 | // Callers of this method might want to use the reachability flags, so if an 'out' parameter 190 | // was passed in, assign the reachability flags to it. 191 | if (outFlags) { 192 | *outFlags = addressReachabilityFlags; 193 | } 194 | 195 | return [self isReachableWithoutRequiringConnection:addressReachabilityFlags]; 196 | } 197 | 198 | // ReachabilityCallback is registered as the callback for network state changes in startListeningForReachabilityChanges. 199 | static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) 200 | { 201 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 202 | 203 | // Post a notification to notify the client that the network reachability changed. 204 | [[NSNotificationCenter defaultCenter] postNotificationName:@"kNetworkReachabilityChangedNotification" object:nil]; 205 | 206 | [pool release]; 207 | } 208 | 209 | // Perform a reachability query for the address 0.0.0.0. If that address is reachable without 210 | // requiring a connection, a network interface is available. We'll have to do more work to 211 | // determine which network interface is available. 212 | - (BOOL)isNetworkAvailableFlags:(SCNetworkReachabilityFlags *)outFlags 213 | { 214 | ReachabilityQuery *query = [self.reachabilityQueries objectForKey:kDefaultRouteKey]; 215 | SCNetworkReachabilityRef defaultRouteReachability = query.reachabilityRef; 216 | 217 | // If a cached reachability query was not found, create one. 218 | if (!defaultRouteReachability) { 219 | 220 | struct sockaddr_in zeroAddress; 221 | bzero(&zeroAddress, sizeof(zeroAddress)); 222 | zeroAddress.sin_len = sizeof(zeroAddress); 223 | zeroAddress.sin_family = AF_INET; 224 | 225 | defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); 226 | 227 | ReachabilityQuery *query = [[[ReachabilityQuery alloc] init] autorelease]; 228 | query.hostNameOrAddress = kDefaultRouteKey; 229 | query.reachabilityRef = defaultRouteReachability; 230 | 231 | [self.reachabilityQueries setObject:query forKey:kDefaultRouteKey]; 232 | } 233 | 234 | // If necessary, register for notifcations for the SCNetworkReachabilityRef on the current run loop. 235 | // If an existing SCNetworkReachabilityRef was found in the cache, we can reuse it and register 236 | // to receive notifications from it in the current run loop, which may be different than the run loop 237 | // that was previously used when registering the SCNetworkReachabilityRef for notifications. 238 | // -scheduleOnRunLoop: will schedule only if network status notifications are enabled in the Reachability instance. 239 | // By default, they are not enabled. 240 | [query scheduleOnRunLoop:[NSRunLoop currentRunLoop]]; 241 | 242 | SCNetworkReachabilityFlags flags; 243 | BOOL gotFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags); 244 | if (!gotFlags) { 245 | return NO; 246 | } 247 | 248 | BOOL isReachable = [self isReachableWithoutRequiringConnection:flags]; 249 | 250 | // Callers of this method might want to use the reachability flags, so if an 'out' parameter 251 | // was passed in, assign the reachability flags to it. 252 | if (outFlags) { 253 | *outFlags = flags; 254 | } 255 | 256 | return isReachable; 257 | } 258 | 259 | // Be a good citizen and unregister for network state changes when the application terminates. 260 | - (void)stopListeningForReachabilityChanges 261 | { 262 | // Walk through the cache that holds SCNetworkReachabilityRefs for reachability 263 | // queries to particular hosts or addresses. 264 | NSEnumerator *enumerator = [self.reachabilityQueries objectEnumerator]; 265 | ReachabilityQuery *reachabilityQuery; 266 | 267 | while (reachabilityQuery = [enumerator nextObject]) { 268 | 269 | CFArrayRef runLoops = reachabilityQuery.runLoops; 270 | NSUInteger runLoopCounter, maxRunLoops = CFArrayGetCount(runLoops); 271 | 272 | for (runLoopCounter = 0; runLoopCounter < maxRunLoops; runLoopCounter++) { 273 | CFRunLoopRef nextRunLoop = (CFRunLoopRef)CFArrayGetValueAtIndex(runLoops, runLoopCounter); 274 | 275 | SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityQuery.reachabilityRef, nextRunLoop, kCFRunLoopDefaultMode); 276 | } 277 | 278 | CFArrayRemoveAllValues(reachabilityQuery.runLoops); 279 | } 280 | } 281 | 282 | /* 283 | Create a SCNetworkReachabilityRef for hostName, which lets us determine if hostName 284 | is currently reachable, and lets us register to receive notifications when the 285 | reachability of hostName changes. 286 | */ 287 | - (SCNetworkReachabilityRef)reachabilityRefForHostName:(NSString *)hostName 288 | { 289 | if (!hostName || ![hostName length]) { 290 | return NULL; 291 | } 292 | 293 | // Look in the cache for an existing SCNetworkReachabilityRef for hostName. 294 | ReachabilityQuery *cachedQuery = [self.reachabilityQueries objectForKey:hostName]; 295 | SCNetworkReachabilityRef reachabilityRefForHostName = cachedQuery.reachabilityRef; 296 | 297 | if (reachabilityRefForHostName) { 298 | return reachabilityRefForHostName; 299 | } 300 | 301 | // Didn't find an existing SCNetworkReachabilityRef for hostName, so create one ... 302 | reachabilityRefForHostName = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [hostName UTF8String]); 303 | 304 | NSAssert1(reachabilityRefForHostName != NULL, @"Failed to create SCNetworkReachabilityRef for host: %@", hostName); 305 | 306 | ReachabilityQuery *query = [[[ReachabilityQuery alloc] init] autorelease]; 307 | query.hostNameOrAddress = hostName; 308 | query.reachabilityRef = reachabilityRefForHostName; 309 | 310 | // If necessary, register for notifcations for the SCNetworkReachabilityRef on the current run loop. 311 | // If an existing SCNetworkReachabilityRef was found in the cache, we can reuse it and register 312 | // to receive notifications from it in the current run loop, which may be different than the run loop 313 | // that was previously used when registering the SCNetworkReachabilityRef for notifications. 314 | // -scheduleOnRunLoop: will schedule only if network status notifications are enabled in the Reachability instance. 315 | // By default, they are not enabled. 316 | [query scheduleOnRunLoop:[NSRunLoop currentRunLoop]]; 317 | 318 | // ... and add it to the cache. 319 | [self.reachabilityQueries setObject:query forKey:hostName]; 320 | return reachabilityRefForHostName; 321 | } 322 | 323 | /* 324 | Create a SCNetworkReachabilityRef for the IP address in addressString, which lets us determine if 325 | the address is currently reachable, and lets us register to receive notifications when the 326 | reachability of the address changes. 327 | */ 328 | - (SCNetworkReachabilityRef)reachabilityRefForAddress:(NSString *)addressString 329 | { 330 | if (!addressString || ![addressString length]) { 331 | return NULL; 332 | } 333 | 334 | struct sockaddr_in address; 335 | 336 | BOOL gotAddress = [self addressFromString:addressString address:&address]; 337 | if (!gotAddress) { 338 | // The attempt to convert addressString to a sockaddr_in failed. 339 | NSAssert1(gotAddress != NO, @"Failed to convert an IP address string to a sockaddr_in: %@", addressString); 340 | return NULL; 341 | } 342 | 343 | // Look in the cache for an existing SCNetworkReachabilityRef for addressString. 344 | ReachabilityQuery *cachedQuery = [self.reachabilityQueries objectForKey:addressString]; 345 | SCNetworkReachabilityRef reachabilityRefForAddress = cachedQuery.reachabilityRef; 346 | 347 | if (reachabilityRefForAddress) { 348 | return reachabilityRefForAddress; 349 | } 350 | 351 | // Didn't find an existing SCNetworkReachabilityRef for addressString, so create one. 352 | reachabilityRefForAddress = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&address); 353 | 354 | NSAssert1(reachabilityRefForAddress != NULL, @"Failed to create SCNetworkReachabilityRef for address: %@", addressString); 355 | 356 | ReachabilityQuery *query = [[[ReachabilityQuery alloc] init] autorelease]; 357 | query.hostNameOrAddress = addressString; 358 | query.reachabilityRef = reachabilityRefForAddress; 359 | 360 | // If necessary, register for notifcations for the SCNetworkReachabilityRef on the current run loop. 361 | // If an existing SCNetworkReachabilityRef was found in the cache, we can reuse it and register 362 | // to receive notifications from it in the current run loop, which may be different than the run loop 363 | // that was previously used when registering the SCNetworkReachabilityRef for notifications. 364 | // -scheduleOnRunLoop: will schedule only if network status notifications are enabled in the Reachability instance. 365 | // By default, they are not enabled. 366 | [query scheduleOnRunLoop:[NSRunLoop currentRunLoop]]; 367 | 368 | // ... and add it to the cache. 369 | [self.reachabilityQueries setObject:query forKey:addressString]; 370 | return reachabilityRefForAddress; 371 | } 372 | 373 | - (NetworkStatus)remoteHostStatus 374 | { 375 | /* 376 | If the current host name or address is reachable, determine which network interface it is reachable through. 377 | If the host is reachable and the reachability flags include kSCNetworkReachabilityFlagsIsWWAN, it 378 | is reachable through the carrier data network. If the host is reachable and the reachability 379 | flags do not include kSCNetworkReachabilityFlagsIsWWAN, it is reachable through the WiFi network. 380 | */ 381 | 382 | SCNetworkReachabilityRef reachabilityRef = nil; 383 | if (self.hostName) { 384 | reachabilityRef = [self reachabilityRefForHostName:self.hostName]; 385 | 386 | } else if (self.address) { 387 | reachabilityRef = [self reachabilityRefForAddress:self.address]; 388 | 389 | } else { 390 | NSAssert(self.hostName != nil && self.address != nil, @"No hostName or address specified. Cannot determine reachability."); 391 | return NotReachable; 392 | } 393 | 394 | if (!reachabilityRef) { 395 | return NotReachable; 396 | } 397 | 398 | SCNetworkReachabilityFlags reachabilityFlags; 399 | BOOL gotFlags = SCNetworkReachabilityGetFlags(reachabilityRef, &reachabilityFlags); 400 | if (!gotFlags) { 401 | return NotReachable; 402 | } 403 | 404 | BOOL reachable = [self isReachableWithoutRequiringConnection:reachabilityFlags]; 405 | 406 | if (!reachable) { 407 | return NotReachable; 408 | } 409 | if (reachabilityFlags & ReachableViaCarrierDataNetwork) { 410 | return ReachableViaCarrierDataNetwork; 411 | } 412 | 413 | return ReachableViaWiFiNetwork; 414 | } 415 | 416 | - (NetworkStatus)internetConnectionStatus 417 | { 418 | /* 419 | To determine if the device has an Internet connection, query the address 420 | 0.0.0.0. If it's reachable without requiring a connection, first check 421 | for the kSCNetworkReachabilityFlagsIsDirect flag, which tell us if the connection 422 | is to an ad-hoc WiFi network. If it is not, the device can access the Internet. 423 | The next thing to determine is how the device can access the Internet, which 424 | can either be through the carrier data network (EDGE or other service) or through 425 | a WiFi connection. 426 | 427 | Note: Knowing that the device has an Internet connection is not the same as 428 | knowing if the device can reach a particular host. To know that, use 429 | -[Reachability remoteHostStatus]. 430 | */ 431 | 432 | SCNetworkReachabilityFlags defaultRouteFlags; 433 | BOOL defaultRouteIsAvailable = [self isNetworkAvailableFlags:&defaultRouteFlags]; 434 | if (defaultRouteIsAvailable) { 435 | 436 | if (defaultRouteFlags & kSCNetworkReachabilityFlagsIsDirect) { 437 | 438 | // The connection is to an ad-hoc WiFi network, so Internet access is not available. 439 | return NotReachable; 440 | } 441 | else if (defaultRouteFlags & ReachableViaCarrierDataNetwork) { 442 | return ReachableViaCarrierDataNetwork; 443 | } 444 | 445 | return ReachableViaWiFiNetwork; 446 | } 447 | 448 | return NotReachable; 449 | } 450 | 451 | - (NetworkStatus)localWiFiConnectionStatus 452 | { 453 | SCNetworkReachabilityFlags selfAssignedAddressFlags; 454 | 455 | /* 456 | To determine if the WiFi connection is to a local ad-hoc network, 457 | check the availability of the address 169.254.x.x. That's an address 458 | in the self-assigned range, and the device will have a self-assigned IP 459 | when it's connected to a ad-hoc WiFi network. So to test if the device 460 | has a self-assigned IP, look for the kSCNetworkReachabilityFlagsIsDirect flag 461 | in the address query. If it's present, we know that the WiFi connection 462 | is to an ad-hoc network. 463 | */ 464 | // This returns YES if the address 169.254.0.0 is reachable without requiring a connection. 465 | BOOL hasLinkLocalNetworkAccess = [self isAdHocWiFiNetworkAvailableFlags:&selfAssignedAddressFlags]; 466 | 467 | if (hasLinkLocalNetworkAccess && (selfAssignedAddressFlags & kSCNetworkReachabilityFlagsIsDirect)) { 468 | return ReachableViaWiFiNetwork; 469 | } 470 | 471 | return NotReachable; 472 | } 473 | 474 | // Convert an IP address from an NSString to a sockaddr_in * that can be used to create 475 | // the reachability request. 476 | - (BOOL)addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)address 477 | { 478 | if (!IPAddress || ![IPAddress length]) { 479 | return NO; 480 | } 481 | 482 | memset((char *) address, sizeof(struct sockaddr_in), 0); 483 | address->sin_family = AF_INET; 484 | address->sin_len = sizeof(struct sockaddr_in); 485 | 486 | int conversionResult = inet_aton([IPAddress UTF8String], &address->sin_addr); 487 | if (conversionResult == 0) { 488 | NSAssert1(conversionResult != 1, @"Failed to convert the IP address string into a sockaddr_in: %@", IPAddress); 489 | return NO; 490 | } 491 | 492 | return YES; 493 | } 494 | 495 | @end 496 | 497 | @interface ReachabilityQuery () 498 | - (CFRunLoopRef)startListeningForReachabilityChanges:(SCNetworkReachabilityRef)reachability onRunLoop:(CFRunLoopRef)runLoop; 499 | @end 500 | 501 | @implementation ReachabilityQuery 502 | 503 | @synthesize reachabilityRef = _reachabilityRef; 504 | @synthesize runLoops = _runLoops; 505 | @synthesize hostNameOrAddress = _hostNameOrAddress; 506 | 507 | - (id)init 508 | { 509 | self = [super init]; 510 | if (self != nil) { 511 | self.runLoops = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); 512 | } 513 | return self; 514 | } 515 | 516 | - (void)dealloc 517 | { 518 | CFRelease(self.runLoops); 519 | [super dealloc]; 520 | } 521 | 522 | - (BOOL)isScheduledOnRunLoop:(CFRunLoopRef)runLoop 523 | { 524 | NSUInteger runLoopCounter, maxRunLoops = CFArrayGetCount(self.runLoops); 525 | 526 | for (runLoopCounter = 0; runLoopCounter < maxRunLoops; runLoopCounter++) { 527 | CFRunLoopRef nextRunLoop = (CFRunLoopRef)CFArrayGetValueAtIndex(self.runLoops, runLoopCounter); 528 | 529 | if (nextRunLoop == runLoop) { 530 | return YES; 531 | } 532 | } 533 | 534 | return NO; 535 | } 536 | 537 | - (void)scheduleOnRunLoop:(NSRunLoop *)inRunLoop 538 | { 539 | // Only register for network state changes if the client has specifically enabled them. 540 | if ([[Reachability sharedReachability] networkStatusNotificationsEnabled] == NO) { 541 | return; 542 | } 543 | 544 | if (!inRunLoop) { 545 | return; 546 | } 547 | 548 | CFRunLoopRef runLoop = [inRunLoop getCFRunLoop]; 549 | 550 | // Notifications of status changes for each reachability query can be scheduled on multiple run loops. 551 | // To support that, register for notifications for each runLoop. 552 | // -isScheduledOnRunLoop: iterates over all of the run loops that have previously been used 553 | // to register for notifications. If one is found that matches the passed in runLoop argument, there's 554 | // no need to register for notifications again. If one is not found, register for notifications 555 | // using the current runLoop. 556 | if (![self isScheduledOnRunLoop:runLoop]) { 557 | 558 | CFRunLoopRef notificationRunLoop = [self startListeningForReachabilityChanges:self.reachabilityRef onRunLoop:runLoop]; 559 | if (notificationRunLoop) { 560 | CFArrayAppendValue(self.runLoops, notificationRunLoop); 561 | } 562 | } 563 | } 564 | 565 | // Register to receive changes to the 'reachability' query so that we can update the 566 | // user interface when the network state changes. 567 | - (CFRunLoopRef)startListeningForReachabilityChanges:(SCNetworkReachabilityRef)reachability onRunLoop:(CFRunLoopRef)runLoop 568 | { 569 | if (!reachability) { 570 | return NULL; 571 | } 572 | 573 | if (!runLoop) { 574 | return NULL; 575 | } 576 | 577 | SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; 578 | SCNetworkReachabilitySetCallback(reachability, ReachabilityCallback, &context); 579 | SCNetworkReachabilityScheduleWithRunLoop(reachability, runLoop, kCFRunLoopDefaultMode); 580 | 581 | return runLoop; 582 | } 583 | 584 | 585 | @end 586 | -------------------------------------------------------------------------------- /Libraries/JSONFramework/SBJSON.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2008 Stig Brautaset. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | Neither the name of the author nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #import "SBJSON.h" 31 | 32 | NSString * SBJSONErrorDomain = @"org.brautaset.JSON.ErrorDomain"; 33 | 34 | @interface SBJSON (Generator) 35 | 36 | - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json error:(NSError**)error; 37 | - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json error:(NSError**)error; 38 | - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json error:(NSError**)error; 39 | - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json error:(NSError**)error; 40 | 41 | - (NSString*)indent; 42 | 43 | @end 44 | 45 | @interface SBJSON (Scanner) 46 | 47 | - (BOOL)scanValue:(NSObject **)o error:(NSError **)error; 48 | 49 | - (BOOL)scanRestOfArray:(NSMutableArray **)o error:(NSError **)error; 50 | - (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o error:(NSError **)error; 51 | - (BOOL)scanRestOfNull:(NSNull **)o error:(NSError **)error; 52 | - (BOOL)scanRestOfFalse:(NSNumber **)o error:(NSError **)error; 53 | - (BOOL)scanRestOfTrue:(NSNumber **)o error:(NSError **)error; 54 | - (BOOL)scanRestOfString:(NSMutableString **)o error:(NSError **)error; 55 | 56 | // Cannot manage without looking at the first digit 57 | - (BOOL)scanNumber:(NSNumber **)o error:(NSError **)error; 58 | 59 | - (BOOL)scanHexQuad:(unichar *)x error:(NSError **)error; 60 | - (BOOL)scanUnicodeChar:(unichar *)x error:(NSError **)error; 61 | 62 | - (BOOL)scanIsAtEnd; 63 | 64 | @end 65 | 66 | #pragma mark Private utilities 67 | 68 | #define skipWhitespace(c) while (isspace(*c)) c++ 69 | #define skipDigits(c) while (isdigit(*c)) c++ 70 | 71 | static NSError *err(int code, NSString *str) { 72 | NSDictionary *ui = [NSDictionary dictionaryWithObject:str forKey:NSLocalizedDescriptionKey]; 73 | return [NSError errorWithDomain:SBJSONErrorDomain code:code userInfo:ui]; 74 | } 75 | 76 | static NSError *errWithUnderlier(int code, NSError **u, NSString *str) { 77 | if (!u) 78 | return err(code, str); 79 | 80 | NSDictionary *ui = [NSDictionary dictionaryWithObjectsAndKeys: 81 | str, NSLocalizedDescriptionKey, 82 | *u, NSUnderlyingErrorKey, 83 | nil]; 84 | return [NSError errorWithDomain:SBJSONErrorDomain code:code userInfo:ui]; 85 | } 86 | 87 | 88 | @implementation SBJSON 89 | 90 | static char ctrl[0x22]; 91 | 92 | + (void)initialize 93 | { 94 | ctrl[0] = '\"'; 95 | ctrl[1] = '\\'; 96 | for (int i = 1; i < 0x20; i++) 97 | ctrl[i+1] = i; 98 | ctrl[0x21] = 0; 99 | } 100 | 101 | - (id)init { 102 | if (self = [super init]) { 103 | [self setMaxDepth:512]; 104 | } 105 | return self; 106 | } 107 | 108 | #pragma mark Generator 109 | 110 | 111 | /** 112 | Returns a string containing JSON representation of the passed in value, or nil on error. 113 | If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. 114 | 115 | @param value any instance that can be represented as a JSON fragment 116 | @param allowScalar wether to return json fragments for scalar objects 117 | @param error used to return an error by reference (pass NULL if this is not desired) 118 | */ 119 | - (NSString*)stringWithObject:(id)value allowScalar:(BOOL)allowScalar error:(NSError**)error { 120 | depth = 0; 121 | NSMutableString *json = [NSMutableString stringWithCapacity:128]; 122 | 123 | NSError *err2 = nil; 124 | if (!allowScalar && ![value isKindOfClass:[NSDictionary class]] && ![value isKindOfClass:[NSArray class]]) { 125 | err2 = err(EFRAGMENT, @"Not valid type for JSON"); 126 | 127 | } else if ([self appendValue:value into:json error:&err2]) { 128 | return json; 129 | } 130 | 131 | if (error) 132 | *error = err2; 133 | return nil; 134 | } 135 | 136 | /** 137 | Returns a string containing JSON representation of the passed in value, or nil on error. 138 | If nil is returned and @p error is not NULL, @p error can be interrogated to find the cause of the error. 139 | 140 | @param value any instance that can be represented as a JSON fragment 141 | @param error used to return an error by reference (pass NULL if this is not desired) 142 | */ 143 | - (NSString*)stringWithFragment:(id)value error:(NSError**)error { 144 | return [self stringWithObject:value allowScalar:YES error:error]; 145 | } 146 | 147 | /** 148 | Returns a string containing JSON representation of the passed in value, or nil on error. 149 | If nil is returned and @p error is not NULL, @p error can be interrogated to find the cause of the error. 150 | 151 | @param value a NSDictionary or NSArray instance 152 | @param error used to return an error by reference (pass NULL if this is not desired) 153 | */ 154 | - (NSString*)stringWithObject:(id)value error:(NSError**)error { 155 | return [self stringWithObject:value allowScalar:NO error:error]; 156 | } 157 | 158 | 159 | - (NSString*)indent { 160 | return [@"\n" stringByPaddingToLength:1 + 2 * depth withString:@" " startingAtIndex:0]; 161 | } 162 | 163 | - (BOOL)appendValue:(id)fragment into:(NSMutableString*)json error:(NSError**)error { 164 | if ([fragment isKindOfClass:[NSDictionary class]]) { 165 | if (![self appendDictionary:fragment into:json error:error]) 166 | return NO; 167 | 168 | } else if ([fragment isKindOfClass:[NSArray class]]) { 169 | if (![self appendArray:fragment into:json error:error]) 170 | return NO; 171 | 172 | } else if ([fragment isKindOfClass:[NSString class]]) { 173 | if (![self appendString:fragment into:json error:error]) 174 | return NO; 175 | 176 | } else if ([fragment isKindOfClass:[NSNumber class]]) { 177 | if ('c' == *[fragment objCType]) 178 | [json appendString:[fragment boolValue] ? @"true" : @"false"]; 179 | else 180 | [json appendString:[fragment stringValue]]; 181 | 182 | } else if ([fragment isKindOfClass:[NSNull class]]) { 183 | [json appendString:@"null"]; 184 | 185 | } else { 186 | *error = err(EUNSUPPORTED, [NSString stringWithFormat:@"JSON serialisation not supported for %@", [fragment class]]); 187 | return NO; 188 | } 189 | return YES; 190 | } 191 | 192 | - (BOOL)appendArray:(NSArray*)fragment into:(NSMutableString*)json error:(NSError**)error { 193 | [json appendString:@"["]; 194 | depth++; 195 | 196 | BOOL addComma = NO; 197 | for (id value in fragment) { 198 | if (addComma) 199 | [json appendString:@","]; 200 | else 201 | addComma = YES; 202 | 203 | if ([self humanReadable]) 204 | [json appendString:[self indent]]; 205 | 206 | if (![self appendValue:value into:json error:error]) { 207 | return NO; 208 | } 209 | } 210 | 211 | depth--; 212 | if ([self humanReadable] && [fragment count]) 213 | [json appendString:[self indent]]; 214 | [json appendString:@"]"]; 215 | return YES; 216 | } 217 | 218 | - (BOOL)appendDictionary:(NSDictionary*)fragment into:(NSMutableString*)json error:(NSError**)error { 219 | [json appendString:@"{"]; 220 | depth++; 221 | 222 | NSString *colon = [self humanReadable] ? @" : " : @":"; 223 | BOOL addComma = NO; 224 | NSArray *keys = [fragment allKeys]; 225 | if (self.sortKeys) 226 | keys = [keys sortedArrayUsingSelector:@selector(compare:)]; 227 | 228 | for (id value in keys) { 229 | if (addComma) 230 | [json appendString:@","]; 231 | else 232 | addComma = YES; 233 | 234 | if ([self humanReadable]) 235 | [json appendString:[self indent]]; 236 | 237 | if (![value isKindOfClass:[NSString class]]) { 238 | *error = err(EUNSUPPORTED, @"JSON object key must be string"); 239 | return NO; 240 | } 241 | 242 | if (![self appendString:value into:json error:error]) 243 | return NO; 244 | 245 | [json appendString:colon]; 246 | if (![self appendValue:[fragment objectForKey:value] into:json error:error]) { 247 | *error = err(EUNSUPPORTED, [NSString stringWithFormat:@"Unsupported value for key %@ in object", value]); 248 | return NO; 249 | } 250 | } 251 | 252 | depth--; 253 | if ([self humanReadable] && [fragment count]) 254 | [json appendString:[self indent]]; 255 | [json appendString:@"}"]; 256 | return YES; 257 | } 258 | 259 | - (BOOL)appendString:(NSString*)fragment into:(NSMutableString*)json error:(NSError**)error { 260 | 261 | static NSMutableCharacterSet *kEscapeChars; 262 | if( ! kEscapeChars ) { 263 | kEscapeChars = [[NSMutableCharacterSet characterSetWithRange: NSMakeRange(0,32)] retain]; 264 | [kEscapeChars addCharactersInString: @"\"\\"]; 265 | } 266 | 267 | [json appendString:@"\""]; 268 | 269 | NSRange esc = [fragment rangeOfCharacterFromSet:kEscapeChars]; 270 | if ( !esc.length ) { 271 | // No special chars -- can just add the raw string: 272 | [json appendString:fragment]; 273 | 274 | } else { 275 | NSUInteger length = [fragment length]; 276 | for (NSUInteger i = 0; i < length; i++) { 277 | unichar uc = [fragment characterAtIndex:i]; 278 | switch (uc) { 279 | case '"': [json appendString:@"\\\""]; break; 280 | case '\\': [json appendString:@"\\\\"]; break; 281 | case '\t': [json appendString:@"\\t"]; break; 282 | case '\n': [json appendString:@"\\n"]; break; 283 | case '\r': [json appendString:@"\\r"]; break; 284 | case '\b': [json appendString:@"\\b"]; break; 285 | case '\f': [json appendString:@"\\f"]; break; 286 | default: 287 | if (uc < 0x20) { 288 | [json appendFormat:@"\\u%04x", uc]; 289 | } else { 290 | [json appendFormat:@"%C", uc]; 291 | } 292 | break; 293 | 294 | } 295 | } 296 | } 297 | 298 | [json appendString:@"\""]; 299 | return YES; 300 | } 301 | 302 | #pragma mark Parser 303 | 304 | /** 305 | Returns the object represented by the passed-in string or nil on error. The returned object can be 306 | a string, number, boolean, null, array or dictionary. 307 | 308 | @param repr the json string to parse 309 | @param allowScalar whether to return objects for JSON fragments 310 | @param error used to return an error by reference (pass NULL if this is not desired) 311 | */ 312 | - (id)objectWithString:(id)repr allowScalar:(BOOL)allowScalar error:(NSError**)error { 313 | 314 | if (!repr) { 315 | if (error) 316 | *error = err(EINPUT, @"Input was 'nil'"); 317 | return nil; 318 | } 319 | 320 | depth = 0; 321 | c = [repr UTF8String]; 322 | 323 | id o; 324 | NSError *err2 = nil; 325 | if (![self scanValue:&o error:&err2]) { 326 | if (error) 327 | *error = err2; 328 | return nil; 329 | } 330 | 331 | // We found some valid JSON. But did it also contain something else? 332 | if (![self scanIsAtEnd]) { 333 | if (error) 334 | *error = err(ETRAILGARBAGE, @"Garbage after JSON"); 335 | return nil; 336 | } 337 | 338 | // If we don't allow scalars, check that the object we've found is a valid JSON container. 339 | if (!allowScalar && ![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) { 340 | if (error) 341 | *error = err(EFRAGMENT, @"Valid fragment, but not JSON"); 342 | return nil; 343 | } 344 | 345 | NSAssert1(o, @"Should have a valid object from %@", repr); 346 | return o; 347 | } 348 | 349 | /** 350 | Returns the object represented by the passed-in string or nil on error. The returned object can be 351 | a string, number, boolean, null, array or dictionary. 352 | 353 | @param repr the json string to parse 354 | @param error used to return an error by reference (pass NULL if this is not desired) 355 | */ 356 | - (id)fragmentWithString:(NSString*)repr error:(NSError**)error { 357 | return [self objectWithString:repr allowScalar:YES error:error]; 358 | } 359 | 360 | /** 361 | Returns the object represented by the passed-in string or nil on error. The returned object 362 | will be either a dictionary or an array. 363 | 364 | @param repr the json string to parse 365 | @param error used to return an error by reference (pass NULL if this is not desired) 366 | */ 367 | - (id)objectWithString:(NSString*)repr error:(NSError**)error { 368 | return [self objectWithString:repr allowScalar:NO error:error]; 369 | } 370 | 371 | /* 372 | In contrast to the public methods, it is an error to omit the error parameter here. 373 | */ 374 | - (BOOL)scanValue:(NSObject **)o error:(NSError **)error 375 | { 376 | skipWhitespace(c); 377 | 378 | switch (*c++) { 379 | case '{': 380 | return [self scanRestOfDictionary:(NSMutableDictionary **)o error:error]; 381 | break; 382 | case '[': 383 | return [self scanRestOfArray:(NSMutableArray **)o error:error]; 384 | break; 385 | case '"': 386 | return [self scanRestOfString:(NSMutableString **)o error:error]; 387 | break; 388 | case 'f': 389 | return [self scanRestOfFalse:(NSNumber **)o error:error]; 390 | break; 391 | case 't': 392 | return [self scanRestOfTrue:(NSNumber **)o error:error]; 393 | break; 394 | case 'n': 395 | return [self scanRestOfNull:(NSNull **)o error:error]; 396 | break; 397 | case '-': 398 | case '0'...'9': 399 | c--; // cannot verify number correctly without the first character 400 | return [self scanNumber:(NSNumber **)o error:error]; 401 | break; 402 | case '+': 403 | *error = err(EPARSENUM, @"Leading + disallowed in number"); 404 | return NO; 405 | break; 406 | case 0x0: 407 | *error = err(EEOF, @"Unexpected end of string"); 408 | return NO; 409 | break; 410 | default: 411 | *error = err(EPARSE, @"Unrecognised leading character"); 412 | return NO; 413 | break; 414 | } 415 | 416 | NSAssert(0, @"Should never get here"); 417 | return NO; 418 | } 419 | 420 | - (BOOL)scanRestOfTrue:(NSNumber **)o error:(NSError **)error 421 | { 422 | if (!strncmp(c, "rue", 3)) { 423 | c += 3; 424 | *o = [NSNumber numberWithBool:YES]; 425 | return YES; 426 | } 427 | *error = err(EPARSE, @"Expected 'true'"); 428 | return NO; 429 | } 430 | 431 | - (BOOL)scanRestOfFalse:(NSNumber **)o error:(NSError **)error 432 | { 433 | if (!strncmp(c, "alse", 4)) { 434 | c += 4; 435 | *o = [NSNumber numberWithBool:NO]; 436 | return YES; 437 | } 438 | *error = err(EPARSE, @"Expected 'false'"); 439 | return NO; 440 | } 441 | 442 | - (BOOL)scanRestOfNull:(NSNull **)o error:(NSError **)error 443 | { 444 | if (!strncmp(c, "ull", 3)) { 445 | c += 3; 446 | *o = [NSNull null]; 447 | return YES; 448 | } 449 | *error = err(EPARSE, @"Expected 'null'"); 450 | return NO; 451 | } 452 | 453 | - (BOOL)scanRestOfArray:(NSMutableArray **)o error:(NSError **)error 454 | { 455 | if (maxDepth && ++depth > maxDepth) { 456 | *error = err(EDEPTH, @"Nested too deep"); 457 | return NO; 458 | } 459 | 460 | *o = [NSMutableArray arrayWithCapacity:8]; 461 | 462 | for (; *c ;) { 463 | id v; 464 | 465 | skipWhitespace(c); 466 | if (*c == ']' && c++) { 467 | depth--; 468 | return YES; 469 | } 470 | 471 | if (![self scanValue:&v error:error]) { 472 | *error = errWithUnderlier(EPARSE, error, @"Expected value while parsing array"); 473 | return NO; 474 | } 475 | 476 | [*o addObject:v]; 477 | 478 | skipWhitespace(c); 479 | if (*c == ',' && c++) { 480 | skipWhitespace(c); 481 | if (*c == ']') { 482 | *error = err(ETRAILCOMMA, @"Trailing comma disallowed in array"); 483 | return NO; 484 | } 485 | } 486 | } 487 | 488 | *error = err(EEOF, @"End of input while parsing array"); 489 | return NO; 490 | } 491 | 492 | - (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o error:(NSError **)error 493 | { 494 | if (maxDepth && ++depth > maxDepth) { 495 | *error = err(EDEPTH, @"Nested too deep"); 496 | return NO; 497 | } 498 | 499 | *o = [NSMutableDictionary dictionaryWithCapacity:7]; 500 | 501 | for (; *c ;) { 502 | id k, v; 503 | 504 | skipWhitespace(c); 505 | if (*c == '}' && c++) { 506 | depth--; 507 | return YES; 508 | } 509 | 510 | if (!(*c == '\"' && c++ && [self scanRestOfString:&k error:error])) { 511 | *error = errWithUnderlier(EPARSE, error, @"Object key string expected"); 512 | return NO; 513 | } 514 | 515 | skipWhitespace(c); 516 | if (*c != ':') { 517 | *error = err(EPARSE, @"Expected ':' separating key and value"); 518 | return NO; 519 | } 520 | 521 | c++; 522 | if (![self scanValue:&v error:error]) { 523 | NSString *string = [NSString stringWithFormat:@"Object value expected for key: %@", k]; 524 | *error = errWithUnderlier(EPARSE, error, string); 525 | return NO; 526 | } 527 | 528 | [*o setObject:v forKey:k]; 529 | 530 | skipWhitespace(c); 531 | if (*c == ',' && c++) { 532 | skipWhitespace(c); 533 | if (*c == '}') { 534 | *error = err(ETRAILCOMMA, @"Trailing comma disallowed in object"); 535 | return NO; 536 | } 537 | } 538 | } 539 | 540 | *error = err(EEOF, @"End of input while parsing object"); 541 | return NO; 542 | } 543 | 544 | - (BOOL)scanRestOfString:(NSMutableString **)o error:(NSError **)error 545 | { 546 | *o = [NSMutableString stringWithCapacity:16]; 547 | do { 548 | // First see if there's a portion we can grab in one go. 549 | // Doing this caused a massive speedup on the long string. 550 | size_t len = strcspn(c, ctrl); 551 | if (len) { 552 | // check for 553 | id t = [[NSString alloc] initWithBytesNoCopy:(char*)c 554 | length:len 555 | encoding:NSUTF8StringEncoding 556 | freeWhenDone:NO]; 557 | if (t) { 558 | [*o appendString:t]; 559 | [t release]; 560 | c += len; 561 | } 562 | } 563 | 564 | if (*c == '"') { 565 | c++; 566 | return YES; 567 | 568 | } else if (*c == '\\') { 569 | unichar uc = *++c; 570 | switch (uc) { 571 | case '\\': 572 | case '/': 573 | case '"': 574 | break; 575 | 576 | case 'b': uc = '\b'; break; 577 | case 'n': uc = '\n'; break; 578 | case 'r': uc = '\r'; break; 579 | case 't': uc = '\t'; break; 580 | case 'f': uc = '\f'; break; 581 | 582 | case 'u': 583 | c++; 584 | if (![self scanUnicodeChar:&uc error:error]) { 585 | *error = errWithUnderlier(EUNICODE, error, @"Broken unicode character"); 586 | return NO; 587 | } 588 | c--; // hack. 589 | break; 590 | default: 591 | *error = err(EESCAPE, [NSString stringWithFormat:@"Illegal escape sequence '0x%x'", uc]); 592 | return NO; 593 | break; 594 | } 595 | [*o appendFormat:@"%C", uc]; 596 | c++; 597 | 598 | } else if (*c < 0x20) { 599 | *error = err(ECTRL, [NSString stringWithFormat:@"Unescaped control character '0x%x'", *c]); 600 | return NO; 601 | 602 | } else { 603 | NSLog(@"should not be able to get here"); 604 | } 605 | } while (*c); 606 | 607 | *error = err(EEOF, @"Unexpected EOF while parsing string"); 608 | return NO; 609 | } 610 | 611 | - (BOOL)scanUnicodeChar:(unichar *)x error:(NSError **)error 612 | { 613 | unichar hi, lo; 614 | 615 | if (![self scanHexQuad:&hi error:error]) { 616 | *error = err(EUNICODE, @"Missing hex quad"); 617 | return NO; 618 | } 619 | 620 | if (hi >= 0xd800) { // high surrogate char? 621 | if (hi < 0xdc00) { // yes - expect a low char 622 | 623 | if (!(*c == '\\' && ++c && *c == 'u' && ++c && [self scanHexQuad:&lo error:error])) { 624 | *error = errWithUnderlier(EUNICODE, error, @"Missing low character in surrogate pair"); 625 | return NO; 626 | } 627 | 628 | if (lo < 0xdc00 || lo >= 0xdfff) { 629 | *error = err(EUNICODE, @"Invalid low surrogate char"); 630 | return NO; 631 | } 632 | 633 | hi = (hi - 0xd800) * 0x400 + (lo - 0xdc00) + 0x10000; 634 | 635 | } else if (hi < 0xe000) { 636 | *error = err(EUNICODE, @"Invalid high character in surrogate pair"); 637 | return NO; 638 | } 639 | } 640 | 641 | *x = hi; 642 | return YES; 643 | } 644 | 645 | - (BOOL)scanHexQuad:(unichar *)x error:(NSError **)error 646 | { 647 | *x = 0; 648 | for (int i = 0; i < 4; i++) { 649 | unichar uc = *c; 650 | c++; 651 | int d = (uc >= '0' && uc <= '9') 652 | ? uc - '0' : (uc >= 'a' && uc <= 'f') 653 | ? (uc - 'a' + 10) : (uc >= 'A' && uc <= 'F') 654 | ? (uc - 'A' + 10) : -1; 655 | if (d == -1) { 656 | *error = err(EUNICODE, @"Missing hex digit in quad"); 657 | return NO; 658 | } 659 | *x *= 16; 660 | *x += d; 661 | } 662 | return YES; 663 | } 664 | 665 | - (BOOL)scanNumber:(NSNumber **)o error:(NSError **)error 666 | { 667 | const char *ns = c; 668 | 669 | // The logic to test for validity of the number formatting is relicensed 670 | // from JSON::XS with permission from its author Marc Lehmann. 671 | // (Available at the CPAN: http://search.cpan.org/dist/JSON-XS/ .) 672 | 673 | if ('-' == *c) 674 | c++; 675 | 676 | if ('0' == *c && c++) { 677 | if (isdigit(*c)) { 678 | *error = err(EPARSENUM, @"Leading 0 disallowed in number"); 679 | return NO; 680 | } 681 | 682 | } else if (!isdigit(*c) && c != ns) { 683 | *error = err(EPARSENUM, @"No digits after initial minus"); 684 | return NO; 685 | 686 | } else { 687 | skipDigits(c); 688 | } 689 | 690 | // Fractional part 691 | if ('.' == *c && c++) { 692 | 693 | if (!isdigit(*c)) { 694 | *error = err(EPARSENUM, @"No digits after decimal point"); 695 | return NO; 696 | } 697 | skipDigits(c); 698 | } 699 | 700 | // Exponential part 701 | if ('e' == *c || 'E' == *c) { 702 | c++; 703 | 704 | if ('-' == *c || '+' == *c) 705 | c++; 706 | 707 | if (!isdigit(*c)) { 708 | *error = err(EPARSENUM, @"No digits after exponent"); 709 | return NO; 710 | } 711 | skipDigits(c); 712 | } 713 | 714 | id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns 715 | length:c - ns 716 | encoding:NSUTF8StringEncoding 717 | freeWhenDone:NO]; 718 | [str autorelease]; 719 | if (str && (*o = [NSDecimalNumber decimalNumberWithString:str])) 720 | return YES; 721 | 722 | *error = err(EPARSENUM, @"Failed creating decimal instance"); 723 | return NO; 724 | } 725 | 726 | - (BOOL)scanIsAtEnd 727 | { 728 | skipWhitespace(c); 729 | return !*c; 730 | } 731 | 732 | 733 | 734 | #pragma mark Properties 735 | 736 | @synthesize humanReadable; 737 | @synthesize sortKeys; 738 | @synthesize maxDepth; 739 | 740 | @end 741 | -------------------------------------------------------------------------------- /Libraries/ASI/ASIHTTPRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASIHTTPRequest.h 3 | // 4 | // Created by Ben Copsey on 04/10/2007. 5 | // Copyright 2007-2009 All-Seeing Interactive. All rights reserved. 6 | // 7 | // A guide to the main features is available at: 8 | // http://allseeing-i.com/ASIHTTPRequest 9 | // 10 | // Portions are based on the ImageClient example from Apple: 11 | // See: http://developer.apple.com/samplecode/ImageClient/listing37.html 12 | 13 | #import 14 | #if TARGET_OS_IPHONE 15 | #import 16 | #endif 17 | #import 18 | 19 | // Make targeting 2.2.1 more reliable 20 | // See: http://www.blumtnwerx.com/blog/2009/06/cross-sdk-code-hygiene-in-xcode/ 21 | #ifndef __IPHONE_3_0 22 | #define __IPHONE_3_0 30000 23 | #endif 24 | 25 | 26 | typedef enum _ASINetworkErrorType { 27 | ASIConnectionFailureErrorType = 1, 28 | ASIRequestTimedOutErrorType = 2, 29 | ASIAuthenticationErrorType = 3, 30 | ASIRequestCancelledErrorType = 4, 31 | ASIUnableToCreateRequestErrorType = 5, 32 | ASIInternalErrorWhileBuildingRequestType = 6, 33 | ASIInternalErrorWhileApplyingCredentialsType = 7, 34 | ASIFileManagementError = 8, 35 | ASITooMuchRedirectionErrorType = 9 36 | 37 | } ASINetworkErrorType; 38 | 39 | // The error domain that all errors generated by ASIHTTPRequest use 40 | extern NSString* const NetworkRequestErrorDomain; 41 | 42 | // You can use this number to throttle upload and download bandwidth in iPhone OS apps send or receive a large amount of data 43 | // This may help apps that might otherwise be rejected for inclusion into the app store for using excessive bandwidth 44 | // This number is not official, as far as I know there is no officially documented bandwidth limit 45 | extern unsigned long const ASIWWANBandwidthThrottleAmount; 46 | 47 | @interface ASIHTTPRequest : NSOperation { 48 | 49 | // The url for this operation, should include GET params in the query string where appropriate 50 | NSURL *url; 51 | 52 | // The delegate, you need to manage setting and talking to your delegate in your subclasses 53 | id delegate; 54 | 55 | // A queue delegate that should *ALSO* be notified of delegate message (used by ASINetworkQueue) 56 | id queue; 57 | 58 | // HTTP method to use (GET / POST / PUT / DELETE / HEAD). Defaults to GET 59 | NSString *requestMethod; 60 | 61 | // Request body - only used when the whole body is stored in memory (shouldStreamPostDataFromDisk is false) 62 | NSMutableData *postBody; 63 | 64 | // gzipped request body used when shouldCompressRequestBody is YES 65 | NSData *compressedPostBody; 66 | 67 | // When true, post body will be streamed from a file on disk, rather than loaded into memory at once (useful for large uploads) 68 | // Automatically set to true in ASIFormDataRequests when using setFile:forKey: 69 | BOOL shouldStreamPostDataFromDisk; 70 | 71 | // Path to file used to store post body (when shouldStreamPostDataFromDisk is true) 72 | // You can set this yourself - useful if you want to PUT a file from local disk 73 | NSString *postBodyFilePath; 74 | 75 | // Path to a temporary file used to store a deflated post body (when shouldCompressPostBody is YES) 76 | NSString *compressedPostBodyFilePath; 77 | 78 | // Set to true when ASIHTTPRequest automatically created a temporary file containing the request body (when true, the file at postBodyFilePath will be deleted at the end of the request) 79 | BOOL didCreateTemporaryPostDataFile; 80 | 81 | // Used when writing to the post body when shouldStreamPostDataFromDisk is true (via appendPostData: or appendPostDataFromFile:) 82 | NSOutputStream *postBodyWriteStream; 83 | 84 | // Used for reading from the post body when sending the request 85 | NSInputStream *postBodyReadStream; 86 | 87 | // Dictionary for custom HTTP request headers 88 | NSMutableDictionary *requestHeaders; 89 | 90 | // Will be populated with HTTP response headers from the server 91 | NSDictionary *responseHeaders; 92 | 93 | // Can be used to manually insert cookie headers to a request, but it's more likely that sessionCookies will do this for you 94 | NSMutableArray *requestCookies; 95 | 96 | // Will be populated with cookies 97 | NSArray *responseCookies; 98 | 99 | // If use useCookiePersistance is true, network requests will present valid cookies from previous requests 100 | BOOL useCookiePersistance; 101 | 102 | // If useKeychainPersistance is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented 103 | BOOL useKeychainPersistance; 104 | 105 | // If useSessionPersistance is true, network requests will save credentials and reuse for the duration of the session (until clearSession is called) 106 | BOOL useSessionPersistance; 107 | 108 | // If allowCompressedResponse is true, requests will inform the server they can accept compressed data, and will automatically decompress gzipped responses. Default is true. 109 | BOOL allowCompressedResponse; 110 | 111 | // If shouldCompressRequestBody is true, the request body will be gzipped. Default is false. 112 | // You will probably need to enable this feature on your webserver to make this work. Tested with apache only. 113 | BOOL shouldCompressRequestBody; 114 | 115 | // When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location 116 | // If downloadDestinationPath is not set, download data will be stored in memory 117 | NSString *downloadDestinationPath; 118 | 119 | //The location that files will be downloaded to. Once a download is complete, files will be decompressed (if necessary) and moved to downloadDestinationPath 120 | NSString *temporaryFileDownloadPath; 121 | 122 | // Used for writing data to a file when downloadDestinationPath is set 123 | NSOutputStream *fileDownloadOutputStream; 124 | 125 | // When the request fails or completes successfully, complete will be true 126 | BOOL complete; 127 | 128 | // If an error occurs, error will contain an NSError 129 | // If error code is = ASIConnectionFailureErrorType (1, Connection failure occurred) - inspect [[error userInfo] objectForKey:NSUnderlyingErrorKey] for more information 130 | NSError *error; 131 | 132 | // Username and password used for authentication 133 | NSString *username; 134 | NSString *password; 135 | 136 | // Domain used for NTLM authentication 137 | NSString *domain; 138 | 139 | // Username and password used for proxy authentication 140 | NSString *proxyUsername; 141 | NSString *proxyPassword; 142 | 143 | // Domain used for NTLM proxy authentication 144 | NSString *proxyDomain; 145 | 146 | // Delegate for displaying upload progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself) 147 | id uploadProgressDelegate; 148 | 149 | // Delegate for displaying download progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself) 150 | id downloadProgressDelegate; 151 | 152 | // Whether we've seen the headers of the response yet 153 | BOOL haveExaminedHeaders; 154 | 155 | // Data we receive will be stored here. Data may be compressed unless allowCompressedResponse is false - you should use [request responseData] instead in most cases 156 | NSMutableData *rawResponseData; 157 | 158 | // Used for sending and receiving data 159 | CFHTTPMessageRef request; 160 | CFReadStreamRef readStream; 161 | 162 | // Used for authentication 163 | CFHTTPAuthenticationRef requestAuthentication; 164 | NSMutableDictionary *requestCredentials; 165 | 166 | // Used during NTLM authentication 167 | int authenticationRetryCount; 168 | 169 | // Authentication scheme (Basic, Digest, NTLM) 170 | NSString *authenticationScheme; 171 | 172 | // Realm for authentication when credentials are required 173 | NSString *authenticationRealm; 174 | 175 | // And now, the same thing, but for authenticating proxies 176 | BOOL needsProxyAuthentication; 177 | 178 | // When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a server that requires authentication 179 | // The dialog will not be shown if your delegate responds to authenticationNeededForRequest: 180 | // Default is NO. 181 | BOOL shouldPresentAuthenticationDialog; 182 | 183 | // When YES, ASIHTTPRequest will present a dialog allowing users to enter credentials when no-matching credentials were found for a proxy server that requires authentication 184 | // The dialog will not be shown if your delegate responds to proxyAuthenticationNeededForRequest: 185 | // Default is YES (basically, because most people won't want the hassle of adding support for authenticating proxies to their apps) 186 | BOOL shouldPresentProxyAuthenticationDialog; 187 | 188 | // Used for proxy authentication 189 | CFHTTPAuthenticationRef proxyAuthentication; 190 | NSMutableDictionary *proxyCredentials; 191 | 192 | // Used during authentication with an NTLM proxy 193 | int proxyAuthenticationRetryCount; 194 | 195 | // Authentication scheme for the proxy (Basic, Digest, NTLM) 196 | NSString *proxyAuthenticationScheme; 197 | 198 | // Realm for proxy authentication when credentials are required 199 | NSString *proxyAuthenticationRealm; 200 | 201 | // HTTP status code, eg: 200 = OK, 404 = Not found etc 202 | int responseStatusCode; 203 | 204 | NSString *responseStatusMessage; 205 | 206 | // Size of the response 207 | unsigned long long contentLength; 208 | 209 | // Size of the partially downloaded content 210 | unsigned long long partialDownloadSize; 211 | 212 | // Size of the POST payload 213 | unsigned long long postLength; 214 | 215 | // The total amount of downloaded data 216 | unsigned long long totalBytesRead; 217 | 218 | // The total amount of uploaded data 219 | unsigned long long totalBytesSent; 220 | 221 | // Last amount of data read (used for incrementing progress) 222 | unsigned long long lastBytesRead; 223 | 224 | // Last amount of data sent (used for incrementing progress) 225 | unsigned long long lastBytesSent; 226 | 227 | // This lock will block the request until the delegate supplies authentication info 228 | NSConditionLock *authenticationLock; 229 | 230 | // This lock prevents the operation from being cancelled at an inopportune moment 231 | NSRecursiveLock *cancelledLock; 232 | 233 | // Called on the delegate when the request completes successfully 234 | SEL didFinishSelector; 235 | 236 | // Called on the delegate when the request fails 237 | SEL didFailSelector; 238 | 239 | // Used for recording when something last happened during the request, we will compare this value with the current date to time out requests when appropriate 240 | NSDate *lastActivityTime; 241 | 242 | // Number of seconds to wait before timing out - default is 10 243 | NSTimeInterval timeOutSeconds; 244 | 245 | // Autorelease pool for the main loop, since it's highly likely that this operation will run in a thread 246 | NSAutoreleasePool *pool; 247 | 248 | // Will be YES when a HEAD request will handle the content-length before this request starts 249 | BOOL shouldResetProgressIndicators; 250 | 251 | // Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request 252 | ASIHTTPRequest *mainRequest; 253 | 254 | // When NO, this request will only update the progress indicator when it completes 255 | // When YES, this request will update the progress indicator according to how much data it has received so far 256 | // The default for requests is YES 257 | // Also see the comments in ASINetworkQueue.h 258 | BOOL showAccurateProgress; 259 | 260 | // Used to ensure the progress indicator is only incremented once when showAccurateProgress = NO 261 | BOOL updatedProgress; 262 | 263 | // Prevents the body of the post being built more than once (largely for subclasses) 264 | BOOL haveBuiltPostBody; 265 | 266 | // Used internally, may reflect the size of the internal buffer used by CFNetwork 267 | // POST / PUT operations with body sizes greater than uploadBufferSize will not timeout unless more than uploadBufferSize bytes have been sent 268 | // Likely to be 32KB on iPhone 3.0, 128KB on Mac OS X Leopard and iPhone 2.2.x 269 | unsigned long long uploadBufferSize; 270 | 271 | // Text encoding for responses that do not send a Content-Type with a charset value. Defaults to NSISOLatin1StringEncoding 272 | NSStringEncoding defaultResponseEncoding; 273 | 274 | // The text encoding of the response, will be defaultResponseEncoding if the server didn't specify. Can't be set. 275 | NSStringEncoding responseEncoding; 276 | 277 | // Tells ASIHTTPRequest not to delete partial downloads, and allows it to use an existing file to resume a download. Defaults to NO. 278 | BOOL allowResumeForFileDownloads; 279 | 280 | // Custom user information associated with the request 281 | NSDictionary *userInfo; 282 | 283 | // Use HTTP 1.0 rather than 1.1 (defaults to false) 284 | BOOL useHTTPVersionOne; 285 | 286 | // When YES, requests will automatically redirect when they get a HTTP 30x header (defaults to YES) 287 | BOOL shouldRedirect; 288 | 289 | // Used internally to tell the main loop we need to stop and retry with a new url 290 | BOOL needsRedirect; 291 | 292 | // Incremented every time this request redirects. When it reaches 5, we give up 293 | int redirectCount; 294 | 295 | // When NO, requests will not check the secure certificate is valid (use for self-signed cerficates during development, DO NOT USE IN PRODUCTION) Default is YES 296 | BOOL validatesSecureCertificate; 297 | 298 | // Details on the proxy to use - you could set these yourself, but it's probably best to let ASIHTTPRequest detect the system proxy settings 299 | NSString *proxyHost; 300 | int proxyPort; 301 | 302 | // URL for a PAC (Proxy Auto Configuration) file. If you want to set this yourself, it's probably best if you use a local file 303 | NSURL *PACurl; 304 | 305 | // True when request is attempting to handle an authentication challenge 306 | BOOL authenticationChallengeInProgress; 307 | 308 | // When YES, ASIHTTPRequests will present credentials from the session store for requests to the same server before being asked for them 309 | // This avoids an extra round trip for requests after authentication has succeeded, which is much for efficient for authenticated requests with large bodies, or on slower connections 310 | // Set to NO to only present credentials when explictly asked for them 311 | // This only affects credentials stored in the session cache when useSessionPersistance is YES. Credentials from the keychain are never presented unless the server asks for them 312 | // Default is YES 313 | BOOL shouldPresentCredentialsBeforeChallenge; 314 | } 315 | 316 | #pragma mark init / dealloc 317 | 318 | // Should be an HTTP or HTTPS url, may include username and password if appropriate 319 | - (id)initWithURL:(NSURL *)newURL; 320 | 321 | // Convenience constructor 322 | + (id)requestWithURL:(NSURL *)newURL; 323 | 324 | #pragma mark setup request 325 | 326 | // Add a custom header to the request 327 | - (void)addRequestHeader:(NSString *)header value:(NSString *)value; 328 | 329 | - (void)buildPostBody; 330 | 331 | // Called to add data to the post body. Will append to postBody when shouldStreamPostDataFromDisk is false, or write to postBodyWriteStream when true 332 | - (void)appendPostData:(NSData *)data; 333 | - (void)appendPostDataFromFile:(NSString *)file; 334 | 335 | #pragma mark get information about this request 336 | 337 | // Returns the contents of the result as an NSString (not appropriate for binary data - used responseData instead) 338 | - (NSString *)responseString; 339 | 340 | // Response data, automatically uncompressed where appropriate 341 | - (NSData *)responseData; 342 | 343 | // Returns true if the response was gzip compressed 344 | - (BOOL)isResponseCompressed; 345 | 346 | #pragma mark running a request 347 | 348 | // Run a request asynchronously by adding it to the global queue 349 | // (Use [request start] for a synchronous request) 350 | - (void)startAsynchronous; 351 | 352 | #pragma mark request logic 353 | 354 | // Main request loop is in here 355 | - (void)loadRequest; 356 | 357 | // Start the read stream. Called by loadRequest, and again to restart the request when authentication is needed 358 | - (void)startRequest; 359 | 360 | // Call to delete the temporary file used during a file download (if it exists) 361 | // No need to call this if the request succeeds - it is removed automatically 362 | - (void)removeTemporaryDownloadFile; 363 | 364 | // Call to remove the file used as the request body 365 | // No need to call this if the request succeeds and you didn't specify postBodyFilePath manually - it is removed automatically 366 | - (void)removePostDataFile; 367 | 368 | #pragma mark upload/download progress 369 | 370 | // Called on main thread to update progress delegates 371 | - (void)updateProgressIndicators; 372 | - (void)resetUploadProgress:(unsigned long long)value; 373 | - (void)updateUploadProgress; 374 | - (void)resetDownloadProgress:(unsigned long long)value; 375 | - (void)updateDownloadProgress; 376 | 377 | // Called when authorisation is needed, as we only find out we don't have permission to something when the upload is complete 378 | - (void)removeUploadProgressSoFar; 379 | 380 | // Helper method for interacting with progress indicators to abstract the details of different APIS (NSProgressIndicator and UIProgressView) 381 | + (void)setProgress:(double)progress forProgressIndicator:(id)indicator; 382 | 383 | #pragma mark handling request complete / failure 384 | 385 | // Called when a request completes successfully, lets the delegate now via didFinishSelector 386 | - (void)requestFinished; 387 | 388 | // Called when a request fails, and lets the delegate now via didFailSelector 389 | - (void)failWithError:(NSError *)theError; 390 | 391 | #pragma mark parsing HTTP response headers 392 | 393 | // Reads the response headers to find the content length, encoding, cookies for the session 394 | // Also initiates request redirection when shouldRedirect is true 395 | // Returns true if the request needs a username and password (or if those supplied were incorrect) 396 | - (BOOL)readResponseHeadersReturningAuthenticationFailure; 397 | 398 | #pragma mark http authentication stuff 399 | 400 | // Apply credentials to this request 401 | - (BOOL)applyCredentials:(NSDictionary *)newCredentials; 402 | - (BOOL)applyProxyCredentials:(NSDictionary *)newCredentials; 403 | 404 | // Attempt to obtain credentials for this request from the URL, username and password or keychain 405 | - (NSMutableDictionary *)findCredentials; 406 | - (NSMutableDictionary *)findProxyCredentials; 407 | 408 | // Unlock (unpause) the request thread so it can resume the request 409 | // Should be called by delegates when they have populated the authentication information after an authentication challenge 410 | - (void)retryUsingSuppliedCredentials; 411 | 412 | // Should be called by delegates when they wish to cancel authentication and stop 413 | - (void)cancelAuthentication; 414 | 415 | // Apply authentication information and resume the request after an authentication challenge 416 | - (void)attemptToApplyCredentialsAndResume; 417 | - (void)attemptToApplyProxyCredentialsAndResume; 418 | 419 | // Attempt to show the built-in authentication dialog, returns YES if credentials were supplied, NO if user cancelled dialog / dialog is disabled / running on main thread 420 | // Currently only used on iPhone OS 421 | - (BOOL)showProxyAuthenticationDialog; 422 | - (BOOL)showAuthenticationDialog; 423 | 424 | // Construct a basic authentication header from the username and password supplied, and add it to the request headers 425 | // Used when shouldPresentCredentialsBeforeChallenge is YES 426 | - (void)addBasicAuthenticationHeaderWithUsername:(NSString *)theUsername andPassword:(NSString *)thePassword; 427 | 428 | #pragma mark stream status handlers 429 | 430 | // CFnetwork event handlers 431 | - (void)handleNetworkEvent:(CFStreamEventType)type; 432 | - (void)handleBytesAvailable; 433 | - (void)handleStreamComplete; 434 | - (void)handleStreamError; 435 | 436 | #pragma mark global queue 437 | 438 | + (NSOperationQueue *)sharedRequestQueue; 439 | 440 | #pragma mark session credentials 441 | 442 | + (NSMutableArray *)sessionProxyCredentialsStore; 443 | + (NSMutableArray *)sessionCredentialsStore; 444 | 445 | + (void)storeProxyAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials; 446 | + (void)storeAuthenticationCredentialsInSessionStore:(NSDictionary *)credentials; 447 | 448 | + (void)removeProxyAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials; 449 | + (void)removeAuthenticationCredentialsFromSessionStore:(NSDictionary *)credentials; 450 | 451 | - (NSDictionary *)findSessionProxyAuthenticationCredentials; 452 | - (NSDictionary *)findSessionAuthenticationCredentials; 453 | 454 | 455 | #pragma mark keychain storage 456 | 457 | // Save credentials for this request to the keychain 458 | - (void)saveCredentialsToKeychain:(NSDictionary *)newCredentials; 459 | 460 | // Save credentials to the keychain 461 | + (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; 462 | + (void)saveCredentials:(NSURLCredential *)credentials forProxy:(NSString *)host port:(int)port realm:(NSString *)realm; 463 | 464 | // Return credentials from the keychain 465 | + (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; 466 | + (NSURLCredential *)savedCredentialsForProxy:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; 467 | 468 | // Remove credentials from the keychain 469 | + (void)removeCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm; 470 | + (void)removeCredentialsForProxy:(NSString *)host port:(int)port realm:(NSString *)realm; 471 | 472 | // We keep track of any cookies we accept, so that we can remove them from the persistent store later 473 | + (void)setSessionCookies:(NSMutableArray *)newSessionCookies; 474 | + (NSMutableArray *)sessionCookies; 475 | 476 | // Adds a cookie to our list of cookies we've accepted, checking first for an old version of the same cookie and removing that 477 | + (void)addSessionCookie:(NSHTTPCookie *)newCookie; 478 | 479 | // Dump all session data (authentication and cookies) 480 | + (void)clearSession; 481 | 482 | #pragma mark gzip decompression 483 | 484 | // Uncompress gzipped data with zlib 485 | + (NSData *)uncompressZippedData:(NSData*)compressedData; 486 | 487 | // Uncompress gzipped data from a file into another file, used when downloading to a file 488 | + (int)uncompressZippedDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath; 489 | + (int)uncompressZippedDataFromSource:(FILE *)source toDestination:(FILE *)dest; 490 | 491 | #pragma mark gzip compression 492 | 493 | // Compress data with gzip using zlib 494 | + (NSData *)compressData:(NSData*)uncompressedData; 495 | 496 | // gzip compress data from a file, saving to another file, used for uploading when shouldCompressRequestBody is true 497 | + (int)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath; 498 | + (int)compressDataFromSource:(FILE *)source toDestination:(FILE *)dest; 499 | 500 | #pragma mark get user agent 501 | 502 | // Will be used as a user agent if requests do not specify a custom user agent 503 | // Is only used when you have specified a Bundle Display Name (CFDisplayBundleName) or Bundle Name (CFBundleName) in your plist 504 | + (NSString *)defaultUserAgentString; 505 | 506 | #pragma mark proxy autoconfiguration 507 | 508 | // Returns an array of proxies to use for a particular url, given the url of a PAC script 509 | + (NSArray *)proxiesForURL:(NSURL *)theURL fromPAC:(NSURL *)pacScriptURL; 510 | 511 | #pragma mark mime-type detection 512 | 513 | // Only works on Mac OS, will always return 'application/octet-stream' on iPhone 514 | + (NSString *)mimeTypeForFileAtPath:(NSString *)path; 515 | 516 | #pragma mark bandwidth measurement / throttling 517 | 518 | // The maximum number of bytes ALL requests can send / receive in a second 519 | // This is a rough figure. The actual amount used will be slightly more, this does not include HTTP headers 520 | + (unsigned long)maxBandwidthPerSecond; 521 | + (void)setMaxBandwidthPerSecond:(unsigned long)bytes; 522 | 523 | // Get a rough average (for the last 5 seconds) of how much bandwidth is being used, in bytes 524 | + (unsigned long)averageBandwidthUsedPerSecond; 525 | 526 | // Will return YES is bandwidth throttling is currently in use 527 | + (BOOL)isBandwidthThrottled; 528 | 529 | // Used internally to record bandwidth use, and by ASIInputStreams when uploading. It's probably best if you don't mess with this. 530 | + (void)incrementBandwidthUsedInLastSecond:(unsigned long)bytes; 531 | 532 | // On iPhone, ASIHTTPRequest can automatically turn throttling on and off as the connection type changes between WWAN and WiFi 533 | 534 | #if TARGET_OS_IPHONE 535 | // Set to YES to automatically turn on throttling when WWAN is connected, and automatically turn it off when it isn't 536 | + (void)setShouldThrottleBandwidthForWWAN:(BOOL)throttle; 537 | 538 | // Turns on throttling automatically when WWAN is connected using a custom limit, and turns it off automatically when it isn't 539 | + (void)throttleBandwidthForWWANUsingLimit:(unsigned long)limit; 540 | 541 | // Called when the status of the network changes 542 | + (void)reachabilityChanged:(NSNotification *)note; 543 | #endif 544 | 545 | // Returns the maximum amount of data we can read as part of the current measurement period, and sleeps this thread if our allowance is used up 546 | + (unsigned long)maxUploadReadLength; 547 | 548 | #pragma mark miscellany 549 | 550 | // Determines whether we're on iPhone OS 2.0 at runtime, currently used to determine whether we should apply a workaround for an issue with converting longs to doubles on iPhone OS 2 551 | + (BOOL)isiPhoneOS2; 552 | 553 | // Used for generating Authorization header when using basic authentication when shouldPresentCredentialsBeforeChallenge is true 554 | // And also by ASIS3Request 555 | + (NSString *)base64forData:(NSData *)theData; 556 | 557 | #pragma mark === 558 | 559 | @property (retain) NSString *username; 560 | @property (retain) NSString *password; 561 | @property (retain) NSString *domain; 562 | 563 | @property (retain) NSString *proxyUsername; 564 | @property (retain) NSString *proxyPassword; 565 | @property (retain) NSString *proxyDomain; 566 | 567 | @property (retain) NSString *proxyHost; 568 | @property (assign) int proxyPort; 569 | 570 | @property (retain,setter=setURL:) NSURL *url; 571 | @property (assign) id delegate; 572 | @property (assign) id queue; 573 | @property (assign) id uploadProgressDelegate; 574 | @property (assign) id downloadProgressDelegate; 575 | @property (assign) BOOL useKeychainPersistance; 576 | @property (assign) BOOL useSessionPersistance; 577 | @property (retain) NSString *downloadDestinationPath; 578 | @property (retain) NSString *temporaryFileDownloadPath; 579 | @property (assign) SEL didFinishSelector; 580 | @property (assign) SEL didFailSelector; 581 | @property (retain,readonly) NSString *authenticationRealm; 582 | @property (retain,readonly) NSString *proxyAuthenticationRealm; 583 | @property (retain) NSError *error; 584 | @property (assign,readonly) BOOL complete; 585 | @property (retain,readonly) NSDictionary *responseHeaders; 586 | @property (retain) NSMutableDictionary *requestHeaders; 587 | @property (retain) NSMutableArray *requestCookies; 588 | @property (retain,readonly) NSArray *responseCookies; 589 | @property (assign) BOOL useCookiePersistance; 590 | @property (retain) NSDictionary *requestCredentials; 591 | @property (retain) NSDictionary *proxyCredentials; 592 | @property (assign,readonly) int responseStatusCode; 593 | @property (retain,readonly) NSString *responseStatusMessage; 594 | @property (retain,readonly) NSMutableData *rawResponseData; 595 | @property (assign) NSTimeInterval timeOutSeconds; 596 | @property (retain) NSString *requestMethod; 597 | @property (retain) NSMutableData *postBody; 598 | @property (assign,readonly) unsigned long long contentLength; 599 | @property (assign) unsigned long long postLength; 600 | @property (assign) BOOL shouldResetProgressIndicators; 601 | @property (retain) ASIHTTPRequest *mainRequest; 602 | @property (assign) BOOL showAccurateProgress; 603 | @property (assign,readonly) unsigned long long totalBytesRead; 604 | @property (assign,readonly) unsigned long long totalBytesSent; 605 | @property (assign) NSStringEncoding defaultResponseEncoding; 606 | @property (assign,readonly) NSStringEncoding responseEncoding; 607 | @property (assign) BOOL allowCompressedResponse; 608 | @property (assign) BOOL allowResumeForFileDownloads; 609 | @property (retain) NSDictionary *userInfo; 610 | @property (retain) NSString *postBodyFilePath; 611 | @property (assign) BOOL shouldStreamPostDataFromDisk; 612 | @property (assign) BOOL didCreateTemporaryPostDataFile; 613 | @property (assign) BOOL useHTTPVersionOne; 614 | @property (assign, readonly) unsigned long long partialDownloadSize; 615 | @property (assign) BOOL shouldRedirect; 616 | @property (assign) BOOL validatesSecureCertificate; 617 | @property (assign) BOOL shouldCompressRequestBody; 618 | @property (assign) BOOL needsProxyAuthentication; 619 | @property (retain) NSURL *PACurl; 620 | @property (retain) NSString *authenticationScheme; 621 | @property (retain) NSString *proxyAuthenticationScheme; 622 | @property (assign) BOOL shouldPresentAuthenticationDialog; 623 | @property (assign) BOOL shouldPresentProxyAuthenticationDialog; 624 | @property (assign) BOOL authenticationChallengeInProgress; 625 | @property (assign) BOOL shouldPresentCredentialsBeforeChallenge; 626 | @property (assign, readonly) int authenticationRetryCount; 627 | @property (assign, readonly) int proxyAuthenticationRetryCount; 628 | @end 629 | --------------------------------------------------------------------------------