123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- //
- // SinaWeiboRequest.m
- // sinaweibo_ios_sdk
- // Based on OAuth 2.0
- //
- // 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.
- //
- // Copyright 2011 Sina. All rights reserved.
- //
- #import "SinaWeiboRequest.h"
- #import "SinaWeiboConstants.h"
- #import "JSONKit.h"
- #import "SinaWeibo.h"
- #define kSinaWeiboRequestTimeOutInterval 180.0
- #define kSinaWeiboRequestStringBoundary @"293iosfksdfkiowjksdf31jsiuwq003s02dsaffafass3qw"
- @interface NSString (SinaWeiboEncode)
- - (NSString *)URLEncodedString;
- @end
- @implementation NSString (SinaWeiboEncode)
- - (NSString *)URLEncodedStringWithCFStringEncoding:(CFStringEncoding)encoding
- {
- return [(NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)[[self mutableCopy] autorelease], NULL, CFSTR("=,!$&'()*+;@?\n\"<>#\t :/"), encoding) autorelease];
- }
- - (NSString *)URLEncodedString
- {
- return [self URLEncodedStringWithCFStringEncoding:kCFStringEncodingUTF8];
- }
- @end
- @interface SinaWeibo (SinaWeiboRequest)
- - (void)requestDidFinish:(SinaWeiboRequest *)request;
- - (void)requestDidFailWithInvalidToken:(NSError *)error;
- @end
- @interface SinaWeiboRequest (Private)
- - (void)appendUTF8Body:(NSMutableData *)body dataString:(NSString *)dataString;
- - (NSMutableData *)postBodyHasRawData:(BOOL*)hasRawData;
- - (void)handleResponseData:(NSData *)data;
- - (id)parseJSONData:(NSData *)data error:(NSError **)error;
- - (id)errorWithCode:(NSInteger)code userInfo:(NSDictionary *)userInfo;
- - (void)failedWithError:(NSError *)error;
- @end
- @implementation SinaWeiboRequest
- @synthesize sinaweibo;
- @synthesize url;
- @synthesize httpMethod;
- @synthesize params;
- @synthesize delegate;
- #pragma mark - SinaWeiboRequest Life Circle
- - (void)dealloc
- {
- sinaweibo = nil;
-
- [url release], url = nil;
- [httpMethod release], httpMethod = nil;
- [params release], params = nil;
-
- [responseData release];
- responseData = nil;
-
- [connection cancel];
- [connection release], connection = nil;
-
- [super dealloc];
- }
- #pragma mark - SinaWeiboRequest Private Methods
- - (void)appendUTF8Body:(NSMutableData *)body dataString:(NSString *)dataString
- {
- [body appendData:[dataString dataUsingEncoding:NSUTF8StringEncoding]];
- }
- - (NSMutableData *)postBodyHasRawData:(BOOL*)hasRawData
- {
- NSString *bodyPrefixString = [NSString stringWithFormat:@"--%@\r\n", kSinaWeiboRequestStringBoundary];
- NSString *bodySuffixString = [NSString stringWithFormat:@"\r\n--%@--\r\n", kSinaWeiboRequestStringBoundary];
-
- NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary];
-
- NSMutableData *body = [NSMutableData data];
- [self appendUTF8Body:body dataString:bodyPrefixString];
-
- for (id key in [params keyEnumerator])
- {
- if (([[params valueForKey:key] isKindOfClass:[UIImage class]]) || ([[params valueForKey:key] isKindOfClass:[NSData class]]))
- {
- [dataDictionary setObject:[params valueForKey:key] forKey:key];
- continue;
- }
-
- [self appendUTF8Body:body dataString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n", key, [params valueForKey:key]]];
- [self appendUTF8Body:body dataString:bodyPrefixString];
- }
-
- if ([dataDictionary count] > 0)
- {
- *hasRawData = YES;
- for (id key in dataDictionary)
- {
- NSObject *dataParam = [dataDictionary valueForKey:key];
-
- if ([dataParam isKindOfClass:[UIImage class]])
- {
- NSData* imageData = UIImagePNGRepresentation((UIImage *)dataParam);
- [self appendUTF8Body:body dataString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file\"\r\n", key]];
- [self appendUTF8Body:body dataString:@"Content-Type: image/png\r\nContent-Transfer-Encoding: binary\r\n\r\n"];
- [body appendData:imageData];
- }
- else if ([dataParam isKindOfClass:[NSData class]])
- {
- [self appendUTF8Body:body dataString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file\"\r\n", key]];
- [self appendUTF8Body:body dataString:@"Content-Type: content/unknown\r\nContent-Transfer-Encoding: binary\r\n\r\n"];
- [body appendData:(NSData*)dataParam];
- }
- [self appendUTF8Body:body dataString:bodySuffixString];
- }
- }
-
- return body;
- }
- - (void)handleResponseData:(NSData *)data
- {
- if ([delegate respondsToSelector:@selector(request:didReceiveRawData:)])
- {
- [delegate request:self didReceiveRawData:data];
- }
-
- NSError *error = nil;
- id result = [self parseJSONData:data error:&error];
-
- if (error)
- {
- [self failedWithError:error];
- }
- else
- {
- NSInteger error_code = 0;
- if([result isKindOfClass:[NSDictionary class]])
- {
- [[result objectForKey:@"error_code"] intValue];
- }
-
- if (error_code != 0)
- {
- NSString *error_description = [result objectForKey:@"error"];
- NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
- result, @"error",
- error_description, NSLocalizedDescriptionKey, nil];
- NSError *error = [NSError errorWithDomain:kSinaWeiboSDKErrorDomain
- code:[[result objectForKey:@"error_code"] intValue]
- userInfo:userInfo];
-
- if (error_code == 21314 //Token已经被使用
- || error_code == 21315 //Token已经过期
- || error_code == 21316 //Token不合法
- || error_code == 21317 //Token不合法
- || error_code == 21327 //token过期
- || error_code == 21332) //access_token 无效
- {
- [sinaweibo requestDidFailWithInvalidToken:error];
- }
- else
- {
- [self failedWithError:error];
- }
- }
- else
- {
- if ([delegate respondsToSelector:@selector(request:didFinishLoadingWithResult:)])
- {
- [delegate request:self didFinishLoadingWithResult:(result == nil ? data : result)];
- }
- }
- }
- }
- - (id)parseJSONData:(NSData *)data error:(NSError **)error
- {
- NSError *parseError = nil;
- id result =[data objectFromJSONDataWithParseOptions:JKParseOptionStrict error:&parseError];
-
- if (parseError && (error != nil))
- {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
- parseError, @"error",
- @"Data parse error", NSLocalizedDescriptionKey, nil];
- *error = [self errorWithCode:kSinaWeiboSDKErrorCodeParseError
- userInfo:userInfo];
- }
-
- return result;
- }
- - (id)errorWithCode:(NSInteger)code userInfo:(NSDictionary *)userInfo
- {
- return [NSError errorWithDomain:kSinaWeiboSDKErrorDomain code:code userInfo:userInfo];
- }
- - (void)failedWithError:(NSError *)error
- {
- if ([delegate respondsToSelector:@selector(request:didFailWithError:)])
- {
- [delegate request:self didFailWithError:error];
- }
- }
- #pragma mark - SinaWeiboRequest Public Methods
- + (NSString *)getParamValueFromUrl:(NSString*)url paramName:(NSString *)paramName
- {
- if (![paramName hasSuffix:@"="])
- {
- paramName = [NSString stringWithFormat:@"%@=", paramName];
- }
-
- NSString * str = nil;
- NSRange start = [url rangeOfString:paramName];
- if (start.location != NSNotFound)
- {
- // confirm that the parameter is not a partial name match
- unichar c = '?';
- if (start.location != 0)
- {
- c = [url characterAtIndex:start.location - 1];
- }
- if (c == '?' || c == '&' || c == '#')
- {
- NSRange end = [[url substringFromIndex:start.location+start.length] rangeOfString:@"&"];
- NSUInteger offset = start.location+start.length;
- str = end.location == NSNotFound ?
- [url substringFromIndex:offset] :
- [url substringWithRange:NSMakeRange(offset, end.location)];
- str = [str stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- }
- }
- return str;
- }
- + (NSString *)serializeURL:(NSString *)baseURL params:(NSDictionary *)params httpMethod:(NSString *)httpMethod
- {
- NSURL* parsedURL = [NSURL URLWithString:baseURL];
- NSString* queryPrefix = parsedURL.query ? @"&" : @"?";
-
- NSMutableArray* pairs = [NSMutableArray array];
- for (NSString* key in [params keyEnumerator])
- {
- if (([[params objectForKey:key] isKindOfClass:[UIImage class]])
- ||([[params objectForKey:key] isKindOfClass:[NSData class]]))
- {
- if ([httpMethod isEqualToString:@"GET"])
- {
- NSLog(@"can not use GET to upload a file");
- }
- continue;
- }
-
- NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(
- NULL, /* allocator */
- (CFStringRef)[params objectForKey:key],
- NULL, /* charactersToLeaveUnescaped */
- (CFStringRef)@"!*'();:@&=+$,/?%#[]",
- kCFStringEncodingUTF8);
-
- [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, escaped_value]];
- [escaped_value release];
- }
- NSString* query = [pairs componentsJoinedByString:@"&"];
-
- return [NSString stringWithFormat:@"%@%@%@", baseURL, queryPrefix, query];
- }
- + (SinaWeiboRequest *)requestWithURL:(NSString *)url
- httpMethod:(NSString *)httpMethod
- params:(NSDictionary *)params
- delegate:(id<SinaWeiboRequestDelegate>)delegate
- {
- SinaWeiboRequest *request = [[[SinaWeiboRequest alloc] init] autorelease];
-
- request.url = url;
- request.httpMethod = httpMethod;
- request.params = params;
- request.delegate = delegate;
-
- return request;
- }
- + (SinaWeiboRequest *)requestWithAccessToken:(NSString *)accessToken
- url:(NSString *)url
- httpMethod:(NSString *)httpMethod
- params:(NSDictionary *)params
- delegate:(id<SinaWeiboRequestDelegate>)delegate
- {
- // add the access token field
- NSMutableDictionary *mutableParams = [NSMutableDictionary dictionaryWithDictionary:params];
- [mutableParams setObject:accessToken forKey:@"access_token"];
- return [SinaWeiboRequest requestWithURL:url
- httpMethod:httpMethod
- params:mutableParams
- delegate:delegate];
- }
- - (void)connect
- {
- NSString* urlString = [[self class] serializeURL:url params:params httpMethod:httpMethod];
- NSMutableURLRequest* request =
- [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]
- cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
- timeoutInterval:kSinaWeiboRequestTimeOutInterval];
-
- [request setHTTPMethod:self.httpMethod];
- if ([self.httpMethod isEqualToString: @"POST"])
- {
- BOOL hasRawData = NO;
- [request setHTTPBody:[self postBodyHasRawData:&hasRawData]];
-
- if (hasRawData)
- {
- NSString* contentType = [NSString
- stringWithFormat:@"multipart/form-data; boundary=%@", kSinaWeiboRequestStringBoundary];
- [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
- }
- }
-
- connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
- }
- - (void)disconnect
- {
- [responseData release];
- responseData = nil;
-
- [connection cancel];
- [connection release], connection = nil;
- }
- #pragma mark - NSURLConnection Delegate Methods
- - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- {
- responseData = [[NSMutableData alloc] init];
-
- if ([delegate respondsToSelector:@selector(request:didReceiveResponse:)])
- {
- [delegate request:self didReceiveResponse:response];
- }
- }
- - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- {
- [responseData appendData:data];
- }
- - (NSCachedURLResponse *)connection:(NSURLConnection *)connection
- willCacheResponse:(NSCachedURLResponse*)cachedResponse
- {
- return nil;
- }
- - (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
- {
- [self handleResponseData:responseData];
-
- [responseData release];
- responseData = nil;
-
- [connection cancel];
- [connection release];
- connection = nil;
-
- [sinaweibo requestDidFinish:self];
- }
- - (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error
- {
- [self failedWithError:error];
-
- [responseData release];
- responseData = nil;
-
- [connection cancel];
- [connection release];
- connection = nil;
-
- [sinaweibo requestDidFinish:self];
- }
- @end
|