Skip to content
This repository
Browse code

Adapted the project for Xcode 4.2, removing compiler and static analy…

…zer warnings
  • Loading branch information...
commit b914325698045ddfa66885e8824e83e4c0bf1dc8 1 parent 6369465
authored October 14, 2011

Showing 26 changed files with 3,573 additions and 1,227 deletions. Show diff stats Hide diff stats

  1. 12  Classes/Controllers/SBTasksController.m
  2. 3  Externals/AKOImageView/AKOImageView.m
  3. 2  Externals/ASIHTTPRequest/ASIAuthenticationDialog.h
  4. 90  Externals/ASIHTTPRequest/ASIAuthenticationDialog.m
  5. 74  Externals/ASIHTTPRequest/ASICacheDelegate.h
  6. 42  Externals/ASIHTTPRequest/ASIDataCompressor.h
  7. 219  Externals/ASIHTTPRequest/ASIDataCompressor.m
  8. 41  Externals/ASIHTTPRequest/ASIDataDecompressor.h
  9. 218  Externals/ASIHTTPRequest/ASIDataDecompressor.m
  10. 42  Externals/ASIHTTPRequest/ASIDownloadCache.h
  11. 504  Externals/ASIHTTPRequest/ASIDownloadCache.m
  12. 7  Externals/ASIHTTPRequest/ASIFormDataRequest.m
  13. 295  Externals/ASIHTTPRequest/ASIHTTPRequest.h
  14. 2,960  Externals/ASIHTTPRequest/ASIHTTPRequest.m
  15. 5  Externals/ASIHTTPRequest/ASIHTTPRequestConfig.h
  16. 4  Externals/ASIHTTPRequest/ASIHTTPRequestDelegate.h
  17. 22  Externals/ASIHTTPRequest/ASIInputStream.m
  18. 16  Externals/ASIHTTPRequest/ASINSStringAdditions.h
  19. 28  Externals/ASIHTTPRequest/ASINSStringAdditions.m
  20. 12  Externals/ASIHTTPRequest/ASINetworkQueue.h
  21. 55  Externals/ASIHTTPRequest/ASINetworkQueue.m
  22. 1  Externals/Reachability/Reachability.h
  23. 10  Externals/Reachability/Reachability.m
  24. 2  Externals/SynthesizeSingleton/SynthesizeSingleton.h
  25. 6  Externals/TBXML/TBXML.m
  26. 130  Senbei.xcodeproj/project.pbxproj
12  Classes/Controllers/SBTasksController.m
@@ -50,7 +50,7 @@ @interface SBTasksController ()
50 50
 @property (nonatomic, retain) NSMutableArray *sections;
51 51
 @property (nonatomic, retain) NSMutableDictionary *categories;
52 52
 @property (nonatomic, retain) NSIndexPath *indexPathToDelete;
53  
-@property (nonatomic, retain) SBNewTaskController *newTaskController;
  53
+@property (nonatomic, retain) SBNewTaskController *formTaskController;
54 54
 @property (nonatomic, getter = isFirstLoad) BOOL firstLoad;
55 55
 
56 56
 @end
@@ -68,7 +68,7 @@ @implementation SBTasksController
68 68
 @synthesize sections = _sections;
69 69
 @synthesize categories = _categories;
70 70
 @synthesize indexPathToDelete = _indexPathToDelete;
71  
-@synthesize newTaskController = _newTaskController;
  71
+@synthesize formTaskController = _formTaskController;
72 72
 @synthesize firstLoad = _firstLoad;
73 73
 
74 74
 - (void)dealloc 
@@ -82,7 +82,7 @@ - (void)dealloc
82 82
     [_tasksDueNextWeek release];
83 83
     [_tasksDueLater release];
84 84
     [_sections release];
85  
-    [_newTaskController release];
  85
+    [_formTaskController release];
86 86
     [_categories release];
87 87
     [super dealloc];
88 88
 }
@@ -165,11 +165,11 @@ - (void)didReceiveMemoryWarning
165 165
 
166 166
 - (void)addNewTask:(id)sender
167 167
 {
168  
-    if (_newTaskController == nil)
  168
+    if (_formTaskController == nil)
169 169
     {
170  
-        _newTaskController = [[SBNewTaskController alloc] init];
  170
+        _formTaskController = [[SBNewTaskController alloc] init];
171 171
     }
172  
-    UINavigationController *controller = [[[UINavigationController alloc] initWithRootViewController:_newTaskController] autorelease];
  172
+    UINavigationController *controller = [[[UINavigationController alloc] initWithRootViewController:_formTaskController] autorelease];
173 173
     [self.navigationController presentModalViewController:controller
174 174
                                                  animated:YES];
175 175
 }
3  Externals/AKOImageView/AKOImageView.m
@@ -21,7 +21,8 @@ @implementation AKOImageView
21 21
 
22 22
 - (id)initWithFrame:(CGRect)frame
23 23
 {
24  
-    if ([super initWithFrame:frame])
  24
+    self = [super initWithFrame:frame];
  25
+    if (self)
25 26
     {
26 27
         _spinningWheel = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
27 28
         _spinningWheel.center = self.center;
2  Externals/ASIHTTPRequest/ASIAuthenticationDialog.h
@@ -31,5 +31,5 @@ typedef enum _ASIAuthenticationType {
31 31
 @property (retain) ASIHTTPRequest *request;
32 32
 @property (assign) ASIAuthenticationType type;
33 33
 @property (assign) BOOL didEnableRotationNotifications;
34  
-@property (retain) UIViewController *presentingController;
  34
+@property (retain, nonatomic) UIViewController *presentingController;
35 35
 @end
90  Externals/ASIHTTPRequest/ASIAuthenticationDialog.m
@@ -8,9 +8,9 @@
8 8
 
9 9
 #import "ASIAuthenticationDialog.h"
10 10
 #import "ASIHTTPRequest.h"
11  
-#import <CoreGraphics/CoreGraphics.h>
  11
+#import <QuartzCore/QuartzCore.h>
12 12
 
13  
-ASIAuthenticationDialog *sharedDialog = nil;
  13
+static ASIAuthenticationDialog *sharedDialog = nil;
14 14
 BOOL isDismissing = NO;
15 15
 static NSMutableArray *requestsNeedingAuthentication = nil;
16 16
 
@@ -37,6 +37,10 @@ - (void)showTitle;
37 37
 - (void)show;
38 38
 - (NSArray *)requestsRequiringTheseCredentials;
39 39
 - (void)presentNextDialog;
  40
+- (void)keyboardWillShow:(NSNotification *)notification;
  41
+- (void)orientationChanged:(NSNotification *)notification;
  42
+- (void)cancelAuthenticationFromDialog:(id)sender;
  43
+- (void)loginWithCredentialsFromDialog:(id)sender;
40 44
 @property (retain) UITableView *tableView;
41 45
 @end
42 46
 
@@ -51,20 +55,20 @@ + (void)initialize
51 55
 	}
52 56
 }
53 57
 
54  
-+ (void)presentAuthenticationDialogForRequest:(ASIHTTPRequest *)request
  58
++ (void)presentAuthenticationDialogForRequest:(ASIHTTPRequest *)theRequest
55 59
 {
56 60
 	// No need for a lock here, this will always be called on the main thread
57 61
 	if (!sharedDialog) {
58 62
 		sharedDialog = [[self alloc] init];
59  
-		[sharedDialog setRequest:request];
60  
-		if ([request authenticationNeeded] == ASIProxyAuthenticationNeeded) {
  63
+		[sharedDialog setRequest:theRequest];
  64
+		if ([theRequest authenticationNeeded] == ASIProxyAuthenticationNeeded) {
61 65
 			[sharedDialog setType:ASIProxyAuthenticationType];
62 66
 		} else {
63 67
 			[sharedDialog setType:ASIStandardAuthenticationType];
64 68
 		}
65 69
 		[sharedDialog show];
66 70
 	} else {
67  
-		[requestsNeedingAuthentication addObject:request];
  71
+		[requestsNeedingAuthentication addObject:theRequest];
68 72
 	}
69 73
 }
70 74
 
@@ -127,33 +131,46 @@ - (void)keyboardWillShow:(NSNotification *)notification
127 131
 // Manually handles orientation changes on iPhone
128 132
 - (void)orientationChanged:(NSNotification *)notification
129 133
 {
130  
-	[[self view] setTransform:CGAffineTransformIdentity];
131 134
 	[self showTitle];
132  
-	CGRect frame = [[self view] frame];
133  
-	[[self view] setCenter:CGPointMake(frame.size.height/2,frame.size.width/2)];
134  
-	float targetRotation = 0;
135  
-
136  
-	frame = [[UIScreen mainScreen] bounds];
137  
-	switch ([[UIDevice currentDevice] orientation]) {
138  
-		case UIDeviceOrientationPortraitUpsideDown:
139  
-			targetRotation = 180;
140  
-			frame = CGRectMake(0, 0, frame.size.width, frame.size.height-20);
141  
-			break;
142  
-		case UIDeviceOrientationLandscapeLeft:
143  
-			targetRotation = 90;
144  
-			frame = CGRectMake(0, 0, frame.size.width-20, frame.size.height);
145  
-			break;
146  
-		case UIDeviceOrientationLandscapeRight:
147  
-			frame = CGRectMake(20, 0, frame.size.width-20, frame.size.height);
148  
-			targetRotation = 270;
149  
-			break;
150  
-		case UIDeviceOrientationPortrait:
151  
-			frame = CGRectMake(0, 20, frame.size.width, frame.size.height-20);
152  
-			break;
  135
+	
  136
+	UIInterfaceOrientation o = [[UIApplication sharedApplication] statusBarOrientation];
  137
+	CGFloat angle = 0;
  138
+	switch (o) {
  139
+		case UIDeviceOrientationLandscapeLeft: angle = 90; break;
  140
+		case UIDeviceOrientationLandscapeRight: angle = -90; break;
  141
+		case UIDeviceOrientationPortraitUpsideDown: angle = 180; break;
  142
+		default: break;
153 143
 	}
154 144
 
155  
-	[[self view] setTransform:CGAffineTransformMakeRotation(targetRotation / 180.0 * M_PI)];
156  
-	[[self view] setFrame:frame];
  145
+	CGRect f = [[UIScreen mainScreen] applicationFrame];
  146
+
  147
+	// Swap the frame height and width if necessary
  148
+ 	if (UIDeviceOrientationIsLandscape(o)) {
  149
+		CGFloat t;
  150
+		t = f.size.width;
  151
+		f.size.width = f.size.height;
  152
+		f.size.height = t;
  153
+	}
  154
+
  155
+	CGAffineTransform previousTransform = self.view.layer.affineTransform;
  156
+	CGAffineTransform newTransform = CGAffineTransformMakeRotation((CGFloat)(angle * M_PI / 180.0));
  157
+
  158
+	// Reset the transform so we can set the size
  159
+	self.view.layer.affineTransform = CGAffineTransformIdentity;
  160
+	self.view.frame = (CGRect){ { 0, 0 }, f.size};
  161
+
  162
+	// Revert to the previous transform for correct animation
  163
+	self.view.layer.affineTransform = previousTransform;
  164
+
  165
+	[UIView beginAnimations:nil context:NULL];
  166
+	[UIView setAnimationDuration:0.3];
  167
+
  168
+	// Set the new transform
  169
+	self.view.layer.affineTransform = newTransform;
  170
+
  171
+	// Fix the view origin
  172
+	self.view.frame = (CGRect){ { f.origin.x, f.origin.y },self.view.frame.size};
  173
+    [UIView commitAnimations];
157 174
 }
158 175
 		 
159 176
 #pragma mark utilities
@@ -165,7 +182,7 @@ - (UIViewController *)presentingController
165 182
 
166 183
 		// Attach to the window, but don't interfere.
167 184
 		UIWindow *window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
168  
-		[window addSubview:presentingController.view];
  185
+		[window addSubview:[presentingController view]];
169 186
 		[[presentingController view] setFrame:CGRectZero];
170 187
 		[[presentingController view] setUserInteractionEnabled:NO];
171 188
 	}
@@ -200,8 +217,15 @@ - (UITextField *)domainField
200 217
 + (void)dismiss
201 218
 {
202 219
 	[[sharedDialog parentViewController] dismissModalViewControllerAnimated:YES];
  220
+}
  221
+
  222
+- (void)viewDidDisappear:(BOOL)animated
  223
+{
  224
+	[self retain];
203 225
 	[sharedDialog release];
204 226
 	sharedDialog = nil;
  227
+	[self performSelector:@selector(presentNextDialog) withObject:nil afterDelay:0];
  228
+	[self release];
205 229
 }
206 230
 
207 231
 - (void)dismiss
@@ -297,7 +321,6 @@ - (void)cancelAuthenticationFromDialog:(id)sender
297 321
 		[requestsNeedingAuthentication removeObject:theRequest];
298 322
 	}
299 323
 	[self dismiss];
300  
-	[self performSelector:@selector(presentNextDialog) withObject:nil afterDelay:1];
301 324
 }
302 325
 
303 326
 - (NSArray *)requestsRequiringTheseCredentials
@@ -356,7 +379,6 @@ - (void)loginWithCredentialsFromDialog:(id)sender
356 379
 		[theRequest retryUsingSuppliedCredentials];
357 380
 		[requestsNeedingAuthentication removeObject:theRequest];
358 381
 	}
359  
-	[self performSelector:@selector(presentNextDialog) withObject:nil afterDelay:1];
360 382
 	[self dismiss];
361 383
 }
362 384
 
@@ -432,7 +454,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
432 454
 	return cell;
433 455
 }
434 456
 
435  
-- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  457
+- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section
436 458
 {
437 459
 	if (section == 0) {
438 460
 		return 2;
74  Externals/ASIHTTPRequest/ASICacheDelegate.h
@@ -9,14 +9,40 @@
9 9
 #import <Foundation/Foundation.h>
10 10
 @class ASIHTTPRequest;
11 11
 
  12
+// Cache policies control the behaviour of a cache and how requests use the cache
  13
+// When setting a cache policy, you can use a combination of these values as a bitmask
  14
+// For example: [request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy|ASIDoNotWriteToCacheCachePolicy];
  15
+// Note that some of the behaviours below are mutally exclusive - you cannot combine ASIAskServerIfModifiedWhenStaleCachePolicy and ASIAskServerIfModifiedCachePolicy, for example.
12 16
 typedef enum _ASICachePolicy {
13  
-	ASIDefaultCachePolicy = 0,
14  
-	ASIIgnoreCachePolicy = 1,
15  
-	ASIReloadIfDifferentCachePolicy = 2,
16  
-	ASIOnlyLoadIfNotCachedCachePolicy = 3,
17  
-	ASIUseCacheIfLoadFailsCachePolicy = 4
  17
+
  18
+	// The default cache policy. When you set a request to use this, it will use the cache's defaultCachePolicy
  19
+	// ASIDownloadCache's default cache policy is 'ASIAskServerIfModifiedWhenStaleCachePolicy'
  20
+	ASIUseDefaultCachePolicy = 0,
  21
+
  22
+	// Tell the request not to read from the cache
  23
+	ASIDoNotReadFromCacheCachePolicy = 1,
  24
+
  25
+	// The the request not to write to the cache
  26
+	ASIDoNotWriteToCacheCachePolicy = 2,
  27
+
  28
+	// Ask the server if there is an updated version of this resource (using a conditional GET) ONLY when the cached data is stale
  29
+	ASIAskServerIfModifiedWhenStaleCachePolicy = 4,
  30
+
  31
+	// Always ask the server if there is an updated version of this resource (using a conditional GET)
  32
+	ASIAskServerIfModifiedCachePolicy = 8,
  33
+
  34
+	// If cached data exists, use it even if it is stale. This means requests will not talk to the server unless the resource they are requesting is not in the cache
  35
+	ASIOnlyLoadIfNotCachedCachePolicy = 16,
  36
+
  37
+	// If cached data exists, use it even if it is stale. If cached data does not exist, stop (will not set an error on the request)
  38
+	ASIDontLoadCachePolicy = 32,
  39
+
  40
+	// Specifies that cached data may be used if the request fails. If cached data is used, the request will succeed without error. Usually used in combination with other options above.
  41
+	ASIFallbackToCacheIfLoadFailsCachePolicy = 64
18 42
 } ASICachePolicy;
19 43
 
  44
+// Cache storage policies control whether cached data persists between application launches (ASICachePermanentlyCacheStoragePolicy) or not (ASICacheForSessionDurationCacheStoragePolicy)
  45
+// Calling [ASIHTTPRequest clearSession] will remove any data stored using ASICacheForSessionDurationCacheStoragePolicy
20 46
 typedef enum _ASICacheStoragePolicy {
21 47
 	ASICacheForSessionDurationCacheStoragePolicy = 0,
22 48
 	ASICachePermanentlyCacheStoragePolicy = 1
@@ -27,10 +53,19 @@ typedef enum _ASICacheStoragePolicy {
27 53
 
28 54
 @required
29 55
 
30  
-// Should return the cache policy that will be used when requests have their cache policy set to ASIDefaultCachePolicy
  56
+// Should return the cache policy that will be used when requests have their cache policy set to ASIUseDefaultCachePolicy
31 57
 - (ASICachePolicy)defaultCachePolicy;
32 58
 
33  
-// Should Remove cached data for a particular request
  59
+// Returns the date a cached response should expire on. Pass a non-zero max age to specify a custom date.
  60
+- (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
  61
+
  62
+// Updates cached response headers with a new expiry date. Pass a non-zero max age to specify a custom date.
  63
+- (void)updateExpiryForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
  64
+
  65
+// Looks at the request's cache policy and any cached headers to determine if the cache data is still valid
  66
+- (BOOL)canUseCachedDataForRequest:(ASIHTTPRequest *)request;
  67
+
  68
+// Removes cached data for a particular request
34 69
 - (void)removeCachedDataForRequest:(ASIHTTPRequest *)request;
35 70
 
36 71
 // Should return YES if the cache considers its cached response current for the request
@@ -41,15 +76,28 @@ typedef enum _ASICacheStoragePolicy {
41 76
 // When a non-zero maxAge is passed, it should be used as the expiry time for the cached response
42 77
 - (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge;
43 78
 
44  
-// Should return an NSDictionary of cached headers for the passed request, if it is stored in the cache
45  
-- (NSDictionary *)cachedHeadersForRequest:(ASIHTTPRequest *)request;
  79
+// Removes cached data for a particular url
  80
+- (void)removeCachedDataForURL:(NSURL *)url;
  81
+
  82
+// Should return an NSDictionary of cached headers for the passed URL, if it is stored in the cache
  83
+- (NSDictionary *)cachedResponseHeadersForURL:(NSURL *)url;
46 84
 
47  
-// Should return the cached body of a response for the passed request, if it is stored in the cache
48  
-- (NSData *)cachedResponseDataForRequest:(ASIHTTPRequest *)request;
  85
+// Should return the cached body of a response for the passed URL, if it is stored in the cache
  86
+- (NSData *)cachedResponseDataForURL:(NSURL *)url;
49 87
 
50  
-// Same as the above, but returns a path to the cached response body instead
51  
-- (NSString *)pathToCachedResponseDataForRequest:(ASIHTTPRequest *)request;
  88
+// Returns a path to the cached response data, if it exists
  89
+- (NSString *)pathToCachedResponseDataForURL:(NSURL *)url;
  90
+
  91
+// Returns a path to the cached response headers, if they url
  92
+- (NSString *)pathToCachedResponseHeadersForURL:(NSURL *)url;
  93
+
  94
+// Returns the location to use to store cached response headers for a particular request
  95
+- (NSString *)pathToStoreCachedResponseHeadersForRequest:(ASIHTTPRequest *)request;
  96
+
  97
+// Returns the location to use to store a cached response body for a particular request
  98
+- (NSString *)pathToStoreCachedResponseDataForRequest:(ASIHTTPRequest *)request;
52 99
 
53 100
 // Clear cached data stored for the passed storage policy
54 101
 - (void)clearCachedResponsesForStoragePolicy:(ASICacheStoragePolicy)cachePolicy;
  102
+
55 103
 @end
42  Externals/ASIHTTPRequest/ASIDataCompressor.h
... ...
@@ -0,0 +1,42 @@
  1
+//
  2
+//  ASIDataCompressor.h
  3
+//  Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4
+//
  5
+//  Created by Ben Copsey on 17/08/2010.
  6
+//  Copyright 2010 All-Seeing Interactive. All rights reserved.
  7
+//
  8
+
  9
+// This is a helper class used by ASIHTTPRequest to handle deflating (compressing) data in memory and on disk
  10
+// You may also find it helpful if you need to deflate data and files yourself - see the class methods below
  11
+// Most of the zlib stuff is based on the sample code by Mark Adler available at http://zlib.net
  12
+
  13
+#import <Foundation/Foundation.h>
  14
+#import <zlib.h>
  15
+
  16
+@interface ASIDataCompressor : NSObject {
  17
+	BOOL streamReady;
  18
+	z_stream zStream;
  19
+}
  20
+
  21
+// Convenience constructor will call setupStream for you
  22
++ (id)compressor;
  23
+
  24
+// Compress the passed chunk of data
  25
+// Passing YES for shouldFinish will finalize the deflated data - you must pass YES when you are on the last chunk of data
  26
+- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err shouldFinish:(BOOL)shouldFinish;
  27
+
  28
+// Convenience method - pass it some data, and you'll get deflated data back
  29
++ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err;
  30
+
  31
+// Convenience method - pass it a file containing the data to compress in sourcePath, and it will write deflated data to destinationPath
  32
++ (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err;
  33
+
  34
+// Sets up zlib to handle the inflating. You only need to call this yourself if you aren't using the convenience constructor 'compressor'
  35
+- (NSError *)setupStream;
  36
+
  37
+// Tells zlib to clean up. You need to call this if you need to cancel deflating part way through
  38
+// If deflating finishes or fails, this method will be called automatically
  39
+- (NSError *)closeStream;
  40
+
  41
+@property (assign, readonly) BOOL streamReady;
  42
+@end
219  Externals/ASIHTTPRequest/ASIDataCompressor.m
... ...
@@ -0,0 +1,219 @@
  1
+//
  2
+//  ASIDataCompressor.m
  3
+//  Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4
+//
  5
+//  Created by Ben Copsey on 17/08/2010.
  6
+//  Copyright 2010 All-Seeing Interactive. All rights reserved.
  7
+//
  8
+
  9
+#import "ASIDataCompressor.h"
  10
+#import "ASIHTTPRequest.h"
  11
+
  12
+#define DATA_CHUNK_SIZE 262144 // Deal with gzipped data in 256KB chunks
  13
+#define COMPRESSION_AMOUNT Z_DEFAULT_COMPRESSION
  14
+
  15
+@interface ASIDataCompressor ()
  16
++ (NSError *)deflateErrorWithCode:(int)code;
  17
+@end
  18
+
  19
+@implementation ASIDataCompressor
  20
+
  21
++ (id)compressor
  22
+{
  23
+	ASIDataCompressor *compressor = [[[self alloc] init] autorelease];
  24
+	[compressor setupStream];
  25
+	return compressor;
  26
+}
  27
+
  28
+- (void)dealloc
  29
+{
  30
+	if (streamReady) {
  31
+		[self closeStream];
  32
+	}
  33
+	[super dealloc];
  34
+}
  35
+
  36
+- (NSError *)setupStream
  37
+{
  38
+	if (streamReady) {
  39
+		return nil;
  40
+	}
  41
+	// Setup the inflate stream
  42
+	zStream.zalloc = Z_NULL;
  43
+	zStream.zfree = Z_NULL;
  44
+	zStream.opaque = Z_NULL;
  45
+	zStream.avail_in = 0;
  46
+	zStream.next_in = 0;
  47
+	int status = deflateInit2(&zStream, COMPRESSION_AMOUNT, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
  48
+	if (status != Z_OK) {
  49
+		return [[self class] deflateErrorWithCode:status];
  50
+	}
  51
+	streamReady = YES;
  52
+	return nil;
  53
+}
  54
+
  55
+- (NSError *)closeStream
  56
+{
  57
+	if (!streamReady) {
  58
+		return nil;
  59
+	}
  60
+	// Close the deflate stream
  61
+	streamReady = NO;
  62
+	int status = deflateEnd(&zStream);
  63
+	if (status != Z_OK) {
  64
+		return [[self class] deflateErrorWithCode:status];
  65
+	}
  66
+	return nil;
  67
+}
  68
+
  69
+- (NSData *)compressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err shouldFinish:(BOOL)shouldFinish
  70
+{
  71
+	if (length == 0) return nil;
  72
+	
  73
+	NSUInteger halfLength = length/2;
  74
+	
  75
+	// We'll take a guess that the compressed data will fit in half the size of the original (ie the max to compress at once is half DATA_CHUNK_SIZE), if not, we'll increase it below
  76
+	NSMutableData *outputData = [NSMutableData dataWithLength:length/2]; 
  77
+	
  78
+	int status;
  79
+	
  80
+	zStream.next_in = bytes;
  81
+	zStream.avail_in = (unsigned int)length;
  82
+	zStream.avail_out = 0;
  83
+
  84
+	NSInteger bytesProcessedAlready = zStream.total_out;
  85
+	while (zStream.avail_out == 0) {
  86
+		
  87
+		if (zStream.total_out-bytesProcessedAlready >= [outputData length]) {
  88
+			[outputData increaseLengthBy:halfLength];
  89
+		}
  90
+		
  91
+		zStream.next_out = [outputData mutableBytes] + zStream.total_out-bytesProcessedAlready;
  92
+		zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready));
  93
+		status = deflate(&zStream, shouldFinish ? Z_FINISH : Z_NO_FLUSH);
  94
+		
  95
+		if (status == Z_STREAM_END) {
  96
+			break;
  97
+		} else if (status != Z_OK) {
  98
+			if (err) {
  99
+				*err = [[self class] deflateErrorWithCode:status];
  100
+			}
  101
+			return NO;
  102
+		}
  103
+	}
  104
+
  105
+	// Set real length
  106
+	[outputData setLength: zStream.total_out-bytesProcessedAlready];
  107
+	return outputData;
  108
+}
  109
+
  110
+
  111
++ (NSData *)compressData:(NSData*)uncompressedData error:(NSError **)err
  112
+{
  113
+	NSError *theError = nil;
  114
+	NSData *outputData = [[ASIDataCompressor compressor] compressBytes:(Bytef *)[uncompressedData bytes] length:[uncompressedData length] error:&theError shouldFinish:YES];
  115
+	if (theError) {
  116
+		if (err) {
  117
+			*err = theError;
  118
+		}
  119
+		return nil;
  120
+	}
  121
+	return outputData;
  122
+}
  123
+
  124
+
  125
+
  126
++ (BOOL)compressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err
  127
+{
  128
+	NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
  129
+
  130
+	// Create an empty file at the destination path
  131
+	if (![fileManager createFileAtPath:destinationPath contents:[NSData data] attributes:nil]) {
  132
+		if (err) {
  133
+			*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were to create a file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,nil]];
  134
+		}
  135
+		return NO;
  136
+	}
  137
+	
  138
+	// Ensure the source file exists
  139
+	if (![fileManager fileExistsAtPath:sourcePath]) {
  140
+		if (err) {
  141
+			*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed the file does not exist",sourcePath],NSLocalizedDescriptionKey,nil]];
  142
+		}
  143
+		return NO;
  144
+	}
  145
+	
  146
+	UInt8 inputData[DATA_CHUNK_SIZE];
  147
+	NSData *outputData;
  148
+	NSInteger readLength;
  149
+	NSError *theError = nil;
  150
+	
  151
+	ASIDataCompressor *compressor = [ASIDataCompressor compressor];
  152
+	
  153
+	NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath];
  154
+	[inputStream open];
  155
+	NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:destinationPath append:NO];
  156
+	[outputStream open];
  157
+	
  158
+    while ([compressor streamReady]) {
  159
+		
  160
+		// Read some data from the file
  161
+		readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE];
  162
+
  163
+		// Make sure nothing went wrong
  164
+		if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
  165
+			if (err) {
  166
+				*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]];
  167
+			}
  168
+			[compressor closeStream];
  169
+			return NO;
  170
+		}
  171
+		// Have we reached the end of the input data?
  172
+		if (!readLength) {
  173
+			break;
  174
+		}
  175
+		
  176
+		// Attempt to deflate the chunk of data
  177
+		outputData = [compressor compressBytes:inputData length:readLength error:&theError shouldFinish:readLength < DATA_CHUNK_SIZE ];
  178
+		if (theError) {
  179
+			if (err) {
  180
+				*err = theError;
  181
+			}
  182
+			[compressor closeStream];
  183
+			return NO;
  184
+		}
  185
+		
  186
+		// Write the deflated data out to the destination file
  187
+		[outputStream write:[outputData bytes] maxLength:[outputData length]];
  188
+		
  189
+		// Make sure nothing went wrong
  190
+		if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
  191
+			if (err) {
  192
+				*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of %@ failed because we were unable to write to the destination data file at &@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]];
  193
+            }
  194
+			[compressor closeStream];
  195
+			return NO;
  196
+		}
  197
+		
  198
+    }
  199
+	[inputStream close];
  200
+	[outputStream close];
  201
+
  202
+	NSError *error = [compressor closeStream];
  203
+	if (error) {
  204
+		if (err) {
  205
+			*err = error;
  206
+		}
  207
+		return NO;
  208
+	}
  209
+
  210
+	return YES;
  211
+}
  212
+
  213
++ (NSError *)deflateErrorWithCode:(int)code
  214
+{
  215
+	return [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Compression of data failed with code %hi",code],NSLocalizedDescriptionKey,nil]];
  216
+}
  217
+
  218
+@synthesize streamReady;
  219
+@end
41  Externals/ASIHTTPRequest/ASIDataDecompressor.h
... ...
@@ -0,0 +1,41 @@
  1
+//
  2
+//  ASIDataDecompressor.h
  3
+//  Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4
+//
  5
+//  Created by Ben Copsey on 17/08/2010.
  6
+//  Copyright 2010 All-Seeing Interactive. All rights reserved.
  7
+//
  8
+
  9
+// This is a helper class used by ASIHTTPRequest to handle inflating (decompressing) data in memory and on disk
  10
+// You may also find it helpful if you need to inflate data and files yourself - see the class methods below
  11
+// Most of the zlib stuff is based on the sample code by Mark Adler available at http://zlib.net
  12
+
  13
+#import <Foundation/Foundation.h>
  14
+#import <zlib.h>
  15
+
  16
+@interface ASIDataDecompressor : NSObject {
  17
+	BOOL streamReady;
  18
+	z_stream zStream;
  19
+}
  20
+
  21
+// Convenience constructor will call setupStream for you
  22
++ (id)decompressor;
  23
+
  24
+// Uncompress the passed chunk of data
  25
+- (NSData *)uncompressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err;
  26
+
  27
+// Convenience method - pass it some deflated data, and you'll get inflated data back
  28
++ (NSData *)uncompressData:(NSData*)compressedData error:(NSError **)err;
  29
+
  30
+// Convenience method - pass it a file containing deflated data in sourcePath, and it will write inflated data to destinationPath
  31
++ (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err;
  32
+
  33
+// Sets up zlib to handle the inflating. You only need to call this yourself if you aren't using the convenience constructor 'decompressor'
  34
+- (NSError *)setupStream;
  35
+
  36
+// Tells zlib to clean up. You need to call this if you need to cancel inflating part way through
  37
+// If inflating finishes or fails, this method will be called automatically
  38
+- (NSError *)closeStream;
  39
+
  40
+@property (assign, readonly) BOOL streamReady;
  41
+@end
218  Externals/ASIHTTPRequest/ASIDataDecompressor.m
... ...
@@ -0,0 +1,218 @@
  1
+//
  2
+//  ASIDataDecompressor.m
  3
+//  Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4
+//
  5
+//  Created by Ben Copsey on 17/08/2010.
  6
+//  Copyright 2010 All-Seeing Interactive. All rights reserved.
  7
+//
  8
+
  9
+#import "ASIDataDecompressor.h"
  10
+#import "ASIHTTPRequest.h"
  11
+
  12
+#define DATA_CHUNK_SIZE 262144 // Deal with gzipped data in 256KB chunks
  13
+
  14
+@interface ASIDataDecompressor ()
  15
++ (NSError *)inflateErrorWithCode:(int)code;
  16
+@end;
  17
+
  18
+@implementation ASIDataDecompressor
  19
+
  20
++ (id)decompressor
  21
+{
  22
+	ASIDataDecompressor *decompressor = [[[self alloc] init] autorelease];
  23
+	[decompressor setupStream];
  24
+	return decompressor;
  25
+}
  26
+
  27
+- (void)dealloc
  28
+{
  29
+	if (streamReady) {
  30
+		[self closeStream];
  31
+	}
  32
+	[super dealloc];
  33
+}
  34
+
  35
+- (NSError *)setupStream
  36
+{
  37
+	if (streamReady) {
  38
+		return nil;
  39
+	}
  40
+	// Setup the inflate stream
  41
+	zStream.zalloc = Z_NULL;
  42
+	zStream.zfree = Z_NULL;
  43
+	zStream.opaque = Z_NULL;
  44
+	zStream.avail_in = 0;
  45
+	zStream.next_in = 0;
  46
+	int status = inflateInit2(&zStream, (15+32));
  47
+	if (status != Z_OK) {
  48
+		return [[self class] inflateErrorWithCode:status];
  49
+	}
  50
+	streamReady = YES;
  51
+	return nil;
  52
+}
  53
+
  54
+- (NSError *)closeStream
  55
+{
  56
+	if (!streamReady) {
  57
+		return nil;
  58
+	}
  59
+	// Close the inflate stream
  60
+	streamReady = NO;
  61
+	int status = inflateEnd(&zStream);
  62
+	if (status != Z_OK) {
  63
+		return [[self class] inflateErrorWithCode:status];
  64
+	}
  65
+	return nil;
  66
+}
  67
+
  68
+- (NSData *)uncompressBytes:(Bytef *)bytes length:(NSUInteger)length error:(NSError **)err
  69
+{
  70
+	if (length == 0) return nil;
  71
+	
  72
+	NSUInteger halfLength = length/2;
  73
+	NSMutableData *outputData = [NSMutableData dataWithLength:length+halfLength];
  74
+
  75
+	int status;
  76
+	
  77
+	zStream.next_in = bytes;
  78
+	zStream.avail_in = (unsigned int)length;
  79
+	zStream.avail_out = 0;
  80
+	
  81
+	NSInteger bytesProcessedAlready = zStream.total_out;
  82
+	while (zStream.avail_in != 0) {
  83
+		
  84
+		if (zStream.total_out-bytesProcessedAlready >= [outputData length]) {
  85
+			[outputData increaseLengthBy:halfLength];
  86
+		}
  87
+		
  88
+		zStream.next_out = [outputData mutableBytes] + zStream.total_out-bytesProcessedAlready;
  89
+		zStream.avail_out = (unsigned int)([outputData length] - (zStream.total_out-bytesProcessedAlready));
  90
+		
  91
+		status = inflate(&zStream, Z_NO_FLUSH);
  92
+		
  93
+		if (status == Z_STREAM_END) {
  94
+			break;
  95
+		} else if (status != Z_OK) {
  96
+			if (err) {
  97
+				*err = [[self class] inflateErrorWithCode:status];
  98
+			}
  99
+			return nil;
  100
+		}
  101
+	}
  102
+	
  103
+	// Set real length
  104
+	[outputData setLength: zStream.total_out-bytesProcessedAlready];
  105
+	return outputData;
  106
+}
  107
+
  108
+
  109
++ (NSData *)uncompressData:(NSData*)compressedData error:(NSError **)err
  110
+{
  111
+	NSError *theError = nil;
  112
+	NSData *outputData = [[ASIDataDecompressor decompressor] uncompressBytes:(Bytef *)[compressedData bytes] length:[compressedData length] error:&theError];
  113
+	if (theError) {
  114
+		if (err) {
  115
+			*err = theError;
  116
+		}
  117
+		return nil;
  118
+	}
  119
+	return outputData;
  120
+}
  121
+
  122
++ (BOOL)uncompressDataFromFile:(NSString *)sourcePath toFile:(NSString *)destinationPath error:(NSError **)err
  123
+{
  124
+	NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
  125
+
  126
+	// Create an empty file at the destination path
  127
+	if (![fileManager createFileAtPath:destinationPath contents:[NSData data] attributes:nil]) {
  128
+		if (err) {
  129
+			*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were to create a file at %@",sourcePath,destinationPath],NSLocalizedDescriptionKey,nil]];
  130
+		}
  131
+		return NO;
  132
+	}
  133
+	
  134
+	// Ensure the source file exists
  135
+	if (![fileManager fileExistsAtPath:sourcePath]) {
  136
+		if (err) {
  137
+			*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed the file does not exist",sourcePath],NSLocalizedDescriptionKey,nil]];
  138
+		}
  139
+		return NO;
  140
+	}
  141
+	
  142
+	UInt8 inputData[DATA_CHUNK_SIZE];
  143
+	NSData *outputData;
  144
+	NSInteger readLength;
  145
+	NSError *theError = nil;
  146
+	
  147
+
  148
+	ASIDataDecompressor *decompressor = [ASIDataDecompressor decompressor];
  149
+
  150
+	NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath];
  151
+	[inputStream open];
  152
+	NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:destinationPath append:NO];
  153
+	[outputStream open];
  154
+	
  155
+    while ([decompressor streamReady]) {
  156
+		
  157
+		// Read some data from the file
  158
+		readLength = [inputStream read:inputData maxLength:DATA_CHUNK_SIZE]; 
  159
+		
  160
+		// Make sure nothing went wrong
  161
+		if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
  162
+			if (err) {
  163
+				*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to read from the source data file",sourcePath],NSLocalizedDescriptionKey,[inputStream streamError],NSUnderlyingErrorKey,nil]];
  164
+			}
  165
+            [decompressor closeStream];
  166
+			return NO;
  167
+		}
  168
+		// Have we reached the end of the input data?
  169
+		if (!readLength) {
  170
+			break;
  171
+		}
  172
+
  173
+		// Attempt to inflate the chunk of data
  174
+		outputData = [decompressor uncompressBytes:inputData length:readLength error:&theError];
  175
+		if (theError) {
  176
+			if (err) {
  177
+				*err = theError;
  178
+			}
  179
+			[decompressor closeStream];
  180
+			return NO;
  181
+		}
  182
+		
  183
+		// Write the inflated data out to the destination file
  184
+		[outputStream write:[outputData bytes] maxLength:[outputData length]];
  185
+		
  186
+		// Make sure nothing went wrong
  187
+		if ([inputStream streamStatus] == NSStreamEventErrorOccurred) {
  188
+			if (err) {
  189
+				*err = [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of %@ failed because we were unable to write to the destination data file at &@",sourcePath,destinationPath],NSLocalizedDescriptionKey,[outputStream streamError],NSUnderlyingErrorKey,nil]];
  190
+            }
  191
+			[decompressor closeStream];
  192
+			return NO;
  193
+		}
  194
+		
  195
+    }
  196
+	
  197
+	[inputStream close];
  198
+	[outputStream close];
  199
+
  200
+	NSError *error = [decompressor closeStream];
  201
+	if (error) {
  202
+		if (err) {
  203
+			*err = error;
  204
+		}
  205
+		return NO;
  206
+	}
  207
+
  208
+	return YES;
  209
+}
  210
+
  211
+
  212
++ (NSError *)inflateErrorWithCode:(int)code
  213
+{
  214
+	return [NSError errorWithDomain:NetworkRequestErrorDomain code:ASICompressionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Decompression of data failed with code %hi",code],NSLocalizedDescriptionKey,nil]];
  215
+}
  216
+
  217
+@synthesize streamReady;
  218
+@end
42  Externals/ASIHTTPRequest/ASIDownloadCache.h
... ...
@@ -0,0 +1,42 @@
  1
+//
  2
+//  ASIDownloadCache.h
  3
+//  Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4
+//
  5
+//  Created by Ben Copsey on 01/05/2010.
  6
+//  Copyright 2010 All-Seeing Interactive. All rights reserved.
  7
+//
  8
+
  9
+#import <Foundation/Foundation.h>
  10
+#import "ASICacheDelegate.h"
  11
+
  12
+@interface ASIDownloadCache : NSObject <ASICacheDelegate> {
  13
+	
  14
+	// The default cache policy for this cache
  15
+	// Requests that store data in the cache will use this cache policy if their cache policy is set to ASIUseDefaultCachePolicy
  16
+	// Defaults to ASIAskServerIfModifiedWhenStaleCachePolicy
  17
+	ASICachePolicy defaultCachePolicy;
  18
+	
  19
+	// The directory in which cached data will be stored
  20
+	// Defaults to a directory called 'ASIHTTPRequestCache' in the temporary directory
  21
+	NSString *storagePath;
  22
+	
  23
+	// Mediates access to the cache
  24
+	NSRecursiveLock *accessLock;
  25
+	
  26
+	// When YES, the cache will look for cache-control / pragma: no-cache headers, and won't reuse store responses if it finds them
  27
+	BOOL shouldRespectCacheControlHeaders;
  28
+}
  29
+
  30
+// Returns a static instance of an ASIDownloadCache
  31
+// In most circumstances, it will make sense to use this as a global cache, rather than creating your own cache
  32
+// To make ASIHTTPRequests use it automatically, use [ASIHTTPRequest setDefaultCache:[ASIDownloadCache sharedCache]];
  33
++ (id)sharedCache;
  34
+
  35
+// A helper function that determines if the server has requested data should not be cached by looking at the request's response headers
  36
++ (BOOL)serverAllowsResponseCachingForRequest:(ASIHTTPRequest *)request;
  37
+
  38
+@property (assign, nonatomic) ASICachePolicy defaultCachePolicy;
  39
+@property (retain, nonatomic) NSString *storagePath;
  40
+@property (retain) NSRecursiveLock *accessLock;
  41
+@property (assign) BOOL shouldRespectCacheControlHeaders;
  42
+@end
504  Externals/ASIHTTPRequest/ASIDownloadCache.m
... ...
@@ -0,0 +1,504 @@
  1
+//
  2
+//  ASIDownloadCache.m
  3
+//  Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4
+//
  5
+//  Created by Ben Copsey on 01/05/2010.
  6
+//  Copyright 2010 All-Seeing Interactive. All rights reserved.
  7
+//
  8
+
  9
+#import "ASIDownloadCache.h"
  10
+#import "ASIHTTPRequest.h"
  11
+#import <CommonCrypto/CommonHMAC.h>
  12
+
  13
+static ASIDownloadCache *sharedCache = nil;
  14
+
  15
+static NSString *sessionCacheFolder = @"SessionStore";
  16
+static NSString *permanentCacheFolder = @"PermanentStore";
  17
+
  18
+@interface ASIDownloadCache ()
  19
++ (NSString *)keyForURL:(NSURL *)url;
  20
+- (NSString *)pathToFile:(NSString *)file;
  21
+@end
  22
+
  23
+@implementation ASIDownloadCache
  24
+
  25
+- (id)init
  26
+{
  27
+	self = [super init];
  28
+	[self setShouldRespectCacheControlHeaders:YES];
  29
+	[self setDefaultCachePolicy:ASIUseDefaultCachePolicy];
  30
+	[self setAccessLock:[[[NSRecursiveLock alloc] init] autorelease]];
  31
+	return self;
  32
+}
  33
+
  34
++ (id)sharedCache
  35
+{
  36
+	if (!sharedCache) {
  37
+		sharedCache = [[self alloc] init];
  38
+		[sharedCache setStoragePath:[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"ASIHTTPRequestCache"]];
  39
+
  40
+	}
  41
+	return sharedCache;
  42
+}
  43
+
  44
+- (void)dealloc
  45
+{
  46
+	[storagePath release];
  47
+	[accessLock release];
  48
+	[super dealloc];
  49
+}
  50
+
  51
+- (NSString *)storagePath
  52
+{
  53
+	[[self accessLock] lock];
  54
+	NSString *p = [[storagePath retain] autorelease];
  55
+	[[self accessLock] unlock];
  56
+	return p;
  57
+}
  58
+
  59
+
  60
+- (void)setStoragePath:(NSString *)path
  61
+{
  62
+	[[self accessLock] lock];
  63
+	[self clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
  64
+	[storagePath release];
  65
+	storagePath = [path retain];
  66
+
  67
+	NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
  68
+
  69
+	BOOL isDirectory = NO;
  70
+	NSArray *directories = [NSArray arrayWithObjects:path,[path stringByAppendingPathComponent:sessionCacheFolder],[path stringByAppendingPathComponent:permanentCacheFolder],nil];
  71
+	for (NSString *directory in directories) {
  72
+		BOOL exists = [fileManager fileExistsAtPath:directory isDirectory:&isDirectory];
  73
+		if (exists && !isDirectory) {
  74
+			[[self accessLock] unlock];
  75
+			[NSException raise:@"FileExistsAtCachePath" format:@"Cannot create a directory for the cache at '%@', because a file already exists",directory];
  76
+		} else if (!exists) {
  77
+			[fileManager createDirectoryAtPath:directory withIntermediateDirectories:NO attributes:nil error:nil];
  78
+			if (![fileManager fileExistsAtPath:directory]) {
  79
+				[[self accessLock] unlock];
  80
+				[NSException raise:@"FailedToCreateCacheDirectory" format:@"Failed to create a directory for the cache at '%@'",directory];
  81
+			}
  82
+		}
  83
+	}
  84
+	[self clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
  85
+	[[self accessLock] unlock];
  86
+}
  87
+
  88
+- (void)updateExpiryForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
  89
+{
  90
+	NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request];
  91
+	NSMutableDictionary *cachedHeaders = [NSMutableDictionary dictionaryWithContentsOfFile:headerPath];
  92
+	if (!cachedHeaders) {
  93
+		return;
  94
+	}
  95
+	NSDate *expires = [self expiryDateForRequest:request maxAge:maxAge];
  96
+	if (!expires) {
  97
+		return;
  98
+	}
  99
+	[cachedHeaders setObject:[NSNumber numberWithDouble:[expires timeIntervalSince1970]] forKey:@"X-ASIHTTPRequest-Expires"];
  100
+	[cachedHeaders writeToFile:headerPath atomically:NO];
  101
+}
  102
+
  103
+- (NSDate *)expiryDateForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
  104
+{
  105
+	NSMutableDictionary *responseHeaders = [NSMutableDictionary dictionaryWithDictionary:[request responseHeaders]];
  106
+
  107
+	// If we weren't given a custom max-age, lets look for one in the response headers
  108
+	if (!maxAge) {
  109
+		NSString *cacheControl = [[responseHeaders objectForKey:@"Cache-Control"] lowercaseString];
  110
+		if (cacheControl) {
  111
+			NSScanner *scanner = [NSScanner scannerWithString:cacheControl];
  112
+			[scanner scanUpToString:@"max-age" intoString:NULL];
  113
+			if ([scanner scanString:@"max-age" intoString:NULL]) {
  114
+				[scanner scanString:@"=" intoString:NULL];
  115
+				[scanner scanDouble:&maxAge];
  116
+			}
  117
+		}
  118
+	}
  119
+
  120
+	// RFC 2612 says max-age must override any Expires header
  121
+	if (maxAge) {
  122
+		return [[NSDate date] addTimeInterval:maxAge];
  123
+	} else {
  124
+		NSString *expires = [responseHeaders objectForKey:@"Expires"];
  125
+		if (expires) {
  126
+			return [ASIHTTPRequest dateFromRFC1123String:expires];
  127
+		}
  128
+	}
  129
+	return nil;
  130
+}
  131
+
  132
+- (void)storeResponseForRequest:(ASIHTTPRequest *)request maxAge:(NSTimeInterval)maxAge
  133
+{
  134
+	[[self accessLock] lock];
  135
+
  136
+	if ([request error] || ![request responseHeaders] || ([request cachePolicy] & ASIDoNotWriteToCacheCachePolicy)) {
  137
+		[[self accessLock] unlock];
  138
+		return;
  139
+	}
  140
+
  141
+	// We only cache 200/OK or redirect reponses (redirect responses are cached so the cache works better with no internet connection)
  142
+	int responseCode = [request responseStatusCode];
  143
+	if (responseCode != 200 && responseCode != 301 && responseCode != 302 && responseCode != 303 && responseCode != 307) {
  144
+		[[self accessLock] unlock];
  145
+		return;
  146
+	}
  147
+
  148
+	if ([self shouldRespectCacheControlHeaders] && ![[self class] serverAllowsResponseCachingForRequest:request]) {
  149
+		[[self accessLock] unlock];
  150
+		return;
  151
+	}
  152
+
  153
+	NSString *headerPath = [self pathToStoreCachedResponseHeadersForRequest:request];
  154
+	NSString *dataPath = [self pathToStoreCachedResponseDataForRequest:request];
  155
+
  156
+	NSMutableDictionary *responseHeaders = [NSMutableDictionary dictionaryWithDictionary:[request responseHeaders]];
  157
+	if ([request isResponseCompressed]) {
  158
+		[responseHeaders removeObjectForKey:@"Content-Encoding"];
  159
+	}
  160
+
  161
+	// Create a special 'X-ASIHTTPRequest-Expires' header
  162
+	// This is what we use for deciding if cached data is current, rather than parsing the expires / max-age headers individually each time
  163
+	// We store this as a timestamp to make reading it easier as NSDateFormatter is quite expensive
  164
+
  165
+	NSDate *expires = [self expiryDateForRequest:request maxAge:maxAge];
  166