Frameworks/Adium Framework/Source/AIService.m
author Zachary West <zacw@adium.im>
Sat Oct 17 17:35:57 2009 -0400 (2009-10-17)
changeset 2743 df2c24e3844c
parent 1956 fe470f38b994
child 3074 c867117688bb
permissions -rw-r--r--
Add -[AIService pathForDefaultServiceIconOfType:], and use it for replacing %serviceIconImg% in headers. Fixes #12697.
     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/AIAccount.h>
    18 #import <Adium/AIService.h>
    19 #import <Adium/AIAccountControllerProtocol.h>
    20 #import <Adium/AIAccountViewController.h>
    21 #import "AICreateCommand.h"
    22 
    23 /*!
    24  * @class AIService
    25  * @brief An IM Service
    26  *
    27  * This abstract class represents a service that Adium supports.  Subclass this for every service.
    28  */
    29 @implementation AIService
    30 
    31 + (void)registerService
    32 {
    33 	[[[self alloc] init] autorelease];
    34 }
    35 
    36 /*!
    37  * @brief Init
    38  */
    39 - (id)init
    40 {
    41 	if ((self = [super init])) {
    42 		//Register this service with Adium
    43 		[adium.accountController registerService:self];
    44 		
    45 		[self registerStatuses];
    46 	}
    47 	
    48 	return self;
    49 }
    50 
    51 
    52 //Account Creation -----------------------------------------------------------------------------------------------------
    53 #pragma mark Account Creation
    54 /*!
    55  * @brief Create a new account for this service
    56  *
    57  * Creates a new account of this service.  Accounts are identified by a unique number.  We can't use service or
    58  * UID, since both those values may change.
    59  * @param inUID A unique identifier for the account being created.
    60  * @param inInternalObjectID A unique internalObjectID for the account being created.
    61  * @return An AIAccount object for this service.
    62  */
    63 - (id)accountWithUID:(NSString *)inUID internalObjectID:(NSString *)inInternalObjectID
    64 {
    65 	return [[[[self accountClass] alloc] initWithUID:[self normalizeUID:inUID removeIgnoredCharacters:YES]
    66 									internalObjectID:inInternalObjectID
    67 											 service:self] autorelease];
    68 }
    69 
    70 /*!
    71  * @brief Account class associated with this service
    72  *
    73  * Subclass to return the account class associated with this service ([AISomethingAccount class]).
    74  * @return The account class associated with this service.
    75  */
    76 - (Class)accountClass
    77 {
    78 	return nil;
    79 }
    80 
    81 /*!
    82  * @brief Account view controller for this service
    83  *
    84  * Subclass to return an account view controller which provides the necessary controls for configuring an account
    85  * on this service.
    86  * @return An AIAccountViewController or subclass for this service.
    87  */
    88 - (AIAccountViewController *)accountViewController
    89 {
    90 	return [AIAccountViewController accountViewController];
    91 }
    92 
    93 /*!
    94  * @brief Join chat view controller for this service
    95  *
    96  * Subclass to return a join chat view controller which provides the necessary controls for joining a chat on this
    97  * service.
    98  * @return An DCJoinChatViewController or subclass for this service.
    99  */
   100 - (DCJoinChatViewController *)joinChatView
   101 {
   102 	return nil;
   103 }
   104 
   105 
   106 //Service Description --------------------------------------------------------------------------------------------------
   107 #pragma mark Service Description
   108 /*!
   109  * @brief Unique ID for this class
   110  *
   111  * Subclass to return a unique string ID which identifies this class.  No two classes should have the same uniqueID.
   112  * This value is used to determine which protocol code to use for the user's accounts.
   113  * Examples: "libgaim-aim", "aim-toc2", "imservices-aim-.mac"
   114  * @return NSString unique ID
   115  */
   116 - (NSString *)serviceCodeUniqueID{
   117     return @"";
   118 }
   119 
   120 /*!
   121  * @brief Service ID for this service
   122  *
   123  * Subclass to return a string which identifies this service.  If multiple service classes are supporting the same
   124  * service they should have the same serviceID.  Not for user-display.
   125  * Examples: "AIM", "MSN", "Jabber", "ICQ", "Mac"
   126  * @return NSString service ID
   127  */
   128 - (NSString *)serviceID{
   129     return @"";
   130 }
   131 
   132 /*!
   133  * @brief Service class for this service
   134  *
   135  * Some separate services can communicate with eachother.  These services, while they have separate serviceID's,
   136  * are all part of a common service class.  For instance, AIM, ICQ, and .Mac are all part of the "AIM" service class.
   137  * For many services, the serviceClass will be identical to the serviceID.  Not for user-display.
   138  * Service classes may change, do not use them for any permenant storage (logs, preferences, etc).
   139  * Examples: "AIM-compatible", "Jabber", "MSN"
   140  * @return NSString service class
   141  */
   142 - (NSString *)serviceClass{
   143 	return @"";
   144 }
   145 
   146 /*!
   147  * @brief Human readable short description
   148  *
   149  * Human readable, short description of this service
   150  * This value is used in tooltips and the message window.
   151  * Examples: "Jabber", "MSN", "AIM", ".Mac"
   152  * @return NSString short description
   153  */
   154 - (NSString *)shortDescription{
   155     return @"";
   156 }
   157 
   158 /*!
   159  * @brief Human readable long description
   160  *
   161  * Human readable, long description of this service
   162  * If there are multiple classes available for the same service, this description should briefly show the difference
   163  * between the implementations.  This value is used in the account preferences service menu.
   164  * Examples: "Jabber", "MSN", "AOL Instant Messenger", ".Mac"
   165  * @return NSString long description
   166  */
   167 - (NSString *)longDescription{
   168     return @"";
   169 }
   170 
   171 /*!
   172  * @brief Label for user name (general)
   173  *
   174  * String to use for describing the UID/username of this service.  This value varies by service, but should be something
   175  * along the lines of "User name", "Account name", "Screen name", "Member name", etc.
   176  *
   177  * This will be used for the account preferences to indicate the field for the account's user name.  By default, contactUserNameLabel
   178  * will return this value, as well.
   179  *
   180  * @return NSString label for username
   181  */
   182 - (NSString *)userNameLabel
   183 {
   184     return AILocalizedStringFromTableInBundle(@"User Name", nil, [NSBundle bundleForClass:[AIService class]], nil);    
   185 }
   186 
   187 /*!
   188  * @brief Label for user name
   189  *
   190  * String to use for describing the UID/username of contacts for this service.  This value varies by service, but should be something
   191  * along the lines of "User name", "Account name", "Screen name", "Member name", etc.
   192  *
   193  * By default, this returns -[self userNameLabel]; only override this method if contacts are named differently than own-account usernames.
   194  *
   195  * @return NSString label for username
   196  */
   197 - (NSString *)contactUserNameLabel
   198 {
   199 	return [self userNameLabel];
   200 }
   201 
   202 /*!
   203  * @brief Placeholder string for the UID field
   204  */
   205 - (NSString *)UIDPlaceholder
   206 {
   207 	return @"";
   208 }
   209 
   210 /*!
   211  * @brief Service importance
   212  *
   213  * Importance grouping of this service.  Used to make service listings and menus more organized by placing more important
   214  * services at the top of lists or displaying them with more visibility.
   215  * @return AIServiceImportance importance of this service
   216  */
   217 - (AIServiceImportance)serviceImportance
   218 {
   219 	return AIServiceUnsupported;
   220 }
   221 
   222 /*!
   223  * @brief Default icon
   224  *
   225  * Service Icon packs should always include images for all the built-in Adium services.  This method allows external
   226  * service plugins to specify an image which will be used when the service icon pack does not specify one.  It will
   227  * also be useful if new services are added to Adium itself after a significant number of Service Icon packs exist
   228  * which do not yet have an image for this service.  If the active Service Icon pack provides an image for this service,
   229  * this method will not be called.
   230  *
   231  * The service should _not_ cache this icon internally; multiple calls should return unique NSImage objects.
   232  *
   233  * @param iconType The AIServiceIconType of the icon to return. This specifies the desired size of the icon.
   234  * @return NSImage to use for this service by default
   235  */
   236 - (NSImage *)defaultServiceIconOfType:(AIServiceIconType)iconType
   237 {
   238 	return nil;
   239 }
   240 
   241 /*!
   242  * @brief Path for default icon
   243  *
   244  * For use in message views, this is the path to a default icon as described above.
   245  *
   246  * @param iconType The AIServiceIconType of the icon to return.
   247  * @return The path to the image, otherwise nil.
   248  */
   249 - (NSString *)pathForDefaultServiceIconOfType:(AIServiceIconType)iconType
   250 {
   251 	return nil;
   252 }
   253 
   254 //Service Properties ---------------------------------------------------------------------------------------------------
   255 #pragma mark Service Properties
   256 /*!
   257  * @brief Allowed characters
   258  *
   259  * Characters allowed in user names on this service.  The user will not be allowed to type any characters not in this
   260  * set as a contact or account name.
   261  * @return NSCharacterSet of allowed characters
   262  */
   263 - (NSCharacterSet *)allowedCharacters
   264 {
   265     return nil;
   266 }
   267 
   268 /*!
   269  * @brief Allowed characters for our account name
   270  *
   271  * Offers further distinction of allowed characters, for situations where certain characters are allowed
   272  * for our account name only, or characters which are allowed in user names are forbidden in our own account name.
   273  * If this distinction is not made, do not subclass this methods and instead subclass allowedCharacters.
   274  * @return NSCharacterSet of allowed characters
   275  */
   276 - (NSCharacterSet *)allowedCharactersForAccountName
   277 {
   278 	return ([self allowedCharacters]);
   279 }
   280 
   281 /*!
   282  * @brief Allowed characters for UIDs
   283  *
   284  * Offers further distinction of allowed characters, for situations where certain characters are allowed
   285  * for our account name only, or characters which are allowed in user names are forbidden in our own account name.
   286  * If this distinction is not made, do not subclass this methods and instead subclass allowedCharacters.
   287  * @return NSCharacterSet of allowed characters
   288  */
   289 - (NSCharacterSet *)allowedCharactersForUIDs
   290 {
   291 	return [self allowedCharacters];
   292 }
   293 
   294 /*!
   295  * @brief Ignored characters
   296  *
   297  * Ignored characters for user names on this service.  Ignored characters are stripped from account and contact names
   298  * before they are used, but the user is free to type them and they may be used by the service code.  For instance, 
   299  * spaces are allowed in AIM usernames, but "ad am" is treated as equal to "adam" because space is an ignored character.
   300  * @return NSCharacterSet of ignored characters
   301  */
   302 - (NSCharacterSet *)ignoredCharacters
   303 {
   304     return [NSCharacterSet characterSetWithCharactersInString:@""];
   305 }
   306 
   307 /*!
   308  * @brief Allowed name length
   309  *
   310  * Max allowed length of user names of this service.  Account and contact names longer than this will not be allowed.
   311  * @return Max name length
   312  */
   313 - (NSUInteger)allowedLength
   314 {
   315     return NSUIntegerMax;
   316 }
   317 
   318 /*!
   319  * @brief Allowed account name length
   320  *
   321  * Offers further distinction of allowed name length, for situations where our account name has a different
   322  * length restriction than the names of our contacts.  If this distinction is not made, do not subclass these methods
   323  * and instead subclass allowedLength.
   324  * @return Max name length
   325  */
   326 - (int)allowedLengthForAccountName
   327 {
   328 	return [self allowedLength];
   329 }
   330 
   331 /*!
   332  * @brief Allowed UID length
   333  *
   334  * Offers further distinction of allowed name length, for situations where our account name has a different
   335  * length restriction than the names of our contacts.  If this distinction is not made, do not subclass these methods
   336  * and instead subclass allowedLength.
   337  * @return Max name length
   338  */
   339 - (int)allowedLengthForUIDs
   340 {
   341 	return [self allowedLength];
   342 }
   343 
   344 /*!
   345  * @brief Case sensitivity of names
   346  *
   347  * Determines if usernames such as "Adam" and "adam" are unique.
   348  * @return Case sensitivity
   349  */
   350 - (BOOL)caseSensitive
   351 {
   352     return NO;
   353 }
   354 
   355 /*!
   356  * @brief Can create group chats?
   357  *
   358  * Does this service support group chats (Also known as multi-user chats, chat rooms, conferences, etc)?  Services
   359  * which do not support group chats are automatically excluded from the group chat interface elements.
   360  * @return Can create group chats
   361  */
   362 - (BOOL)canCreateGroupChats
   363 {
   364 	return NO;
   365 }
   366 
   367 /*!
   368  * @brief Can register accounts?
   369  *
   370  * Does this service support registering new accounts from within Adium?  This is here for Jabber.
   371  * @return Can register accounts
   372  */
   373 - (BOOL)canRegisterNewAccounts
   374 {
   375 	return NO;
   376 }
   377 
   378 /*!
   379  * @brief Supports proxy settings?
   380  *
   381  * Does this service support connecting via a proxy?
   382  * @return Supports proxy settings
   383  */
   384 - (BOOL)supportsProxySettings
   385 {
   386 	return YES;
   387 }
   388 /*!
   389  * @brief Supports password
   390  *
   391  * Subclasses should return NO if this service does not use passwords at all for connectivity.
   392  * If NO, all fields related to passwords will be hidden for this service and the user will never be prompted to
   393  * enter passwords.
   394  */
   395 - (BOOL)supportsPassword
   396 {
   397 	return YES;
   398 }
   399 
   400 /*!
   401  * @brief Requires Password
   402  *
   403  * Subclasses should return NO if this service does not require a password.  Returning NO from this method will use the password if
   404  * entered but allow a conection to be initiated with no password without prompting for one.
   405  * If YES, Adium will insist upon a password being entered before a connection can begin.
   406  *
   407  * By default, the service requires a password if it is supported. See -[AIService supportsPassword].
   408  */
   409 - (BOOL)requiresPassword
   410 {
   411 	return [self supportsPassword];
   412 }
   413 
   414 /*!
   415  * @brief Register statuses
   416  *
   417  * Called automatically.  Services should register any supported status with the statusController.
   418  */
   419 - (void)registerStatuses{};
   420 
   421 /*!
   422  * @brief Default user name 
   423  * 
   424  * The default user name for a service is set for all new accounts. As it's not 
   425  * possible to guess the user name for most service types (AIM, MSN, etc.), the 
   426  * base class returns @"".
   427  *
   428  * @return The default user name for this service, or @"" for no default 
   429  */ 
   430 - (NSString *)defaultUserName 
   431 { 
   432 	return @""; 
   433 }
   434 
   435 /*!
   436  * @brief Is this a social networking service like Twitter or Facebook?
   437  *
   438  * If YES, accounts on this service treat status very differently than non-social-networking accounts.
   439  * For example, global status messages dont apply to social networking services, and their status is handled uniquely.
   440  */
   441 - (BOOL)isSocialNetworkingService
   442 {
   443 	return NO;
   444 }
   445 
   446 /*!
   447  * @brief Is this service hidden?
   448  *
   449  * If YES, it will not appear in service dropdowns.
   450  * This is useful for allowing a legacy service to stick around seamlessly.
   451  */
   452 - (BOOL)isHidden
   453 {
   454 	return NO;
   455 }
   456 
   457 //Utilities ------------------------------------------------------------------------------------------------------------
   458 #pragma mark Utilities
   459 /*!
   460  * @brief Normalize a UID
   461  *
   462  * Normalizes a UID.  All invalid characters and ignored characters are removed.
   463  * UID's are ONLY filtered when creating contacts, and when renaming contacts.
   464  * - When changing ownership of a contact, a filter is not necessary, since all the accounts should have the same service
   465  *   types and requirements.
   466  * - When account code retrieves contacts from the contact list, filtering is NOT done.  It is up to the account to
   467  *   ensure it passes UIDs in the proper format for its service type.
   468  * - Filter UIDs only when the user has entered or mucked with them in some way... UID's TO and FROM account code
   469  *   SHOULD ALWAYS BE VALID.
   470  * @return NSString filtered UID
   471  */
   472 - (NSString *)normalizeUID:(NSString *)inUID removeIgnoredCharacters:(BOOL)removeIgnored
   473 {
   474 	NSString		*workingString = ([self caseSensitive] ? inUID : [inUID lowercaseString]);
   475 	NSCharacterSet	*allowedCharacters = [self allowedCharactersForUIDs];
   476 	NSCharacterSet	*ignoredCharacters = [self ignoredCharacters];
   477 
   478 	//Prepare a little buffer for our filtered UID
   479 	unsigned	destLength = 0;
   480 	unsigned	workingStringLength = [workingString length];
   481 	unichar		*dest = malloc(workingStringLength * sizeof(unichar));
   482 
   483 	//Filter the UID
   484 	unsigned	pos;
   485 	for (pos = 0; pos < workingStringLength; pos++) {
   486 		unichar c = [workingString characterAtIndex:pos];
   487 		
   488         if ([allowedCharacters characterIsMember:c] && (!removeIgnored || ![ignoredCharacters characterIsMember:c])) {
   489             dest[destLength] = (removeIgnored ? c : [inUID characterAtIndex:pos]);
   490 			destLength++;
   491 		}
   492 	}
   493 
   494 	//Turn it back into a string and return
   495     NSString *filteredString = [NSString stringWithCharacters:dest length:destLength];
   496 	free(dest);
   497 
   498 	return filteredString;
   499 }
   500 
   501 /*!
   502  * @brief Normalize a chat name
   503  *
   504  * The default implementation only lowercases the name.
   505  */
   506 - (NSString *)normalizeChatName:(NSString *)inChatName
   507 {
   508 	return [inChatName lowercaseString];
   509 }
   510 
   511 /*!
   512  * @brief Compare this service to another, ranking by long description
   513  */
   514 - (NSComparisonResult)compareLongDescription:(AIService *)inService
   515 {
   516 	return [[self longDescription] compare:[inService longDescription]];
   517 }
   518 
   519 - (NSString *)description
   520 {
   521 	return [NSString stringWithFormat:@"<%@: serviceCodeUniqueID = %@; serviceID = %@; serviceClass = %@; longDescription = %@>",
   522 		NSStringFromClass([self class]), self.serviceCodeUniqueID, self.serviceID, self.serviceClass, self.longDescription];
   523 	
   524 }
   525 
   526 #pragma mark AppleScript
   527 
   528 /**
   529  * @brief Returns a list of all accounts that use this service.
   530  */
   531 - (NSArray *)accounts
   532 {
   533 	NSMutableArray *accountsForThisService = [[[NSMutableArray alloc] init] autorelease];
   534 	for (AIAccount *account in adium.accountController.accounts) {
   535 		if (account.service == self)
   536 			[accountsForThisService addObject:account];
   537 	}
   538 	return accountsForThisService;
   539 }
   540 
   541 /**
   542  * @brief This class is specified using the 'services' key of AIApplication
   543  */
   544 - (NSScriptObjectSpecifier *)objectSpecifier
   545 {
   546 	NSScriptClassDescription *containerClassDesc = (NSScriptClassDescription *)[NSScriptClassDescription classDescriptionForClass:[NSApp class]];
   547 	return [[[NSNameSpecifier alloc]
   548 		   initWithContainerClassDescription:containerClassDesc
   549 		   containerSpecifier:nil key:@"services"
   550 		   name:self.serviceID] autorelease];
   551 }
   552 
   553 - (NSData *)image
   554 {
   555 	return [[AIServiceIcons serviceIconForService:self type:AIServiceIconLarge direction:AIIconNormal] TIFFRepresentation];
   556 }
   557 
   558 @end