adium 2352:c36fe87b1679: Create an abstract CFNetwork uploader t...
commits at adium.im
commits at adium.im
Thu May 28 01:45:19 UTC 2009
details: http://hg.adium.im/adium/rev/c36fe87b1679
revision: 2352:c36fe87b1679
author: Zachary West <zacw at adium.im>
date: Wed May 27 21:45:04 2009 -0400
Create an abstract CFNetwork uploader to provide progress information to the image uploader plugin.
diffstat:
Adium.xcodeproj/project.pbxproj | 8 +
Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.h | 44 +
Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.m | 298 ++++++++++
Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m | 6 +
Plugins/Image Uploading Plugin/AIPicImImageUploader.h | 8 +-
Plugins/Image Uploading Plugin/AIPicImImageUploader.m | 91 +-
6 files changed, 400 insertions(+), 55 deletions(-)
diffs (566 lines):
diff -r eae1a4069cee -r c36fe87b1679 Adium.xcodeproj/project.pbxproj
--- a/Adium.xcodeproj/project.pbxproj Wed May 27 18:56:25 2009 -0400
+++ b/Adium.xcodeproj/project.pbxproj Wed May 27 21:45:04 2009 -0400
@@ -145,6 +145,8 @@
11945F400F7935A2002A54B3 /* Ignore.png in Resources */ = {isa = PBXBuildFile; fileRef = 11945F3F0F7935A2002A54B3 /* Ignore.png */; };
11A2F10A0FC8FC1A00C3F05C /* AIMessageAlertsAdvancedPreferences.nib in Resources */ = {isa = PBXBuildFile; fileRef = 11A2F1090FC8FC1A00C3F05C /* AIMessageAlertsAdvancedPreferences.nib */; };
11A2F1220FC8FC9D00C3F05C /* AIMessageAlertsAdvancedPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 11A2F1210FC8FC9D00C3F05C /* AIMessageAlertsAdvancedPreferences.m */; };
+ 11AA10130FCE0969003908B6 /* AIProgressDataUploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 11AA10110FCE0969003908B6 /* AIProgressDataUploader.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 11AA10140FCE0969003908B6 /* AIProgressDataUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 11AA10120FCE0969003908B6 /* AIProgressDataUploader.m */; };
11AA1EFA0BCAE9C3003DDA66 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11AA1EF90BCAE9C3003DDA66 /* Quartz.framework */; };
11AE53610F68CE3300BE8077 /* AIPurpleOscarAccountViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 11AE52000F68CA5000BE8077 /* AIPurpleOscarAccountViewController.h */; };
11AE53620F68CE3300BE8077 /* AIPurpleOscarAccountViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 11AE52010F68CA5000BE8077 /* AIPurpleOscarAccountViewController.m */; };
@@ -1883,6 +1885,8 @@
11A2F1090FC8FC1A00C3F05C /* AIMessageAlertsAdvancedPreferences.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = AIMessageAlertsAdvancedPreferences.nib; path = Resources/AIMessageAlertsAdvancedPreferences.nib; sourceTree = "<group>"; };
11A2F1200FC8FC9D00C3F05C /* AIMessageAlertsAdvancedPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIMessageAlertsAdvancedPreferences.h; path = Source/AIMessageAlertsAdvancedPreferences.h; sourceTree = "<group>"; };
11A2F1210FC8FC9D00C3F05C /* AIMessageAlertsAdvancedPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIMessageAlertsAdvancedPreferences.m; path = Source/AIMessageAlertsAdvancedPreferences.m; sourceTree = "<group>"; };
+ 11AA10110FCE0969003908B6 /* AIProgressDataUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIProgressDataUploader.h; path = "Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.h"; sourceTree = "<group>"; };
+ 11AA10120FCE0969003908B6 /* AIProgressDataUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIProgressDataUploader.m; path = "Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.m"; sourceTree = "<group>"; };
11AA1EF90BCAE9C3003DDA66 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = /System/Library/Frameworks/Quartz.framework; sourceTree = "<absolute>"; };
11AE52000F68CA5000BE8077 /* AIPurpleOscarAccountViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIPurpleOscarAccountViewController.h; path = "Plugins/Purple Service/AIPurpleOscarAccountViewController.h"; sourceTree = "<group>"; };
11AE52010F68CA5000BE8077 /* AIPurpleOscarAccountViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIPurpleOscarAccountViewController.m; path = "Plugins/Purple Service/AIPurpleOscarAccountViewController.m"; sourceTree = "<group>"; };
@@ -7393,6 +7397,8 @@
6334FF050F9C14BF003C77A9 /* Fun New Classes */ = {
isa = PBXGroup;
children = (
+ 11AA10110FCE0969003908B6 /* AIProgressDataUploader.h */,
+ 11AA10120FCE0969003908B6 /* AIProgressDataUploader.m */,
6334FF2D0F9C14BF003C77A9 /* Tooltip Display */,
6334FF2A0F9C14BF003C77A9 /* System network and proxy settings */,
6334FF270F9C14BF003C77A9 /* Smooth tracking tooltips */,
@@ -9165,6 +9171,7 @@
633400D00F9C14E0003C77A9 /* AIUtilities.framework_Prefix.pch in Headers */,
634DC5570F9C3B2F007B6479 /* AIStringUtilities.h in Headers */,
63BB1CB90F9EDD2B00424B80 /* AISharedWriterQueue.h in Headers */,
+ 11AA10130FCE0969003908B6 /* AIProgressDataUploader.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -10662,6 +10669,7 @@
633400C60F9C14C2003C77A9 /* AIWindowControllerAdditions.m in Sources */,
633400C80F9C14C2003C77A9 /* AIPasteboardAdditions.m in Sources */,
63BB1CC90F9EDDB600424B80 /* AISharedWriterQueue.m in Sources */,
+ 11AA10140FCE0969003908B6 /* AIProgressDataUploader.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff -r eae1a4069cee -r c36fe87b1679 Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.h Wed May 27 21:45:04 2009 -0400
@@ -0,0 +1,44 @@
+//
+// AIProgressDataUploader.h
+// Adium
+//
+// Created by Zachary West on 2009-05-27.
+//
+// Interpreted from the source of OFPOSTRequest at http://objectiveflickr.googlecode.com/svn/trunk/Source/OFPOSTRequest.m
+//
+
+ at protocol AIProgressDataUploaderDelegate;
+
+ at interface AIProgressDataUploader : NSObject {
+ NSData *uploadData;
+ NSURL *url;
+ NSDictionary *headers;
+ id <AIProgressDataUploaderDelegate> delegate;
+ id context;
+
+ CFReadStreamRef stream;
+ NSMutableData *returnedData;
+
+ NSInteger totalSize;
+ NSInteger bytesSent;
+
+ NSTimer *timeoutTimer;
+ NSTimer *periodicTimer;
+}
+
++ (id)dataUploaderWithData:(NSData *)uploadData
+ URL:(NSURL *)url
+ headers:(NSDictionary *)headers
+ delegate:(id <AIProgressDataUploaderDelegate>)delegate
+ context:(id)context;
+
+- (void)upload;
+- (void)cancel;
+
+ at end
+
+ at protocol AIProgressDataUploaderDelegate
+- (void)updateUploadPercent:(CGFloat)percent context:(id)context;
+- (void)uploadCompleted:(id)context result:(NSData *)result;
+- (void)uploadFailed:(id)context;
+ at end
diff -r eae1a4069cee -r c36fe87b1679 Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Frameworks/AIUtilities Framework/Source/AIProgressDataUploader.m Wed May 27 21:45:04 2009 -0400
@@ -0,0 +1,298 @@
+//
+// AIProgressDataUploader.m
+// Adium
+//
+// Created by Zachary West on 2009-05-27.
+//
+// Interpreted from the source of OFPOSTRequest at http://objectiveflickr.googlecode.com/svn/trunk/Source/OFPOSTRequest.m
+//
+
+#import "AIProgressDataUploader.h"
+
+#define BUFFER_SIZE 1024
+#define UPDATE_INTERVAL 0.5
+#define TIMEOUT_INTERVAL 30.0
+
+ at interface AIProgressDataUploader()
+- (id)initWithData:(NSData *)inUploadData
+ URL:(NSURL *)inUrl
+ headers:(NSDictionary *)inHeaders
+ delegate:(id <AIProgressDataUploaderDelegate>)inDelegate
+ context:(id)inContext;
+
+// Timers
+- (void)timeoutDidOccur;
+- (void)updateProgress;
+
+// Callbacks
+- (void)streamDidOpen;
+- (void)errorDidOccur;
+- (void)uploadSucceeded;
+- (void)bytesAvailable;
+ at end
+
+static void AIProgressDataUploaderCallback(CFReadStreamRef callbackStream,
+ CFStreamEventType type,
+ void *info);
+
+ at implementation AIProgressDataUploader
+/*!
+ * @brief Create a data uploader.
+ *
+ * @param delegate The delegate
+ * @param context The context for this upload
+ *
+ * Uploading does not begin until -upload is called.
+ */
++ (id)dataUploaderWithData:(NSData *)uploadData
+ URL:(NSURL *)url
+ headers:(NSDictionary *)headers
+ delegate:(id <AIProgressDataUploaderDelegate>)delegate
+ context:(id)context
+{
+ return [[[self alloc] initWithData:uploadData URL:url headers:headers delegate:delegate context:context] autorelease];
+}
+
+- (id)initWithData:(NSData *)inUploadData
+ URL:(NSURL *)inURL
+ headers:(NSDictionary *)inHeaders
+ delegate:(id <AIProgressDataUploaderDelegate>)inDelegate
+ context:(id)inContext
+{
+ if ((self = [super init])) {
+ uploadData = inUploadData;
+ delegate = inDelegate;
+ context = inContext;
+ url = [inURL retain];
+ headers = [inHeaders retain];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [url release];
+ [headers release];
+ [returnedData release];
+
+ [super dealloc];
+}
+
+/*!
+ * @brief Begin the upload.
+ *
+ * Immediately begins the upload.
+ */
+- (void)upload
+{
+ CFHTTPMessageRef httpRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
+ CFSTR("POST"),
+ (CFURLRef)url,
+ kCFHTTPVersion1_1);
+
+ for (NSString *headerKey in headers) {
+ CFHTTPMessageSetHeaderFieldValue(httpRequest,
+ (CFStringRef)headerKey,
+ (CFStringRef)[headers objectForKey:headerKey]);
+ }
+
+ CFHTTPMessageSetBody(httpRequest, (CFDataRef)uploadData);
+
+ stream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, httpRequest);
+
+ CFStreamClientContext streamClientContext = {
+ 0,
+ self,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ BOOL success = YES;
+
+ if (CFReadStreamSetClient(stream,
+ /* flags */ (kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered),
+ /* callback */ AIProgressDataUploaderCallback,
+ /* context */ &streamClientContext)) {
+ CFReadStreamScheduleWithRunLoop(stream,
+ CFRunLoopGetCurrent(),
+ kCFRunLoopCommonModes);
+
+ returnedData = [[NSMutableData alloc] init];
+
+ if (CFReadStreamOpen(stream)) {
+ [self streamDidOpen];
+ } else {
+ success = NO;
+ }
+ } else {
+ success = NO;
+ }
+
+ if (!success) {
+ [delegate uploadFailed:context];
+ }
+
+ CFRelease(httpRequest);
+}
+
+/*!
+ * @brief Cancel the upload.
+ *
+ * Cancels the upload and returns no further status messages to the delegate.
+ */
+- (void)cancel
+{
+ if (!stream) {
+ return;
+ }
+
+ CFReadStreamUnscheduleFromRunLoop(stream,
+ CFRunLoopGetCurrent(),
+ kCFRunLoopCommonModes);
+ CFReadStreamClose(stream);
+
+ [timeoutTimer invalidate]; timeoutTimer = nil;
+ [periodicTimer invalidate]; periodicTimer = nil;
+}
+
+/*!
+ * @brief Stream opened
+ *
+ * Called when the stream is opened.
+ * Sets up our periodic timer to gather the current status of the upload
+ */
+- (void)streamDidOpen
+{
+ totalSize = [uploadData length];
+
+ periodicTimer = [[NSTimer scheduledTimerWithTimeInterval:UPDATE_INTERVAL
+ target:self
+ selector:@selector(updateProgress)
+ userInfo:nil
+ repeats:YES] retain];
+
+ timeoutTimer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:TIMEOUT_INTERVAL]
+ interval:TIMEOUT_INTERVAL
+ target:self
+ selector:@selector(timeoutDidOccur)
+ userInfo:nil
+ repeats:NO];
+}
+
+/*!
+ * @brief Update our progress
+ *
+ * Updates our delegate with our current upload percent.
+ */
+- (void)updateProgress
+{
+ if (!stream) {
+ return;
+ }
+
+ NSInteger bytesWritten;
+ CFNumberRef bytesWrittenProperty = (CFNumberRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPRequestBytesWrittenCount);
+ CFNumberGetValue(bytesWrittenProperty,
+ kCFNumberNSIntegerType,
+ &bytesWritten);
+
+ if (bytesWritten > bytesSent) {
+ bytesSent = bytesWritten;
+
+ [delegate updateUploadPercent:(CGFloat)bytesSent/(CGFloat)totalSize
+ context:context];
+
+ [timeoutTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:TIMEOUT_INTERVAL]];
+ }
+}
+
+/*!
+ * @brief A timeout occured
+ *
+ * We weren't able to upload any more data after a specified duration of time.
+ * Fail cleanly and let our delegate know.
+ */
+- (void)timeoutDidOccur
+{
+ [self cancel];
+ [delegate uploadFailed:context];
+}
+
+/*!
+ * @brief Our callback function
+ *
+ * Handles events which occur during the stream, as specified in the "flags".
+ */
+static void AIProgressDataUploaderCallback(CFReadStreamRef callbackStream,
+ CFStreamEventType type,
+ void *info)
+{
+ AIProgressDataUploader *uploader = (AIProgressDataUploader *)info;
+
+ switch (type) {
+ case kCFStreamEventHasBytesAvailable:
+ [uploader bytesAvailable];
+ break;
+
+ case kCFStreamEventErrorOccurred:
+ [uploader errorDidOccur];
+ break;
+
+ case kCFStreamEventEndEncountered:
+ [uploader uploadSucceeded];
+ break;
+
+ case kCFStreamEventOpenCompleted:
+ case kCFStreamEventNone:
+ case kCFStreamEventCanAcceptBytes:
+ break;
+ }
+}
+
+/*!
+ * @brief Bytes are available
+ *
+ * Gobble up as much from the stream as we can.
+ */
+- (void)bytesAvailable
+{
+ UInt8 buffer[BUFFER_SIZE];
+
+ CFIndex read = CFReadStreamRead(stream,
+ buffer,
+ BUFFER_SIZE);
+
+ if (read) {
+ [returnedData appendBytes:(const void *)buffer
+ length:(NSUInteger)read];
+ }
+}
+
+/*!
+ * @brief An error occured
+ *
+ * Let our delegate know the upload was unsuccessful.
+ */
+- (void)errorDidOccur
+{
+ [delegate uploadFailed:context];
+}
+
+/*!
+ * @brief Upload succeeded
+ *
+ * Let our delegate know the unload was successful.
+ */
+- (void)uploadSucceeded
+{
+ stream = NULL;
+
+ [periodicTimer invalidate]; periodicTimer = nil;
+ [timeoutTimer invalidate]; timeoutTimer = nil;
+
+ [delegate uploadCompleted:context result:returnedData];
+}
+
+ at end
diff -r eae1a4069cee -r c36fe87b1679 Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m
--- a/Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m Wed May 27 18:56:25 2009 -0400
+++ b/Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m Wed May 27 21:45:04 2009 -0400
@@ -48,6 +48,12 @@
return self;
}
+- (void)dealloc
+{
+ NSLog(@"Dealloc");
+ [super dealloc];
+}
+
- (void)windowDidLoad
{
[super windowDidLoad];
diff -r eae1a4069cee -r c36fe87b1679 Plugins/Image Uploading Plugin/AIPicImImageUploader.h
--- a/Plugins/Image Uploading Plugin/AIPicImImageUploader.h Wed May 27 18:56:25 2009 -0400
+++ b/Plugins/Image Uploading Plugin/AIPicImImageUploader.h Wed May 27 21:45:04 2009 -0400
@@ -8,13 +8,15 @@
#import "AIImageUploaderPlugin.h"
- at interface AIPicImImageUploader : NSObject <AIImageUploader> {
+#import <AIUtilities/AIProgressDataUploader.h>
+
+ at interface AIPicImImageUploader : NSObject <AIImageUploader, AIProgressDataUploaderDelegate> {
AIChat *chat;
NSImage *image;
AIImageUploaderPlugin *uploader;
- NSMutableData *receivedData;
- NSURLConnection *connection;
+ AIProgressDataUploader *dataUploader;
+
NSXMLParser *responseParser;
// Parsing
diff -r eae1a4069cee -r c36fe87b1679 Plugins/Image Uploading Plugin/AIPicImImageUploader.m
--- a/Plugins/Image Uploading Plugin/AIPicImImageUploader.m Wed May 27 18:56:25 2009 -0400
+++ b/Plugins/Image Uploading Plugin/AIPicImImageUploader.m Wed May 27 21:45:04 2009 -0400
@@ -10,6 +10,7 @@
#import <Adium/AIInterfaceControllerProtocol.h>
#import <AIUtilities/AIStringAdditions.h>
+#import <AIUtilities/AIProgressDataUploader.h>
#define MULTIPART_FORM_BOUNDARY @"bf5faadd239c17e35f91e6dafe1d2f96"
#define PIC_IM_URL @"http://api.tr.im/api/picim_url.xml"
@@ -53,12 +54,38 @@
[response release];
[responseParser release];
[image release];
- [connection release];
- [receivedData release];
[super dealloc];
}
+#pragma mark Data uploader delegate
+- (void)updateUploadPercent:(CGFloat)percent context:(id)context
+{
+ [uploader updateProgressPercent:percent forChat:chat];
+}
+
+- (void)uploadCompleted:(id)context result:(NSData *)result
+{
+ if (result.length) {
+ response = [[NSMutableDictionary alloc] init];
+
+ responseParser = [[NSXMLParser alloc] initWithData:result];
+
+ [dataUploader release]; dataUploader = nil;
+
+ [responseParser setDelegate:self];
+ [responseParser parse];
+ } else {
+ [uploader errorWithMessage:AILocalizedString(@"Unable to upload", nil) forChat:chat];
+ }
+}
+
+- (void)uploadFailed:(id)context
+{
+ [uploader errorWithMessage:AILocalizedString(@"Unable to upload", nil) forChat:chat];
+ [dataUploader release];
+}
+
#pragma mark Image upload
- (void)uploadImage
@@ -86,60 +113,22 @@
[body appendData:[bitmapImageRep representationUsingType:NSPNGFileType properties:nil]];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", MULTIPART_FORM_BOUNDARY] dataUsingEncoding:NSUTF8StringEncoding]];
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:PIC_IM_URL]];
+ NSDictionary *headers = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"multipart/form-data; boundary=%@", MULTIPART_FORM_BOUNDARY], @"Content-type", nil];
- [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", MULTIPART_FORM_BOUNDARY] forHTTPHeaderField:@"Content-type"];
- [request setHTTPMethod:@"POST"];
- [request setHTTPBody:body];
-
- connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
+ dataUploader = [[AIProgressDataUploader dataUploaderWithData:body
+ URL:[NSURL URLWithString:PIC_IM_URL]
+ headers:headers
+ delegate:self
+ context:nil] retain];
- if (connection) {
- receivedData = [[NSMutableData data] retain];
- } else {
- [uploader errorWithMessage:AILocalizedString(@"Unable to connect", nil) forChat:chat];
- }
-}
-
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
-{
- [receivedData setLength:0];
-}
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
-{
- [receivedData appendData:data];
-}
-
-- (void)connection:(NSURLConnection *)inConnection
- didFailWithError:(NSError *)error
-{
- [connection release]; connection = nil;
- [receivedData release]; receivedData = nil;
-
- [uploader errorWithMessage:error.localizedDescription forChat:chat];
-}
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)inConnection
-{
- AILogWithSignature(@"%@", [NSString stringWithData:receivedData encoding:NSUTF8StringEncoding]);
-
- response = [[NSMutableDictionary alloc] init];
-
- responseParser = [[NSXMLParser alloc] initWithData:receivedData];
- [responseParser setDelegate:self];
- [responseParser parse];
-
- [connection release]; connection = nil;
- [receivedData release]; receivedData = nil;
+ [dataUploader upload];
}
- (void)cancel
{
- [connection cancel];
-
- [connection release]; connection = nil;
- [receivedData release]; receivedData = nil;
+ [dataUploader cancel];
+ [dataUploader release];
}
#pragma mark Response parsing
@@ -208,8 +197,6 @@
} else {
// TODO when api key :(
}
-
- NSLog(@"%@", response);
}
@end
More information about the commits
mailing list