Plugins/Purple Service/ESPurpleYahooAccount.m
author Zachary West <zacw@adium.im>
Fri Nov 27 13:16:32 2009 -0500 (2009-11-27)
changeset 2833 fbb560e54233
parent 2570 634e2660696b
permissions -rw-r--r--
Use the account proxy for SSL connections for Yahoo. Fixes #13210.
     1 /* 
     2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
     3  * with this source distribution.
     4  * 
     5  * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
     6  * General Public License as published by the Free Software Foundation; either version 2 of the License,
     7  * or (at your option) any later version.
     8  * 
     9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
    10  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    11  * Public License for more details.
    12  * 
    13  * You should have received a copy of the GNU General Public License along with this program; if not,
    14  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    15  */
    16 
    17 #import <Adium/AIAccountControllerProtocol.h>
    18 #import <Adium/AIStatusControllerProtocol.h>
    19 #import "ESPurpleYahooAccount.h"
    20 #import "ESPurpleYahooAccountViewController.h"
    21 #import <AdiumLibpurple/SLPurpleCocoaAdapter.h>
    22 #import <Adium/AIHTMLDecoder.h>
    23 #import <Adium/AIListContact.h>
    24 #import <Adium/AIStatus.h>
    25 #import <Adium/ESFileTransfer.h>
    26 #import <libpurple/libymsg.h>
    27 #import <libpurple/yahoo_friend.h>
    28 
    29 @implementation ESPurpleYahooAccount
    30 
    31 - (const char*)protocolPlugin
    32 {
    33     return "prpl-yahoo";
    34 }
    35 
    36 - (void)configurePurpleAccount
    37 {
    38 	[super configurePurpleAccount];
    39 
    40 	purple_account_set_string(account, "room_list_locale", [[self preferenceForKey:KEY_YAHOO_ROOM_LIST_LOCALE
    41 																		   group:GROUP_ACCOUNT_STATUS] UTF8String]);
    42 	
    43 	// We only have account proxies; use the account proxy for SSL connections.
    44 	purple_account_set_bool(account, "proxy_ssl", TRUE);
    45 }
    46 
    47 - (NSString *)stringByRemovingYahooSuffix:(NSString *)inString
    48 {
    49 	if ((inString && ([inString length] > 0))) {
    50 		//If inString contains @yahoo., we consider this to be an email address suffix such as @yahoo.com or @yahoo.it, and delete it and everything after it. Thus, jdoe@yahoo.com becomes simply jdoe.
    51 		//However, we must leave other suffixes, such as sbcglobal.net, alone. We can't simply match @, or we would delete the @sbcglobal.net suffix and leave the user unable to connect with it.
    52 		NSRange yahooRange = [inString rangeOfString:@"@yahoo." 
    53 		                                     options:NSLiteralSearch];
    54 		if (yahooRange.location != NSNotFound) {
    55 			//Future expansion: Only delete if@yahoo.is followed by a known TLD and (if appropriate) 2LD, in order to support oddball suffixes such as@yahoo.example.com. I don't know whether any such suffixes exist.boredzo
    56 			inString = [inString substringToIndex:yahooRange.location];
    57 		}
    58 	}
    59 	
    60 	return inString;
    61 }
    62 
    63 /*!
    64 * @brief The UID will be changed. The account has a chance to perform modifications
    65  *
    66  * Remove @yahoo.com from the proposedUID - a common mistake is to include it in the yahoo ID
    67  *
    68  * @param proposedUID The proposed, pre-filtered UID (filtered means it has no characters invalid for this servce)
    69  * @result The UID to use; the default implementation just returns proposedUID.
    70  */
    71 - (NSString *)accountWillSetUID:(NSString *)proposedUID
    72 {
    73 	return [self stringByRemovingYahooSuffix:proposedUID];
    74 }
    75 
    76 /*!
    77  * @brief Name to use when creating a PurpleAccount for this CBPurpleAccount
    78  */
    79 - (const char *)purpleAccountName
    80 {
    81 	return [[self stringByRemovingYahooSuffix:self.formattedUID] UTF8String];
    82 }
    83 
    84 - (NSSet *)supportedPropertyKeys
    85 {
    86 	static NSMutableSet *supportedPropertyKeys = nil;
    87 	
    88 	if (!supportedPropertyKeys) {
    89 		supportedPropertyKeys = [[NSMutableSet alloc] initWithObjects:
    90 			@"AvailableMessage",
    91 			@"Invisible",
    92 			nil];
    93 		[supportedPropertyKeys unionSet:[super supportedPropertyKeys]];
    94 	}
    95 	
    96 	return supportedPropertyKeys;
    97 }
    98 
    99 /*!
   100  * @brief Should set aliases serverside?
   101  *
   102  * Yahoo supports serverside aliases.
   103  */
   104 - (BOOL)shouldSetAliasesServerside
   105 {
   106 	return YES;
   107 }
   108 
   109 #pragma mark Connection
   110 - (NSString *)connectionStringForStep:(int)step
   111 {
   112 	switch (step)
   113 	{
   114 		case 0:
   115 			return AILocalizedString(@"Connecting",nil);
   116 			break;
   117 	}
   118 	return nil;
   119 }
   120 
   121 #pragma mark Encoding
   122 - (NSString *)encodedAttributedString:(NSAttributedString *)inAttributedString forListObject:(AIListObject *)inListObject
   123 {	
   124 	return [AIHTMLDecoder encodeHTML:inAttributedString
   125 							 headers:NO
   126 							fontTags:YES
   127 				  includingColorTags:YES
   128 					   closeFontTags:YES
   129 						   styleTags:YES
   130 		  closeStyleTagsOnFontChange:YES
   131 					  encodeNonASCII:NO
   132 						encodeSpaces:NO
   133 						  imagesPath:nil
   134 				   attachmentsAsText:YES
   135 		   onlyIncludeOutgoingImages:NO
   136 					  simpleTagsOnly:YES
   137 					  bodyBackground:NO
   138 				 allowJavascriptURLs:YES];
   139 }
   140 
   141 #pragma mark File transfer
   142 - (BOOL)canSendFolders
   143 {
   144 	return NO;
   145 }
   146 
   147 - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer
   148 {
   149 	[super _beginSendOfFileTransfer:fileTransfer];
   150 }
   151 
   152 
   153 - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer
   154 {
   155     [super acceptFileTransferRequest:fileTransfer];    
   156 }
   157 
   158 - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer
   159 {
   160     [super rejectFileReceiveRequest:fileTransfer];    
   161 }
   162 
   163 - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer
   164 {
   165 	[super cancelFileTransfer:fileTransfer];
   166 }
   167 
   168 #pragma mark Status Messages
   169 
   170 /*!
   171  * @brief Status name to use for a Purple buddy
   172  */
   173 - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy
   174 {
   175 	NSString		*statusName = nil;
   176 	PurplePresence	*presence = purple_buddy_get_presence(buddy);
   177 	PurpleStatus		*status = purple_presence_get_active_status(presence);
   178 	const char		*purpleStatusID = purple_status_get_id(status);
   179 	
   180 	if (!purpleStatusID) return nil;
   181 	
   182 	if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_BRB)) {
   183 		statusName = STATUS_NAME_BRB;
   184 		
   185 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_BUSY)) {
   186 		statusName = STATUS_NAME_BUSY;
   187 		
   188 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTATHOME)) {
   189 		statusName = STATUS_NAME_NOT_AT_HOME;
   190 		
   191 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTATDESK)) {
   192 		statusName = STATUS_NAME_NOT_AT_DESK;
   193 		
   194 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
   195 		statusName = STATUS_NAME_NOT_IN_OFFICE;
   196 		
   197 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_ONPHONE)) {
   198 		statusName = STATUS_NAME_PHONE;
   199 		
   200 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_ONVACATION)) {
   201 		statusName = STATUS_NAME_VACATION;
   202 		
   203 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
   204 		statusName = STATUS_NAME_LUNCH;
   205 		
   206 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
   207 		statusName = STATUS_NAME_STEPPED_OUT;
   208 		
   209 	} else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_INVISIBLE)) {
   210 		statusName = STATUS_NAME_INVISIBLE;
   211 	}
   212 	
   213 	return statusName;
   214 }
   215 
   216 /*!
   217  * @brief Update the status message and away state of the contact
   218  */
   219 - (void)updateStatusForContact:(AIListContact *)theContact toStatusType:(NSNumber *)statusTypeNumber statusName:(NSString *)statusName statusMessage:(NSAttributedString *)statusMessage isMobile:(BOOL)isMobile
   220 {
   221 	NSString	*statusMessageString = [statusMessage string];
   222 	char		*normalized = g_strdup(purple_normalize(account, [theContact.UID UTF8String]));
   223 	YahooData	*od;
   224 	YahooFriend	*f;
   225 
   226 	/* Grab the idle time while we have a chance */
   227 	if ((purple_account_is_connected(account)) &&
   228 		(od = purple_account_get_connection(account)->proto_data) &&
   229 		(f = g_hash_table_lookup(od->friends, normalized))) {
   230 
   231 		if (f->status == YAHOO_STATUS_IDLE) {
   232 			//Now idle
   233 			int		idle = f->idle;
   234 			NSDate	*idleSince;
   235 			
   236 			if (idle != -1) {
   237 				idleSince = [NSDate dateWithTimeIntervalSinceNow:-idle];
   238 			} else {
   239 				idleSince = [NSDate date];
   240 			}
   241 			
   242 			[theContact setValue:idleSince
   243 								 forProperty:@"IdleSince"
   244 								 notify:NotifyLater];
   245 			
   246 		} else if (f->status == YAHOO_STATUS_INVISIBLE) {
   247 			statusTypeNumber = [NSNumber numberWithInt:AIInvisibleStatusType]; /* Invisible has a special status type */
   248 		}
   249 	}
   250 
   251 	g_free(normalized);
   252 	
   253 	//Yahoo doesn't have an explicit mobile state; instead the status message is automatically set to indicate mobility.
   254 	if (statusMessageString && ([statusMessageString isEqualToString:@"I'm on SMS"] ||
   255 								([statusMessageString rangeOfString:@"I'm mobile"].location != NSNotFound))) {
   256 		[theContact setIsMobile:YES notify:NotifyLater];
   257 
   258 	} else if (theContact.isMobile) {
   259 		[theContact setIsMobile:NO notify:NotifyLater];		
   260 	}
   261 	
   262 	[super updateStatusForContact:theContact
   263 					 toStatusType:statusTypeNumber
   264 					   statusName:statusName
   265 					statusMessage:statusMessage
   266 						 isMobile:isMobile];
   267 }
   268 
   269 /*!
   270  * @brief Return the purple status ID to be used for a status
   271  *
   272  * Most subclasses should override this method; these generic values may be appropriate for others.
   273  *
   274  * Active services provided nonlocalized status names.  An AIStatus is passed to this method along with a pointer
   275  * to the status message.  This method should handle any status whose statusNname this service set as well as any statusName
   276  * defined in  AIStatusController.h (which will correspond to the services handled by Adium by default).
   277  * It should also handle a status name not specified in either of these places with a sane default, most likely by loooking at
   278  * statusState.statusType for a general idea of the status's type.
   279  *
   280  * @param statusState The status for which to find the purple status ID
   281  * @param arguments Prpl-specific arguments which will be passed with the state. Message is handled automatically.
   282  *
   283  * @result The purple status ID
   284  */
   285 - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState
   286 							arguments:(NSMutableDictionary *)arguments
   287 {
   288 	const char		*statusID = NULL;
   289 	NSString		*statusName = statusState.statusName;
   290 	NSString		*statusMessageString = [statusState statusMessageString];
   291 
   292 	if (!statusMessageString) statusMessageString = @"";
   293 
   294 	switch (statusState.statusType) {
   295 		case AIAvailableStatusType:
   296 			statusID = YAHOO_STATUS_TYPE_AVAILABLE;
   297 			break;
   298 
   299 		case AIAwayStatusType:
   300 		{
   301 			if (([statusName isEqualToString:STATUS_NAME_BRB]) ||
   302 				([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_BRB]] == NSOrderedSame))
   303 				statusID = YAHOO_STATUS_TYPE_BRB;
   304 
   305 			else if (([statusName isEqualToString:STATUS_NAME_BUSY]) ||
   306 					 ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_BUSY]] == NSOrderedSame))
   307 				statusID = YAHOO_STATUS_TYPE_BUSY;
   308 
   309 			else if (([statusName isEqualToString:STATUS_NAME_NOT_AT_HOME]) ||
   310 					 ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_HOME]] == NSOrderedSame))
   311 				statusID = YAHOO_STATUS_TYPE_NOTATHOME;
   312 
   313 			else if (([statusName isEqualToString:STATUS_NAME_NOT_AT_DESK]) ||
   314 				([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_DESK]] == NSOrderedSame))
   315 				statusID = YAHOO_STATUS_TYPE_NOTATDESK;
   316 			
   317 			else if (([statusName isEqualToString:STATUS_NAME_PHONE]) ||
   318 					 ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_PHONE]] == NSOrderedSame))
   319 				statusID = YAHOO_STATUS_TYPE_ONPHONE;
   320 			
   321 			else if (([statusName isEqualToString:STATUS_NAME_VACATION]) ||
   322 					 ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_VACATION]] == NSOrderedSame))
   323 				statusID = YAHOO_STATUS_TYPE_ONVACATION;
   324 			
   325 			else if (([statusName isEqualToString:STATUS_NAME_LUNCH]) ||
   326 					 ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_LUNCH]] == NSOrderedSame))
   327 				statusID = YAHOO_STATUS_TYPE_OUTTOLUNCH;
   328 			
   329 			else if (([statusName isEqualToString:STATUS_NAME_STEPPED_OUT]) ||
   330 					 ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_STEPPED_OUT]] == NSOrderedSame))
   331 				statusID = YAHOO_STATUS_TYPE_STEPPEDOUT;
   332 			
   333 			
   334 			break;
   335 		}
   336 			
   337 		case AIInvisibleStatusType:
   338 			statusID = YAHOO_STATUS_TYPE_INVISIBLE;
   339 			break;
   340 		
   341 		case AIOfflineStatusType:
   342 			break;
   343 	}
   344 	
   345 	//If we didn't get a purple status ID, request one from super
   346 	if (statusID == NULL) statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];
   347 	
   348 	return statusID;
   349 }
   350 
   351 - (BOOL)shouldAddMusicalNoteToNowPlayingStatus
   352 {
   353 	return NO;
   354 }
   355 
   356 #pragma mark Contact List Menu Items
   357 - (NSString *)titleForContactMenuLabel:(const char *)label forContact:(AIListContact *)inContact
   358 {
   359 	if (!strcmp(label, _("Add Buddy"))) {
   360 		//We handle Add Buddy ourselves
   361 		return nil;
   362 		
   363 	} else if (!strcmp(label, _("Join in Chat"))) {
   364 		return [NSString stringWithFormat:AILocalizedString(@"Join %@'s Chat",nil),inContact.formattedUID];
   365 
   366 	} else if (!strcmp(label, _("Initiate Conference"))) {
   367 		return [NSString stringWithFormat:AILocalizedString(@"Initiate Conference with %@",nil), inContact.formattedUID];
   368 
   369 	} else if (!strcmp(label, _("Presence Settings"))) {
   370 		return [NSString stringWithFormat:AILocalizedString(@"Presence Settings for %@",nil), inContact.formattedUID];
   371 
   372 	} else if (!strcmp(label, _("Appear Online"))) {
   373 		return [NSString stringWithFormat:AILocalizedString(@"Appear Online to %@",nil), inContact.formattedUID];
   374 		
   375 	} else if (!strcmp(label, _("Appear Offline"))) {
   376 		return [NSString stringWithFormat:AILocalizedString(@"Appear Offline to %@",nil), inContact.formattedUID];
   377 		
   378 	} else if (!strcmp(label, _("Appear Permanently Offline"))) {
   379 		return [NSString stringWithFormat:AILocalizedString(@"Always Appear Offline to %@",nil), inContact.formattedUID];
   380 		
   381 	} else if (!strcmp(label, _("Don't Appear Permanently Offline"))) {
   382 		return [NSString stringWithFormat:AILocalizedString(@"Don't Always Appear Offline to %@",nil), inContact.formattedUID];
   383 		
   384 	} else if (!strcmp(label, _("View Webcam"))) {
   385 		//return [NSString stringWithFormat:AILocalizedString(@"View %@'s Webcam",nil), inContact.formattedUID];		
   386 		return nil;
   387 
   388 	} else if (!strcmp(label, _("Start Doodling"))) {
   389 		return nil;
   390 	}
   391 
   392 	return [super titleForContactMenuLabel:label forContact:inContact];
   393 }
   394 
   395 #pragma mark Account Action Menu Items
   396 - (NSString *)titleForAccountActionMenuLabel:(const char *)label
   397 {
   398 	/* The Yahoo actions are "Activate ID" (or perhaps "Active ID," depending on where in the code you look)
   399 	 * and "Join User in Chat...".  These are dumb. Additionally, Join User in Chat doesn't work as of purple 1.1.4. */
   400 	return nil;
   401 }
   402 
   403 @end