PageRenderTime 62ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.m

https://bitbucket.org/jsixface/adium
Objective C | 1612 lines | 1187 code | 352 blank | 73 comment | 174 complexity | 6b198c3aaf87dd3bee44c9056ab2a930 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, ISC, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. //
  2. // MGTwitterEngine.m
  3. // MGTwitterEngine
  4. //
  5. // Created by Matt Gemmell on 10/02/2008.
  6. // Copyright 2008 Instinctive Code.
  7. //
  8. #import "MGTwitterEngine.h"
  9. #import "MGTwitterHTTPURLConnection.h"
  10. #import "NSData+Base64.h"
  11. #define USE_LIBXML 0
  12. #import "MGTwitterStatusesParser.h"
  13. #import "MGTwitterUsersParser.h"
  14. #import "MGTwitterMessagesParser.h"
  15. #import "MGTwitterMiscParser.h"
  16. #define TWITTER_DOMAIN @"api.twitter.com/1"
  17. #define HTTP_POST_METHOD @"POST"
  18. #define HTTP_MULTIPART_METHOD @"MULTIPART" //adium
  19. #define MULTIPART_FORM_BOUNDARY @"bf5faadd239c17e35f91e6dafe1d2f96" //adium
  20. #define MAX_MESSAGE_LENGTH 140 // Twitter recommends tweets of max 140 chars
  21. #define MAX_LOCATION_LENGTH 31
  22. #define DEFAULT_CLIENT_NAME @"MGTwitterEngine"
  23. #define DEFAULT_CLIENT_VERSION @"1.0"
  24. #define DEFAULT_CLIENT_URL @"http://mattgemmell.com/source"
  25. #define DEFAULT_CLIENT_TOKEN @"mgtwitterengine"
  26. #define URL_REQUEST_TIMEOUT 50.0 // Twitter usually fails quickly if it's going to fail at all.
  27. #define DEFAULT_TWEET_COUNT 20
  28. @interface MGTwitterEngine (PrivateMethods)
  29. // Utility methods
  30. - (NSDateFormatter *)_HTTPDateFormatter;
  31. - (NSString *)_queryStringWithBase:(NSString *)base parameters:(NSDictionary *)params prefixed:(BOOL)prefixed;
  32. - (NSDate *)_HTTPToDate:(NSString *)httpDate;
  33. - (NSString *)_dateToHTTP:(NSDate *)date;
  34. - (NSString *)_encodeString:(NSString *)string;
  35. // Connection/Request methods
  36. - (NSString *)_sendRequestWithMethod:(NSString *)method
  37. path:(NSString *)path
  38. queryParameters:(NSDictionary *)params
  39. body:(id)body
  40. requestType:(MGTwitterRequestType)requestType
  41. responseType:(MGTwitterResponseType)responseType;
  42. // Parsing methods
  43. - (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection;
  44. // Delegate methods
  45. - (BOOL) _isValidDelegateForSelector:(SEL)selector;
  46. @end
  47. @implementation MGTwitterEngine
  48. #pragma mark Constructors
  49. + (MGTwitterEngine *)twitterEngineWithDelegate:(NSObject *)theDelegate
  50. {
  51. return [[MGTwitterEngine alloc] initWithDelegate:theDelegate];
  52. }
  53. - (MGTwitterEngine *)initWithDelegate:(NSObject <MGTwitterEngineDelegate> *)newDelegate
  54. {
  55. if ((self = [super init])) {
  56. _delegate = newDelegate; // deliberately weak reference
  57. _connections = [[NSMutableDictionary alloc] initWithCapacity:0];
  58. _clientName = DEFAULT_CLIENT_NAME;
  59. _clientVersion = DEFAULT_CLIENT_VERSION;
  60. _clientURL = DEFAULT_CLIENT_URL;
  61. _clientSourceToken = DEFAULT_CLIENT_TOKEN;
  62. _APIDomain = TWITTER_DOMAIN;
  63. _secureConnection = YES;
  64. _clearsCookies = NO;
  65. }
  66. return self;
  67. }
  68. - (void)dealloc
  69. {
  70. _delegate = nil;
  71. [[_connections allValues] makeObjectsPerformSelector:@selector(cancel)];
  72. }
  73. #pragma mark Configuration and Accessors
  74. + (NSString *)version
  75. {
  76. // 1.0.0 = 22 Feb 2008
  77. // 1.0.1 = 26 Feb 2008
  78. // 1.0.2 = 04 Mar 2008
  79. // 1.0.3 = 04 Mar 2008
  80. // 1.0.4 = 11 Apr 2008
  81. // 1.0.5 = 06 Jun 2008
  82. // 1.0.6 = 05 Aug 2008
  83. // 1.0.7 = 28 Sep 2008
  84. // 1.0.8 = 01 Oct 2008
  85. return @"1.0.8";
  86. }
  87. - (NSString *)username
  88. {
  89. return _username;
  90. }
  91. - (NSString *)password
  92. {
  93. return _password;
  94. }
  95. - (void)setUsername:(NSString *)newUsername password:(NSString *)newPassword
  96. {
  97. // Set new credentials.
  98. _username = newUsername;
  99. _password = newPassword;
  100. if ([self clearsCookies]) {
  101. // Remove all cookies for twitter, to ensure next connection uses new credentials.
  102. NSString *urlString = [NSString stringWithFormat:@"%@://%@",
  103. (_secureConnection) ? @"https" : @"http",
  104. _APIDomain];
  105. NSURL *url = [NSURL URLWithString:urlString];
  106. NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  107. NSEnumerator *enumerator = [[cookieStorage cookiesForURL:url] objectEnumerator];
  108. NSHTTPCookie *cookie = nil;
  109. while ((cookie = [enumerator nextObject])) {
  110. [cookieStorage deleteCookie:cookie];
  111. }
  112. }
  113. }
  114. - (NSString *)clientName
  115. {
  116. return _clientName;
  117. }
  118. - (NSString *)clientVersion
  119. {
  120. return _clientVersion;
  121. }
  122. - (NSString *)clientURL
  123. {
  124. return _clientURL;
  125. }
  126. - (NSString *)clientSourceToken
  127. {
  128. return _clientSourceToken;
  129. }
  130. - (void)setClientName:(NSString *)name version:(NSString *)version URL:(NSString *)url token:(NSString *)token;
  131. {
  132. _clientName = name;
  133. _clientVersion = version;
  134. _clientURL = url;
  135. _clientSourceToken = token;
  136. }
  137. - (NSString *)APIDomain
  138. {
  139. return _APIDomain;
  140. }
  141. - (void)setAPIDomain:(NSString *)domain
  142. {
  143. if (!domain || [domain length] == 0) {
  144. _APIDomain = TWITTER_DOMAIN;
  145. } else {
  146. _APIDomain = domain;
  147. }
  148. }
  149. - (BOOL)usesSecureConnection
  150. {
  151. return _secureConnection;
  152. }
  153. - (void)setUsesSecureConnection:(BOOL)flag
  154. {
  155. _secureConnection = flag;
  156. }
  157. - (BOOL)clearsCookies
  158. {
  159. return _clearsCookies;
  160. }
  161. - (void)setClearsCookies:(BOOL)flag
  162. {
  163. _clearsCookies = flag;
  164. }
  165. #pragma mark Connection methods
  166. - (NSUInteger)numberOfConnections
  167. {
  168. return [_connections count];
  169. }
  170. - (NSArray *)connectionIdentifiers
  171. {
  172. return [_connections allKeys];
  173. }
  174. - (void)closeConnection:(NSString *)identifier
  175. {
  176. MGTwitterHTTPURLConnection *connection = [_connections objectForKey:identifier];
  177. if (connection) {
  178. [connection cancel];
  179. [_connections removeObjectForKey:identifier];
  180. }
  181. }
  182. - (void)closeAllConnections
  183. {
  184. [[_connections allValues] makeObjectsPerformSelector:@selector(cancel)];
  185. [_connections removeAllObjects];
  186. }
  187. #pragma mark Utility methods
  188. - (NSDateFormatter *)_HTTPDateFormatter
  189. {
  190. // Returns a formatter for dates in HTTP format (i.e. RFC 822, updated by RFC 1123).
  191. // e.g. "Sun, 06 Nov 1994 08:49:37 GMT"
  192. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  193. //[dateFormatter setDateFormat:@"%a, %d %b %Y %H:%M:%S GMT"]; // won't work with -init, which uses new (unicode) format behaviour.
  194. [dateFormatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss GMT"];
  195. return dateFormatter;
  196. }
  197. - (NSString *)_queryStringWithBase:(NSString *)base parameters:(NSDictionary *)params prefixed:(BOOL)prefixed
  198. {
  199. // Append base if specified.
  200. NSMutableString *str = [NSMutableString stringWithCapacity:0];
  201. if (base) {
  202. [str appendString:base];
  203. }
  204. // Append each name-value pair.
  205. if (params) {
  206. int i;
  207. NSArray *names = [params allKeys];
  208. for (i = 0; i < [names count]; i++) {
  209. if (i == 0 && prefixed) {
  210. [str appendString:@"?"];
  211. } else if (i > 0) {
  212. [str appendString:@"&"];
  213. }
  214. NSString *name = [names objectAtIndex:i];
  215. [str appendString:[NSString stringWithFormat:@"%@=%@",
  216. name, [self _encodeString:[params objectForKey:name]]]];
  217. }
  218. }
  219. return str;
  220. }
  221. - (NSDate *)_HTTPToDate:(NSString *)httpDate
  222. {
  223. NSDateFormatter *dateFormatter = [self _HTTPDateFormatter];
  224. return [dateFormatter dateFromString:httpDate];
  225. }
  226. - (NSString *)_dateToHTTP:(NSDate *)date
  227. {
  228. NSDateFormatter *dateFormatter = [self _HTTPDateFormatter];
  229. return [dateFormatter stringFromDate:date];
  230. }
  231. - (NSString *)_encodeString:(NSString *)string
  232. {
  233. NSString *result = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
  234. (__bridge CFStringRef)string,
  235. NULL,
  236. (CFStringRef)@";/?:@&=$+{}<>,",
  237. kCFStringEncodingUTF8);
  238. return result;
  239. }
  240. - (NSString *)getImageAtURL:(NSString *)urlString
  241. {
  242. // This is a method implemented for the convenience of the client,
  243. // allowing asynchronous downloading of users' Twitter profile images.
  244. NSString *encodedUrlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  245. NSURL *url = [NSURL URLWithString:encodedUrlString];
  246. if (!url) {
  247. return nil;
  248. }
  249. // Construct an NSMutableURLRequest for the URL and set appropriate request method.
  250. NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
  251. cachePolicy:NSURLRequestReloadIgnoringCacheData
  252. timeoutInterval:URL_REQUEST_TIMEOUT];
  253. [theRequest setHTTPShouldHandleCookies:NO];
  254. // Create a connection using this request, with the default timeout and caching policy,
  255. // and appropriate Twitter request and response types for parsing and error reporting.
  256. MGTwitterHTTPURLConnection *connection;
  257. connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
  258. delegate:self
  259. requestType:MGTwitterImageRequest
  260. responseType:MGTwitterImage];
  261. if (!connection) {
  262. return nil;
  263. } else {
  264. [_connections setObject:connection forKey:[connection identifier]];
  265. }
  266. return [connection identifier];
  267. }
  268. #pragma mark Request sending methods
  269. #define SET_AUTHORIZATION_IN_HEADER 1
  270. /* See Adium Additions/Changes below—oauth support */
  271. #if 0
  272. - (NSString *)_sendRequestWithMethod:(NSString *)method
  273. path:(NSString *)path
  274. queryParameters:(NSDictionary *)params
  275. body:(id)body
  276. requestType:(MGTwitterRequestType)requestType
  277. responseType:(MGTwitterResponseType)responseType
  278. {
  279. // Construct appropriate URL string.
  280. NSString *fullPath = path;
  281. if (params) {
  282. fullPath = [self _queryStringWithBase:fullPath parameters:params prefixed:YES];
  283. }
  284. #if SET_AUTHORIZATION_IN_HEADER
  285. NSString *urlString = [NSString stringWithFormat:@"%@://%@/%@",
  286. (_secureConnection) ? @"https" : @"http",
  287. _APIDomain, fullPath];
  288. #else
  289. NSString *urlString = [NSString stringWithFormat:@"%@://%@:%@@%@/%@",
  290. (_secureConnection) ? @"https" : @"http",
  291. [self _encodeString:_username], [self _encodeString:_password],
  292. _APIDomain, fullPath];
  293. #endif
  294. NSURL *finalURL = [NSURL URLWithString:urlString];
  295. if (!finalURL) {
  296. return nil;
  297. }
  298. // Construct an NSMutableURLRequest for the URL and set appropriate request method.
  299. NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:finalURL
  300. cachePolicy:NSURLRequestReloadIgnoringCacheData
  301. timeoutInterval:URL_REQUEST_TIMEOUT];
  302. [theRequest setHTTPShouldHandleCookies:NO];
  303. if(method && [method isEqualToString:HTTP_MULTIPART_METHOD]) {
  304. method = HTTP_POST_METHOD;
  305. [theRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", MULTIPART_FORM_BOUNDARY] forHTTPHeaderField:@"Content-type"];
  306. }
  307. if (method) {
  308. [theRequest setHTTPMethod:method];
  309. }
  310. [theRequest setHTTPShouldHandleCookies:NO];
  311. // Set headers for client information, for tracking purposes at Twitter.
  312. [theRequest setValue:_clientName forHTTPHeaderField:@"X-Twitter-Client"];
  313. [theRequest setValue:_clientVersion forHTTPHeaderField:@"X-Twitter-Client-Version"];
  314. [theRequest setValue:_clientURL forHTTPHeaderField:@"X-Twitter-Client-URL"];
  315. #if SET_AUTHORIZATION_IN_HEADER
  316. if ([self username] && [self password]) {
  317. // Set header for HTTP Basic authentication explicitly, to avoid problems with proxies and other intermediaries
  318. NSString *authStr = [NSString stringWithFormat:@"%@:%@", [self username], [self password]];
  319. NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
  320. NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodingWithLineLength:80]];
  321. [theRequest setValue:authValue forHTTPHeaderField:@"Authorization"];
  322. }
  323. #endif
  324. // Set the request body if this is a POST request.
  325. BOOL isPOST = (method && [method isEqualToString:HTTP_POST_METHOD]);
  326. if (isPOST) {
  327. // Set request body, if specified (hopefully so), with 'source' parameter if appropriate.
  328. if([body isKindOfClass:[NSString class]]) {
  329. NSString *finalBody = @"";
  330. if (body) {
  331. finalBody = [finalBody stringByAppendingString:body];
  332. }
  333. if (_clientSourceToken) {
  334. finalBody = [finalBody stringByAppendingString:[NSString stringWithFormat:@"%@source=%@",
  335. (body) ? @"&" : @"?" ,
  336. _clientSourceToken]];
  337. }
  338. if (finalBody) {
  339. [theRequest setHTTPBody:[finalBody dataUsingEncoding:NSUTF8StringEncoding]];
  340. }
  341. } else if ([body isKindOfClass:[NSData class]]) {
  342. [theRequest setHTTPBody:body];
  343. }
  344. }
  345. // Create a connection using this request, with the default timeout and caching policy,
  346. // and appropriate Twitter request and response types for parsing and error reporting.
  347. MGTwitterHTTPURLConnection *connection;
  348. connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
  349. delegate:self
  350. requestType:requestType
  351. responseType:responseType];
  352. if (!connection) {
  353. return nil;
  354. } else {
  355. [_connections setObject:connection forKey:[connection identifier]];
  356. [connection release];
  357. }
  358. return [connection identifier];
  359. }
  360. #endif
  361. #pragma mark Parsing methods
  362. - (void)_parseXMLForConnection:(MGTwitterHTTPURLConnection *)connection
  363. {
  364. NSString *identifier = [[connection identifier] copy];
  365. NSData *xmlData = [[connection data] copy];
  366. MGTwitterRequestType requestType = [connection requestType];
  367. MGTwitterResponseType responseType = [connection responseType];
  368. #if USE_LIBXML
  369. NSURL *URL = [connection URL];
  370. switch (responseType) {
  371. case MGTwitterStatuses:
  372. case MGTwitterStatus:
  373. [MGTwitterStatusesLibXMLParser parserWithXML:xmlData delegate:self
  374. connectionIdentifier:identifier requestType:requestType
  375. responseType:responseType URL:URL];
  376. break;
  377. case MGTwitterUsers:
  378. case MGTwitterUser:
  379. [MGTwitterUsersLibXMLParser parserWithXML:xmlData delegate:self
  380. connectionIdentifier:identifier requestType:requestType
  381. responseType:responseType URL:URL];
  382. break;
  383. case MGTwitterDirectMessages:
  384. case MGTwitterDirectMessage:
  385. [MGTwitterMessagesLibXMLParser parserWithXML:xmlData delegate:self
  386. connectionIdentifier:identifier requestType:requestType
  387. responseType:responseType URL:URL];
  388. break;
  389. case MGTwitterMiscellaneous:
  390. [MGTwitterMiscLibXMLParser parserWithXML:xmlData delegate:self
  391. connectionIdentifier:identifier requestType:requestType
  392. responseType:responseType URL:URL];
  393. break;
  394. default:
  395. break;
  396. }
  397. #else
  398. // Determine which type of parser to use.
  399. switch (responseType) {
  400. case MGTwitterStatuses:
  401. case MGTwitterStatus:
  402. [MGTwitterStatusesParser parserWithXML:xmlData delegate:self
  403. connectionIdentifier:identifier requestType:requestType
  404. responseType:responseType];
  405. break;
  406. case MGTwitterUsers:
  407. case MGTwitterUser:
  408. [MGTwitterUsersParser parserWithXML:xmlData delegate:self
  409. connectionIdentifier:identifier requestType:requestType
  410. responseType:responseType];
  411. break;
  412. case MGTwitterDirectMessages:
  413. case MGTwitterDirectMessage:
  414. [MGTwitterMessagesParser parserWithXML:xmlData delegate:self
  415. connectionIdentifier:identifier requestType:requestType
  416. responseType:responseType];
  417. break;
  418. case MGTwitterMiscellaneous:
  419. [MGTwitterMiscParser parserWithXML:xmlData delegate:self
  420. connectionIdentifier:identifier requestType:requestType
  421. responseType:responseType];
  422. break;
  423. default:
  424. break;
  425. }
  426. #endif
  427. }
  428. #pragma mark Delegate methods
  429. - (BOOL) _isValidDelegateForSelector:(SEL)selector
  430. {
  431. return ((_delegate != nil) && [_delegate respondsToSelector:selector]);
  432. }
  433. #pragma mark MGTwitterParserDelegate methods
  434. - (void)parsingSucceededForRequest:(NSString *)identifier
  435. ofResponseType:(MGTwitterResponseType)responseType
  436. withParsedObjects:(NSArray *)parsedObjects
  437. {
  438. // Forward appropriate message to _delegate, depending on responseType.
  439. switch (responseType) {
  440. case MGTwitterStatuses:
  441. case MGTwitterStatus:
  442. if ([self _isValidDelegateForSelector:@selector(statusesReceived:forRequest:)])
  443. [_delegate statusesReceived:parsedObjects forRequest:identifier];
  444. break;
  445. case MGTwitterUsers:
  446. case MGTwitterUser:
  447. if ([self _isValidDelegateForSelector:@selector(userInfoReceived:forRequest:)])
  448. [_delegate userInfoReceived:parsedObjects forRequest:identifier];
  449. break;
  450. case MGTwitterDirectMessages:
  451. case MGTwitterDirectMessage:
  452. if ([self _isValidDelegateForSelector:@selector(directMessagesReceived:forRequest:)])
  453. [_delegate directMessagesReceived:parsedObjects forRequest:identifier];
  454. break;
  455. case MGTwitterMiscellaneous:
  456. if ([self _isValidDelegateForSelector:@selector(miscInfoReceived:forRequest:)])
  457. [_delegate miscInfoReceived:parsedObjects forRequest:identifier];
  458. break;
  459. default:
  460. break;
  461. }
  462. }
  463. - (void)parsingFailedForRequest:(NSString *)requestIdentifier
  464. ofResponseType:(MGTwitterResponseType)responseType
  465. withError:(NSError *)error
  466. {
  467. if ([self _isValidDelegateForSelector:@selector(requestFailed:withError:)])
  468. [_delegate requestFailed:requestIdentifier withError:error];
  469. }
  470. #pragma mark NSURLConnection delegate methods
  471. - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
  472. {
  473. if ([challenge previousFailureCount] == 0 && ![challenge proposedCredential] && !_useOAuth && _password && _username) {
  474. NSURLCredential *credential = [NSURLCredential credentialWithUser:_username password:_password
  475. persistence:NSURLCredentialPersistenceForSession];
  476. [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
  477. } else {
  478. [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
  479. }
  480. }
  481. - (void)connection:(MGTwitterHTTPURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
  482. {
  483. // This method is called when the server has determined that it has enough information to create the NSURLResponse.
  484. // it can be called multiple times, for example in the case of a redirect, so each time we reset the data.
  485. [connection resetDataLength];
  486. // Get response code.
  487. NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response;
  488. NSInteger statusCode = [resp statusCode];
  489. if (statusCode >= 400) {
  490. // Assume failure, and report to delegate.
  491. NSError *error = [NSError errorWithDomain:@"HTTP" code:statusCode userInfo:nil];
  492. if ([self _isValidDelegateForSelector:@selector(requestFailed:withError:)])
  493. [_delegate requestFailed:[connection identifier] withError:error];
  494. // Destroy the connection.
  495. [connection cancel];
  496. [_connections removeObjectForKey:[connection identifier]];
  497. } else if (statusCode == 304 || [connection responseType] == MGTwitterGeneric) {
  498. // Not modified, or generic success.
  499. if ([self _isValidDelegateForSelector:@selector(requestSucceeded:)])
  500. [_delegate requestSucceeded:[connection identifier]];
  501. if (statusCode == 304) {
  502. [self parsingSucceededForRequest:[connection identifier]
  503. ofResponseType:[connection responseType]
  504. withParsedObjects:[NSArray array]];
  505. }
  506. // Destroy the connection.
  507. [connection cancel];
  508. [_connections removeObjectForKey:[connection identifier]];
  509. }
  510. if (NO) {
  511. // Display headers for debugging.
  512. NSHTTPURLResponse *noResp = (NSHTTPURLResponse *)response;
  513. NSLog(@"(%ld) [%@]:\r%@",
  514. (long)[noResp statusCode],
  515. [NSHTTPURLResponse localizedStringForStatusCode:[noResp statusCode]],
  516. [noResp allHeaderFields]);
  517. }
  518. }
  519. - (void)connection:(MGTwitterHTTPURLConnection *)connection didReceiveData:(NSData *)data
  520. {
  521. // Append the new data to the receivedData.
  522. [connection appendData:data];
  523. }
  524. - (void)connection:(MGTwitterHTTPURLConnection *)connection didFailWithError:(NSError *)error
  525. {
  526. // Inform delegate.
  527. if ([self _isValidDelegateForSelector:@selector(requestFailed:withError:)])
  528. [_delegate requestFailed:[connection identifier] withError:error];
  529. // Release the connection.
  530. [_connections removeObjectForKey:[connection identifier]];
  531. }
  532. - (void)connectionDidFinishLoading:(MGTwitterHTTPURLConnection *)connection
  533. {
  534. // Inform delegate.
  535. if ([self _isValidDelegateForSelector:@selector(requestSucceeded:)])
  536. [_delegate requestSucceeded:[connection identifier]];
  537. NSData *receivedData = [connection data];
  538. if (receivedData) {
  539. if (NO) {
  540. // Dump data as string for debugging.
  541. NSString *dataString = [NSString stringWithUTF8String:[receivedData bytes]];
  542. NSLog(@"Succeeded! Received %lu bytes of data:\r\r%@", (unsigned long)[receivedData length], dataString);
  543. }
  544. if (NO) {
  545. // Dump XML to file for debugging.
  546. NSString *dataString = [NSString stringWithUTF8String:[receivedData bytes]];
  547. [dataString writeToFile:[@"~/Desktop/twitter_messages.xml" stringByExpandingTildeInPath]
  548. atomically:NO encoding:NSUnicodeStringEncoding error:NULL];
  549. }
  550. if ([connection responseType] == MGTwitterImage) {
  551. // Create image from data.
  552. #if TARGET_OS_IPHONE
  553. UIImage *image = [[[UIImage alloc] initWithData:[connection data]] autorelease];
  554. #else
  555. NSImage *image = [[NSImage alloc] initWithData:[connection data]];
  556. #endif
  557. // Inform delegate.
  558. if ([self _isValidDelegateForSelector:@selector(imageReceived:forRequest:)])
  559. [_delegate imageReceived:image forRequest:[connection identifier]];
  560. } else {
  561. // Parse XML appropriately.
  562. [self _parseXMLForConnection:connection];
  563. }
  564. }
  565. // Release the connection.
  566. [_connections removeObjectForKey:[connection identifier]];
  567. }
  568. #pragma mark -
  569. #pragma mark Twitter API methods
  570. #pragma mark -
  571. #pragma mark Account methods
  572. - (NSString *)endUserSession
  573. {
  574. NSString *path = @"account/end_session"; // deliberately no format specified
  575. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  576. requestType:MGTwitterAccountRequest
  577. responseType:MGTwitterGeneric];
  578. }
  579. - (NSString *)enableUpdatesFor:(NSString *)username
  580. {
  581. // i.e. follow
  582. if (!username) {
  583. return nil;
  584. }
  585. NSString *path = [NSString stringWithFormat:@"friendships/create/%@.xml", username];
  586. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  587. requestType:MGTwitterAccountRequest
  588. responseType:MGTwitterUser];
  589. }
  590. - (NSString *)disableUpdatesFor:(NSString *)username
  591. {
  592. // i.e. no longer follow
  593. if (!username) {
  594. return nil;
  595. }
  596. NSString *path = [NSString stringWithFormat:@"friendships/destroy/%@.xml", username];
  597. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  598. requestType:MGTwitterAccountRequest
  599. responseType:MGTwitterUser];
  600. }
  601. - (NSString *)isUser:(NSString *)username1 receivingUpdatesFor:(NSString *)username2
  602. {
  603. if (!username1 || !username2) {
  604. return nil;
  605. }
  606. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  607. [params setObject:username1 forKey:@"user_a"];
  608. [params setObject:username2 forKey:@"user_b"];
  609. NSString *path = @"friendships/exists.xml";
  610. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  611. requestType:MGTwitterAccountRequest
  612. responseType:MGTwitterMiscellaneous];
  613. }
  614. - (NSString *)enableNotificationsFor:(NSString *)username
  615. {
  616. if (!username) {
  617. return nil;
  618. }
  619. NSString *path = [NSString stringWithFormat:@"notifications/follow/%@.xml", username];
  620. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  621. requestType:MGTwitterAccountRequest
  622. responseType:MGTwitterUser];
  623. }
  624. - (NSString *)disableNotificationsFor:(NSString *)username
  625. {
  626. if (!username) {
  627. return nil;
  628. }
  629. NSString *path = [NSString stringWithFormat:@"notifications/leave/%@.xml", username];
  630. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  631. requestType:MGTwitterAccountRequest
  632. responseType:MGTwitterUser];
  633. }
  634. - (NSString *)getRateLimitStatus
  635. {
  636. NSString *path = @"account/rate_limit_status.xml";
  637. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  638. requestType:MGTwitterAccountRequest
  639. responseType:MGTwitterMiscellaneous];
  640. }
  641. - (NSString *)setLocation:(NSString *)location
  642. {
  643. if (!location) {
  644. return nil;
  645. }
  646. NSString *path = @"account/update_location.xml";
  647. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  648. [params setObject:location forKey:@"location"];
  649. NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
  650. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
  651. queryParameters:nil body:body
  652. requestType:MGTwitterAccountRequest
  653. responseType:MGTwitterGeneric];
  654. }
  655. - (NSString *)setNotificationsDeliveryMethod:(NSString *)method
  656. {
  657. NSString *deliveryMethod = method;
  658. if (!method || [method length] == 0) {
  659. deliveryMethod = @"none";
  660. }
  661. NSString *path = @"account/update_delivery_device.xml";
  662. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  663. if (deliveryMethod) {
  664. [params setObject:deliveryMethod forKey:@"device"];
  665. }
  666. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  667. requestType:MGTwitterAccountRequest
  668. responseType:MGTwitterGeneric];
  669. }
  670. - (NSString *)block:(NSString *)username
  671. {
  672. if (!username) {
  673. return nil;
  674. }
  675. NSString *path = [NSString stringWithFormat:@"blocks/create/%@.xml", username];
  676. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  677. requestType:MGTwitterAccountRequest
  678. responseType:MGTwitterUser];
  679. }
  680. - (NSString *)unblock:(NSString *)username
  681. {
  682. if (!username) {
  683. return nil;
  684. }
  685. NSString *path = [NSString stringWithFormat:@"blocks/destroy/%@.xml", username];
  686. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  687. requestType:MGTwitterAccountRequest
  688. responseType:MGTwitterUser];
  689. }
  690. - (NSString *)testService
  691. {
  692. NSString *path = @"help/test.xml";
  693. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  694. requestType:MGTwitterAccountRequest
  695. responseType:MGTwitterGeneric];
  696. }
  697. - (NSString *)getDowntimeSchedule
  698. {
  699. NSString *path = @"help/downtime_schedule.xml";
  700. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  701. requestType:MGTwitterAccountRequest
  702. responseType:MGTwitterMiscellaneous];
  703. }
  704. #pragma mark Retrieving updates
  705. - (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum
  706. {
  707. // Included for backwards-compatibility.
  708. return [self getFollowedTimelineFor:username since:date startingAtPage:pageNum count:0]; // zero means default
  709. }
  710. - (NSString *)getFollowedTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)count
  711. {
  712. NSString *path = @"statuses/home_timeline.xml";
  713. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  714. if (date) {
  715. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  716. }
  717. if (pageNum > 0) {
  718. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  719. }
  720. if (username) {
  721. path = [NSString stringWithFormat:@"statuses/home_timeline/%@.xml", username];
  722. }
  723. int tweetCount = DEFAULT_TWEET_COUNT;
  724. if (count > 0) {
  725. tweetCount = count;
  726. }
  727. [params setObject:[NSString stringWithFormat:@"%d", tweetCount] forKey:@"count"];
  728. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  729. requestType:MGTwitterStatusesRequest
  730. responseType:MGTwitterStatuses];
  731. }
  732. - (NSString *)getFollowedTimelineFor:(NSString *)username sinceID:(NSString *)updateID startingAtPage:(int)pageNum count:(int)count
  733. {
  734. NSString *path = @"statuses/home_timeline.xml";
  735. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  736. if (updateID > 0) {
  737. [params setObject:updateID forKey:@"since_id"];
  738. }
  739. if (pageNum > 0) {
  740. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  741. }
  742. if (username) {
  743. path = [NSString stringWithFormat:@"statuses/home_timeline/%@.xml", username];
  744. }
  745. int tweetCount = DEFAULT_TWEET_COUNT;
  746. if (count > 0) {
  747. tweetCount = count;
  748. }
  749. [params setObject:[NSString stringWithFormat:@"%d", tweetCount] forKey:@"count"];
  750. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  751. requestType:MGTwitterStatusesRequest
  752. responseType:MGTwitterStatuses];
  753. }
  754. - (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date count:(int)numUpdates
  755. {
  756. // Included for backwards-compatibility.
  757. return [self getUserTimelineFor:username since:date startingAtPage:0 count:numUpdates];
  758. }
  759. - (NSString *)getUserTimelineFor:(NSString *)username since:(NSDate *)date startingAtPage:(int)pageNum count:(int)numUpdates
  760. {
  761. NSString *path = @"statuses/user_timeline.xml";
  762. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  763. if (date) {
  764. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  765. }
  766. if (pageNum > 0) {
  767. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  768. }
  769. if (numUpdates > 0) {
  770. [params setObject:[NSString stringWithFormat:@"%d", numUpdates] forKey:@"count"];
  771. }
  772. if (username) {
  773. path = [NSString stringWithFormat:@"statuses/user_timeline/%@.xml", username];
  774. }
  775. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  776. requestType:MGTwitterStatusesRequest
  777. responseType:MGTwitterStatuses];
  778. }
  779. - (NSString *)getUserTimelineFor:(NSString *)username sinceID:(NSString *)updateID startingAtPage:(int)pageNum count:(int)numUpdates
  780. {
  781. NSString *path = @"statuses/user_timeline.xml";
  782. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  783. if (updateID > 0) {
  784. [params setObject:updateID forKey:@"since_id"];
  785. }
  786. if (pageNum > 0) {
  787. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  788. }
  789. if (numUpdates > 0) {
  790. [params setObject:[NSString stringWithFormat:@"%d", numUpdates] forKey:@"count"];
  791. }
  792. if (username) {
  793. path = [NSString stringWithFormat:@"statuses/user_timeline/%@.xml", username];
  794. }
  795. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  796. requestType:MGTwitterStatusesRequest
  797. responseType:MGTwitterStatuses];
  798. }
  799. - (NSString *)getUserUpdatesArchiveStartingAtPage:(int)pageNum
  800. {
  801. NSString *path = @"account/archive.xml";
  802. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  803. if (pageNum > 0) {
  804. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  805. }
  806. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  807. requestType:MGTwitterStatusesRequest
  808. responseType:MGTwitterStatuses];
  809. }
  810. - (NSString *)getPublicTimelineSinceID:(NSString *)updateID
  811. {
  812. NSString *path = @"statuses/public_timeline.xml";
  813. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  814. if (updateID > 0) {
  815. [params setObject:updateID forKey:@"since_id"];
  816. }
  817. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  818. requestType:MGTwitterStatusesRequest
  819. responseType:MGTwitterStatuses];
  820. }
  821. - (NSString *)getRepliesStartingAtPage:(int)pageNum
  822. {
  823. NSString *path = @"statuses/mentions.xml";
  824. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  825. if (pageNum > 0) {
  826. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  827. }
  828. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  829. requestType:MGTwitterRepliesRequest
  830. responseType:MGTwitterStatuses];
  831. }
  832. - (NSString *)getFavoriteUpdatesFor:(NSString *)username startingAtPage:(int)pageNum
  833. {
  834. NSString *path = @"favorites.xml";
  835. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  836. if (pageNum > 0) {
  837. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  838. }
  839. if (username) {
  840. path = [NSString stringWithFormat:@"favorites/%@.xml", username];
  841. }
  842. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  843. requestType:MGTwitterStatusesRequest
  844. responseType:MGTwitterStatuses];
  845. }
  846. - (NSString *)getUpdate:(NSString *)updateID
  847. {
  848. NSString *path = [NSString stringWithFormat:@"statuses/show/%@.xml", updateID];
  849. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  850. requestType:MGTwitterStatusesRequest
  851. responseType:MGTwitterStatus];
  852. }
  853. #pragma mark Retrieving direct messages
  854. - (NSString *)getDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
  855. {
  856. NSString *path = @"direct_messages.xml";
  857. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  858. if (date) {
  859. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  860. }
  861. if (pageNum > 0) {
  862. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  863. }
  864. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  865. requestType:MGTwitterDirectMessagesRequest
  866. responseType:MGTwitterDirectMessages];
  867. }
  868. - (NSString *)getDirectMessagesSinceID:(NSString *)updateID startingAtPage:(int)pageNum
  869. {
  870. NSString *path = @"direct_messages.xml";
  871. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  872. if (updateID > 0) {
  873. [params setObject:updateID forKey:@"since_id"];
  874. }
  875. if (pageNum > 0) {
  876. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  877. }
  878. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  879. requestType:MGTwitterDirectMessagesRequest
  880. responseType:MGTwitterDirectMessages];
  881. }
  882. - (NSString *)getSentDirectMessagesSince:(NSDate *)date startingAtPage:(int)pageNum
  883. {
  884. NSString *path = @"direct_messages/sent.xml";
  885. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  886. if (date) {
  887. [params setObject:[self _dateToHTTP:date] forKey:@"since"];
  888. }
  889. if (pageNum > 0) {
  890. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  891. }
  892. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  893. requestType:MGTwitterDirectMessagesRequest
  894. responseType:MGTwitterDirectMessages];
  895. }
  896. - (NSString *)getSentDirectMessagesSinceID:(NSString *)updateID startingAtPage:(int)pageNum
  897. {
  898. NSString *path = @"direct_messages/sent.xml";
  899. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  900. if (updateID > 0) {
  901. [params setObject:updateID forKey:@"since_id"];
  902. }
  903. if (pageNum > 0) {
  904. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  905. }
  906. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  907. requestType:MGTwitterDirectMessagesRequest
  908. responseType:MGTwitterDirectMessages];
  909. }
  910. #pragma mark Retrieving user information
  911. - (NSString *)getUserInformationFor:(NSString *)username
  912. {
  913. if (!username) {
  914. return nil;
  915. }
  916. NSString *path = [NSString stringWithFormat:@"users/show/%@.xml", username];
  917. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  918. requestType:MGTwitterUserInfoRequest
  919. responseType:MGTwitterUser];
  920. }
  921. - (NSString *)getUserInformationForEmail:(NSString *)email
  922. {
  923. NSString *path = @"users/show.xml";
  924. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  925. if (email) {
  926. [params setObject:email forKey:@"email"];
  927. } else {
  928. return nil;
  929. }
  930. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  931. requestType:MGTwitterUserInfoRequest
  932. responseType:MGTwitterUser];
  933. }
  934. - (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtPage:(int)pageNum
  935. {
  936. NSString *path = @"statuses/friends.xml";
  937. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  938. if (username) {
  939. path = [NSString stringWithFormat:@"statuses/friends/%@.xml", username];
  940. }
  941. if (pageNum > 0) {
  942. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  943. }
  944. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  945. requestType:MGTwitterUserInfoRequest
  946. responseType:MGTwitterUsers];
  947. }
  948. - (NSString *)getRecentlyUpdatedFriendsFor:(NSString *)username startingAtCursor:(long long)cursorNum
  949. {
  950. NSString *path = @"statuses/friends.xml";
  951. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  952. if (username) {
  953. path = [NSString stringWithFormat:@"statuses/friends/%@.xml", username];
  954. }
  955. if (cursorNum >= -1) {
  956. [params setObject:[NSString stringWithFormat:@"%lld", cursorNum] forKey:@"cursor"];
  957. }
  958. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  959. requestType:MGTwitterUserInfoRequest
  960. responseType:MGTwitterUsers];
  961. }
  962. - (NSString *)getFollowersIncludingCurrentStatus:(BOOL)flag
  963. {
  964. NSString *path = @"statuses/followers.xml";
  965. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  966. if (!flag) {
  967. [params setObject:@"true" forKey:@"lite"]; // slightly bizarre, but correct.
  968. }
  969. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  970. requestType:MGTwitterUserInfoRequest
  971. responseType:MGTwitterUsers];
  972. }
  973. - (NSString *)getFeaturedUsers
  974. {
  975. NSString *path = @"statuses/featured.xml";
  976. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  977. requestType:MGTwitterUserInfoRequest
  978. responseType:MGTwitterUsers];
  979. }
  980. #pragma mark Sending and editing updates
  981. - (NSString *)sendUpdate:(NSString *)status
  982. {
  983. return [self sendUpdate:status inReplyTo:0];
  984. }
  985. - (NSString *)sendUpdate:(NSString *)status inReplyTo:(NSString *)updateID
  986. {
  987. if (!status) {
  988. return nil;
  989. }
  990. NSString *path = @"statuses/update.xml";
  991. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  992. [params setObject:status forKey:@"status"];
  993. if (updateID > 0) {
  994. [params setObject:updateID forKey:@"in_reply_to_status_id"];
  995. }
  996. NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
  997. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
  998. queryParameters:nil body:body
  999. requestType:MGTwitterStatusSend
  1000. responseType:MGTwitterStatus];
  1001. }
  1002. - (NSString *)deleteUpdate:(NSString *)updateID
  1003. {
  1004. NSString *path = [NSString stringWithFormat:@"statuses/destroy/%@.xml", updateID];
  1005. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  1006. requestType:MGTwitterAccountRequest
  1007. responseType:MGTwitterGeneric];
  1008. }
  1009. - (NSString *)markUpdate:(NSString *)updateID asFavorite:(BOOL)flag
  1010. {
  1011. NSString *path = [NSString stringWithFormat:@"favorites/%@/%@.xml",
  1012. (flag) ? @"create" : @"destroy" ,
  1013. updateID];
  1014. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  1015. requestType:MGTwitterAccountRequest
  1016. responseType:MGTwitterStatus];
  1017. }
  1018. - (NSString *)retweetUpdate:(NSString *)updateID
  1019. {
  1020. NSString *path = [NSString stringWithFormat:@"statuses/retweet/%@.xml", updateID];
  1021. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  1022. requestType:MGTwitterAccountRequest
  1023. responseType:MGTwitterStatus];
  1024. }
  1025. #pragma mark Sending and editing direct messages
  1026. - (NSString *)sendDirectMessage:(NSString *)message to:(NSString *)username
  1027. {
  1028. if (!message || !username) {
  1029. return nil;
  1030. }
  1031. NSString *path = @"direct_messages/new.xml";
  1032. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  1033. [params setObject:message forKey:@"text"];
  1034. [params setObject:username forKey:@"user"];
  1035. NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
  1036. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
  1037. queryParameters:nil body:body
  1038. requestType:MGTwitterDirectMessageSend
  1039. responseType:MGTwitterDirectMessage];
  1040. }
  1041. - (NSString *)deleteDirectMessage:(NSString *)updateID
  1042. {
  1043. NSString *path = [NSString stringWithFormat:@"direct_messages/destroy/%@.xml", updateID];
  1044. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path queryParameters:nil body:nil
  1045. requestType:MGTwitterAccountRequest
  1046. responseType:MGTwitterGeneric];
  1047. }
  1048. #pragma mark Adium Additions/Changes
  1049. #define MAX_NAME_LENGTH 20
  1050. #define MAX_EMAIL_LENGTH 40
  1051. #define MAX_URL_LENGTH 100
  1052. #define MAX_DESCRIPTION_LENGTH 160
  1053. - (NSString *)checkUserCredentials
  1054. {
  1055. NSString *path = @"account/verify_credentials.xml";
  1056. return [self _sendRequestWithMethod:nil path:path queryParameters:nil body:nil
  1057. requestType:MGTwitterAccountRequest
  1058. responseType:MGTwitterUser];
  1059. }
  1060. - (NSString *)getRepliesSinceID:(NSString *)updateID startingAtPage:(int)pageNum
  1061. {
  1062. NSString *path = @"statuses/mentions.xml";
  1063. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  1064. if (updateID > 0) {
  1065. [params setObject:updateID forKey:@"since_id"];
  1066. }
  1067. if (pageNum > 0) {
  1068. [params setObject:[NSString stringWithFormat:@"%d", pageNum] forKey:@"page"];
  1069. }
  1070. return [self _sendRequestWithMethod:nil path:path queryParameters:params body:nil
  1071. requestType:MGTwitterRepliesRequest
  1072. responseType:MGTwitterStatuses];
  1073. }
  1074. - (NSString *)updateProfileName:(NSString *)name
  1075. email:(NSString *)email
  1076. url:(NSString *)url
  1077. location:(NSString *)location
  1078. description:(NSString *)description
  1079. {
  1080. if (!name && !email && !url && !location && !description) {
  1081. return nil;
  1082. }
  1083. NSString *path = @"account/update_profile.xml";
  1084. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  1085. if (name) {
  1086. if(name.length > MAX_NAME_LENGTH) {
  1087. name = [name substringToIndex:MAX_NAME_LENGTH];
  1088. }
  1089. [params setObject:name forKey:@"name"];
  1090. }
  1091. if (email) {
  1092. if(email.length > MAX_EMAIL_LENGTH) {
  1093. email = [email substringToIndex:MAX_EMAIL_LENGTH];
  1094. }
  1095. [params setObject:email forKey:@"email"];
  1096. }
  1097. if (url) {
  1098. if(url.length > MAX_URL_LENGTH) {
  1099. url = [url substringToIndex:MAX_URL_LENGTH];
  1100. }
  1101. [params setObject:url forKey:@"url"];
  1102. }
  1103. if (location) {
  1104. if(location.length > MAX_LOCATION_LENGTH) {
  1105. location = [location substringToIndex:MAX_LOCATION_LENGTH];
  1106. }
  1107. [params setObject:location forKey:@"location"];
  1108. }
  1109. if (description) {
  1110. if(description.length > MAX_DESCRIPTION_LENGTH) {
  1111. description = [description substringToIndex:MAX_DESCRIPTION_LENGTH];
  1112. }
  1113. [params setObject:description forKey:@"description"];
  1114. }
  1115. NSString *body = [self _queryStringWithBase:nil parameters:params prefixed:NO];
  1116. return [self _sendRequestWithMethod:HTTP_POST_METHOD path:path
  1117. queryParameters:nil body:body
  1118. requestType:MGTwitterAccountRequest
  1119. responseType:MGTwitterUser];
  1120. }
  1121. - (NSString *)updateProfileImage:(NSData *)profileImage
  1122. {
  1123. if (!profileImage || _useOAuth) {
  1124. return nil;
  1125. }
  1126. NSString *path = @"account/update_profile_image.xml";
  1127. NSMutableData *body = [NSMutableData data];
  1128. NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
  1129. NSImage *image = [[NSImage alloc] initWithData:profileImage];
  1130. NSBitmapImageRep *bitmapImageRep = nil;
  1131. for(NSImageRep *imageRep in image.representations) {
  1132. if([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
  1133. bitmapImageRep = (NSBitmapImageRep *)imageRep;
  1134. }
  1135. }
  1136. if(!bitmapImageRep) {
  1137. return nil;
  1138. }
  1139. [body appendData:[[NSString stringWithFormat:@"--%@\r\n", MULTIPART_FORM_BOUNDARY] dataUsingEncoding:NSUTF8StringEncoding]];
  1140. [body appendData:[@"Content-Disposition: form-data; name=\"image\"; filename=\"adium_icon.png\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
  1141. [body appendData:[@"Content-Type: image/png\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
  1142. [body appendData:[bitmapImageRep representationUsingType:NSPNGFileType properties:nil]];
  1143. [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", MULTIPART_FORM_BOUNDARY] dataUsingEncoding:NSUTF8StringEncoding]];
  1144. return [self _sendRequestWithMethod:HTTP_MULTIPART_METHOD path:path
  1145. queryParameters:params body:body
  1146. requestType:MGTwitterAccountRequest
  1147. responseType:MGTwitterUser];
  1148. }
  1149. #pragma mark Adium OAuth Changes
  1150. - (NSString *)_sendRequestWithMethod:(NSString *)method
  1151. path:(NSString *)path
  1152. queryParameters:(NSDictionary *)params
  1153. body:(id)body
  1154. requestType:(MGTwitterRequestType)requestType
  1155. responseType:(MGTwitterResponseType)responseType
  1156. {
  1157. // Construct appropriate URL string.
  1158. NSString *fullPath = path;
  1159. if (params) {
  1160. fullPath = [self _queryStringWithBase:fullPath parameters:params prefixed:YES];
  1161. }
  1162. NSString *urlString = nil;
  1163. if (!_useOAuth) {
  1164. #if SET_AUTHORIZATION_IN_HEADER
  1165. urlString = [NSString stri

Large files files are truncated, but you can click here to view the full file