// OFFlickrInvocation.m // // Copyright (c) 2004-2006 Lukhnos D. Liu (lukhnos {at} gmail.com) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // 3. Neither the name of ObjectiveFlickr nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. #import // this stops compiler from giving us a warning when we call this // undocumented method (yes, it's undocumented...) @interface NSMethodSignature (OFFlickrInvocationHack) + (NSMethodSignature*)signatureWithObjCTypes:(const char*)type; @end @interface OFFlickrInvocation (OFFlickrInvocationInternals) - (void)dealloc; - (NSString*)combineStringWithComma:(NSArray*)array; - (NSDictionary*)prepareParameterDictionary:(NSArray*)array; @end @interface OFFlickrInvocation (OFFlickrInvocationUtility) + (NSSet*)getRequiresSignSet; + (NSSet*)getRequiresPOSTSet; + (NSSet*)getRequiresAuthSet; // anything requires auth requires sign (the reverse not necessarily true) + (NSArray*)parseSelectorString:(const char *)selname; @end @interface OFFlickrInvocation (OFFlickrInvocationCallbacks) - (void)HTTPRequest:(OFHTTPRequest*)request didCancel:(id)userinfo; - (void)HTTPRequest:(OFHTTPRequest*)request didFetchData:(NSData*)data userInfo:(id)userinfo; - (void)HTTPRequest:(OFHTTPRequest*)request didTimeout:(id)userinfo; - (void)HTTPRequest:(OFHTTPRequest*)request error:(NSError*)err userInfo:(id)userinfo; - (void)HTTPRequest:(OFHTTPRequest*)request progress:(size_t)receivedBytes expectedTotal:(size_t)total userInfo:(id)userinfo; @end @interface OFFlickrInvocation (OFFlickrInvocationHack) -(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector; -(void)forwardInvocation:(NSInvocation*)inv; @end @implementation OFFlickrInvocation + (OFFlickrInvocation*)invocationWithContext:(OFFlickrContext*)context { return [[[OFFlickrInvocation alloc] initWithContext:context] autorelease]; } + (OFFlickrInvocation*)invocationWithContext:(OFFlickrContext*)context delegate:(id)aDelegate { return [[[OFFlickrInvocation alloc] initWithContext:context delegate:aDelegate] autorelease]; } + (OFFlickrInvocation*)invocationWithContext:(OFFlickrContext*)context delegate:(id)aDelegate timeoutInterval:(NSTimeInterval)interval { return [[[OFFlickrInvocation alloc] initWithContext:context delegate:aDelegate timeoutInterval:interval] autorelease]; } - (OFFlickrInvocation*)initWithContext:(OFFlickrContext*)context { return [self initWithContext:context delegate:nil timeoutInterval:OFDefaultTimeoutInterval]; } - (OFFlickrInvocation*)initWithContext:(OFFlickrContext*)context delegate:(id)aDelegate { return [self initWithContext:context delegate:aDelegate timeoutInterval:OFDefaultTimeoutInterval]; } - (OFFlickrInvocation*)initWithContext:(OFFlickrContext*)context delegate:(id)aDelegate timeoutInterval:(NSTimeInterval)interval { if ((self = [super init])) { _context = [context retain]; _request = [[OFHTTPRequest requestWithDelegate:self timeoutInterval:interval] retain]; _selector = nil; if (aDelegate) _delegate = [aDelegate retain]; else _delegate = nil; _userInfo = nil; } return self; } - (id)delegate { return _delegate; } - (void)setDelegate:(id)aDelegate { id tmp = [aDelegate retain]; if (_delegate) [_delegate release]; _delegate = tmp; } - (void)setSelector:(SEL)aSelector { _selector = nil; if (_delegate) { if ([_delegate respondsToSelector:aSelector]) _selector = aSelector; } } - (id)userInfo { return _userInfo; } - (void)setUserInfo:(id)userinfo { id tmp = [userinfo retain]; if (_userInfo) [_userInfo release]; _userInfo = tmp; } - (OFFlickrContext*)context { return _context; } - (void)cancel { if ([self isClosed]) return; [_request cancel]; } - (BOOL)isClosed { return [_request isClosed]; } - (BOOL)callMethod:(NSString*)method arguments:(NSArray*)parameter { if (![self isClosed]) return NO; NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:[self prepareParameterDictionary:parameter]]; BOOL auth = NO; BOOL sign = NO; BOOL post = NO; if ([[OFFlickrInvocation getRequiresSignSet] containsObject:method]) sign = YES; if ([[OFFlickrInvocation getRequiresAuthSet] containsObject:method]) auth = YES; if ([[OFFlickrInvocation getRequiresPOSTSet] containsObject:method]) post = YES; // NSLog(@"invoking method %@, use auth = %@, signed = %@, use HTTP POST = %@", method, auth ? @"YES" : @"NO", sign ? @"YES" : @"NO", post ? @"YES" : @"NO"); if ([d objectForKey:@"auth"]) { // force to use authentication auth = YES; [d removeObjectForKey:@"auth"]; } if (auth) sign = YES; [d setObject:method forKey:@"method"]; BOOL r=NO; if (post) { return [_request POST:[_context RESTAPIEndPoint] data:[_context prepareRESTPOSTData:d authentication:auth sign:sign] separator:[OFFlickrContext POSTDataSeparator] userInfo:nil]; } else { return [_request GET:[_context prepareRESTGETURL:d authentication:auth sign:sign] userInfo:nil]; } } - (BOOL)callMethod:(NSString*)method arguments:(NSArray*)parameter selector:(SEL)aSelector { [self setSelector:aSelector]; return [self callMethod:method arguments:parameter]; } - (BOOL)callMethod:(NSString*)method arguments:(NSArray*)parameter delegate:(id)aDelegate selector:(SEL)aSelector { [self setDelegate:aDelegate]; [self setSelector:aSelector]; return [self callMethod:method arguments:parameter]; } @end @implementation OFFlickrInvocation (OFFlickrInvocationInternals) - (void)dealloc { if (_request) [_request release]; if (_context) [_context release]; if (_delegate) [_delegate release]; if (_userInfo) [_userInfo release]; [super dealloc]; } - (NSString*)combineStringWithComma:(NSArray*)array { NSMutableString *s=[NSMutableString string]; unsigned i, c=[array count]; if (!c) return s; for (i=0; i 0) { methodname = [selarray objectAtIndex:0]; [inv getArgument:&arg atIndex:2]; [self setUserInfo:arg]; // NSLog(@"has user info = %@", arg); } else { methodname = [NSString stringWithUTF8String:selname]; } // replace every _ to . char *repstr = (char*)malloc(strlen([methodname UTF8String] + 1)); strcpy(repstr, [methodname UTF8String]); char *rp = repstr; while (*rp) { if (*rp == '_') *rp = '.'; rp++; } methodname = [NSString stringWithUTF8String:repstr]; for (i = 1; i < c; i++) { NSString *paramname = [selarray objectAtIndex:i]; [inv getArgument:&arg atIndex:2+i]; if ([paramname isEqualToString:@"selector"]) { [self setSelector:(SEL)arg]; } else if ([paramname isEqualToString:@"delegate"]) { [self setDelegate:arg]; } else { [param addObject:paramname]; [param addObject:arg]; } } // NSLog(@"finished prepared, method = %@, argument = %@", methodname, [param description]); BOOL r; r = [self callMethod:methodname arguments:param]; if (r) [inv setReturnValue:&self]; } @end @implementation OFFlickrInvocation (OFFlickrInvocationUtility) + (NSSet*)getRequiresSignSet { static NSSet *ofRequiresSignSet = nil; if (ofRequiresSignSet) return ofRequiresSignSet; ofRequiresSignSet = [NSSet setWithObjects: @"flickr.auth.getFrob", nil]; [ofRequiresSignSet retain]; return ofRequiresSignSet; } + (NSSet*)getRequiresPOSTSet { static NSSet *ofRequiresPOSTSet = nil; if (ofRequiresPOSTSet) return ofRequiresPOSTSet; ofRequiresPOSTSet = [NSSet setWithObjects: @"flickr.blogs.postPhoto", @"flickr.favorites.add", @"flickr.favorites.remove", @"flickr.groups.pools.add", @"flickr.groups.pools.remove", @"flickr.photos.addTags", @"flickr.photos.delete", @"flickr.photos.removeTag", @"flickr.photos.setDates", @"flickr.photos.setMeta", @"flickr.photos.setPerms", @"flickr.photos.setTags", @"flickr.photos.comments.addComment", @"flickr.photos.comments.deleteComment", @"flickr.photos.comments.editComment", @"flickr.photos.geo.removeLocation", @"flickr.photos.geo.setLocation", @"flickr.photos.geo.setPerms", @"flickr.photos.licenses.setLicense", @"flickr.photos.notes.add", @"flickr.photos.notes.delete", @"flickr.photos.notes.edit", @"flickr.photos.transform", @"flickr.photosets.addPhoto", @"flickr.photosets.create", @"flickr.photosets.delete", @"flickr.photosets.editMeta", @"flickr.photosets.editPhotos", @"flickr.photosets.orderSets", @"flickr.photosets.removePhoto", @"flickr.photosets.comments.addComment", @"flickr.photosets.comments.deleteComment", @"flickr.photosets.comments.editComment", nil]; [ofRequiresPOSTSet retain]; return ofRequiresPOSTSet; } + (NSSet*)getRequiresAuthSet { static NSSet *ofRequiresAuthSet = nil; if (ofRequiresAuthSet) return ofRequiresAuthSet; ofRequiresAuthSet = [NSSet setWithObjects: @"flickr.auth.checkToken", // @"flickr.auth.getFrob", @"flickr.auth.getFullToken", @"flickr.auth.getToken", @"flickr.blogs.getList", @"flickr.blogs.postPhoto", @"flickr.contacts.getList", // @"flickr.contacts.getPublicList", @"flickr.favorites.add", @"flickr.favorites.getList", // @"flickr.favorites.getPublicList", @"flickr.favorites.remove", @"flickr.groups.browse", // @"flickr.groups.getInfo", // @"flickr.groups.search", // makes a differnece if auth'ed ! @"flickr.groups.pools.add", // @"flickr.groups.pools.getContext", @"flickr.groups.pools.getGroups", // @"flickr.groups.pools.getPhotos", @"flickr.groups.pools.remove", // @"flickr.interestingness.getList", // @"flickr.people.findByEmail", // @"flickr.people.findByUserName", // @"flickr.people.getInfo", // @"flickr.people.getPublicGroups", // @"flickr.people.getPublicPhotos", @"flickr.people.getUploadStatus", @"flickr.photos.addTags", @"flickr.photos.delete", // @"flickr.photos.getAllContexts", @"flickr.photos.getContactsPhotos", // @"flickr.photos.getContactsPublicPhotos", // @"flickr.photos.getContext", @"flickr.photos.getCounts", // @"flickr.photos.getExif", // @"flickr.photos.getInfo", // makes a differnece if auth'ed ! @"flickr.photos.getNotInSet", @"flickr.photos.getPerms", // @"flickr.photos.getRecent", // @"flickr.photos.getSizes", @"flickr.photos.getUntagged", @"flickr.photos.getWithGeoData", @"flickr.photos.getWithoutGeoData", @"flickr.photos.recentlyUpdated", @"flickr.photos.removeTag", // @"flickr.photos.search", // makes a differnece if auth'ed ! @"flickr.photos.setDates", @"flickr.photos.setMeta", @"flickr.photos.setPerms", @"flickr.photos.setTags", @"flickr.photos.comments.addComment", @"flickr.photos.comments.deleteComment", @"flickr.photos.comments.editComment", // @"flickr.photos.comments.getList", // @"flickr.photos.geo.getLocation", @"flickr.photos.geo.getPerms", @"flickr.photos.geo.removeLocation", @"flickr.photos.geo.setLocation", @"flickr.photos.geo.setPerms", // @"flickr.photos.licenses.getInfo", @"flickr.photos.licenses.setLicense", @"flickr.photos.notes.add", @"flickr.photos.notes.delete", @"flickr.photos.notes.edit", @"flickr.photos.transform", // @"flickr.photos.upload.checkTickets", @"flickr.photosets.addPhoto", @"flickr.photosets.create", @"flickr.photosets.delete", @"flickr.photosets.editMeta", @"flickr.photosets.editPhotos", // @"flickr.photosets.getContext", // @"flickr.photosets.getInfo", // @"flickr.photosets.getList", // @"flickr.photosets.getPhotos", @"flickr.photosets.orderSets", @"flickr.photosets.removePhoto", @"flickr.photosets.comments.addComment", @"flickr.photosets.comments.deleteComment", @"flickr.photosets.comments.editComment", // @"flickr.photosets.comments.getList", // @"flickr.reflection.getMethodInfo", // @"flickr.reflection.getMethods", // @"flickr.tags.getListPhoto", // @"flickr.tags.getListUser", // @"flickr.tags.getListUserPopular", // @"flickr.tags.getRelated", // @"flickr.test.echo", @"flickr.test.login", @"flickr.test.null", // @"flickr.urls.getGroup", // @"flickr.urls.getUserPhotos", // @"flickr.urls.getUserProfile", // @"flickr.urls.lookupGroup", @"flickr.urls.lookupUser", nil]; [ofRequiresAuthSet retain]; return ofRequiresAuthSet; } + (NSArray*)parseSelectorString:(const char *)selname { NSMutableArray *a = [NSMutableArray array]; if (selname[strlen(selname) - 1] != ':') return a; NSMutableString *current = [NSMutableString string]; const char *p = selname; while (*p) { if (*p == ':') { [a addObject:current]; current = [NSMutableString string]; } else { [current appendString:[NSString stringWithFormat:@"%c", *p]]; } p++; } return a; } @end