PageRenderTime 64ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Plugins/Twitter Plugin/MGTwitterEngine/MGTwitterEngine.m

https://bitbucket.org/nostradani/adium
Objective C | 1635 lines | 1207 code | 355 blank | 73 comment | 174 complexity | 3fdd2b35c87c7a1832b05dff20d3b28d MD5 | raw file
Possible License(s): LGPL-2.0, ISC, BSD-3-Clause, GPL-2.0

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

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