SinaWeiboRequest.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. //
  2. // SinaWeiboRequest.m
  3. // sinaweibo_ios_sdk
  4. // Based on OAuth 2.0
  5. //
  6. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  7. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  8. // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  9. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  10. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  11. // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  12. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  13. // OTHER DEALINGS IN THE SOFTWARE.
  14. //
  15. // Copyright 2011 Sina. All rights reserved.
  16. //
  17. #import "SinaWeiboRequest.h"
  18. #import "SinaWeiboConstants.h"
  19. #import "JSONKit.h"
  20. #import "SinaWeibo.h"
  21. #define kSinaWeiboRequestTimeOutInterval 180.0
  22. #define kSinaWeiboRequestStringBoundary @"293iosfksdfkiowjksdf31jsiuwq003s02dsaffafass3qw"
  23. @interface NSString (SinaWeiboEncode)
  24. - (NSString *)URLEncodedString;
  25. @end
  26. @implementation NSString (SinaWeiboEncode)
  27. - (NSString *)URLEncodedStringWithCFStringEncoding:(CFStringEncoding)encoding
  28. {
  29. return [(NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)[[self mutableCopy] autorelease], NULL, CFSTR("=,!$&'()*+;@?\n\"<>#\t :/"), encoding) autorelease];
  30. }
  31. - (NSString *)URLEncodedString
  32. {
  33. return [self URLEncodedStringWithCFStringEncoding:kCFStringEncodingUTF8];
  34. }
  35. @end
  36. @interface SinaWeibo (SinaWeiboRequest)
  37. - (void)requestDidFinish:(SinaWeiboRequest *)request;
  38. - (void)requestDidFailWithInvalidToken:(NSError *)error;
  39. @end
  40. @interface SinaWeiboRequest (Private)
  41. - (void)appendUTF8Body:(NSMutableData *)body dataString:(NSString *)dataString;
  42. - (NSMutableData *)postBodyHasRawData:(BOOL*)hasRawData;
  43. - (void)handleResponseData:(NSData *)data;
  44. - (id)parseJSONData:(NSData *)data error:(NSError **)error;
  45. - (id)errorWithCode:(NSInteger)code userInfo:(NSDictionary *)userInfo;
  46. - (void)failedWithError:(NSError *)error;
  47. @end
  48. @implementation SinaWeiboRequest
  49. @synthesize sinaweibo;
  50. @synthesize url;
  51. @synthesize httpMethod;
  52. @synthesize params;
  53. @synthesize delegate;
  54. #pragma mark - SinaWeiboRequest Life Circle
  55. - (void)dealloc
  56. {
  57. sinaweibo = nil;
  58. [url release], url = nil;
  59. [httpMethod release], httpMethod = nil;
  60. [params release], params = nil;
  61. [responseData release];
  62. responseData = nil;
  63. [connection cancel];
  64. [connection release], connection = nil;
  65. [super dealloc];
  66. }
  67. #pragma mark - SinaWeiboRequest Private Methods
  68. - (void)appendUTF8Body:(NSMutableData *)body dataString:(NSString *)dataString
  69. {
  70. [body appendData:[dataString dataUsingEncoding:NSUTF8StringEncoding]];
  71. }
  72. - (NSMutableData *)postBodyHasRawData:(BOOL*)hasRawData
  73. {
  74. NSString *bodyPrefixString = [NSString stringWithFormat:@"--%@\r\n", kSinaWeiboRequestStringBoundary];
  75. NSString *bodySuffixString = [NSString stringWithFormat:@"\r\n--%@--\r\n", kSinaWeiboRequestStringBoundary];
  76. NSMutableDictionary *dataDictionary = [NSMutableDictionary dictionary];
  77. NSMutableData *body = [NSMutableData data];
  78. [self appendUTF8Body:body dataString:bodyPrefixString];
  79. for (id key in [params keyEnumerator])
  80. {
  81. if (([[params valueForKey:key] isKindOfClass:[UIImage class]]) || ([[params valueForKey:key] isKindOfClass:[NSData class]]))
  82. {
  83. [dataDictionary setObject:[params valueForKey:key] forKey:key];
  84. continue;
  85. }
  86. [self appendUTF8Body:body dataString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n", key, [params valueForKey:key]]];
  87. [self appendUTF8Body:body dataString:bodyPrefixString];
  88. }
  89. if ([dataDictionary count] > 0)
  90. {
  91. *hasRawData = YES;
  92. for (id key in dataDictionary)
  93. {
  94. NSObject *dataParam = [dataDictionary valueForKey:key];
  95. if ([dataParam isKindOfClass:[UIImage class]])
  96. {
  97. NSData* imageData = UIImagePNGRepresentation((UIImage *)dataParam);
  98. [self appendUTF8Body:body dataString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file\"\r\n", key]];
  99. [self appendUTF8Body:body dataString:@"Content-Type: image/png\r\nContent-Transfer-Encoding: binary\r\n\r\n"];
  100. [body appendData:imageData];
  101. }
  102. else if ([dataParam isKindOfClass:[NSData class]])
  103. {
  104. [self appendUTF8Body:body dataString:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file\"\r\n", key]];
  105. [self appendUTF8Body:body dataString:@"Content-Type: content/unknown\r\nContent-Transfer-Encoding: binary\r\n\r\n"];
  106. [body appendData:(NSData*)dataParam];
  107. }
  108. [self appendUTF8Body:body dataString:bodySuffixString];
  109. }
  110. }
  111. return body;
  112. }
  113. - (void)handleResponseData:(NSData *)data
  114. {
  115. if ([delegate respondsToSelector:@selector(request:didReceiveRawData:)])
  116. {
  117. [delegate request:self didReceiveRawData:data];
  118. }
  119. NSError *error = nil;
  120. id result = [self parseJSONData:data error:&error];
  121. if (error)
  122. {
  123. [self failedWithError:error];
  124. }
  125. else
  126. {
  127. NSInteger error_code = 0;
  128. if([result isKindOfClass:[NSDictionary class]])
  129. {
  130. [[result objectForKey:@"error_code"] intValue];
  131. }
  132. if (error_code != 0)
  133. {
  134. NSString *error_description = [result objectForKey:@"error"];
  135. NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  136. result, @"error",
  137. error_description, NSLocalizedDescriptionKey, nil];
  138. NSError *error = [NSError errorWithDomain:kSinaWeiboSDKErrorDomain
  139. code:[[result objectForKey:@"error_code"] intValue]
  140. userInfo:userInfo];
  141. if (error_code == 21314 //Token已经被使用
  142. || error_code == 21315 //Token已经过期
  143. || error_code == 21316 //Token不合法
  144. || error_code == 21317 //Token不合法
  145. || error_code == 21327 //token过期
  146. || error_code == 21332) //access_token 无效
  147. {
  148. [sinaweibo requestDidFailWithInvalidToken:error];
  149. }
  150. else
  151. {
  152. [self failedWithError:error];
  153. }
  154. }
  155. else
  156. {
  157. if ([delegate respondsToSelector:@selector(request:didFinishLoadingWithResult:)])
  158. {
  159. [delegate request:self didFinishLoadingWithResult:(result == nil ? data : result)];
  160. }
  161. }
  162. }
  163. }
  164. - (id)parseJSONData:(NSData *)data error:(NSError **)error
  165. {
  166. NSError *parseError = nil;
  167. id result =[data objectFromJSONDataWithParseOptions:JKParseOptionStrict error:&parseError];
  168. if (parseError && (error != nil))
  169. {
  170. NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  171. parseError, @"error",
  172. @"Data parse error", NSLocalizedDescriptionKey, nil];
  173. *error = [self errorWithCode:kSinaWeiboSDKErrorCodeParseError
  174. userInfo:userInfo];
  175. }
  176. return result;
  177. }
  178. - (id)errorWithCode:(NSInteger)code userInfo:(NSDictionary *)userInfo
  179. {
  180. return [NSError errorWithDomain:kSinaWeiboSDKErrorDomain code:code userInfo:userInfo];
  181. }
  182. - (void)failedWithError:(NSError *)error
  183. {
  184. if ([delegate respondsToSelector:@selector(request:didFailWithError:)])
  185. {
  186. [delegate request:self didFailWithError:error];
  187. }
  188. }
  189. #pragma mark - SinaWeiboRequest Public Methods
  190. + (NSString *)getParamValueFromUrl:(NSString*)url paramName:(NSString *)paramName
  191. {
  192. if (![paramName hasSuffix:@"="])
  193. {
  194. paramName = [NSString stringWithFormat:@"%@=", paramName];
  195. }
  196. NSString * str = nil;
  197. NSRange start = [url rangeOfString:paramName];
  198. if (start.location != NSNotFound)
  199. {
  200. // confirm that the parameter is not a partial name match
  201. unichar c = '?';
  202. if (start.location != 0)
  203. {
  204. c = [url characterAtIndex:start.location - 1];
  205. }
  206. if (c == '?' || c == '&' || c == '#')
  207. {
  208. NSRange end = [[url substringFromIndex:start.location+start.length] rangeOfString:@"&"];
  209. NSUInteger offset = start.location+start.length;
  210. str = end.location == NSNotFound ?
  211. [url substringFromIndex:offset] :
  212. [url substringWithRange:NSMakeRange(offset, end.location)];
  213. str = [str stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  214. }
  215. }
  216. return str;
  217. }
  218. + (NSString *)serializeURL:(NSString *)baseURL params:(NSDictionary *)params httpMethod:(NSString *)httpMethod
  219. {
  220. NSURL* parsedURL = [NSURL URLWithString:baseURL];
  221. NSString* queryPrefix = parsedURL.query ? @"&" : @"?";
  222. NSMutableArray* pairs = [NSMutableArray array];
  223. for (NSString* key in [params keyEnumerator])
  224. {
  225. if (([[params objectForKey:key] isKindOfClass:[UIImage class]])
  226. ||([[params objectForKey:key] isKindOfClass:[NSData class]]))
  227. {
  228. if ([httpMethod isEqualToString:@"GET"])
  229. {
  230. NSLog(@"can not use GET to upload a file");
  231. }
  232. continue;
  233. }
  234. NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(
  235. NULL, /* allocator */
  236. (CFStringRef)[params objectForKey:key],
  237. NULL, /* charactersToLeaveUnescaped */
  238. (CFStringRef)@"!*'();:@&=+$,/?%#[]",
  239. kCFStringEncodingUTF8);
  240. [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, escaped_value]];
  241. [escaped_value release];
  242. }
  243. NSString* query = [pairs componentsJoinedByString:@"&"];
  244. return [NSString stringWithFormat:@"%@%@%@", baseURL, queryPrefix, query];
  245. }
  246. + (SinaWeiboRequest *)requestWithURL:(NSString *)url
  247. httpMethod:(NSString *)httpMethod
  248. params:(NSDictionary *)params
  249. delegate:(id<SinaWeiboRequestDelegate>)delegate
  250. {
  251. SinaWeiboRequest *request = [[[SinaWeiboRequest alloc] init] autorelease];
  252. request.url = url;
  253. request.httpMethod = httpMethod;
  254. request.params = params;
  255. request.delegate = delegate;
  256. return request;
  257. }
  258. + (SinaWeiboRequest *)requestWithAccessToken:(NSString *)accessToken
  259. url:(NSString *)url
  260. httpMethod:(NSString *)httpMethod
  261. params:(NSDictionary *)params
  262. delegate:(id<SinaWeiboRequestDelegate>)delegate
  263. {
  264. // add the access token field
  265. NSMutableDictionary *mutableParams = [NSMutableDictionary dictionaryWithDictionary:params];
  266. [mutableParams setObject:accessToken forKey:@"access_token"];
  267. return [SinaWeiboRequest requestWithURL:url
  268. httpMethod:httpMethod
  269. params:mutableParams
  270. delegate:delegate];
  271. }
  272. - (void)connect
  273. {
  274. NSString* urlString = [[self class] serializeURL:url params:params httpMethod:httpMethod];
  275. NSMutableURLRequest* request =
  276. [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]
  277. cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
  278. timeoutInterval:kSinaWeiboRequestTimeOutInterval];
  279. [request setHTTPMethod:self.httpMethod];
  280. if ([self.httpMethod isEqualToString: @"POST"])
  281. {
  282. BOOL hasRawData = NO;
  283. [request setHTTPBody:[self postBodyHasRawData:&hasRawData]];
  284. if (hasRawData)
  285. {
  286. NSString* contentType = [NSString
  287. stringWithFormat:@"multipart/form-data; boundary=%@", kSinaWeiboRequestStringBoundary];
  288. [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
  289. }
  290. }
  291. connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
  292. }
  293. - (void)disconnect
  294. {
  295. [responseData release];
  296. responseData = nil;
  297. [connection cancel];
  298. [connection release], connection = nil;
  299. }
  300. #pragma mark - NSURLConnection Delegate Methods
  301. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
  302. {
  303. responseData = [[NSMutableData alloc] init];
  304. if ([delegate respondsToSelector:@selector(request:didReceiveResponse:)])
  305. {
  306. [delegate request:self didReceiveResponse:response];
  307. }
  308. }
  309. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
  310. {
  311. [responseData appendData:data];
  312. }
  313. - (NSCachedURLResponse *)connection:(NSURLConnection *)connection
  314. willCacheResponse:(NSCachedURLResponse*)cachedResponse
  315. {
  316. return nil;
  317. }
  318. - (void)connectionDidFinishLoading:(NSURLConnection *)theConnection
  319. {
  320. [self handleResponseData:responseData];
  321. [responseData release];
  322. responseData = nil;
  323. [connection cancel];
  324. [connection release];
  325. connection = nil;
  326. [sinaweibo requestDidFinish:self];
  327. }
  328. - (void)connection:(NSURLConnection *)theConnection didFailWithError:(NSError *)error
  329. {
  330. [self failedWithError:error];
  331. [responseData release];
  332. responseData = nil;
  333. [connection cancel];
  334. [connection release];
  335. connection = nil;
  336. [sinaweibo requestDidFinish:self];
  337. }
  338. @end