/** * Purple */ #import "Purple.h" #import "PurpAccount.h" #import "PurpCallbacks.h" #import #import /** * The glib main loop. */ static GMainLoop * g_mainLoop = 0; /** * Protocol names. */ static GList * g_protocolList = 0; /** * Notifications */ NSString * kPurpAccountDidSignOnNotification = @"purpAccountDidSignOnNotification" ; NSString * kPurpBuddyDidUpdateNotification = @"purpBuddyDidUpdateNotification" ; NSString * kPurpConversationDidReceiveNotification = @"purpConversationDidReceiveNotification" ; @implementation Purple @synthesize accounts = _accounts; @synthesize buddies = _buddies; @synthesize conversations = _conversations; static id g_sharedInstance = nil; + (void)initialize { g_sharedInstance = nil; } + (id)sharedInstance { if (!g_sharedInstance) { g_sharedInstance = [[[self class] alloc] init]; } return g_sharedInstance; } - (id)init { if (self = [super init]) { LOG(@"Initializing stack."); g_sharedInstance = self; /** * Initialize libpurple. */ init_libpurple(); /* purple_prefs_init(); */ LOG(@"Purple initialized."); g_mainLoop = g_main_loop_new(NULL, FALSE); GList * iter = purple_plugins_get_protocols(); for (unsigned int i = 0; iter; iter = iter->next) { PurplePlugin * plugin = iter->data; PurplePluginInfo * info = plugin->info; if (info && info->name) { LOG(@"%d -- %s", i++, info->name); g_protocolList = g_list_append(g_protocolList, info->id); } } [NSThread detachNewThreadSelector:@selector(thread) toTarget:self withObject:nil ]; /** * Allocate accounts. */ self.accounts = [[[NSMutableDictionary alloc] init] autorelease]; /** * Allocate buddies. */ self.buddies = [[[NSMutableArray alloc] init] autorelease]; /** * Allocate conversations. */ self.conversations = [[[NSMutableArray alloc] init] autorelease]; return self; } return nil; } - (void)stop { dealloc_libpurple(); } - (NSArray *)protocols { GList * iter = purple_plugins_get_protocols(); NSMutableArray * array = [NSMutableArray array]; for (unsigned int i = 0; iter; iter = iter->next) { (void)i; PurplePlugin * plugin = iter->data; PurplePluginInfo * info = plugin->info; if (info && info->name && info->id) { NSMutableDictionary * dict = [NSMutableDictionary dictionary]; [dict setObject:[NSString stringWithUTF8String:info->name] forKey:@"plugin_info_name" ]; [dict setObject:[NSString stringWithUTF8String:info->id] forKey:@"plugin_info_id" ]; [array addObject:dict]; } } return array; } - (void)signedOff:(void *)account { NSDictionary * buddy = nil; for (buddy in self.buddies) { NSValue * accountValue = [buddy objectForKey:@"account"]; NSLog(@"%p:%p", accountValue.pointerValue, account); if (accountValue.pointerValue == account) { NSLog( @"Updating buddy %@ in response to sign off event for " "account %p.", [buddy objectForKey:@"name"], account ); [[Purple sharedInstance] updateBuddy:buddy withEvent:BUDDY_EVENT_SIGNOFF ]; //break; } } } - (PurpAccount *)enableAccountType:(NSUInteger)aType username:(NSString *)aUsername password:(NSString *)password { const char * protocol = [[[[self protocols] objectAtIndex:aType] objectForKey:@"plugin_info_id"] UTF8String] ; PurpAccount * account = [[PurpAccount alloc] initWithUsername:aUsername password:password protocol:protocol ]; // :TODO: Implement preference to set enable SSL. [account setUseSSL:NO]; [account setEnabled:YES]; [self.accounts setObject:account forKey:aUsername]; return [account autorelease]; } - (PurpAccount *)enableAccount:(NSString *)aProtocol username:(NSString *)aUsername password:(NSString *)password { const char * protocol = [aProtocol UTF8String]; PurpAccount * account = [[PurpAccount alloc] initWithUsername:aUsername password:password protocol:protocol ]; if (account) { LOG(@"Adding account for username %@.", aUsername); // :TODO: Implement preference to set enable SSL. [account setUseSSL:NO]; [account setEnabled:YES]; [self.accounts setObject:account forKey:aUsername]; } return [account autorelease]; } - (void)disableAccount:(NSString *)aUsername { PurpAccount * account = [self.accounts objectForKey:aUsername]; if (account) { LOG(@"Removing account for username %@.", aUsername); [account setEnabled:NO]; [self.accounts removeObjectForKey:aUsername]; } else { LOG(@"Unable to remove account, not found."); } } - (void)sendConversationMessage:(NSString *)aMessage to:(NSString *)toUsername from:(NSString *)fromUsername { PurpleConversation * conversation = 0; PurpleAccount * purple_account = 0; for (NSData * data in self.conversations) { /** * Is this the conversation we are looking for? */ if ( strncmp(((PurpleConversation *)data.bytes)->name, [toUsername UTF8String], toUsername.length) == 0 ) { NSLog( @"PurpleConversation: break: %s", ((PurpleConversation *)data.bytes)->name ); conversation = (PurpleConversation *)data.bytes; break; } } PurpAccount * account = [self.accounts objectForKey:fromUsername]; purple_account = account->_account; NSAssert(purple_account, @"Null purple account."); if (conversation) { NSLog(@"Using existing conversation: %s.", conversation->name); } else { conversation = purple_conversation_new( PURPLE_CONV_TYPE_IM, purple_account, [toUsername UTF8String] ); NSLog(@"Creating new conversation: %s.", conversation->name); [self.conversations addObject:[NSData dataWithBytes:conversation length:sizeof(PurpleConversation)] ]; } NSLog(@"PurpleConversation sending message to: %s", conversation->name); PurpleConvIm * conv = purple_conversation_get_im_data(conversation); purple_conv_im_send(conv, [aMessage UTF8String]); } - (void)receiveConversationMessage:(NSString *)aWho alias:(NSString *)anAlias message:(NSString *)aMessage time:(NSDate *)aDate { NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithCapacity:4]; [dict setObject:aWho forKey:@"who"]; [dict setObject:anAlias forKey:@"alias"]; [dict setObject:aMessage forKey:@"message"]; [dict setObject:aDate forKey:@"time"]; [[NSNotificationCenter defaultCenter] postNotificationName:kPurpConversationDidReceiveNotification object:dict ]; } - (void)updateBuddy:(NSDictionary *)aBuddy withEvent:(NSUInteger)aEvent { static NSLock * lock = nil; if (lock) { lock = [[NSLock alloc] init]; } [lock lock]; NSMutableDictionary * buddy = nil; #if 0 NSDictionary * buddies = [[self.buddies copy] autorelease]; NSUInteger i = 0; for (buddy in buddies) { if ([[buddy objectForKey:@"name"] isEqualToString:[aBuddy objectForKey:@"name"]] ) { [self.buddies replaceObjectAtIndex:i withObject:aBuddy]; break; } else { buddy = nil; } i++; } #else for (buddy in self.buddies) { if ([[buddy objectForKey:@"name"] isEqualToString:[aBuddy objectForKey:@"name"]] ) { break; } else { buddy = nil; } } #endif if (buddy) { /** * Update the buddy. */ #warning ":FIXME: mutating while iterating" @try { [buddy setValuesForKeysWithDictionary:aBuddy]; #if 0 [self.buddies replaceObjectAtIndex:i withObject:buddy]; #endif } @catch (NSException * e) { LOG(@":FIXME: %@", e.description); } @finally { // ... } } else { buddy = [NSMutableDictionary dictionaryWithDictionary:aBuddy]; [self.buddies addObject:buddy]; } if (buddy) { [[NSNotificationCenter defaultCenter] postNotificationName:kPurpBuddyDidUpdateNotification object:buddy ]; } [lock unlock]; } - (void)thread { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; connect_signals(); g_main_loop_run(g_mainLoop); [pool drain]; } - (void)dealloc { [_accounts removeAllObjects]; [_accounts release], _accounts = nil; [_buddies removeAllObjects]; [_buddies release], _buddies = nil; [_conversations release], _conversations = nil; [super dealloc]; } @end