From 91aa8280a07317713f0842c4926796c71d79e192 Mon Sep 17 00:00:00 2001 From: Neil Bostrom Date: Tue, 29 Mar 2011 17:51:52 +0100 Subject: [PATCH 1/3] Added Flickr sharer service Modified Config.h to include Flickr API keys --- Classes/ShareKit/SHKConfig.h | 5 + .../Sharers/Services/Flickr/LFHTTPRequest.h | 157 ++++ .../Sharers/Services/Flickr/LFHTTPRequest.m | 776 ++++++++++++++++++ .../Services/Flickr/LFSiteReachability.h | 72 ++ .../Services/Flickr/LFSiteReachability.m | 285 +++++++ .../Sharers/Services/Flickr/LFWebAPIKit.h | 35 + .../Flickr/NSData_LFHTTPFormExtensions.h | 33 + .../Flickr/NSData_LFHTTPFormExtensions.m | 50 ++ .../Sharers/Services/Flickr/OFUtilities.h | 85 ++ .../Sharers/Services/Flickr/OFXMLMapper.h | 53 ++ .../Sharers/Services/Flickr/OFXMLMapper.m | 154 ++++ .../Sharers/Services/Flickr/ObjectiveFlickr.h | 161 ++++ .../Sharers/Services/Flickr/ObjectiveFlickr.m | 565 +++++++++++++ .../Sharers/Services/Flickr/SHKFlickr.h | 45 + .../Sharers/Services/Flickr/SHKFlickr.m | 219 +++++ 15 files changed, 2695 insertions(+) create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.m create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/OFUtilities.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.m create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.m create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.h create mode 100644 Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m diff --git a/Classes/ShareKit/SHKConfig.h b/Classes/ShareKit/SHKConfig.h index c2aa3565..db07ec7d 100644 --- a/Classes/ShareKit/SHKConfig.h +++ b/Classes/ShareKit/SHKConfig.h @@ -86,6 +86,11 @@ #define SHKSharedWithSignature 0 +// Flickr - http://www.flickr.com/services/apps/create/ +#define SHKFlickrConsumerKey @"" // The consumer key +#define SHKFlickrSecretKey @"" // The secret key +#define SHKFlickrCallbackUrl @"app://flickr" // The user defined callback url + /* UI Configuration : Basic diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.h b/Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.h new file mode 100644 index 00000000..14dc73f8 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.h @@ -0,0 +1,157 @@ +// +// LFHTTPRequest.h +// +// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) +// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import +#if TARGET_OS_IPHONE + #import + #import + #import +#endif + +extern NSString *const LFHTTPRequestConnectionError; +extern NSString *const LFHTTPRequestTimeoutError; +extern const NSTimeInterval LFHTTPRequestDefaultTimeoutInterval; +extern NSString *const LFHTTPRequestWWWFormURLEncodedContentType; +extern NSString *const LFHTTPRequestGETMethod; +extern NSString *const LFHTTPRequestHEADMethod; +extern NSString *const LFHTTPRequestPOSTMethod; + +@interface LFHTTPRequest : NSObject +{ + id _delegate; + + NSTimeInterval _timeoutInterval; + NSString *_userAgent; + NSString *_contentType; + + NSDictionary *_requestHeader; + + NSMutableData *_receivedData; + NSString *_receivedContentType; + + CFReadStreamRef _readStream; + NSTimer *_receivedDataTracker; + NSTimeInterval _lastReceivedDataUpdateTime; + + NSTimer *_requestMessageBodyTracker; + NSTimeInterval _lastSentDataUpdateTime; +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 + NSUInteger _requestMessageBodySize; + NSUInteger _expectedDataLength; + NSUInteger _lastReceivedBytes; + NSUInteger _lastSentBytes; +#else + unsigned int _requestMessageBodySize; + unsigned int _expectedDataLength; + unsigned int _lastReceivedBytes; + unsigned int _lastSentBytes; +#endif + + void *_readBuffer; + size_t _readBufferSize; + + id _sessionInfo; + + BOOL _shouldWaitUntilDone; + NSMessagePort *_synchronousMessagePort; +} + +- (id)init; +- (BOOL)isRunning; +- (void)cancel; +- (void)cancelWithoutDelegateMessage; + +- (BOOL)shouldWaitUntilDone; +- (void)setShouldWaitUntilDone:(BOOL)waitUntilDone; + +- (BOOL)performMethod:(NSString *)methodName onURL:(NSURL *)url withData:(NSData *)data; + +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 +- (BOOL)performMethod:(NSString *)methodName onURL:(NSURL *)url withInputStream:(NSInputStream *)inputStream knownContentSize:(NSUInteger)byteStreamSize; +#else +- (BOOL)performMethod:(NSString *)methodName onURL:(NSURL *)url withInputStream:(NSInputStream *)inputStream knownContentSize:(unsigned int)byteStreamSize; +#endif + +- (NSData *)getReceivedDataAndDetachFromRequest; + +- (NSDictionary *)requestHeader; +- (void)setRequestHeader:(NSDictionary *)requestHeader; +- (NSTimeInterval)timeoutInterval; +- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval; +- (NSString *)userAgent; +- (void)setUserAgent:(NSString *)userAgent; +- (NSString *)contentType; +- (void)setContentType:(NSString *)contentType; +- (NSData *)receivedData; +- (NSString *)receivedContentType; +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 +- (NSUInteger)expectedDataLength; +#else +- (unsigned int)expectedDataLength; +#endif +- (id)delegate; +- (void)setDelegate:(id)delegate; + +- (void)setSessionInfo:(id)aSessionInfo; +- (id)sessionInfo; + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 +@property (copy) NSDictionary *requestHeader; +@property (assign) NSTimeInterval timeoutInterval; +@property (copy) NSString *userAgent; +@property (copy) NSString *contentType; +@property (readonly) NSData *receivedData; +@property (readonly) NSUInteger expectedDataLength; +@property (assign) id delegate; +@property (retain) id sessionInfo; +@property (assign) BOOL shouldWaitUntilDone; +@property (readonly) BOOL isRunning; +#endif +@end + +@interface NSObject (LFHTTPRequestDelegate) +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 +- (void)httpRequest:(LFHTTPRequest *)request didReceiveStatusCode:(NSUInteger)statusCode URL:(NSURL *)url responseHeader:(CFHTTPMessageRef)header; +- (void)httpRequestDidComplete:(LFHTTPRequest *)request; +- (void)httpRequestDidCancel:(LFHTTPRequest *)request; +- (void)httpRequest:(LFHTTPRequest *)request didFailWithError:(NSString *)error; +- (void)httpRequest:(LFHTTPRequest *)request receivedBytes:(NSUInteger)bytesReceived expectedTotal:(NSUInteger)total; +- (void)httpRequest:(LFHTTPRequest *)request sentBytes:(NSUInteger)bytesSent total:(NSUInteger)total; + +// note if you implemented this, the data is never written to the receivedData of the HTTP request instance +- (void)httpRequest:(LFHTTPRequest *)request writeReceivedBytes:(void *)bytes size:(NSUInteger)blockSize expectedTotal:(NSUInteger)total; +#else +- (void)httpRequest:(LFHTTPRequest *)request didReceiveStatusCode:(unsigned int)statusCode URL:(NSURL *)url responseHeader:(CFHTTPMessageRef)header; +- (void)httpRequestDidComplete:(LFHTTPRequest *)request; +- (void)httpRequestDidCancel:(LFHTTPRequest *)request; +- (void)httpRequest:(LFHTTPRequest *)request didFailWithError:(NSString *)error; +- (void)httpRequest:(LFHTTPRequest *)request receivedBytes:(unsigned int)bytesReceived expectedTotal:(unsigned int)total; +- (void)httpRequest:(LFHTTPRequest *)request sentBytes:(unsigned int)bytesSent total:(unsigned int)total; +- (void)httpRequest:(LFHTTPRequest *)request writeReceivedBytes:(void *)bytes size:(unsigned int)blockSize expectedTotal:(unsigned int)total; +#endif +@end \ No newline at end of file diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.m b/Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.m new file mode 100644 index 00000000..5c658251 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/LFHTTPRequest.m @@ -0,0 +1,776 @@ +// +// LFHTTPRequest.m +// +// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) +// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import +#import "LFHTTPRequest.h" + +// these typedefs are for this compilation unit only +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + typedef unsigned int NSUInteger; + typedef int NSInteger; + #define NSUIntegerMax UINT_MAX +#endif + + + +NSString *const LFHTTPRequestConnectionError = @"HTTP request connection lost"; +NSString *const LFHTTPRequestTimeoutError = @"HTTP request timeout"; + +const NSTimeInterval LFHTTPRequestDefaultTimeoutInterval = 10.0; +NSString *const LFHTTPRequestWWWFormURLEncodedContentType = @"application/x-www-form-urlencoded"; +NSString *const LFHTTPRequestGETMethod = @"GET"; +NSString *const LFHTTPRequestHEADMethod = @"HEAD"; +NSString *const LFHTTPRequestPOSTMethod = @"POST"; + + +// internal defaults +NSString *const LFHRDefaultUserAgent = nil; +const size_t LFHTTPRequestDefaultReadBufferSize = 16384; +const NSTimeInterval LFHTTPRequestDefaultTrackerFireInterval = 1.0; + + +void LFHRReadStreamClientCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo); + +@interface LFHTTPRequest (PrivateMethods) +- (void)cleanUp; +- (void)dealloc; +- (void)handleTimeout; +- (void)handleRequestMessageBodyTrackerTick:(NSTimer *)timer; +- (void)handleReceivedDataTrackerTick:(NSTimer *)timer; +- (void)readStreamHasBytesAvailable; +- (void)readStreamErrorOccurred; +- (void)readStreamEndEncountered; +@end + +@implementation LFHTTPRequest (PrivateMethods) +- (void)cleanUp +{ + if (_readStream) { + CFReadStreamUnscheduleFromRunLoop(_readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFReadStreamClose(_readStream); + CFRelease(_readStream); + _readStream = NULL; + } + + if (_receivedDataTracker) { + [_receivedDataTracker invalidate]; + [_receivedDataTracker release]; + _receivedDataTracker = nil; + } + + if (_requestMessageBodyTracker) { + [_requestMessageBodyTracker invalidate]; + [_requestMessageBodyTracker release]; + _requestMessageBodyTracker = nil; + } + + _requestMessageBodySize = 0; + _expectedDataLength = NSUIntegerMax; + + _lastReceivedDataUpdateTime = 0.0; + _lastReceivedBytes = 0; + + _lastSentDataUpdateTime = 0.0; + _lastSentBytes = 0; + +} +- (void)dealloc +{ + [self cleanUp]; + [_userAgent release]; + [_contentType release]; + [_requestHeader release]; + [_receivedData release]; + [_receivedContentType release]; + + [_sessionInfo release]; + _sessionInfo = nil; + + free(_readBuffer); + [super dealloc]; +} + +- (void)finalize +{ + [self cleanUp]; + if (_readBuffer) { + free(_readBuffer); + _readBuffer = NULL; + } + + [super finalize]; +} + + +- (void)_exitRunLoop +{ +#if TARGET_OS_IPHONE + [_synchronousMessagePort sendBeforeDate:[NSDate date] msgid:0 components:nil from:_synchronousMessagePort reserved:0]; + +#else + NSPortMessage *message = [[[NSPortMessage alloc] initWithSendPort:_synchronousMessagePort receivePort:_synchronousMessagePort components:nil] autorelease]; + [message setMsgid:0]; + [message sendBeforeDate:[NSDate date]]; +#endif +} + +- (void)handleTimeout +{ + if (_shouldWaitUntilDone) { + [self _exitRunLoop]; + } + + [self cleanUp]; + if ([_delegate respondsToSelector:@selector(httpRequest:didFailWithError:)]) { + [_delegate httpRequest:self didFailWithError:LFHTTPRequestTimeoutError]; + } +} +- (void)handleRequestMessageBodyTrackerTick:(NSTimer *)timer +{ + if (timer != _requestMessageBodyTracker) { + return; + } + + // get the number of sent bytes + CFTypeRef sentBytesObject = CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount); + if (!sentBytesObject) { + // or should we send an error message? + return; + } + + NSInteger signedSentBytes = 0; + CFNumberGetValue(sentBytesObject, kCFNumberCFIndexType, &signedSentBytes); + CFRelease(sentBytesObject); + + if (signedSentBytes < 0) { + // or should we send an error message? + return; + } + + // interestingly, this logic also works when ALL REQUEST MESSAGE BODY IS SENT + NSUInteger sentBytes = (NSUInteger)signedSentBytes; + if (sentBytes > _lastSentBytes) { + _lastSentBytes = sentBytes; + _lastSentDataUpdateTime = [NSDate timeIntervalSinceReferenceDate]; + + if ([_delegate respondsToSelector:@selector(httpRequest:sentBytes:total:)]) { + [_delegate httpRequest:self sentBytes:_lastSentBytes total:_requestMessageBodySize]; + } + + return; + } + + if ([NSDate timeIntervalSinceReferenceDate] - _lastSentDataUpdateTime > _timeoutInterval) { + // remove ourselve from the runloop + [_requestMessageBodyTracker invalidate]; + [self handleTimeout]; + } +} +- (void)handleReceivedDataTrackerTick:(NSTimer *)timer +{ + if (timer != _receivedDataTracker) { + return; + } + + if ([NSDate timeIntervalSinceReferenceDate] - _lastReceivedDataUpdateTime > _timeoutInterval) { + // remove ourselves from the runloop + [_receivedDataTracker invalidate]; + [self handleTimeout]; + } +} +- (void)readStreamHasBytesAvailable +{ + // to prevent from stray callbacks entering here + if (![self isRunning]) { + return; + } + + if (!_receivedDataTracker) { + // update one last time the total sent bytes + if ([_delegate respondsToSelector:@selector(httpRequest:sentBytes:total:)]) { + [_delegate httpRequest:self sentBytes:_lastSentBytes total:_lastSentBytes]; + } + + // stops _requestMessageBodyTracker + [_requestMessageBodyTracker invalidate]; + [_requestMessageBodyTracker release]; + _requestMessageBodyTracker = nil; + + NSUInteger statusCode = 0; + + CFURLRef finalURL = CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPFinalURL); + CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPResponseHeader); + if (response) { + statusCode = (NSUInteger)CFHTTPMessageGetResponseStatusCode(response); + + CFStringRef contentLengthString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Length")); + if (contentLengthString) { + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + _expectedDataLength = [(NSString *)contentLengthString intValue]; +#else + if ([(NSString *)contentLengthString respondsToSelector:@selector(integerValue)]) { + _expectedDataLength = [(NSString *)contentLengthString integerValue]; + } + else { + _expectedDataLength = [(NSString *)contentLengthString intValue]; + } +#endif + + CFRelease(contentLengthString); + } + + [_receivedContentType release]; + _receivedContentType = nil; + + CFStringRef contentTypeString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type")); + if (contentTypeString) { + _receivedContentType = [(NSString *)contentTypeString copy]; + CFRelease(contentTypeString); + } + } + + CFReadStreamRef presentReadStream = _readStream; + + if ([_delegate respondsToSelector:@selector(httpRequest:didReceiveStatusCode:URL:responseHeader:)]) { + [_delegate httpRequest:self didReceiveStatusCode:statusCode URL:(NSURL *)finalURL responseHeader:response]; + } + + if (finalURL) { + CFRelease(finalURL); + } + + if (response) { + CFRelease(response); + } + + // better to see if we're still running... (we might be canceled by the delegate's httpRequest:didReceiveStatusCode:URL:responseHeader: !) + if (presentReadStream != _readStream) { + return; + } + + // start tracking received bytes + _lastReceivedBytes = 0; + _lastReceivedDataUpdateTime = [NSDate timeIntervalSinceReferenceDate]; + + // now we fire _receivedDataTracker + _receivedDataTracker = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:LFHTTPRequestDefaultTrackerFireInterval target:self selector:@selector(handleReceivedDataTrackerTick:) userInfo:nil repeats:YES]; + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 + // this is 10.5 only + [[NSRunLoop currentRunLoop] addTimer:_receivedDataTracker forMode:NSRunLoopCommonModes]; + #endif + + [[NSRunLoop currentRunLoop] addTimer:_receivedDataTracker forMode:NSDefaultRunLoopMode]; + + // These two are defined in the AppKit, not in the Foundation + #if TARGET_OS_MAC && !TARGET_OS_IPHONE + extern NSString *NSModalPanelRunLoopMode; + extern NSString *NSEventTrackingRunLoopMode; + [[NSRunLoop currentRunLoop] addTimer:_receivedDataTracker forMode:NSEventTrackingRunLoopMode]; + [[NSRunLoop currentRunLoop] addTimer:_receivedDataTracker forMode:NSModalPanelRunLoopMode]; + #endif + } + + // sets a 25,600-byte block, approximately for 256 KBPS connection + CFIndex bytesRead = CFReadStreamRead(_readStream, _readBuffer, _readBufferSize); + if (bytesRead > 0) { + if ([_delegate respondsToSelector:@selector(httpRequest:writeReceivedBytes:size:expectedTotal:)]) { + [_delegate httpRequest:self writeReceivedBytes:_readBuffer size:bytesRead expectedTotal:_expectedDataLength]; + + _lastReceivedBytes += bytesRead; + _lastReceivedDataUpdateTime = [NSDate timeIntervalSinceReferenceDate]; + + } + else { + [_receivedData appendBytes:_readBuffer length:bytesRead]; + _lastReceivedBytes = [_receivedData length]; + _lastReceivedDataUpdateTime = [NSDate timeIntervalSinceReferenceDate]; + + if ([_delegate respondsToSelector:@selector(httpRequest:receivedBytes:expectedTotal:)]) { + [_delegate httpRequest:self receivedBytes:_lastReceivedBytes expectedTotal:_expectedDataLength]; + } + } + } +} + +- (void)readStreamEndEncountered +{ + // to prevent from stray callbacks entering here + if (![self isRunning]) { + return; + } + + // if no byte read, we need to present the header at least again, because readStreamHasBytesAvailable is never called + if (![_receivedData length] && ![_delegate respondsToSelector:@selector(httpRequest:writeReceivedBytes:size:expectedTotal:)]) { + if ([_delegate respondsToSelector:@selector(httpRequest:sentBytes:total:)]) { + [_delegate httpRequest:self sentBytes:_lastSentBytes total:_lastSentBytes]; + } + + // stops _requestMessageBodyTracker + [_requestMessageBodyTracker invalidate]; + [_requestMessageBodyTracker release]; + _requestMessageBodyTracker = nil; + + NSUInteger statusCode = 0; + + CFURLRef finalURL = CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPFinalURL); + CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPResponseHeader); + if (response) { + statusCode = (NSUInteger)CFHTTPMessageGetResponseStatusCode(response); + + CFStringRef contentLengthString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Length")); + if (contentLengthString) { + + #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + _expectedDataLength = [(NSString *)contentLengthString intValue]; + #else + if ([(NSString *)contentLengthString respondsToSelector:@selector(integerValue)]) { + _expectedDataLength = [(NSString *)contentLengthString integerValue]; + } + else { + _expectedDataLength = [(NSString *)contentLengthString intValue]; + } + #endif + + CFRelease(contentLengthString); + } + + [_receivedContentType release]; + _receivedContentType = nil; + + CFStringRef contentTypeString = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type")); + if (contentTypeString) { + _receivedContentType = [(NSString *)contentTypeString copy]; + CFRelease(contentTypeString); + } + } + + if ([_delegate respondsToSelector:@selector(httpRequest:didReceiveStatusCode:URL:responseHeader:)]) { + [_delegate httpRequest:self didReceiveStatusCode:statusCode URL:(NSURL *)finalURL responseHeader:response]; + } + + if (finalURL) { + CFRelease(finalURL); + } + + if (response) { + CFRelease(response); + } + } + + + [self cleanUp]; + + if ([_delegate respondsToSelector:@selector(httpRequestDidComplete:)]) { + [_delegate httpRequestDidComplete:self]; + } +} +- (void)readStreamErrorOccurred +{ + // to prevent from stray callbacks entering here + if (![self isRunning]) { + return; + } + + [self cleanUp]; + + if ([_delegate respondsToSelector:@selector(httpRequest:didFailWithError:)]) { + [_delegate httpRequest:self didFailWithError:LFHTTPRequestConnectionError]; + } +} +@end + +@implementation LFHTTPRequest +- (id)init +{ + if ((self = [super init])) { + _timeoutInterval = LFHTTPRequestDefaultTimeoutInterval; + + _receivedData = [NSMutableData new]; + _expectedDataLength = NSUIntegerMax; + _readBufferSize = LFHTTPRequestDefaultReadBufferSize; + _readBuffer = calloc(1, _readBufferSize); + NSAssert(_readBuffer, @"Must have enough memory for _readBuffer"); + } + + return self; +} + +- (BOOL)isRunning +{ + return !!_readStream; +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 +- (BOOL)_performMethod:(NSString *)methodName onURL:(NSURL *)url withData:(NSData *)data orWithInputStream:(NSInputStream *)inputStream knownContentSize:(NSUInteger)byteStreamSize +#else +- (BOOL)_performMethod:(NSString *)methodName onURL:(NSURL *)url withData:(NSData *)data orWithInputStream:(NSInputStream *)inputStream knownContentSize:(unsigned int)byteStreamSize +#endif +{ + if (!url) { + return NO; + } + + if (_readStream) { + return NO; + } + + CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, (CFStringRef)methodName, (CFURLRef)url, kCFHTTPVersion1_1); + if (!request) { + return NO; + } + + // combine the header + NSMutableDictionary *headerDictionary = [NSMutableDictionary dictionary]; + if (_userAgent) { + [headerDictionary setObject:_userAgent forKey:@"User-Agent"]; + } + + if (_contentType) { + [headerDictionary setObject:_contentType forKey:@"Content-Type"]; + } + + if (inputStream) { + if (byteStreamSize && byteStreamSize != NSUIntegerMax) { + [headerDictionary setObject:[NSString stringWithFormat:@"%lu", byteStreamSize] forKey:@"Content-Length"]; + _requestMessageBodySize = byteStreamSize; + } + else { + _requestMessageBodySize = NSUIntegerMax; + } + } + else { + if ([data length]) { + [headerDictionary setObject:[NSString stringWithFormat:@"%lu", [data length]] forKey:@"Content-Length"]; + } + _requestMessageBodySize = [data length]; + } + + if (_requestHeader) { + [headerDictionary addEntriesFromDictionary:_requestHeader]; + } + + NSEnumerator *dictEnumerator = [headerDictionary keyEnumerator]; + id key; + while ((key = [dictEnumerator nextObject])) { + CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)[key description], (CFStringRef)[headerDictionary objectForKey:key]); + } + + if (!inputStream && data) { + CFHTTPMessageSetBody(request, (CFDataRef)data); + } + + CFReadStreamRef tmpReadStream; + + if (inputStream) { + tmpReadStream = CFReadStreamCreateForStreamedHTTPRequest(NULL, request, (CFReadStreamRef)inputStream); + } + else { + tmpReadStream = CFReadStreamCreateForHTTPRequest(NULL, request); + } + + CFRelease(request); + if (!tmpReadStream) { + return NO; + } + + CFReadStreamSetProperty(tmpReadStream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue); + + // apply current proxy settings + #if !TARGET_OS_IPHONE + CFDictionaryRef proxyDict = SCDynamicStoreCopyProxies(NULL); // kCFNetworkProxiesHTTPProxy + #else + CFDictionaryRef proxyDict = CFNetworkCopySystemProxySettings(); + #endif + + if (proxyDict) { + CFReadStreamSetProperty(tmpReadStream, kCFStreamPropertyHTTPProxy, proxyDict); + CFRelease(proxyDict); + } + + CFStreamClientContext streamContext; + streamContext.version = 0; + streamContext.info = self; + streamContext.retain = 0; + streamContext.release = 0; + streamContext.copyDescription = 0; + + CFOptionFlags eventFlags = kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred; + + // open the stream with callback function + if (!CFReadStreamSetClient(tmpReadStream, eventFlags, LFHRReadStreamClientCallBack, &streamContext)) + { + CFRelease(tmpReadStream); + return NO; + } + + // detach and release the previous data buffer + if ([_receivedData length]) { + NSMutableData *tmp = _receivedData; + _receivedData = [NSMutableData new]; + [tmp release]; + } + + CFReadStreamScheduleWithRunLoop(tmpReadStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + + // we need to assign this in advance, because the callback might be called anytime between this and the next statement + _readStream = tmpReadStream; + + _expectedDataLength = NSUIntegerMax; + + // open the stream + Boolean result = CFReadStreamOpen(tmpReadStream); + if (!result) { + CFReadStreamUnscheduleFromRunLoop(tmpReadStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFRelease(tmpReadStream); + _readStream = NULL; + return NO; + } + + + _lastSentBytes = 0; + _lastSentDataUpdateTime = [NSDate timeIntervalSinceReferenceDate]; + + // we create _requestMessageBodyTracker (timer for tracking sent data) first + _requestMessageBodyTracker = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:LFHTTPRequestDefaultTrackerFireInterval target:self selector:@selector(handleRequestMessageBodyTrackerTick:) userInfo:nil repeats:YES]; + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 + // this is 10.5 only + [[NSRunLoop currentRunLoop] addTimer:_requestMessageBodyTracker forMode:NSRunLoopCommonModes]; +#endif + + [[NSRunLoop currentRunLoop] addTimer:_requestMessageBodyTracker forMode:NSDefaultRunLoopMode]; + + // These two are defined in the AppKit, not in the Foundation + #if TARGET_OS_MAC && !TARGET_OS_IPHONE + extern NSString *NSModalPanelRunLoopMode; + extern NSString *NSEventTrackingRunLoopMode; + [[NSRunLoop currentRunLoop] addTimer:_requestMessageBodyTracker forMode:NSEventTrackingRunLoopMode]; + [[NSRunLoop currentRunLoop] addTimer:_requestMessageBodyTracker forMode:NSModalPanelRunLoopMode]; + #endif + + if (_shouldWaitUntilDone) { + NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop]; + NSString *currentMode = [currentRunLoop currentMode]; + + if (![currentMode length]) { + currentMode = NSDefaultRunLoopMode; + } + + BOOL isReentrant = (_synchronousMessagePort != nil); + + if (!isReentrant) { + _synchronousMessagePort = [[NSPort port] retain]; + [currentRunLoop addPort:_synchronousMessagePort forMode:currentMode]; + } + + while ([self isRunning]) { + [currentRunLoop runMode:currentMode beforeDate:[NSDate distantFuture]]; + } + + if (!isReentrant) { + [currentRunLoop removePort:_synchronousMessagePort forMode:currentMode]; + [_synchronousMessagePort release]; + _synchronousMessagePort = nil; + } + else { + // sends another message to exit the runloop + [self _exitRunLoop]; + } + } + + return YES; +} + +- (BOOL)performMethod:(NSString *)methodName onURL:(NSURL *)url withData:(NSData *)data +{ + return [self _performMethod:methodName onURL:url withData:data orWithInputStream:nil knownContentSize:0]; +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 +- (BOOL)performMethod:(NSString *)methodName onURL:(NSURL *)url withInputStream:(NSInputStream *)inputStream knownContentSize:(NSUInteger)byteStreamSize +#else +- (BOOL)performMethod:(NSString *)methodName onURL:(NSURL *)url withInputStream:(NSInputStream *)inputStream knownContentSize:(unsigned int)byteStreamSize +#endif +{ + return [self _performMethod:methodName onURL:url withData:nil orWithInputStream:inputStream knownContentSize:byteStreamSize]; +} + +- (void)cancel +{ + [self cancelWithoutDelegateMessage]; + if ([_delegate respondsToSelector:@selector(httpRequestDidCancel:)]) { + [_delegate httpRequestDidCancel:self]; + } +} +- (void)cancelWithoutDelegateMessage +{ + [self cleanUp]; +} +- (NSData *)getReceivedDataAndDetachFromRequest +{ + NSData *returnedData = [_receivedData autorelease]; + _receivedData = [NSMutableData new]; + + [_receivedContentType release]; + _receivedContentType = nil; + + return returnedData; +} +- (NSDictionary *)requestHeader +{ + return [[_requestHeader copy] autorelease]; +} +- (void)setRequestHeader:(NSDictionary *)requestHeader +{ + if (![_requestHeader isEqualToDictionary:requestHeader]) { + NSDictionary *tmp = _requestHeader; + _requestHeader = [requestHeader copy]; + [tmp release]; + } +} +- (NSTimeInterval)timeoutInterval +{ + return _timeoutInterval; +} +- (void)setTimeoutInterval:(NSTimeInterval)timeoutInterval +{ + if (_timeoutInterval != timeoutInterval) { + _timeoutInterval = timeoutInterval; + } +} +- (NSString *)userAgent +{ + return [[_userAgent copy] autorelease]; +} +- (void)setUserAgent:(NSString *)userAgent +{ + if ([_userAgent isEqualToString:userAgent]) { + return; + } + + NSString *tmp = _userAgent; + _userAgent = [userAgent copy]; + [tmp release]; +} +- (NSString *)contentType +{ + return [[_contentType copy] autorelease]; +} +- (void)setContentType:(NSString *)contentType +{ + if ([_contentType isEqualToString:contentType]) { + return; + } + + NSString *tmp = _contentType; + _contentType = [contentType copy]; + [tmp release]; +} +- (NSData *)receivedData +{ + return [[_receivedData retain] autorelease]; +} + +- (NSString *)receivedContentType +{ + return [[_receivedContentType copy] autorelease]; +} + +- (NSUInteger)expectedDataLength +{ + return _expectedDataLength; +} +- (id)delegate +{ + return _delegate; +} +- (void)setDelegate:(id)delegate +{ + if (delegate != _delegate) { + _delegate = delegate; + } +} + +- (void)setSessionInfo:(id)aSessionInfo +{ + id tmp = _sessionInfo; + _sessionInfo = [aSessionInfo retain]; + [tmp release]; +} +- (id)sessionInfo +{ + return [[_sessionInfo retain] autorelease]; +} + +- (size_t)readBufferSize +{ + return _readBufferSize; +} + +- (void)setReadBufferSize:(size_t)newSize +{ + NSAssert(![self isRunning], @"Cannot set read buffer size while the request is running"); + NSAssert(newSize, @"Read buffer size must > 0"); + + _readBufferSize = newSize; + _readBuffer = realloc(_readBuffer, newSize); + NSAssert(_readBuffer, @"Must have enough memory for reallocing _readBuffer"); + bzero(_readBuffer, newSize); +} + +- (BOOL)shouldWaitUntilDone +{ + return _shouldWaitUntilDone; +} + +- (void)setShouldWaitUntilDone:(BOOL)waitUntilDone +{ + _shouldWaitUntilDone = waitUntilDone; +} + +@end + +void LFHRReadStreamClientCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo) +{ + id pool = [NSAutoreleasePool new]; + + LFHTTPRequest *request = (LFHTTPRequest *)clientCallBackInfo; + switch (eventType) { + case kCFStreamEventHasBytesAvailable: + [request readStreamHasBytesAvailable]; + break; + case kCFStreamEventEndEncountered: + [request readStreamEndEncountered]; + break; + case kCFStreamEventErrorOccurred: + [request readStreamErrorOccurred]; + break; + } + [pool drain]; +} diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h b/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h new file mode 100644 index 00000000..94998c55 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h @@ -0,0 +1,72 @@ +// +// LFSiteReachability.h +// +// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) +// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import +#import "LFHTTPRequest.h" + +extern NSString *const LFSiteReachabilityConnectionTypeWiFi; +extern NSString *const LFSiteReachabilityConnectionTypeWWAN; + +@class LFSiteReachability; + +@protocol LFSiteReachabilityDelegate +- (void)reachability:(LFSiteReachability *)inReachability site:(NSURL *)inURL isAvailableOverConnectionType:(NSString *)inConnectionType; +- (void)reachability:(LFSiteReachability *)inReachability siteIsNotAvailable:(NSURL *)inURL; +@end + +@interface LFSiteReachability : NSObject +{ + id delegate; + NSURL *siteURL; + SCNetworkReachabilityRef reachability; + LFHTTPRequest *siteRequest; + NSTimer *timeoutTimer; + id lastCheckStatus; +} +- (void)startChecking; +- (void)stopChecking; +- (BOOL)isChecking; + +// When networkConnectivityExists returns YES, it simply means network interface is available +// (e.g. WiFi not disabled, has 3G, etc.); that is, the device has the "potential" to connect, +// but that does not mean that an HTTP request will succeed, for various reasons--such as +// the IP is not yet obtained; the interface is not yet fully "up", base station or WiFi hasn't +// assigned a valid IP yet, etc. To read this way: If this method returns NO, it means +// "forget about network, it doesn't exist at all" +- (BOOL)networkConnectivityExists; + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +- (id)delegate; +- (NSURL*)siteURL; +- (NSTimeInterval)timeoutInterval; +#else +@property (assign, nonatomic) id delegate; +@property (retain, nonatomic) NSURL *siteURL; +@property (nonatomic) NSTimeInterval timeoutInterval; +#endif +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m b/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m new file mode 100644 index 00000000..361bc65b --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m @@ -0,0 +1,285 @@ +// +// LFSiteReachability.m +// +// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) +// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "LFSiteReachability.h" +#import + +static NSString *kDefaultSite = @"http://google.com"; +static NSTimeInterval kDefaultTimeoutInterval = 15.0; + +#define LFSRDebug(format, ...) +// #define LFSRDebug NSLog + +NSString *const LFSiteReachabilityConnectionTypeWiFi = @"LFSiteReachabilityConnectionTypeWiFi"; +NSString *const LFSiteReachabilityConnectionTypeWWAN = @"LFSiteReachabilityConnectionTypeWWAN"; +NSString *const LFSiteReachabilityNotReachableStatus = @"LFSiteReachabilityNotReachable"; + +#if !TARGET_OS_IPHONE + #define SCNetworkReachabilityFlags SCNetworkConnectionFlags + #define kSCNetworkReachabilityFlagsConnectionRequired kSCNetworkFlagsConnectionRequired + #define kSCNetworkReachabilityFlagsReachable kSCNetworkFlagsReachable + #define kSCNetworkReachabilityFlagsIsWWAN 0 +#endif + +static void LFSiteReachabilityCallback(SCNetworkReachabilityRef inTarget, SCNetworkReachabilityFlags inFlags, void *inInfo); + +@implementation LFSiteReachability +- (void)dealloc +{ + delegate = nil; + + [siteRequest setDelegate:nil]; + [self stopChecking]; + [siteRequest release]; + [siteURL release]; + + [super dealloc]; +} + +- (void)finalize +{ + [siteRequest setDelegate:nil]; + [self stopChecking]; + [super finalize]; +} + +- (id)init +{ + if ((self = [super init])) { + siteRequest = [[LFHTTPRequest alloc] init]; + [siteRequest setDelegate:self]; + [siteRequest setTimeoutInterval:kDefaultTimeoutInterval]; + + siteURL = [[NSURL URLWithString:kDefaultSite] retain]; + } + + return self; +} + +- (void)handleTimeoutTimer:(NSTimer *)inTimer +{ + LFSRDebug(@"%s", __PRETTY_FUNCTION__); + [inTimer invalidate]; + + if (lastCheckStatus != LFSiteReachabilityNotReachableStatus) { + lastCheckStatus = LFSiteReachabilityNotReachableStatus; + if ([delegate respondsToSelector:@selector(reachability:siteIsNotAvailable:)]) { + [delegate reachability:self siteIsNotAvailable:siteURL]; + } + } +} + +- (void)stopTimeoutTimer +{ + if ([timeoutTimer isValid]) { + [timeoutTimer invalidate]; + } + + [timeoutTimer release]; + timeoutTimer = nil; +} + +- (void)handleReachabilityCallbackFlags:(SCNetworkReachabilityFlags)inFlags +{ + [self stopTimeoutTimer]; + + LFSRDebug(@"%s, flags: 0x%08x", __PRETTY_FUNCTION__, inFlags); + + if (inFlags & kSCNetworkReachabilityFlagsReachable) { + NSString *connectionType = (inFlags & kSCNetworkReachabilityFlagsIsWWAN) ? LFSiteReachabilityConnectionTypeWWAN : LFSiteReachabilityConnectionTypeWiFi; + + BOOL connectionRequestNotRequired = !(inFlags & kSCNetworkReachabilityFlagsConnectionRequired); + + if (siteURL) { + LFSRDebug(@"%s, connectionRequestNotRequired: %d, attempting to request from: %@", __PRETTY_FUNCTION__, connectionRequestNotRequired, siteURL); + + // next stage: send the request + [siteRequest cancelWithoutDelegateMessage]; + [siteRequest setSessionInfo:connectionType]; + if ([siteRequest performMethod:LFHTTPRequestHEADMethod onURL:siteURL withData:nil]) { + return; + } + } + else { + if (lastCheckStatus != connectionType) { + lastCheckStatus = connectionType; + if (connectionRequestNotRequired && [delegate respondsToSelector:@selector(reachability:site:isAvailableOverConnectionType:)]) { + [delegate reachability:self site:siteURL isAvailableOverConnectionType:connectionType]; + return; + } + } + } + } + + // if all fails + if (lastCheckStatus != LFSiteReachabilityNotReachableStatus) { + lastCheckStatus = LFSiteReachabilityNotReachableStatus; + if ([delegate respondsToSelector:@selector(reachability:siteIsNotAvailable:)]) { + [delegate reachability:self siteIsNotAvailable:siteURL]; + } + } +} + +- (BOOL)networkConnectivityExists +{ + // 0.0.0.0 + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + SCNetworkReachabilityRef localReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); + SCNetworkReachabilityFlags flags = 0; + + BOOL capable = NO; + if (SCNetworkReachabilityGetFlags(localReachability, &flags)) { + if (flags & kSCNetworkReachabilityFlagsReachable) { + capable = YES; + } + } + + CFRelease(localReachability); + return capable; +} + +- (void)startChecking +{ + [self stopChecking]; + + // 0.0.0.0 + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); + SCNetworkReachabilityFlags flags = 0; + + BOOL createTimeoutTimer = YES; + if (SCNetworkReachabilityGetFlags(reachability, &flags)) { + [self handleReachabilityCallbackFlags:flags]; + createTimeoutTimer = NO; + } + + SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; + SCNetworkReachabilitySetCallback(reachability, LFSiteReachabilityCallback, &context); + SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + + if (createTimeoutTimer) { + timeoutTimer = [[NSTimer scheduledTimerWithTimeInterval:[siteRequest timeoutInterval] target:self selector:@selector(handleTimeoutTimer:) userInfo:NULL repeats:NO] retain]; + } +} + +- (void)stopChecking +{ + [siteRequest cancelWithoutDelegateMessage]; + [self stopTimeoutTimer]; + + if (reachability) { + SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + CFRelease(reachability); + reachability = NULL; + } + + lastCheckStatus = nil; +} + +- (BOOL)isChecking +{ + return !!reachability; +} + +- (NSTimeInterval)timeoutInterval +{ + return [siteRequest timeoutInterval]; +} + +- (void)setTimeoutInterval:(NSTimeInterval)inInterval +{ + [siteRequest setTimeoutInterval:inInterval]; +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +- (void)httpRequest:(LFHTTPRequest *)request didReceiveStatusCode:(int)statusCode URL:(NSURL *)url responseHeader:(CFHTTPMessageRef)header +#else +- (void)httpRequest:(LFHTTPRequest *)request didReceiveStatusCode:(NSUInteger)statusCode URL:(NSURL *)url responseHeader:(CFHTTPMessageRef)header +#endif +{ + LFSRDebug(@"%s, code: %d, URL: %@, header: %@", __PRETTY_FUNCTION__, statusCode, url, (id)header); +} + +- (void)httpRequestDidComplete:(LFHTTPRequest *)request +{ + LFSRDebug(@"%s, connection type: %@, received data: %@", __PRETTY_FUNCTION__, [request sessionInfo], [request receivedData]); + + if (lastCheckStatus != [request sessionInfo]) { + lastCheckStatus = [request sessionInfo]; + if ([delegate respondsToSelector:@selector(reachability:site:isAvailableOverConnectionType:)]) { + [delegate reachability:self site:siteURL isAvailableOverConnectionType:[request sessionInfo]]; + } + } +} + +- (void)httpRequest:(LFHTTPRequest *)request didFailWithError:(NSString *)error +{ + LFSRDebug(@"%s, error: %@", __PRETTY_FUNCTION__, error); + + if (lastCheckStatus != LFSiteReachabilityNotReachableStatus) { + lastCheckStatus = LFSiteReachabilityNotReachableStatus; + if ([delegate respondsToSelector:@selector(reachability:siteIsNotAvailable:)]) { + [delegate reachability:self siteIsNotAvailable:siteURL]; + } + } +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +- (id)delegate +{ + return delegate; +} + +- (NSURL*)siteURL +{ + return [[siteURL retain] autorelease]; +} + +#else +@synthesize delegate; +@synthesize siteURL; +#endif +@end + + +void LFSiteReachabilityCallback(SCNetworkReachabilityRef inTarget, SCNetworkReachabilityFlags inFlags, void *inInfo) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + LFSRDebug(@"%s, flags: 0x%08x", __PRETTY_FUNCTION__, inFlags); + + [(LFSiteReachability *)inInfo handleReachabilityCallbackFlags:inFlags]; + [pool drain]; +} diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h b/Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h new file mode 100644 index 00000000..b277eca9 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h @@ -0,0 +1,35 @@ +// +// LFWebAPIKit.h +// +// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) +// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "LFHTTPRequest.h" +#import "NSData_LFHTTPFormExtensions.h" + +// LFSiteReachability is only available when built aginst OS X 10.5+ or iPhone SDK +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 + #import "LFSiteReachability.h" +#endif diff --git a/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h b/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h new file mode 100644 index 00000000..43597396 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h @@ -0,0 +1,33 @@ +// +// NSData+LFHTTPFormExtensions.h +// +// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) +// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +@interface NSData (LFHTTPFormExtensions) ++ (id)dataAsWWWURLEncodedFormFromDictionary:(NSDictionary *)formDictionary; +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m b/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m new file mode 100644 index 00000000..8f4d6e8c --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m @@ -0,0 +1,50 @@ +// +// NSData+LFHTTPFormExtensions.m +// +// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) +// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "NSData_LFHTTPFormExtensions.h" + +@implementation NSData (LFHTTPFormExtensions) ++ (id)dataAsWWWURLEncodedFormFromDictionary:(NSDictionary *)formDictionary +{ + NSMutableString *combinedDataString = [NSMutableString string]; + NSEnumerator *enumerator = [formDictionary keyEnumerator]; + + id key = [enumerator nextObject]; + if (key) { + id value = [formDictionary objectForKey:key]; + [combinedDataString appendString:[NSString stringWithFormat:@"%@=%@", [(NSString*)key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; + + while ((key = [enumerator nextObject])) { + value = [formDictionary objectForKey:key]; + [combinedDataString appendString:[NSString stringWithFormat:@"&%@=%@", [(NSString*)key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; + } + } + + return [combinedDataString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; +} +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/OFUtilities.h b/Classes/ShareKit/Sharers/Services/Flickr/OFUtilities.h new file mode 100644 index 00000000..9925afad --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/OFUtilities.h @@ -0,0 +1,85 @@ +// +// OFUtilities.h +// +// Copyright (c) 2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +#if !defined(NS_INLINE) +#if defined(__GNUC__) +#define NS_INLINE static __inline__ __attribute__((always_inline)) +#elif defined(__MWERKS__) || defined(__cplusplus) +#define NS_INLINE static inline +#elif defined(_MSC_VER) +#define NS_INLINE static __inline +#elif defined(__WIN32__) +#define NS_INLINE static __inline__ +#endif +#endif + +NS_INLINE NSString *OFMD5HexStringFromNSString(NSString *inStr) +{ + const char *data = [inStr UTF8String]; + CC_LONG length = (CC_LONG) strlen(data); + + unsigned char *md5buf = (unsigned char*)calloc(1, CC_MD5_DIGEST_LENGTH); + + CC_MD5_CTX md5ctx; + CC_MD5_Init(&md5ctx); + CC_MD5_Update(&md5ctx, data, length); + CC_MD5_Final(md5buf, &md5ctx); + + NSMutableString *md5hex = [NSMutableString string]; + size_t i; + for (i = 0 ; i < CC_MD5_DIGEST_LENGTH ; i++) { + [md5hex appendFormat:@"%02x", md5buf[i]]; + } + free(md5buf); + return md5hex; +} + +NS_INLINE NSString *OFEscapedURLStringFromNSString(NSString *inStr) +{ + CFStringRef escaped = CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)inStr, NULL, CFSTR("&"), kCFStringEncodingUTF8); + + #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + return (NSString *)[(NSString*)escaped autorelease]; + #else + return (NSString *)[NSMakeCollectable(escaped) autorelease]; + #endif +} + +NS_INLINE NSString *OFGenerateUUIDString() +{ + CFUUIDRef uuid = CFUUIDCreate(NULL); + CFStringRef uuidStr = CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + return (NSString *)[(NSString*)uuidStr autorelease]; +#else + return (NSString *)[NSMakeCollectable(uuidStr) autorelease]; +#endif +} diff --git a/Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.h b/Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.h new file mode 100644 index 00000000..c5c16c4e --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.h @@ -0,0 +1,53 @@ +// +// OFXMLMapper.h +// +// Copyright (c) 2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import + +extern NSString *const OFXMLTextContentKey; + +#if (MAC_OS_X_VERSION_10_6 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) || (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_2_0) +@interface OFXMLMapper : NSObject +#else +@interface OFXMLMapper : NSObject +#endif +{ + NSMutableDictionary *resultantDictionary; + + NSMutableArray *elementStack; + NSMutableDictionary *currentDictionary; + NSString *currentElementName; +} ++ (NSDictionary *)dictionaryMappedFromXMLData:(NSData *)inData; +@end + +@interface NSDictionary (OFXMLMapperExtension) +- (NSString *)textContent; + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 +@property (nonatomic, readonly) NSString *textContent; +#endif +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.m b/Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.m new file mode 100644 index 00000000..d9e6dec5 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/OFXMLMapper.m @@ -0,0 +1,154 @@ +// +// OFXMLMapper.m +// +// Copyright (c) 2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "OFXMLMapper.h" + +NSString *const OFXMLMapperExceptionName = @"OFXMLMapperException"; +NSString *const OFXMLTextContentKey = @"_text"; + +@implementation OFXMLMapper +- (void)dealloc +{ + [resultantDictionary release]; + [elementStack release]; + [currentElementName release]; + [super dealloc]; +} + +- (id)init +{ + if ((self = [super init])) { + resultantDictionary = [[NSMutableDictionary alloc] init]; + elementStack = [[NSMutableArray alloc] init]; + } + + return self; +} + +- (void)runWithData:(NSData *)inData +{ + currentDictionary = resultantDictionary; + + NSXMLParser *parser = [[NSXMLParser alloc] initWithData:inData]; + [parser setDelegate:self]; + [parser parse]; + [parser release]; +} + +- (NSDictionary *)resultantDictionary +{ + return [[resultantDictionary retain] autorelease]; +} + ++ (NSDictionary *)dictionaryMappedFromXMLData:(NSData *)inData +{ + OFXMLMapper *mapper = [[OFXMLMapper alloc] init]; + [mapper runWithData:inData]; + NSDictionary *result = [mapper resultantDictionary]; + [mapper release]; + return result; +} + +- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict +{ + NSMutableDictionary *mutableAttrDict = attributeDict ? [NSMutableDictionary dictionaryWithDictionary:attributeDict] : [NSMutableDictionary dictionary]; + + // see if it's duplicated + id element = [currentDictionary objectForKey:elementName]; + if (element) { + if (![element isKindOfClass:[NSMutableArray class]]) { + if ([element isKindOfClass:[NSMutableDictionary class]]) { + [element retain]; + [currentDictionary removeObjectForKey:elementName]; + + NSMutableArray *newArray = [NSMutableArray arrayWithObject:element]; + [currentDictionary setObject:newArray forKey:elementName]; + [element release]; + + element = newArray; + } + else { + @throw [NSException exceptionWithName:OFXMLMapperExceptionName reason:@"Faulty XML structure" userInfo:nil]; + } + } + + [element addObject:mutableAttrDict]; + } + else { + // plural tag rule: if the parent's tag is plural and the incoming is singular, we'll make it into an array (we only handles the -s case) + + if ([currentElementName length] > [elementName length] && [currentElementName hasPrefix:elementName] && [currentElementName hasSuffix:@"s"]) { + [currentDictionary setObject:[NSMutableArray arrayWithObject:mutableAttrDict] forKey:elementName]; + } + else { + [currentDictionary setObject:mutableAttrDict forKey:elementName]; + } + } + + [elementStack insertObject:currentDictionary atIndex:0]; + currentDictionary = mutableAttrDict; + + NSString *tmp = currentElementName; + currentElementName = [elementName retain]; + [tmp release]; +} + +- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName +{ + if (![elementStack count]) { + @throw [NSException exceptionWithName:OFXMLMapperExceptionName reason:@"Unbalanced XML element tag closing" userInfo:nil]; + } + + currentDictionary = [elementStack objectAtIndex:0]; + [elementStack removeObjectAtIndex:0]; +} + +- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string +{ + NSString *existingContent = [currentDictionary objectForKey:OFXMLTextContentKey]; + if (existingContent) { + NSString *newContent = [existingContent stringByAppendingString:string]; + [currentDictionary setObject:newContent forKey:OFXMLTextContentKey]; + } + else { + [currentDictionary setObject:string forKey:OFXMLTextContentKey]; + } +} + +- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError +{ + [resultantDictionary release]; + resultantDictionary = nil; +} +@end + +@implementation NSDictionary (OFXMLMapperExtension) +- (NSString *)textContent +{ + return [self objectForKey:OFXMLTextContentKey]; +} +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h b/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h new file mode 100644 index 00000000..bd63e2af --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h @@ -0,0 +1,161 @@ +// +// ObjectiveFlickr.h +// +// Copyright (c) 2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "LFWebAPIKit.h" +#import "OFUtilities.h" +#import "OFXMLMapper.h" + +extern NSString *const OFFlickrSmallSquareSize; // "s" - 75x75 +extern NSString *const OFFlickrThumbnailSize; // "t" - 100 on longest side +extern NSString *const OFFlickrSmallSize; // "m" - 240 on longest side +extern NSString *const OFFlickrMediumSize; // (no size modifier) - 500 on longest side +extern NSString *const OFFlickrLargeSize; // "b" - 1024 on longest side + +extern NSString *const OFFlickrReadPermission; +extern NSString *const OFFlickrWritePermission; +extern NSString *const OFFlickrDeletePermission; + +@interface OFFlickrAPIContext : NSObject +{ + NSString *key; + NSString *sharedSecret; + NSString *authToken; + + NSString *RESTAPIEndpoint; + NSString *photoSource; + NSString *photoWebPageSource; + NSString *authEndpoint; + NSString *uploadEndpoint; +} +- (id)initWithAPIKey:(NSString *)inKey sharedSecret:(NSString *)inSharedSecret; + +- (void)setAuthToken:(NSString *)inAuthToken; +- (NSString *)authToken; + +// URL provisioning +- (NSURL *)photoSourceURLFromDictionary:(NSDictionary *)inDictionary size:(NSString *)inSizeModifier; +- (NSURL *)photoWebPageURLFromDictionary:(NSDictionary *)inDictionary; +- (NSURL *)loginURLFromFrobDictionary:(NSDictionary *)inFrob requestedPermission:(NSString *)inPermission; + +// API endpoints +- (void)setRESTAPIEndpoint:(NSString *)inEndpoint; +- (NSString *)RESTAPIEndpoint; + +- (void)setPhotoSource:(NSString *)inSource; +- (NSString *)photoSource; + +- (void)setAuthEndpoint:(NSString *)inEndpoint; +- (NSString *)authEndpoint; + +- (void)setUploadEndpoint:(NSString *)inEndpoint; +- (NSString *)uploadEndpoint; + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 +@property (nonatomic, readonly) NSString *key; +@property (nonatomic, readonly) NSString *sharedSecret; +@property (nonatomic, retain) NSString *authToken; + +@property (nonatomic, retain) NSString *RESTAPIEndpoint; +@property (nonatomic, retain) NSString *photoSource; +@property (nonatomic, retain) NSString *photoWebPageSource; +@property (nonatomic, retain) NSString *authEndpoint; +@property (nonatomic, retain) NSString *uploadEndpoint; +#endif +@end + +extern NSString *const OFFlickrAPIReturnedErrorDomain; +extern NSString *const OFFlickrAPIRequestErrorDomain; + +enum { + // refer to Flickr API document for Flickr's own error codes + OFFlickrAPIRequestConnectionError = 0x7fff0001, + OFFlickrAPIRequestTimeoutError = 0x7fff0002, + OFFlickrAPIRequestFaultyXMLResponseError = 0x7fff0003, + OFFlickrAPIRequestUnknownError = 0x7fff0042 +}; + +@class OFFlickrAPIRequest; + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 +@protocol OFFlickrAPIRequestDelegate +@optional +#else +@interface NSObject (OFFlickrAPIRequestDelegateCategory) +#endif +- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary; +- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didFailWithError:(NSError *)inError; +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 +- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest imageUploadSentBytes:(NSUInteger)inSentBytes totalBytes:(NSUInteger)inTotalBytes; +#else +- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest imageUploadSentBytes:(unsigned int)inSentBytes totalBytes:(unsigned int)inTotalBytes; +#endif +@end + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 +typedef id OFFlickrAPIRequestDelegateType; +#else +typedef id OFFlickrAPIRequestDelegateType; +#endif + +@interface OFFlickrAPIRequest : NSObject +{ + OFFlickrAPIContext *context; + LFHTTPRequest *HTTPRequest; + + OFFlickrAPIRequestDelegateType delegate; + id sessionInfo; + + NSString *uploadTempFilename; +} +- (id)initWithAPIContext:(OFFlickrAPIContext *)inContext; +- (OFFlickrAPIContext *)context; + +- (OFFlickrAPIRequestDelegateType)delegate; +- (void)setDelegate:(OFFlickrAPIRequestDelegateType)inDelegate; + +- (id)sessionInfo; +- (void)setSessionInfo:(id)inInfo; + +- (NSTimeInterval)requestTimeoutInterval; +- (void)setRequestTimeoutInterval:(NSTimeInterval)inTimeInterval; +- (BOOL)isRunning; +- (void)cancel; + +// elementary methods +- (BOOL)callAPIMethodWithGET:(NSString *)inMethodName arguments:(NSDictionary *)inArguments; +- (BOOL)callAPIMethodWithPOST:(NSString *)inMethodName arguments:(NSDictionary *)inArguments; + +// image upload—we use NSInputStream here because we want to have flexibity; with this you can upload either a file or NSData from NSImage +- (BOOL)uploadImageStream:(NSInputStream *)inImageStream suggestedFilename:(NSString *)inFilename MIMEType:(NSString *)inType arguments:(NSDictionary *)inArguments; + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 +@property (nonatomic, readonly) OFFlickrAPIContext *context; +@property (nonatomic, assign) OFFlickrAPIRequestDelegateType delegate; +@property (nonatomic, retain) id sessionInfo; +@property (nonatomic, assign) NSTimeInterval requestTimeoutInterval; +#endif +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.m b/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.m new file mode 100644 index 00000000..ca0ed7a2 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.m @@ -0,0 +1,565 @@ +// +// ObjectiveFlickr.m +// +// Copyright (c) 2009 Lukhnos D. Liu (http://lukhnos.org) +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +#import "ObjectiveFlickr.h" +#import "OFUtilities.h" +#import "OFXMLMapper.h" + +NSString *const OFFlickrSmallSquareSize = @"s"; +NSString *const OFFlickrThumbnailSize = @"t"; +NSString *const OFFlickrSmallSize = @"m"; +NSString *const OFFlickrMediumSize = nil; +NSString *const OFFlickrLargeSize = @"b"; + +NSString *const OFFlickrReadPermission = @"read"; +NSString *const OFFlickrWritePermission = @"write"; +NSString *const OFFlickrDeletePermission = @"delete"; + +NSString *const OFFlickrUploadTempFilenamePrefix = @"org.lukhnos.ObjectiveFlickr.upload"; +NSString *const OFFlickrAPIReturnedErrorDomain = @"com.flickr"; +NSString *const OFFlickrAPIRequestErrorDomain = @"org.lukhnos.ObjectiveFlickr"; + +// compatibility typedefs +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +typedef unsigned int NSUInteger; +#endif + +@interface OFFlickrAPIContext (PrivateMethods) +- (NSArray *)signedArgumentComponentsFromArguments:(NSDictionary *)inArguments useURIEscape:(BOOL)inUseEscape; +- (NSString *)signedQueryFromArguments:(NSDictionary *)inArguments; +@end + +#define kDefaultFlickrRESTAPIEndpoint @"http://api.flickr.com/services/rest/" +#define kDefaultFlickrPhotoSource @"http://static.flickr.com/" +#define kDefaultFlickrPhotoWebPageSource @"http://www.flickr.com/photos/" +#define kDefaultFlickrAuthEndpoint @"http://flickr.com/services/auth/" +#define kDefaultFlickrUploadEndpoint @"http://api.flickr.com/services/upload/" + +@implementation OFFlickrAPIContext +- (void)dealloc +{ + [key release]; + [sharedSecret release]; + [authToken release]; + + [RESTAPIEndpoint release]; + [photoSource release]; + [photoWebPageSource release]; + [authEndpoint release]; + [uploadEndpoint release]; + + [super dealloc]; +} + +- (id)initWithAPIKey:(NSString *)inKey sharedSecret:(NSString *)inSharedSecret +{ + if ((self = [super init])) { + key = [inKey copy]; + sharedSecret = [inSharedSecret copy]; + + RESTAPIEndpoint = kDefaultFlickrRESTAPIEndpoint; + photoSource = kDefaultFlickrPhotoSource; + photoWebPageSource = kDefaultFlickrPhotoWebPageSource; + authEndpoint = kDefaultFlickrAuthEndpoint; + uploadEndpoint = kDefaultFlickrUploadEndpoint; + } + return self; +} + +- (void)setAuthToken:(NSString *)inAuthToken +{ + NSString *tmp = authToken; + authToken = [inAuthToken copy]; + [tmp release]; +} + +- (NSString *)authToken +{ + return authToken; +} + +- (NSURL *)photoSourceURLFromDictionary:(NSDictionary *)inDictionary size:(NSString *)inSizeModifier +{ + // http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg + // http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg + + NSString *farm = [inDictionary objectForKey:@"farm"]; + NSString *photoID = [inDictionary objectForKey:@"id"]; + NSString *secret = [inDictionary objectForKey:@"secret"]; + NSString *server = [inDictionary objectForKey:@"server"]; + + NSMutableString *URLString = [NSMutableString stringWithString:@"http://"]; + if ([farm length]) { + [URLString appendFormat:@"farm%@.", farm]; + } + + // skips "http://" + NSAssert([server length], @"Must have server attribute"); + NSAssert([photoID length], @"Must have id attribute"); + NSAssert([secret length], @"Must have secret attribute"); + [URLString appendString:[photoSource substringFromIndex:7]]; + [URLString appendFormat:@"%@/%@_%@", server, photoID, secret]; + + if ([inSizeModifier length]) { + [URLString appendFormat:@"_%@.jpg", inSizeModifier]; + } + else { + [URLString appendString:@".jpg"]; + } + + return [NSURL URLWithString:URLString]; +} + +- (NSURL *)photoWebPageURLFromDictionary:(NSDictionary *)inDictionary +{ + return [NSURL URLWithString:[NSString stringWithFormat:@"%@%@/%@", photoWebPageSource, [inDictionary objectForKey:@"owner"], [inDictionary objectForKey:@"id"]]]; +} + +- (NSURL *)loginURLFromFrobDictionary:(NSDictionary *)inFrob requestedPermission:(NSString *)inPermission +{ + NSString *frob = [[inFrob objectForKey:@"frob"] objectForKey:OFXMLTextContentKey]; + NSDictionary *argDict = [frob length] ? [NSDictionary dictionaryWithObjectsAndKeys:frob, @"frob", inPermission, @"perms", nil] : [NSDictionary dictionaryWithObjectsAndKeys:inPermission, @"perms", nil]; + NSString *URLString = [NSString stringWithFormat:@"%@?%@", authEndpoint, [self signedQueryFromArguments:argDict]]; + return [NSURL URLWithString:URLString]; +} + +- (void)setRESTAPIEndpoint:(NSString *)inEndpoint +{ + NSString *tmp = RESTAPIEndpoint; + RESTAPIEndpoint = [inEndpoint copy]; + [tmp release]; +} + +- (NSString *)RESTAPIEndpoint +{ + return RESTAPIEndpoint; +} + +- (void)setPhotoSource:(NSString *)inSource +{ + if (![inSource hasPrefix:@"http://"]) { + return; + } + + NSString *tmp = photoSource; + photoSource = [inSource copy]; + [tmp release]; +} + +- (NSString *)photoSource +{ + return photoSource; +} + +- (void)setPhotoWebPageSource:(NSString *)inSource +{ + if (![inSource hasPrefix:@"http://"]) { + return; + } + + NSString *tmp = photoWebPageSource; + photoWebPageSource = [inSource copy]; + [tmp release]; +} + +- (NSString *)photoWebPageSource +{ + return photoWebPageSource; +} + +- (void)setAuthEndpoint:(NSString *)inEndpoint +{ + NSString *tmp = authEndpoint; + authEndpoint = [inEndpoint copy]; + [tmp release]; +} + +- (NSString *)authEndpoint +{ + return authEndpoint; +} + +- (void)setUploadEndpoint:(NSString *)inEndpoint +{ + NSString *tmp = uploadEndpoint; + uploadEndpoint = [inEndpoint copy]; + [tmp release]; +} + +- (NSString *)uploadEndpoint +{ + return uploadEndpoint; +} + +#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 +@synthesize key; +@synthesize sharedSecret; +#endif +@end + +@implementation OFFlickrAPIContext (PrivateMethods) +- (NSArray *)signedArgumentComponentsFromArguments:(NSDictionary *)inArguments useURIEscape:(BOOL)inUseEscape +{ + NSMutableDictionary *newArgs = [NSMutableDictionary dictionaryWithDictionary:inArguments]; + if ([key length]) { + [newArgs setObject:key forKey:@"api_key"]; + } + + if ([authToken length]) { + [newArgs setObject:authToken forKey:@"auth_token"]; + } + + // combine the args + NSMutableArray *argArray = [NSMutableArray array]; + NSMutableString *sigString = [NSMutableString stringWithString:[sharedSecret length] ? sharedSecret : @""]; + NSArray *sortedArgs = [[newArgs allKeys] sortedArrayUsingSelector:@selector(compare:)]; + NSEnumerator *argEnumerator = [sortedArgs objectEnumerator]; + NSString *nextKey; + while ((nextKey = [argEnumerator nextObject])) { + NSString *value = [newArgs objectForKey:nextKey]; + [sigString appendFormat:@"%@%@", nextKey, value]; + [argArray addObject:[NSArray arrayWithObjects:nextKey, (inUseEscape ? OFEscapedURLStringFromNSString(value) : value), nil]]; + } + + NSString *signature = OFMD5HexStringFromNSString(sigString); + [argArray addObject:[NSArray arrayWithObjects:@"api_sig", signature, nil]]; + return argArray; +} + + +- (NSString *)signedQueryFromArguments:(NSDictionary *)inArguments +{ + NSArray *argComponents = [self signedArgumentComponentsFromArguments:inArguments useURIEscape:YES]; + NSMutableArray *args = [NSMutableArray array]; + NSEnumerator *componentEnumerator = [argComponents objectEnumerator]; + NSArray *nextArg; + while ((nextArg = [componentEnumerator nextObject])) { + [args addObject:[nextArg componentsJoinedByString:@"="]]; + } + + return [args componentsJoinedByString:@"&"]; +} +@end + +@interface OFFlickrAPIRequest (PrivateMethods) +- (void)cleanUpTempFile; +@end + +@implementation OFFlickrAPIRequest +- (void)dealloc +{ + [context release]; + [HTTPRequest release]; + [sessionInfo release]; + + [self cleanUpTempFile]; + + [super dealloc]; +} + +- (id)initWithAPIContext:(OFFlickrAPIContext *)inContext +{ + if ((self = [super init])) { + context = [inContext retain]; + + HTTPRequest = [[LFHTTPRequest alloc] init]; + [HTTPRequest setDelegate:self]; + } + + return self; +} + +- (OFFlickrAPIContext *)context +{ + return context; +} + +- (OFFlickrAPIRequestDelegateType)delegate +{ + return delegate; +} + +- (void)setDelegate:(OFFlickrAPIRequestDelegateType)inDelegate +{ + delegate = inDelegate; +} + +- (id)sessionInfo +{ + return [[sessionInfo retain] autorelease]; +} + +- (void)setSessionInfo:(id)inInfo +{ + id tmp = sessionInfo; + sessionInfo = [inInfo retain]; + [tmp release]; +} + +- (NSTimeInterval)requestTimeoutInterval +{ + return [HTTPRequest timeoutInterval]; +} + +- (void)setRequestTimeoutInterval:(NSTimeInterval)inTimeInterval +{ + [HTTPRequest setTimeoutInterval:inTimeInterval]; +} + +- (BOOL)isRunning +{ + return [HTTPRequest isRunning]; +} + +- (void)cancel +{ + [HTTPRequest cancelWithoutDelegateMessage]; + [self cleanUpTempFile]; +} + +- (BOOL)callAPIMethodWithGET:(NSString *)inMethodName arguments:(NSDictionary *)inArguments +{ + if ([HTTPRequest isRunning]) { + return NO; + } + + // combine the parameters + NSMutableDictionary *newArgs = inArguments ? [NSMutableDictionary dictionaryWithDictionary:inArguments] : [NSMutableDictionary dictionary]; + [newArgs setObject:inMethodName forKey:@"method"]; + NSString *query = [context signedQueryFromArguments:newArgs]; + NSString *URLString = [NSString stringWithFormat:@"%@?%@", [context RESTAPIEndpoint], query]; + + [HTTPRequest setContentType:nil]; + return [HTTPRequest performMethod:LFHTTPRequestGETMethod onURL:[NSURL URLWithString:URLString] withData:nil]; +} + +- (BOOL)callAPIMethodWithPOST:(NSString *)inMethodName arguments:(NSDictionary *)inArguments +{ + if ([HTTPRequest isRunning]) { + return NO; + } + + // combine the parameters + NSMutableDictionary *newArgs = inArguments ? [NSMutableDictionary dictionaryWithDictionary:inArguments] : [NSMutableDictionary dictionary]; + [newArgs setObject:inMethodName forKey:@"method"]; + NSString *arguments = [context signedQueryFromArguments:newArgs]; + NSData *postData = [arguments dataUsingEncoding:NSUTF8StringEncoding]; + + [HTTPRequest setContentType:LFHTTPRequestWWWFormURLEncodedContentType]; + return [HTTPRequest performMethod:LFHTTPRequestPOSTMethod onURL:[NSURL URLWithString:[context RESTAPIEndpoint]] withData:postData]; +} + +- (BOOL)uploadImageStream:(NSInputStream *)inImageStream suggestedFilename:(NSString *)inFilename MIMEType:(NSString *)inType arguments:(NSDictionary *)inArguments +{ + if ([HTTPRequest isRunning]) { + return NO; + } + + if (![[context authToken] length]) { + return NO; + } + + // get the api_sig + NSArray *argComponents = [[self context] signedArgumentComponentsFromArguments:(inArguments ? inArguments : [NSDictionary dictionary]) useURIEscape:NO]; + NSString *separator = OFGenerateUUIDString(); + NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", separator]; + + // build the multipart form + NSMutableString *multipartBegin = [NSMutableString string]; + NSMutableString *multipartEnd = [NSMutableString string]; + + NSEnumerator *componentEnumerator = [argComponents objectEnumerator]; + NSArray *nextArgComponent; + while ((nextArgComponent = [componentEnumerator nextObject])) { + [multipartBegin appendFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n", separator, [nextArgComponent objectAtIndex:0], [nextArgComponent objectAtIndex:1]]; + } + + // add filename, if nil, generate a UUID + [multipartBegin appendFormat:@"--%@\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"%@\"\r\n", separator, [inFilename length] ? inFilename : OFGenerateUUIDString()]; + [multipartBegin appendFormat:@"Content-Type: %@\r\n\r\n", inType]; + + [multipartEnd appendFormat:@"\r\n--%@--", separator]; + + + // now we have everything, create a temp file for this purpose; although UUID is inferior to + [self cleanUpTempFile]; + + uploadTempFilename = [[NSTemporaryDirectory() stringByAppendingFormat:@"%@.%@", OFFlickrUploadTempFilenamePrefix, OFGenerateUUIDString()] retain]; + + // create the write stream + NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:uploadTempFilename append:NO]; + [outputStream open]; + + const char *UTF8String; + size_t writeLength; + UTF8String = [multipartBegin UTF8String]; + writeLength = strlen(UTF8String); + + size_t __unused actualWrittenLength; + actualWrittenLength = [outputStream write:(uint8_t *)UTF8String maxLength:writeLength]; + NSAssert(actualWrittenLength == writeLength, @"Must write multipartBegin"); + + // open the input stream + const size_t bufferSize = 65536; + size_t readSize = 0; + uint8_t *buffer = (uint8_t *)calloc(1, bufferSize); + NSAssert(buffer, @"Must have enough memory for copy buffer"); + + [inImageStream open]; + while ([inImageStream hasBytesAvailable]) { + if (!(readSize = [inImageStream read:buffer maxLength:bufferSize])) { + break; + } + + + size_t __unused actualWrittenLength; + actualWrittenLength = [outputStream write:buffer maxLength:readSize]; + NSAssert (actualWrittenLength == readSize, @"Must completes the writing"); + } + + [inImageStream close]; + free(buffer); + + + UTF8String = [multipartEnd UTF8String]; + writeLength = strlen(UTF8String); + actualWrittenLength = [outputStream write:(uint8_t *)UTF8String maxLength:writeLength]; + NSAssert(actualWrittenLength == writeLength, @"Must write multipartBegin"); + [outputStream close]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + NSDictionary *fileInfo = [[NSFileManager defaultManager] fileAttributesAtPath:uploadTempFilename traverseLink:YES]; + NSAssert(fileInfo, @"Must have upload temp file"); +#else + NSError *error = nil; + NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath:uploadTempFilename error:&error]; + NSAssert(fileInfo && !error, @"Must have upload temp file"); +#endif + NSNumber *fileSizeNumber = [fileInfo objectForKey:NSFileSize]; + NSUInteger fileSize = 0; + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + fileSize = [fileSizeNumber intValue]; +#else + if ([fileSizeNumber respondsToSelector:@selector(integerValue)]) { + fileSize = [fileSizeNumber integerValue]; + } + else { + fileSize = [fileSizeNumber intValue]; + } +#endif + + NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:uploadTempFilename]; + + [HTTPRequest setContentType:contentType]; + return [HTTPRequest performMethod:LFHTTPRequestPOSTMethod onURL:[NSURL URLWithString:[context uploadEndpoint]] withInputStream:inputStream knownContentSize:fileSize]; +} + +#pragma mark LFHTTPRequest delegate methods +- (void)httpRequestDidComplete:(LFHTTPRequest *)request +{ + NSDictionary *responseDictionary = [OFXMLMapper dictionaryMappedFromXMLData:[request receivedData]]; + NSDictionary *rsp = [responseDictionary objectForKey:@"rsp"]; + NSString *stat = [rsp objectForKey:@"stat"]; + + // this also fails when (responseDictionary, rsp, stat) == nil, so it's a guranteed way of checking the result + if (![stat isEqualToString:@"ok"]) { + NSDictionary *err = [rsp objectForKey:@"err"]; + NSString *code = [err objectForKey:@"code"]; + NSString *msg = [err objectForKey:@"msg"]; + + NSError *toDelegateError; + if ([code length]) { + // intValue for 10.4-compatibility + toDelegateError = [NSError errorWithDomain:OFFlickrAPIReturnedErrorDomain code:[code intValue] userInfo:[msg length] ? [NSDictionary dictionaryWithObjectsAndKeys:msg, NSLocalizedFailureReasonErrorKey, nil] : nil]; + } + else { + toDelegateError = [NSError errorWithDomain:OFFlickrAPIRequestErrorDomain code:OFFlickrAPIRequestFaultyXMLResponseError userInfo:nil]; + } + + if ([delegate respondsToSelector:@selector(flickrAPIRequest:didFailWithError:)]) { + [delegate flickrAPIRequest:self didFailWithError:toDelegateError]; + } + return; + } + + [self cleanUpTempFile]; + if ([delegate respondsToSelector:@selector(flickrAPIRequest:didCompleteWithResponse:)]) { + [delegate flickrAPIRequest:self didCompleteWithResponse:rsp]; + } +} + +- (void)httpRequest:(LFHTTPRequest *)request didFailWithError:(NSString *)error +{ + NSError *toDelegateError = nil; + if ([error isEqualToString:LFHTTPRequestConnectionError]) { + toDelegateError = [NSError errorWithDomain:OFFlickrAPIRequestErrorDomain code:OFFlickrAPIRequestConnectionError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Network connection error", NSLocalizedFailureReasonErrorKey, nil]]; + } + else if ([error isEqualToString:LFHTTPRequestTimeoutError]) { + toDelegateError = [NSError errorWithDomain:OFFlickrAPIRequestErrorDomain code:OFFlickrAPIRequestTimeoutError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Request timeout", NSLocalizedFailureReasonErrorKey, nil]]; + } + else { + toDelegateError = [NSError errorWithDomain:OFFlickrAPIRequestErrorDomain code:OFFlickrAPIRequestUnknownError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unknown error", NSLocalizedFailureReasonErrorKey, nil]]; + } + + [self cleanUpTempFile]; + if ([delegate respondsToSelector:@selector(flickrAPIRequest:didFailWithError:)]) { + [delegate flickrAPIRequest:self didFailWithError:toDelegateError]; + } +} + +- (void)httpRequest:(LFHTTPRequest *)request sentBytes:(NSUInteger)bytesSent total:(NSUInteger)total +{ + if (uploadTempFilename && [delegate respondsToSelector:@selector(flickrAPIRequest:imageUploadSentBytes:totalBytes:)]) { + [delegate flickrAPIRequest:self imageUploadSentBytes:bytesSent totalBytes:total]; + } +} +@end + +@implementation OFFlickrAPIRequest (PrivateMethods) +- (void)cleanUpTempFile + +{ + if (uploadTempFilename) { + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:uploadTempFilename]) { + BOOL __unused removeResult = NO; +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + removeResult = [fileManager removeFileAtPath:uploadTempFilename handler:nil]; +#else + NSError *error = nil; + removeResult = [fileManager removeItemAtPath:uploadTempFilename error:&error]; +#endif + + NSAssert(removeResult, @"Should be able to remove temp file"); + } + + [uploadTempFilename release]; + uploadTempFilename = nil; + } +} +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.h b/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.h new file mode 100644 index 00000000..8ae2dcf5 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.h @@ -0,0 +1,45 @@ +// +// SHKFlickr +// Flickr +// +// Created by Neil Bostrom on 23/02/2011. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +#import +#import "SHK.h" +#import "SHKSharer.h" +#import "SHKOAuthView.h" +#import "ObjectiveFlickr.h" + +@interface SHKFlickr : SHKSharer { + + OFFlickrAPIContext *flickrContext; + OFFlickrAPIRequest *flickrRequest; + NSString *flickrUserName; +} + +@property (nonatomic, readonly) OFFlickrAPIContext *flickrContext; +@property (nonatomic, retain) NSString *flickrUserName; + +- (void)sendPhoto; + +@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m b/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m new file mode 100644 index 00000000..c9d15593 --- /dev/null +++ b/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m @@ -0,0 +1,219 @@ +// +// SHKFlickr +// Flickr +// +// Created by Neil Bostrom on 23/02/2011. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// Flickr Library: ObjectiveFlickr - https://github.com/lukhnos/objectiveflickr + + +#import "SHKFlickr.h" + +NSString *kStoredAuthTokenKeyName = @"FlickrAuthToken"; + +NSString *kGetAuthTokenStep = @"kGetAuthTokenStep"; +NSString *kCheckTokenStep = @"kCheckTokenStep"; +NSString *kUploadImageStep = @"kUploadImageStep"; +NSString *kSetImagePropertiesStep = @"kSetImagePropertiesStep"; + +@implementation SHKFlickr + +@synthesize flickrContext, flickrUserName; + ++ (NSString *)sharerTitle +{ + return @"Flickr"; +} + ++ (BOOL)canShareImage +{ + return YES; +} + ++ (BOOL)canShare +{ + return YES; +} + +- (BOOL)isAuthorized +{ + return [self.flickrContext.authToken length]; +} + +- (OFFlickrAPIContext *)flickrContext +{ + if (!flickrContext) { + flickrContext = [[OFFlickrAPIContext alloc] initWithAPIKey: SHKFlickrConsumerKey sharedSecret: SHKFlickrSecretKey]; + + NSString *authToken = [SHK getAuthValueForKey: kStoredAuthTokenKeyName forSharer:[self sharerId]]; + if (authToken != nil) { + flickrContext.authToken = authToken; + } + } + + return flickrContext; +} + +- (OFFlickrAPIRequest *)flickrRequest +{ + if (!flickrRequest) { + flickrRequest = [[OFFlickrAPIRequest alloc] initWithAPIContext:self.flickrContext]; + flickrRequest.delegate = self; + flickrRequest.requestTimeoutInterval = 60.0; + } + + return flickrRequest; +} + ++ (void)logout +{ + [SHK removeAuthValueForKey:kStoredAuthTokenKeyName forSharer:[self sharerId]]; +} + +- (void)authorizationFormShow +{ + NSURL *loginURL = [self.flickrContext loginURLFromFrobDictionary:nil requestedPermission:OFFlickrWritePermission]; + SHKOAuthView *auth = [[SHKOAuthView alloc] initWithURL:loginURL delegate:self]; + [[SHK currentHelper] showViewController:auth]; + [auth release]; +} + +- (BOOL)send +{ + if (self.flickrUserName != nil) { + [self sendPhoto]; + } + else { + + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + + [self flickrRequest].sessionInfo = kCheckTokenStep; + [flickrRequest callAPIMethodWithGET:@"flickr.auth.checkToken" arguments:nil]; + } + + return YES; +} + +- (void)sendPhoto { + + [self sendDidStart]; + + NSData *JPEGData = UIImageJPEGRepresentation(item.image, 1.0); + + self.flickrRequest.sessionInfo = kUploadImageStep; + [self.flickrRequest uploadImageStream:[NSInputStream inputStreamWithData:JPEGData] suggestedFilename:item.title MIMEType:@"image/jpeg" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"0", @"is_public", nil]]; +} + +- (NSURL *)authorizeCallbackURL { + return [NSURL URLWithString: SHKFlickrCallbackUrl]; +} + +- (void)tokenAuthorizeView:(SHKOAuthView *)authView didFinishWithSuccess:(BOOL)success queryParams:(NSMutableDictionary *)queryParams error:(NSError *)error { + + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; + + if (!success) + { + [[[[UIAlertView alloc] initWithTitle:SHKLocalizedString(@"Authorize Error") + message:error!=nil?[error localizedDescription]:SHKLocalizedString(@"There was an error while authorizing") + delegate:nil + cancelButtonTitle:SHKLocalizedString(@"Close") + otherButtonTitles:nil] autorelease] show]; + } + else + { + [[SHKActivityIndicator currentIndicator] displayActivity:SHKLocalizedString(@"Logging In...")]; + + // query has the form of "&frob=", the rest is the frob + NSString *frob = [queryParams objectForKey:@"frob"]; + + [self flickrRequest].sessionInfo = kGetAuthTokenStep; + [flickrRequest callAPIMethodWithGET:@"flickr.auth.getToken" arguments:[NSDictionary dictionaryWithObjectsAndKeys:frob, @"frob", nil]]; + } +} + +- (void)tokenAuthorizeCancelledView:(SHKOAuthView *)authView { + + [[SHK currentHelper] hideCurrentViewControllerAnimated:YES]; +} + +- (void)setAndStoreFlickrAuthToken:(NSString *)inAuthToken +{ + if (![inAuthToken length]) { + + [SHKFlickr logout]; + } + else { + + self.flickrContext.authToken = inAuthToken; + [SHK setAuthValue:inAuthToken forKey:kStoredAuthTokenKeyName forSharer:[self sharerId]]; + } +} + +- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary +{ + if (inRequest.sessionInfo == kUploadImageStep) { + + NSString *photoID = [[inResponseDictionary valueForKeyPath:@"photoid"] textContent]; + + flickrRequest.sessionInfo = kSetImagePropertiesStep; + [flickrRequest callAPIMethodWithPOST:@"flickr.photos.setMeta" arguments:[NSDictionary dictionaryWithObjectsAndKeys:photoID, @"photo_id", item.title, @"title", nil, @"description", nil]]; + } + else if (inRequest.sessionInfo == kSetImagePropertiesStep) { + + [self sendDidFinish]; + } + else { + + if (inRequest.sessionInfo == kGetAuthTokenStep) { + [self setAndStoreFlickrAuthToken:[[inResponseDictionary valueForKeyPath:@"auth.token"] textContent]]; + self.flickrUserName = [inResponseDictionary valueForKeyPath:@"auth.user.username"]; + + [self share]; + } + else if (inRequest.sessionInfo == kCheckTokenStep) { + self.flickrUserName = [inResponseDictionary valueForKeyPath:@"auth.user.username"]; + + [self sendPhoto]; + } + } +} + +- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didFailWithError:(NSError *)inError +{ + if (inRequest.sessionInfo == kGetAuthTokenStep) { + } + else if (inRequest.sessionInfo == kCheckTokenStep) { + [self setAndStoreFlickrAuthToken:nil]; + } + + [self sharer: self failedWithError: inError shouldRelogin: NO]; +} + +- (void)dealloc +{ + [flickrContext release]; + [flickrRequest release]; + [flickrUserName release]; + [super dealloc]; +} + +@end From f1f8eb57f931965f4c6f2c37deef3b8fd4268f71 Mon Sep 17 00:00:00 2001 From: Neil Bostrom Date: Thu, 31 Mar 2011 18:53:49 +0100 Subject: [PATCH 2/3] Added Flickr sharer to SHKSharers.plist --- Classes/ShareKit/Core/SHKSharers.plist | 1 + 1 file changed, 1 insertion(+) diff --git a/Classes/ShareKit/Core/SHKSharers.plist b/Classes/ShareKit/Core/SHKSharers.plist index db686302..8ca035e5 100644 --- a/Classes/ShareKit/Core/SHKSharers.plist +++ b/Classes/ShareKit/Core/SHKSharers.plist @@ -21,6 +21,7 @@ SHKReadItLater SHKInstapaper SHKTumblr + SHKFlickr From 18df3650fe91592bec11282306acbc779e8c1b32 Mon Sep 17 00:00:00 2001 From: Neil Bostrom Date: Fri, 23 Sep 2011 08:59:11 +0100 Subject: [PATCH 3/3] Removed unused files and removed is public flag With feedback from Eric-Wright, Removed unused files NSData_LFHTTPFormExtensions, LFWebAPIKit, LFSiteReachability. Also removed is public flag to fall back to users default when upload pictures. This branch also seems to have issues building Evernote so removed reference to this --- .../Services/Flickr/LFSiteReachability.h | 72 ----- .../Services/Flickr/LFSiteReachability.m | 285 ------------------ .../Sharers/Services/Flickr/LFWebAPIKit.h | 35 --- .../Flickr/NSData_LFHTTPFormExtensions.h | 33 -- .../Flickr/NSData_LFHTTPFormExtensions.m | 50 --- .../Sharers/Services/Flickr/ObjectiveFlickr.h | 2 +- .../Sharers/Services/Flickr/SHKFlickr.m | 2 +- ShareKit.xcodeproj/project.pbxproj | 190 +++--------- 8 files changed, 40 insertions(+), 629 deletions(-) delete mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h delete mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m delete mode 100644 Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h delete mode 100644 Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h delete mode 100644 Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h b/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h deleted file mode 100644 index 94998c55..00000000 --- a/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.h +++ /dev/null @@ -1,72 +0,0 @@ -// -// LFSiteReachability.h -// -// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) -// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import -#import "LFHTTPRequest.h" - -extern NSString *const LFSiteReachabilityConnectionTypeWiFi; -extern NSString *const LFSiteReachabilityConnectionTypeWWAN; - -@class LFSiteReachability; - -@protocol LFSiteReachabilityDelegate -- (void)reachability:(LFSiteReachability *)inReachability site:(NSURL *)inURL isAvailableOverConnectionType:(NSString *)inConnectionType; -- (void)reachability:(LFSiteReachability *)inReachability siteIsNotAvailable:(NSURL *)inURL; -@end - -@interface LFSiteReachability : NSObject -{ - id delegate; - NSURL *siteURL; - SCNetworkReachabilityRef reachability; - LFHTTPRequest *siteRequest; - NSTimer *timeoutTimer; - id lastCheckStatus; -} -- (void)startChecking; -- (void)stopChecking; -- (BOOL)isChecking; - -// When networkConnectivityExists returns YES, it simply means network interface is available -// (e.g. WiFi not disabled, has 3G, etc.); that is, the device has the "potential" to connect, -// but that does not mean that an HTTP request will succeed, for various reasons--such as -// the IP is not yet obtained; the interface is not yet fully "up", base station or WiFi hasn't -// assigned a valid IP yet, etc. To read this way: If this method returns NO, it means -// "forget about network, it doesn't exist at all" -- (BOOL)networkConnectivityExists; - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 -- (id)delegate; -- (NSURL*)siteURL; -- (NSTimeInterval)timeoutInterval; -#else -@property (assign, nonatomic) id delegate; -@property (retain, nonatomic) NSURL *siteURL; -@property (nonatomic) NSTimeInterval timeoutInterval; -#endif -@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m b/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m deleted file mode 100644 index 361bc65b..00000000 --- a/Classes/ShareKit/Sharers/Services/Flickr/LFSiteReachability.m +++ /dev/null @@ -1,285 +0,0 @@ -// -// LFSiteReachability.m -// -// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) -// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "LFSiteReachability.h" -#import - -static NSString *kDefaultSite = @"http://google.com"; -static NSTimeInterval kDefaultTimeoutInterval = 15.0; - -#define LFSRDebug(format, ...) -// #define LFSRDebug NSLog - -NSString *const LFSiteReachabilityConnectionTypeWiFi = @"LFSiteReachabilityConnectionTypeWiFi"; -NSString *const LFSiteReachabilityConnectionTypeWWAN = @"LFSiteReachabilityConnectionTypeWWAN"; -NSString *const LFSiteReachabilityNotReachableStatus = @"LFSiteReachabilityNotReachable"; - -#if !TARGET_OS_IPHONE - #define SCNetworkReachabilityFlags SCNetworkConnectionFlags - #define kSCNetworkReachabilityFlagsConnectionRequired kSCNetworkFlagsConnectionRequired - #define kSCNetworkReachabilityFlagsReachable kSCNetworkFlagsReachable - #define kSCNetworkReachabilityFlagsIsWWAN 0 -#endif - -static void LFSiteReachabilityCallback(SCNetworkReachabilityRef inTarget, SCNetworkReachabilityFlags inFlags, void *inInfo); - -@implementation LFSiteReachability -- (void)dealloc -{ - delegate = nil; - - [siteRequest setDelegate:nil]; - [self stopChecking]; - [siteRequest release]; - [siteURL release]; - - [super dealloc]; -} - -- (void)finalize -{ - [siteRequest setDelegate:nil]; - [self stopChecking]; - [super finalize]; -} - -- (id)init -{ - if ((self = [super init])) { - siteRequest = [[LFHTTPRequest alloc] init]; - [siteRequest setDelegate:self]; - [siteRequest setTimeoutInterval:kDefaultTimeoutInterval]; - - siteURL = [[NSURL URLWithString:kDefaultSite] retain]; - } - - return self; -} - -- (void)handleTimeoutTimer:(NSTimer *)inTimer -{ - LFSRDebug(@"%s", __PRETTY_FUNCTION__); - [inTimer invalidate]; - - if (lastCheckStatus != LFSiteReachabilityNotReachableStatus) { - lastCheckStatus = LFSiteReachabilityNotReachableStatus; - if ([delegate respondsToSelector:@selector(reachability:siteIsNotAvailable:)]) { - [delegate reachability:self siteIsNotAvailable:siteURL]; - } - } -} - -- (void)stopTimeoutTimer -{ - if ([timeoutTimer isValid]) { - [timeoutTimer invalidate]; - } - - [timeoutTimer release]; - timeoutTimer = nil; -} - -- (void)handleReachabilityCallbackFlags:(SCNetworkReachabilityFlags)inFlags -{ - [self stopTimeoutTimer]; - - LFSRDebug(@"%s, flags: 0x%08x", __PRETTY_FUNCTION__, inFlags); - - if (inFlags & kSCNetworkReachabilityFlagsReachable) { - NSString *connectionType = (inFlags & kSCNetworkReachabilityFlagsIsWWAN) ? LFSiteReachabilityConnectionTypeWWAN : LFSiteReachabilityConnectionTypeWiFi; - - BOOL connectionRequestNotRequired = !(inFlags & kSCNetworkReachabilityFlagsConnectionRequired); - - if (siteURL) { - LFSRDebug(@"%s, connectionRequestNotRequired: %d, attempting to request from: %@", __PRETTY_FUNCTION__, connectionRequestNotRequired, siteURL); - - // next stage: send the request - [siteRequest cancelWithoutDelegateMessage]; - [siteRequest setSessionInfo:connectionType]; - if ([siteRequest performMethod:LFHTTPRequestHEADMethod onURL:siteURL withData:nil]) { - return; - } - } - else { - if (lastCheckStatus != connectionType) { - lastCheckStatus = connectionType; - if (connectionRequestNotRequired && [delegate respondsToSelector:@selector(reachability:site:isAvailableOverConnectionType:)]) { - [delegate reachability:self site:siteURL isAvailableOverConnectionType:connectionType]; - return; - } - } - } - } - - // if all fails - if (lastCheckStatus != LFSiteReachabilityNotReachableStatus) { - lastCheckStatus = LFSiteReachabilityNotReachableStatus; - if ([delegate respondsToSelector:@selector(reachability:siteIsNotAvailable:)]) { - [delegate reachability:self siteIsNotAvailable:siteURL]; - } - } -} - -- (BOOL)networkConnectivityExists -{ - // 0.0.0.0 - struct sockaddr_in zeroAddress; - bzero(&zeroAddress, sizeof(zeroAddress)); - zeroAddress.sin_len = sizeof(zeroAddress); - zeroAddress.sin_family = AF_INET; - - SCNetworkReachabilityRef localReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); - SCNetworkReachabilityFlags flags = 0; - - BOOL capable = NO; - if (SCNetworkReachabilityGetFlags(localReachability, &flags)) { - if (flags & kSCNetworkReachabilityFlagsReachable) { - capable = YES; - } - } - - CFRelease(localReachability); - return capable; -} - -- (void)startChecking -{ - [self stopChecking]; - - // 0.0.0.0 - struct sockaddr_in zeroAddress; - bzero(&zeroAddress, sizeof(zeroAddress)); - zeroAddress.sin_len = sizeof(zeroAddress); - zeroAddress.sin_family = AF_INET; - - reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress); - SCNetworkReachabilityFlags flags = 0; - - BOOL createTimeoutTimer = YES; - if (SCNetworkReachabilityGetFlags(reachability, &flags)) { - [self handleReachabilityCallbackFlags:flags]; - createTimeoutTimer = NO; - } - - SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL}; - SCNetworkReachabilitySetCallback(reachability, LFSiteReachabilityCallback, &context); - SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); - - if (createTimeoutTimer) { - timeoutTimer = [[NSTimer scheduledTimerWithTimeInterval:[siteRequest timeoutInterval] target:self selector:@selector(handleTimeoutTimer:) userInfo:NULL repeats:NO] retain]; - } -} - -- (void)stopChecking -{ - [siteRequest cancelWithoutDelegateMessage]; - [self stopTimeoutTimer]; - - if (reachability) { - SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); - CFRelease(reachability); - reachability = NULL; - } - - lastCheckStatus = nil; -} - -- (BOOL)isChecking -{ - return !!reachability; -} - -- (NSTimeInterval)timeoutInterval -{ - return [siteRequest timeoutInterval]; -} - -- (void)setTimeoutInterval:(NSTimeInterval)inInterval -{ - [siteRequest setTimeoutInterval:inInterval]; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 -- (void)httpRequest:(LFHTTPRequest *)request didReceiveStatusCode:(int)statusCode URL:(NSURL *)url responseHeader:(CFHTTPMessageRef)header -#else -- (void)httpRequest:(LFHTTPRequest *)request didReceiveStatusCode:(NSUInteger)statusCode URL:(NSURL *)url responseHeader:(CFHTTPMessageRef)header -#endif -{ - LFSRDebug(@"%s, code: %d, URL: %@, header: %@", __PRETTY_FUNCTION__, statusCode, url, (id)header); -} - -- (void)httpRequestDidComplete:(LFHTTPRequest *)request -{ - LFSRDebug(@"%s, connection type: %@, received data: %@", __PRETTY_FUNCTION__, [request sessionInfo], [request receivedData]); - - if (lastCheckStatus != [request sessionInfo]) { - lastCheckStatus = [request sessionInfo]; - if ([delegate respondsToSelector:@selector(reachability:site:isAvailableOverConnectionType:)]) { - [delegate reachability:self site:siteURL isAvailableOverConnectionType:[request sessionInfo]]; - } - } -} - -- (void)httpRequest:(LFHTTPRequest *)request didFailWithError:(NSString *)error -{ - LFSRDebug(@"%s, error: %@", __PRETTY_FUNCTION__, error); - - if (lastCheckStatus != LFSiteReachabilityNotReachableStatus) { - lastCheckStatus = LFSiteReachabilityNotReachableStatus; - if ([delegate respondsToSelector:@selector(reachability:siteIsNotAvailable:)]) { - [delegate reachability:self siteIsNotAvailable:siteURL]; - } - } -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 -- (id)delegate -{ - return delegate; -} - -- (NSURL*)siteURL -{ - return [[siteURL retain] autorelease]; -} - -#else -@synthesize delegate; -@synthesize siteURL; -#endif -@end - - -void LFSiteReachabilityCallback(SCNetworkReachabilityRef inTarget, SCNetworkReachabilityFlags inFlags, void *inInfo) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - LFSRDebug(@"%s, flags: 0x%08x", __PRETTY_FUNCTION__, inFlags); - - [(LFSiteReachability *)inInfo handleReachabilityCallbackFlags:inFlags]; - [pool drain]; -} diff --git a/Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h b/Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h deleted file mode 100644 index b277eca9..00000000 --- a/Classes/ShareKit/Sharers/Services/Flickr/LFWebAPIKit.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// LFWebAPIKit.h -// -// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) -// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "LFHTTPRequest.h" -#import "NSData_LFHTTPFormExtensions.h" - -// LFSiteReachability is only available when built aginst OS X 10.5+ or iPhone SDK -#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 - #import "LFSiteReachability.h" -#endif diff --git a/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h b/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h deleted file mode 100644 index 43597396..00000000 --- a/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// NSData+LFHTTPFormExtensions.h -// -// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) -// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import - -@interface NSData (LFHTTPFormExtensions) -+ (id)dataAsWWWURLEncodedFormFromDictionary:(NSDictionary *)formDictionary; -@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m b/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m deleted file mode 100644 index 8f4d6e8c..00000000 --- a/Classes/ShareKit/Sharers/Services/Flickr/NSData_LFHTTPFormExtensions.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// NSData+LFHTTPFormExtensions.m -// -// Copyright (c) 2007-2009 Lithoglyph Inc. (http://lithoglyph.com) -// Copyright (c) 2007-2009 Lukhnos D. Liu (http://lukhnos.org) -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -// - -#import "NSData_LFHTTPFormExtensions.h" - -@implementation NSData (LFHTTPFormExtensions) -+ (id)dataAsWWWURLEncodedFormFromDictionary:(NSDictionary *)formDictionary -{ - NSMutableString *combinedDataString = [NSMutableString string]; - NSEnumerator *enumerator = [formDictionary keyEnumerator]; - - id key = [enumerator nextObject]; - if (key) { - id value = [formDictionary objectForKey:key]; - [combinedDataString appendString:[NSString stringWithFormat:@"%@=%@", [(NSString*)key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; - - while ((key = [enumerator nextObject])) { - value = [formDictionary objectForKey:key]; - [combinedDataString appendString:[NSString stringWithFormat:@"&%@=%@", [(NSString*)key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], [value stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; - } - } - - return [combinedDataString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; -} -@end diff --git a/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h b/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h index bd63e2af..9cf735fa 100644 --- a/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h +++ b/Classes/ShareKit/Sharers/Services/Flickr/ObjectiveFlickr.h @@ -25,7 +25,7 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#import "LFWebAPIKit.h" +#import "LFHTTPRequest.h" #import "OFUtilities.h" #import "OFXMLMapper.h" diff --git a/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m b/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m index c9d15593..dd3d2dd8 100644 --- a/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m +++ b/Classes/ShareKit/Sharers/Services/Flickr/SHKFlickr.m @@ -119,7 +119,7 @@ - (void)sendPhoto { NSData *JPEGData = UIImageJPEGRepresentation(item.image, 1.0); self.flickrRequest.sessionInfo = kUploadImageStep; - [self.flickrRequest uploadImageStream:[NSInputStream inputStreamWithData:JPEGData] suggestedFilename:item.title MIMEType:@"image/jpeg" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"0", @"is_public", nil]]; + [self.flickrRequest uploadImageStream:[NSInputStream inputStreamWithData:JPEGData] suggestedFilename:item.title MIMEType:@"image/jpeg" arguments: nil]; } - (NSURL *)authorizeCallbackURL { diff --git a/ShareKit.xcodeproj/project.pbxproj b/ShareKit.xcodeproj/project.pbxproj index adcbf90e..bcfbcf54 100755 --- a/ShareKit.xcodeproj/project.pbxproj +++ b/ShareKit.xcodeproj/project.pbxproj @@ -11,27 +11,16 @@ 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */; }; + 2896265F142C725C00BFC10F /* LFHTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 28962652142C725C00BFC10F /* LFHTTPRequest.m */; }; + 28962662142C725C00BFC10F /* ObjectiveFlickr.m in Sources */ = {isa = PBXBuildFile; fileRef = 28962659142C725C00BFC10F /* ObjectiveFlickr.m */; }; + 28962663142C725C00BFC10F /* OFXMLMapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 2896265C142C725C00BFC10F /* OFXMLMapper.m */; }; + 28962664142C725C00BFC10F /* SHKFlickr.m in Sources */ = {isa = PBXBuildFile; fileRef = 2896265E142C725C00BFC10F /* SHKFlickr.m */; }; + 28962666142C728100BFC10F /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28962665142C728100BFC10F /* CFNetwork.framework */; }; 28AD73600D9D9599002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD735F0D9D9599002E5188 /* MainWindow.xib */; }; 28F335F11007B36200424DE2 /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28F335F01007B36200424DE2 /* RootViewController.xib */; }; 4312CF7C11CB33E200E61D7A /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4312CF7B11CB33E200E61D7A /* MessageUI.framework */; }; 43150A8D11E78697008C6B68 /* SHKInstapaper.m in Sources */ = {isa = PBXBuildFile; fileRef = 43150A8C11E78697008C6B68 /* SHKInstapaper.m */; }; 432B147C11FF4B0700291B37 /* SHKPhotoAlbum.m in Sources */ = {isa = PBXBuildFile; fileRef = 432B147B11FF4B0700291B37 /* SHKPhotoAlbum.m */; }; - 4379F2B31291AC9700D2A41E /* EDAMLimits.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F28D1291AC9700D2A41E /* EDAMLimits.m */; }; - 4379F2B41291AC9700D2A41E /* Errors.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F28F1291AC9700D2A41E /* Errors.m */; }; - 4379F2B51291AC9700D2A41E /* NoteStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2911291AC9700D2A41E /* NoteStore.m */; }; - 4379F2B61291AC9700D2A41E /* Types.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2931291AC9700D2A41E /* Types.m */; }; - 4379F2B71291AC9700D2A41E /* UserStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2951291AC9700D2A41E /* UserStore.m */; }; - 4379F2B81291AC9700D2A41E /* TBinaryProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2991291AC9700D2A41E /* TBinaryProtocol.m */; }; - 4379F2B91291AC9700D2A41E /* TProtocolException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F29C1291AC9700D2A41E /* TProtocolException.m */; }; - 4379F2BA1291AC9700D2A41E /* TProtocolUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F29F1291AC9700D2A41E /* TProtocolUtil.m */; }; - 4379F2BB1291AC9700D2A41E /* TApplicationException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2A11291AC9700D2A41E /* TApplicationException.m */; }; - 4379F2BC1291AC9700D2A41E /* TException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2A31291AC9700D2A41E /* TException.m */; }; - 4379F2BD1291AC9700D2A41E /* THTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2A81291AC9700D2A41E /* THTTPClient.m */; }; - 4379F2BE1291AC9700D2A41E /* TMemoryBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2AA1291AC9700D2A41E /* TMemoryBuffer.m */; }; - 4379F2BF1291AC9700D2A41E /* TTransportException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2AD1291AC9700D2A41E /* TTransportException.m */; }; - 4379F2C01291AC9700D2A41E /* TSharedProcessorFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2AF1291AC9700D2A41E /* TSharedProcessorFactory.m */; }; - 4379F2C11291AC9700D2A41E /* SHKEvernote.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2B11291AC9700D2A41E /* SHKEvernote.m */; }; - 4379F2C21291AC9700D2A41E /* SHKEvernote.md in Resources */ = {isa = PBXBuildFile; fileRef = 4379F2B21291AC9700D2A41E /* SHKEvernote.md */; }; 4379F2EA1291AE5700D2A41E /* NSData+md5.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F2E91291AE5700D2A41E /* NSData+md5.m */; }; 4379F3B21291C45700D2A41E /* SHKTextMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4379F3B11291C45700D2A41E /* SHKTextMessage.m */; }; 43A5370011DBE3B9004A1712 /* SHKOAuthSharer.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A5367511DBE3B9004A1712 /* SHKOAuthSharer.m */; }; @@ -113,6 +102,16 @@ 1D6058910D05DD3D006BFB54 /* ShareKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShareKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 28962651142C725C00BFC10F /* LFHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LFHTTPRequest.h; sourceTree = ""; }; + 28962652142C725C00BFC10F /* LFHTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LFHTTPRequest.m; sourceTree = ""; }; + 28962658142C725C00BFC10F /* ObjectiveFlickr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectiveFlickr.h; sourceTree = ""; }; + 28962659142C725C00BFC10F /* ObjectiveFlickr.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectiveFlickr.m; sourceTree = ""; }; + 2896265A142C725C00BFC10F /* OFUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OFUtilities.h; sourceTree = ""; }; + 2896265B142C725C00BFC10F /* OFXMLMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OFXMLMapper.h; sourceTree = ""; }; + 2896265C142C725C00BFC10F /* OFXMLMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OFXMLMapper.m; sourceTree = ""; }; + 2896265D142C725C00BFC10F /* SHKFlickr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKFlickr.h; sourceTree = ""; }; + 2896265E142C725C00BFC10F /* SHKFlickr.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKFlickr.m; sourceTree = ""; }; + 28962665142C728100BFC10F /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; 28A0AAE50D9B0CCF005BE974 /* ShareKit_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShareKit_Prefix.pch; sourceTree = ""; }; 28AD735F0D9D9599002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 28F335F01007B36200424DE2 /* RootViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RootViewController.xib; sourceTree = ""; }; @@ -122,42 +121,6 @@ 43150A8C11E78697008C6B68 /* SHKInstapaper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKInstapaper.m; sourceTree = ""; }; 432B147A11FF4B0700291B37 /* SHKPhotoAlbum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKPhotoAlbum.h; sourceTree = ""; }; 432B147B11FF4B0700291B37 /* SHKPhotoAlbum.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKPhotoAlbum.m; sourceTree = ""; }; - 4379F28C1291AC9700D2A41E /* EDAMLimits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EDAMLimits.h; sourceTree = ""; }; - 4379F28D1291AC9700D2A41E /* EDAMLimits.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EDAMLimits.m; sourceTree = ""; }; - 4379F28E1291AC9700D2A41E /* Errors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Errors.h; sourceTree = ""; }; - 4379F28F1291AC9700D2A41E /* Errors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Errors.m; sourceTree = ""; }; - 4379F2901291AC9700D2A41E /* NoteStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoteStore.h; sourceTree = ""; }; - 4379F2911291AC9700D2A41E /* NoteStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NoteStore.m; sourceTree = ""; }; - 4379F2921291AC9700D2A41E /* Types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Types.h; sourceTree = ""; }; - 4379F2931291AC9700D2A41E /* Types.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Types.m; sourceTree = ""; }; - 4379F2941291AC9700D2A41E /* UserStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserStore.h; sourceTree = ""; }; - 4379F2951291AC9700D2A41E /* UserStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UserStore.m; sourceTree = ""; }; - 4379F2981291AC9700D2A41E /* TBinaryProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBinaryProtocol.h; sourceTree = ""; }; - 4379F2991291AC9700D2A41E /* TBinaryProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBinaryProtocol.m; sourceTree = ""; }; - 4379F29A1291AC9700D2A41E /* TProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TProtocol.h; sourceTree = ""; }; - 4379F29B1291AC9700D2A41E /* TProtocolException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TProtocolException.h; sourceTree = ""; }; - 4379F29C1291AC9700D2A41E /* TProtocolException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TProtocolException.m; sourceTree = ""; }; - 4379F29D1291AC9700D2A41E /* TProtocolFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TProtocolFactory.h; sourceTree = ""; }; - 4379F29E1291AC9700D2A41E /* TProtocolUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TProtocolUtil.h; sourceTree = ""; }; - 4379F29F1291AC9700D2A41E /* TProtocolUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TProtocolUtil.m; sourceTree = ""; }; - 4379F2A01291AC9700D2A41E /* TApplicationException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TApplicationException.h; sourceTree = ""; }; - 4379F2A11291AC9700D2A41E /* TApplicationException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TApplicationException.m; sourceTree = ""; }; - 4379F2A21291AC9700D2A41E /* TException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TException.h; sourceTree = ""; }; - 4379F2A31291AC9700D2A41E /* TException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TException.m; sourceTree = ""; }; - 4379F2A41291AC9700D2A41E /* TProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TProcessor.h; sourceTree = ""; }; - 4379F2A51291AC9700D2A41E /* TProcessorFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TProcessorFactory.h; sourceTree = ""; }; - 4379F2A71291AC9700D2A41E /* THTTPClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = THTTPClient.h; sourceTree = ""; }; - 4379F2A81291AC9700D2A41E /* THTTPClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = THTTPClient.m; sourceTree = ""; }; - 4379F2A91291AC9700D2A41E /* TMemoryBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TMemoryBuffer.h; sourceTree = ""; }; - 4379F2AA1291AC9700D2A41E /* TMemoryBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TMemoryBuffer.m; sourceTree = ""; }; - 4379F2AB1291AC9700D2A41E /* TTransport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTransport.h; sourceTree = ""; }; - 4379F2AC1291AC9700D2A41E /* TTransportException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TTransportException.h; sourceTree = ""; }; - 4379F2AD1291AC9700D2A41E /* TTransportException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TTransportException.m; sourceTree = ""; }; - 4379F2AE1291AC9700D2A41E /* TSharedProcessorFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSharedProcessorFactory.h; sourceTree = ""; }; - 4379F2AF1291AC9700D2A41E /* TSharedProcessorFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSharedProcessorFactory.m; sourceTree = ""; }; - 4379F2B01291AC9700D2A41E /* SHKEvernote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SHKEvernote.h; sourceTree = ""; }; - 4379F2B11291AC9700D2A41E /* SHKEvernote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SHKEvernote.m; sourceTree = ""; }; - 4379F2B21291AC9700D2A41E /* SHKEvernote.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SHKEvernote.md; sourceTree = ""; }; 4379F2D31291ACD800D2A41E /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = Classes/ShareKit/Localization/ja.lproj/Localizable.strings; sourceTree = SOURCE_ROOT; }; 4379F2D41291ACE600D2A41E /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = Classes/ShareKit/Localization/eu.lproj/Localizable.strings; sourceTree = SOURCE_ROOT; }; 4379F2D51291ACF700D2A41E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = Classes/ShareKit/Localization/nl.lproj/Localizable.strings; sourceTree = SOURCE_ROOT; }; @@ -318,6 +281,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 28962666142C728100BFC10F /* CFNetwork.framework in Frameworks */, 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 2892E4100DC94CBA00A64D0F /* CoreGraphics.framework in Frameworks */, @@ -347,6 +311,22 @@ name = Products; sourceTree = ""; }; + 28962650142C725C00BFC10F /* Flickr */ = { + isa = PBXGroup; + children = ( + 28962651142C725C00BFC10F /* LFHTTPRequest.h */, + 28962652142C725C00BFC10F /* LFHTTPRequest.m */, + 28962658142C725C00BFC10F /* ObjectiveFlickr.h */, + 28962659142C725C00BFC10F /* ObjectiveFlickr.m */, + 2896265A142C725C00BFC10F /* OFUtilities.h */, + 2896265B142C725C00BFC10F /* OFXMLMapper.h */, + 2896265C142C725C00BFC10F /* OFXMLMapper.m */, + 2896265D142C725C00BFC10F /* SHKFlickr.h */, + 2896265E142C725C00BFC10F /* SHKFlickr.m */, + ); + path = Flickr; + sourceTree = ""; + }; 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( @@ -384,6 +364,7 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + 28962665142C728100BFC10F /* CFNetwork.framework */, 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 1D30AB110D05D00D00671497 /* Foundation.framework */, 2892E40F0DC94CBA00A64D0F /* CoreGraphics.framework */, @@ -431,89 +412,6 @@ name = "Example Project"; sourceTree = ""; }; - 4379F2891291AC9700D2A41E /* Evernote */ = { - isa = PBXGroup; - children = ( - 4379F28A1291AC9700D2A41E /* Helpers */, - 4379F2B01291AC9700D2A41E /* SHKEvernote.h */, - 4379F2B11291AC9700D2A41E /* SHKEvernote.m */, - 4379F2B21291AC9700D2A41E /* SHKEvernote.md */, - ); - path = Evernote; - sourceTree = ""; - }; - 4379F28A1291AC9700D2A41E /* Helpers */ = { - isa = PBXGroup; - children = ( - 4379F28B1291AC9700D2A41E /* edam */, - 4379F2961291AC9700D2A41E /* thrift */, - ); - path = Helpers; - sourceTree = ""; - }; - 4379F28B1291AC9700D2A41E /* edam */ = { - isa = PBXGroup; - children = ( - 4379F28C1291AC9700D2A41E /* EDAMLimits.h */, - 4379F28D1291AC9700D2A41E /* EDAMLimits.m */, - 4379F28E1291AC9700D2A41E /* Errors.h */, - 4379F28F1291AC9700D2A41E /* Errors.m */, - 4379F2901291AC9700D2A41E /* NoteStore.h */, - 4379F2911291AC9700D2A41E /* NoteStore.m */, - 4379F2921291AC9700D2A41E /* Types.h */, - 4379F2931291AC9700D2A41E /* Types.m */, - 4379F2941291AC9700D2A41E /* UserStore.h */, - 4379F2951291AC9700D2A41E /* UserStore.m */, - ); - path = edam; - sourceTree = ""; - }; - 4379F2961291AC9700D2A41E /* thrift */ = { - isa = PBXGroup; - children = ( - 4379F2971291AC9700D2A41E /* protocol */, - 4379F2A01291AC9700D2A41E /* TApplicationException.h */, - 4379F2A11291AC9700D2A41E /* TApplicationException.m */, - 4379F2A21291AC9700D2A41E /* TException.h */, - 4379F2A31291AC9700D2A41E /* TException.m */, - 4379F2A41291AC9700D2A41E /* TProcessor.h */, - 4379F2A51291AC9700D2A41E /* TProcessorFactory.h */, - 4379F2A61291AC9700D2A41E /* transport */, - 4379F2AE1291AC9700D2A41E /* TSharedProcessorFactory.h */, - 4379F2AF1291AC9700D2A41E /* TSharedProcessorFactory.m */, - ); - path = thrift; - sourceTree = ""; - }; - 4379F2971291AC9700D2A41E /* protocol */ = { - isa = PBXGroup; - children = ( - 4379F2981291AC9700D2A41E /* TBinaryProtocol.h */, - 4379F2991291AC9700D2A41E /* TBinaryProtocol.m */, - 4379F29A1291AC9700D2A41E /* TProtocol.h */, - 4379F29B1291AC9700D2A41E /* TProtocolException.h */, - 4379F29C1291AC9700D2A41E /* TProtocolException.m */, - 4379F29D1291AC9700D2A41E /* TProtocolFactory.h */, - 4379F29E1291AC9700D2A41E /* TProtocolUtil.h */, - 4379F29F1291AC9700D2A41E /* TProtocolUtil.m */, - ); - path = protocol; - sourceTree = ""; - }; - 4379F2A61291AC9700D2A41E /* transport */ = { - isa = PBXGroup; - children = ( - 4379F2A71291AC9700D2A41E /* THTTPClient.h */, - 4379F2A81291AC9700D2A41E /* THTTPClient.m */, - 4379F2A91291AC9700D2A41E /* TMemoryBuffer.h */, - 4379F2AA1291AC9700D2A41E /* TMemoryBuffer.m */, - 4379F2AB1291AC9700D2A41E /* TTransport.h */, - 4379F2AC1291AC9700D2A41E /* TTransportException.h */, - 4379F2AD1291AC9700D2A41E /* TTransportException.m */, - ); - path = transport; - sourceTree = ""; - }; 4379F3AF1291C45700D2A41E /* Text Message */ = { isa = PBXGroup; children = ( @@ -720,8 +618,8 @@ 43A536C011DBE3B9004A1712 /* Services */ = { isa = PBXGroup; children = ( + 28962650142C725C00BFC10F /* Flickr */, 43A536C111DBE3B9004A1712 /* Delicious */, - 4379F2891291AC9700D2A41E /* Evernote */, 43A536C411DBE3B9004A1712 /* Facebook */, 43A536DE11DBE3B9004A1712 /* Google Reader */, 43150A8A11E78697008C6B68 /* Instapaper */, @@ -957,7 +855,6 @@ 43A5382911DBE480004A1712 /* sanFran.jpg in Resources */, 43C91D1D11EB963600F31FAE /* MainWindow-iPad.xib in Resources */, 43FF9C7412270E9F00ADE53C /* Localizable.strings in Resources */, - 4379F2C21291AC9700D2A41E /* SHKEvernote.md in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1035,23 +932,12 @@ 43C91DF511EBAE4800F31FAE /* SHKTumblr.m in Sources */, 43B934B511FE682600C9D3F3 /* SHKFBStreamDialog.m in Sources */, 432B147C11FF4B0700291B37 /* SHKPhotoAlbum.m in Sources */, - 4379F2B31291AC9700D2A41E /* EDAMLimits.m in Sources */, - 4379F2B41291AC9700D2A41E /* Errors.m in Sources */, - 4379F2B51291AC9700D2A41E /* NoteStore.m in Sources */, - 4379F2B61291AC9700D2A41E /* Types.m in Sources */, - 4379F2B71291AC9700D2A41E /* UserStore.m in Sources */, - 4379F2B81291AC9700D2A41E /* TBinaryProtocol.m in Sources */, - 4379F2B91291AC9700D2A41E /* TProtocolException.m in Sources */, - 4379F2BA1291AC9700D2A41E /* TProtocolUtil.m in Sources */, - 4379F2BB1291AC9700D2A41E /* TApplicationException.m in Sources */, - 4379F2BC1291AC9700D2A41E /* TException.m in Sources */, - 4379F2BD1291AC9700D2A41E /* THTTPClient.m in Sources */, - 4379F2BE1291AC9700D2A41E /* TMemoryBuffer.m in Sources */, - 4379F2BF1291AC9700D2A41E /* TTransportException.m in Sources */, - 4379F2C01291AC9700D2A41E /* TSharedProcessorFactory.m in Sources */, - 4379F2C11291AC9700D2A41E /* SHKEvernote.m in Sources */, 4379F2EA1291AE5700D2A41E /* NSData+md5.m in Sources */, 4379F3B21291C45700D2A41E /* SHKTextMessage.m in Sources */, + 2896265F142C725C00BFC10F /* LFHTTPRequest.m in Sources */, + 28962662142C725C00BFC10F /* ObjectiveFlickr.m in Sources */, + 28962663142C725C00BFC10F /* OFXMLMapper.m in Sources */, + 28962664142C725C00BFC10F /* SHKFlickr.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };