2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
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.
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.
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.
17 #import "GBFireImporter.h"
18 #import <Adium/AIContactControllerProtocol.h>
19 #import <Adium/AIPreferenceControllerProtocol.h>
20 #import <Adium/AIAccountControllerProtocol.h>
22 #import <AIUtilities/AIFileManagerAdditions.h>
24 #import <Adium/AIStatus.h>
25 #import "AIHTMLDecoder.h"
26 #import "AIStatusController.h"
28 #import "AIListGroup.h"
29 #import <Adium/AIListContact.h>
30 #import "AIMetaContact.h"
31 #import "GBFireLogImporter.h"
33 #define FIRECONFIGURATION2 @"FireConfiguration2.plist"
34 #define FIRECONFIGURATION @"FireConfiguration.plist"
35 #define ACCOUNTS2 @"Accounts2.plist"
36 #define ACCOUNTS @"Accounts.plist"
38 @interface GBFireImportedBuddy : AIObject{
39 AIListContact *contact;
44 NSString *displayName;
48 - (id)initScreenname:(NSString *)screen forAccount:(AIAccount *)acct;
49 - (AIAccount *)account;
50 - (void)setGroup:(NSString *)group;
51 - (void)setBlocked:(BOOL)block;
52 - (void)setDisplayName:(NSString *)alias;
53 - (AIListContact *)createContact;
54 - (AIListContact *)contact;
57 @implementation GBFireImportedBuddy
58 - (id)initScreenname:(NSString *)screen forAccount:(AIAccount *)acct
64 screenname = [screen retain];
65 account = [acct retain];
78 [displayName release];
82 - (AIAccount *)account
87 - (void)setGroup:(NSString *)group
90 groupName = [group retain];
93 - (void)setBlocked:(BOOL)block
98 - (void)setDisplayName:(NSString *)alias
100 [displayName release];
101 displayName = [alias retain];
104 - (AIListContact *)createContact
109 id <AIContactController> contactController = [adium contactController];
111 contact = [contactController contactWithService:[account service] account:account UID:screenname];
112 if(displayName != nil)
113 [contact setDisplayName:displayName];
115 [contact setIsBlocked:YES updateList:YES];
117 [contact setRemoteGroupName:groupName];
122 - (AIListContact *)contact
128 @interface GBFireImporter (private)
129 - (BOOL)importFireConfiguration;
130 - (BOOL)import2:(NSString *)fireDir;
131 - (BOOL)import1:(NSString *)fireDir;
135 * @class GBFireImporter
136 * @brief Importer for Fire's configuration
138 * This class attempts to import Fire's configuration. This includes status messages, accounts,
139 * groups, buddies, and meta-contacts.
142 @implementation GBFireImporter
150 accountUIDtoAccount = [[NSMutableDictionary alloc] init];
151 aliasToContacts = [[NSMutableDictionary alloc] init];
152 buddiesToContact = [[NSMutableDictionary alloc] init];
153 personLists = [[NSMutableArray alloc] init];
155 NSEnumerator *serviceEnum = [[[adium accountController] services] objectEnumerator];
156 AIService *service = nil;
157 serviceDict = [[NSMutableDictionary alloc] init];
158 while ((service = [serviceEnum nextObject]) != nil)
160 [serviceDict setObject:service forKey:[service serviceID]];
162 [serviceDict setObject:[serviceDict objectForKey:@"Bonjour"] forKey:@"Rendezvous"];
163 [serviceDict setObject:[serviceDict objectForKey:@"GTalk"] forKey:@"GoogleTalk"];
164 [serviceDict setObject:[serviceDict objectForKey:@"Yahoo!"] forKey:@"Yahoo"];
171 [accountUIDtoAccount release];
172 [aliasToContacts release];
173 [buddiesToContact release];
174 [personLists release];
175 [serviceDict release];
179 - (void)accountConnected:(NSNotification *)notification
181 id <AIContactController> contactController = [adium contactController];
182 AIAccount *acct = [notification object];
184 NSEnumerator *personEnum = [[NSArray arrayWithArray:personLists] objectEnumerator];
185 NSArray *personContacts = nil;
186 while((personContacts = [personEnum nextObject]) != nil)
188 BOOL aBuddyNotCreated = NO;
189 BOOL aBuddyCreated = NO;
190 NSEnumerator *contactEnum = [personContacts objectEnumerator];
191 GBFireImportedBuddy *buddy = nil;
192 NSMutableArray *thisMetaContact = [[NSMutableArray alloc] init];
193 while((buddy = [contactEnum nextObject]) != nil)
195 AIListContact *contact = [buddy contact];
198 [thisMetaContact addObject:contact];
201 if([buddy account] == acct)
203 contact = [buddy createContact];
204 [thisMetaContact addObject:contact];
209 aBuddyNotCreated = YES;
212 if(aBuddyCreated && [thisMetaContact count] > 1)
213 [contactController groupListContacts:thisMetaContact];
214 if(!aBuddyNotCreated)
215 [personLists removeObject:personContacts];
216 [thisMetaContact release];
218 [[adium notificationCenter] removeObserver:self name:ACCOUNT_CONNECTED object:acct];
223 * @brief Attempt to import Fire's config. Returns YES if successful
226 + (BOOL)importFireConfiguration
228 GBFireImporter *importer = [[GBFireImporter alloc] init];
229 BOOL ret = [importer importFireConfiguration];
235 - (BOOL)importFireConfiguration
237 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
238 NSString *fireDir = [[[NSFileManager defaultManager] userApplicationSupportFolder] stringByAppendingPathComponent:@"Fire"];
239 BOOL version2Succeeded = NO;
240 BOOL version1Succeeded = NO;
243 version2Succeeded = [self import2:fireDir];
245 if(!version2Succeeded)
247 version1Succeeded = [self import1:fireDir];
249 if(!version2Succeeded && !version1Succeeded)
254 [GBFireLogImporter importLogs];
258 - (AIService *)translateServiceName:(NSString *)serviceName screenName:(NSString *)screenName;
260 if([serviceName isEqualTo:@"Jabber"] && [screenName hasSuffix:@"@gmail.com"])
261 serviceName = @"GoogleTalk";
263 return [serviceDict objectForKey:serviceName];
266 - (int)checkPort:(int)port forService:(AIService *)service
270 if([[service serviceID] isEqualTo:@"AIM"] && port == 9898)
272 if([[service serviceID] isEqualTo:@"GTalk"] && port == 5223)
277 - (NSString *)checkHost:(NSString *)host forService:(AIService *)service
281 if([[service serviceID] isEqualTo:@"AIM"] && [host hasPrefix:@"toc"])
286 - (void)importAccounts2:(NSArray *)accountsDict
288 NSEnumerator *accountEnum = [accountsDict objectEnumerator];
289 NSDictionary *account = nil;
290 while((account = [accountEnum nextObject]) != nil)
292 NSString *serviceName = [account objectForKey:@"ServiceName"];
293 NSString *accountName = [account objectForKey:@"Username"];
294 if(![serviceName length] || ![accountName length])
296 AIService *service = [self translateServiceName:serviceName screenName:accountName];
300 AIAccount *newAcct = [[adium accountController] createAccountWithService:service
305 [newAcct setPreference:[account objectForKey:@"AutoLogin"]
307 group:GROUP_ACCOUNT_STATUS];
309 NSDictionary *properties = [account objectForKey:@"Properties"];
310 NSString *connectHost = [properties objectForKey:@"server"];
311 if([self checkHost:connectHost forService:service])
312 [newAcct setPreference:connectHost
313 forKey:KEY_CONNECT_HOST
314 group:GROUP_ACCOUNT_STATUS];
316 int port = [[properties objectForKey:@"port"] intValue];
317 if([self checkPort:port forService:service])
318 [newAcct setPreference:[NSNumber numberWithInt:port]
319 forKey:KEY_CONNECT_PORT
320 group:GROUP_ACCOUNT_STATUS];
322 [accountUIDtoAccount setObject:newAcct forKey:[account objectForKey:@"UniqueID"]];
323 [[adium accountController] addAccount:newAcct];
324 [[adium notificationCenter] addObserver:self
325 selector:@selector(accountConnected:)
326 name:ACCOUNT_CONNECTED
328 //Retain for each account
330 [newAcct setShouldBeOnline:YES];
334 - (void)importAways2:(NSArray *)awayList
336 NSEnumerator *awayEnum = [awayList objectEnumerator];
337 NSDictionary *away = nil;
338 while((away = [awayEnum nextObject]) != nil)
340 NSString *title = [away objectForKey:@"Title"];
341 BOOL isDefault = [[away objectForKey:@"isIdleMessage"] boolValue];
342 BOOL goIdle = [[away objectForKey:@"idleMessage"] boolValue];
343 NSString *attrMessage = [away objectForKey:@"message"];
344 int fireType = [[away objectForKey:@"messageType"] intValue];
345 AIStatusType adiumType = 0;
351 adiumType = AIAvailableStatusType;
354 adiumType = AIInvisibleStatusType;
358 adiumType = AIAwayStatusType;
361 AIStatus *newStatus = [AIStatus statusOfType:adiumType];
362 [newStatus setTitle:title];
363 [newStatus setStatusMessage:[AIHTMLDecoder decodeHTML:attrMessage]];
364 [newStatus setAutoReplyIsStatusMessage:YES];
365 [newStatus setShouldForceInitialIdleTime:goIdle];
367 [[adium preferenceController] setPreference:[newStatus uniqueStatusID]
368 forKey:KEY_STATUS_AUTO_AWAY_STATUS_STATE_ID
369 group:PREF_GROUP_STATUS_PREFERENCES];
370 [[adium statusController] addStatusState:newStatus];
374 NSComparisonResult groupSort(id left, id right, void *context)
376 NSNumber *leftNum = [left objectForKey:@"position"];
377 NSNumber *rightNum = [right objectForKey:@"position"];
378 NSComparisonResult ret = NSOrderedSame;
383 ret = NSOrderedAscending;
385 else if (rightNum == nil)
386 ret = NSOrderedDescending;
388 ret = [leftNum compare:rightNum];
393 - (void)importGroups2:(NSDictionary *)groupList
395 id <AIContactController> contactController = [adium contactController];
397 //First itterate through the groups and create an array we can sort
398 NSEnumerator *groupEnum = [groupList keyEnumerator];
399 NSString *groupName = nil;
400 NSMutableArray *groupArray = [NSMutableArray array];
401 while((groupName = [groupEnum nextObject]) != nil)
403 NSMutableDictionary *groupDict = [[groupList objectForKey:groupName] mutableCopy];
404 [groupDict setObject:groupName forKey:@"Name"];
405 [groupArray addObject:groupDict];
408 [groupArray sortUsingFunction:groupSort context:NULL];
409 groupEnum = [groupArray objectEnumerator];
410 NSDictionary *group = nil;
411 while((group = [groupEnum nextObject]) != nil)
413 AIListGroup *newGroup = [contactController groupWithUID:[group objectForKey:@"Name"]];
414 NSNumber *expanded = [group objectForKey:@"groupexpanded"];
416 [newGroup setExpanded:[expanded boolValue]];
420 - (void)importBuddies2:(NSArray *)buddyArray
422 NSEnumerator *buddyEnum = [buddyArray objectEnumerator];
423 NSDictionary *buddy = nil;
424 while((buddy = [buddyEnum nextObject]) != nil)
426 NSNumber *inList = [buddy objectForKey:@"BuddyInList"];
427 if(inList == nil || [inList boolValue] == NO)
430 NSNumber *accountNumber = [buddy objectForKey:@"account"];
431 AIAccount *account = [accountUIDtoAccount objectForKey:accountNumber];
435 NSString *buddyName = [buddy objectForKey:@"buddyname"];
436 if([buddyName length] == 0)
439 GBFireImportedBuddy *newContact = [[GBFireImportedBuddy alloc] initScreenname:buddyName forAccount:account];
441 NSMutableDictionary *accountBuddyList = [buddiesToContact objectForKey:accountNumber];
442 if(accountBuddyList == nil)
444 accountBuddyList = [NSMutableDictionary dictionary];
445 [buddiesToContact setObject:accountBuddyList forKey:accountNumber];
447 [accountBuddyList setObject:newContact forKey:buddyName];
449 NSString *alias = [buddy objectForKey:@"displayname"];
450 if([alias length] != 0)
452 [newContact setDisplayName:alias];
453 NSMutableArray *contactArray = [aliasToContacts objectForKey:alias];
454 if(contactArray == nil)
456 contactArray = [NSMutableArray array];
457 [aliasToContacts setObject:contactArray forKey:alias];
459 [contactArray addObject:newContact];
462 BOOL blocked = [[buddy objectForKey:@"BuddyBlocked"] boolValue];
464 [newContact setBlocked:YES];
466 //Adium can only support a single group per buddy (boo!!!) so use the first
467 NSString *groupName = ([[buddy objectForKey:@"Groups"] count] ? [[buddy objectForKey:@"Groups"] objectAtIndex:0] : nil);
468 if([groupName length] != 0)
469 [newContact setGroup:groupName];
470 [newContact release];
474 - (void)importPersons2:(NSArray *)personArray
476 NSEnumerator *personEnum = [personArray objectEnumerator];
477 NSDictionary *person = nil;
478 while((person = [personEnum nextObject]) != nil)
480 NSString *personName = [person objectForKey:@"Name"];
481 if([personName length] == 0)
484 if([personName hasPrefix:@"Screenname:"])
487 NSArray *buddyArray = [person objectForKey:@"Buddies"];
488 if([buddyArray count] == 0)
489 //Empty meta-contact; don't bother
492 NSEnumerator *buddyEnum = [buddyArray objectEnumerator];
493 NSDictionary *buddyInfo = nil;
494 NSMutableArray *buddies = [NSMutableArray array];
495 while ((buddyInfo = [buddyEnum nextObject]) != nil)
497 NSNumber *buddyAccount = [buddyInfo objectForKey:@"BuddyAccount"];
498 NSString *buddySN = [buddyInfo objectForKey:@"BuddyName"];
500 if(buddyAccount == nil || buddySN == nil)
503 GBFireImportedBuddy *contact = [[buddiesToContact objectForKey:buddyAccount] objectForKey:buddySN];
505 //Contact lookup failed
508 [buddies addObject:contact];
510 [personLists addObject:buddies];
514 - (void)createMetaContacts
516 NSEnumerator *metaContantEnum = [aliasToContacts objectEnumerator];
517 NSArray *contacts = nil;
518 while ((contacts = [metaContantEnum nextObject]) != nil)
519 [personLists addObject:contacts];
522 - (BOOL)import2:(NSString *)fireDir
524 NSString *configPath = [fireDir stringByAppendingPathComponent:FIRECONFIGURATION2];
525 NSString *accountPath = [fireDir stringByAppendingPathComponent:ACCOUNTS2];
526 NSDictionary *configDict = [NSDictionary dictionaryWithContentsOfFile:configPath];
527 NSDictionary *accountDict = [NSDictionary dictionaryWithContentsOfFile:accountPath];
529 if(configDict == nil || accountDict == nil)
530 //no dictionary or no account, can't import
533 //Start with accounts
534 [self importAccounts2:[accountDict objectForKey:@"Accounts"]];
537 [self importAways2:[configDict objectForKey:@"awayMessages"]];
540 [self importGroups2:[configDict objectForKey:@"groups"]];
543 [self importBuddies2:[configDict objectForKey:@"buddies"]];
546 NSArray *personArray = [configDict objectForKey:@"persons"];
547 if([personArray count] > 0)
548 [self importPersons2:personArray];
550 [self createMetaContacts];
555 - (void)importAccounts1:(NSDictionary *)accountsDict
557 NSEnumerator *serviceNameEnum = [accountsDict keyEnumerator];
558 NSString *serviceName = nil;
559 while ((serviceName = [serviceNameEnum nextObject]) != nil)
561 if(![serviceName length])
564 NSEnumerator *accountEnum = [[accountsDict objectForKey:serviceName] objectEnumerator];
565 NSDictionary *account = nil;
567 while((account = [accountEnum nextObject]) != nil)
569 NSString *accountName = [account objectForKey:@"userName"];
570 if(![accountName length])
572 AIService *service = [self translateServiceName:serviceName screenName:accountName];
573 AIAccount *newAcct = [[adium accountController] createAccountWithService:service
578 [newAcct setPreference:[account objectForKey:@"autoLogin"]
580 group:GROUP_ACCOUNT_STATUS];
582 NSString *connectHost = [account objectForKey:@"server"];
583 if([self checkHost:connectHost forService:service])
584 [newAcct setPreference:connectHost
585 forKey:KEY_CONNECT_HOST
586 group:GROUP_ACCOUNT_STATUS];
588 int port = [[account objectForKey:@"port"] intValue];
589 if([self checkPort:port forService:service])
590 [newAcct setPreference:[NSNumber numberWithInt:port]
591 forKey:KEY_CONNECT_PORT
592 group:GROUP_ACCOUNT_STATUS];
594 [accountUIDtoAccount setObject:newAcct forKey:[NSString stringWithFormat:@"%@-%@@%@", serviceName, accountName, connectHost]];
595 [[adium accountController] addAccount:newAcct];
596 [[adium notificationCenter] addObserver:self
597 selector:@selector(accountConnected:)
598 name:ACCOUNT_CONNECTED
600 //Retain for each account
602 [newAcct setShouldBeOnline:YES];
607 - (void)importAways1:(NSArray *)awayList
609 NSEnumerator *awayEnum = [awayList objectEnumerator];
610 NSDictionary *away = nil;
611 while((away = [awayEnum nextObject]) != nil)
613 NSString *title = [away objectForKey:@"Title"];
614 BOOL isDefault = [[away objectForKey:@"isIdleMessage"] boolValue];
615 BOOL goIdle = [[away objectForKey:@"idleMessage"] boolValue];
616 NSString *message = [away objectForKey:@"message"];
617 int fireType = [[away objectForKey:@"messageType"] intValue];
618 AIStatusType adiumType = 0;
624 adiumType = AIAvailableStatusType;
627 adiumType = AIInvisibleStatusType;
631 adiumType = AIAwayStatusType;
634 AIStatus *newStatus = [AIStatus statusOfType:adiumType];
635 [newStatus setTitle:title];
636 [newStatus setStatusMessage:[[[NSAttributedString alloc] initWithString:message] autorelease]];
637 [newStatus setAutoReplyIsStatusMessage:YES];
638 [newStatus setShouldForceInitialIdleTime:goIdle];
640 [[adium preferenceController] setPreference:[newStatus uniqueStatusID]
641 forKey:KEY_STATUS_AUTO_AWAY_STATUS_STATE_ID
642 group:PREF_GROUP_STATUS_PREFERENCES];
643 [[adium statusController] addStatusState:newStatus];
647 - (void)importBuddies1:(NSArray *)buddyArray
648 toGroup:(NSString *)groupName
650 NSEnumerator *buddyEnum = [buddyArray objectEnumerator];
651 NSDictionary *buddy = nil;
652 while((buddy = [buddyEnum nextObject]) != nil)
654 NSString *buddyName = [buddy objectForKey:@"buddyname"];
655 if([buddyName length] == 0)
658 NSDictionary *permissionsDict = [buddy objectForKey:@"BuddyPermissions"];
659 NSMutableArray *accounts = [NSMutableArray array];
660 if(permissionsDict != nil)
662 NSEnumerator *permissionsEnum = [permissionsDict keyEnumerator];
663 NSString *accountKey = nil;
664 while ((accountKey = [permissionsEnum nextObject]) != nil)
666 AIAccount *acct = [accountUIDtoAccount objectForKey:accountKey];
667 if(acct != nil && [[[permissionsDict objectForKey:accountKey] objectForKey:@"BuddyinList"] boolValue])
668 [accounts addObject:acct];
673 NSEnumerator *acctEnum = [accountUIDtoAccount objectEnumerator];
674 AIAccount *acct = nil;
675 while ((acct = [acctEnum nextObject]) != nil)
677 if([[acct serviceClass] isEqualToString:[buddy objectForKey:@"service"]])
678 [accounts addObject:acct];
682 NSEnumerator *accountEnum = [accounts objectEnumerator];
683 AIAccount *account = nil;
684 while ((account = [accountEnum nextObject]) != nil)
686 GBFireImportedBuddy *newContact = [[GBFireImportedBuddy alloc] initScreenname:buddyName forAccount:account];
687 if(newContact == nil)
690 NSString *alias = [buddy objectForKey:@"displayname"];
691 if([alias length] != 0)
693 [newContact setDisplayName:alias];
694 NSMutableArray *contactArray = [aliasToContacts objectForKey:alias];
695 if(contactArray == nil)
697 contactArray = [NSMutableArray array];
698 [aliasToContacts setObject:contactArray forKey:alias];
700 [contactArray addObject:newContact];
703 if([groupName length] != 0)
704 [newContact setGroup:groupName];
705 [newContact release];
710 - (void)importGroups1:(NSDictionary *)groupList
712 id <AIContactController> contactController = [adium contactController];
714 //First itterate through the groups and create an array we can sort
715 NSEnumerator *groupEnum = [groupList keyEnumerator];
716 NSString *groupName = nil;
717 NSMutableArray *groupArray = [NSMutableArray array];
718 while((groupName = [groupEnum nextObject]) != nil)
720 NSMutableDictionary *groupDict = [[groupList objectForKey:groupName] mutableCopy];
721 [groupDict setObject:groupName forKey:@"Name"];
722 [groupArray addObject:groupDict];
725 [groupArray sortUsingFunction:groupSort context:NULL];
726 groupEnum = [groupArray objectEnumerator];
727 NSDictionary *group = nil;
728 while((group = [groupEnum nextObject]) != nil)
730 NSString *groupName = [group objectForKey:@"Name"];
731 AIListGroup *newGroup = [contactController groupWithUID:groupName];
732 NSNumber *expanded = [group objectForKey:@"groupexpanded"];
734 [newGroup setExpanded:[expanded boolValue]];
735 [self importBuddies1:[group objectForKey:@"buddies"]
740 - (BOOL)import1:(NSString *)fireDir
742 NSString *configPath = [fireDir stringByAppendingPathComponent:FIRECONFIGURATION];
743 NSString *accountPath = [fireDir stringByAppendingPathComponent:ACCOUNTS];
744 NSDictionary *configDict = [NSDictionary dictionaryWithContentsOfFile:configPath];
745 NSDictionary *accountDict = [NSDictionary dictionaryWithContentsOfFile:accountPath];
747 if(configDict == nil || accountDict == nil)
748 //no dictionary or no account, can't import
751 //Start with accounts
752 [self importAccounts1:accountDict];
755 [self importAways1:[configDict objectForKey:@"awayMessages"]];
758 [self importGroups1:[configDict objectForKey:@"groups"]];
761 [self createMetaContacts];