Plugins/Purple Service/adiumPurpleConversation.m
author Zachary West <zacw@adium.im>
Sun Nov 01 14:04:11 2009 -0500 (2009-11-01)
changeset 2848 d88a4b7a70a8
parent 2693 4bcad311909f
child 3081 6388b2768ef1
permissions -rw-r--r--
Display unhandled purple conversation writes in the next run loop. Fixes #13190.
David@0
     1
/*
David@0
     2
 * Adium is the legal property of its developers, whose names are listed in the copyright file included
David@0
     3
 * with this source distribution.
David@0
     4
 *
David@0
     5
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
David@0
     6
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
David@0
     7
 * or (at your option) any later version.
David@0
     8
 *
David@0
     9
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
David@0
    10
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
David@0
    11
 * Public License for more details.
David@0
    12
 *
David@0
    13
 * You should have received a copy of the GNU General Public License along with this program; if not,
David@0
    14
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
David@0
    15
 */
David@0
    16
David@0
    17
#import "adiumPurpleConversation.h"
David@0
    18
#import <AIUtilities/AIObjectAdditions.h>
zacw@1353
    19
#import <AIUtilities/AIAttributedStringAdditions.h>
David@0
    20
#import <Adium/AIChat.h>
David@0
    21
#import <Adium/AIContentTyping.h>
David@0
    22
#import <Adium/AIHTMLDecoder.h>
David@0
    23
#import <Adium/AIListContact.h>
David@0
    24
#import <Adium/AIContentControllerProtocol.h>
catfish@1894
    25
#import "AINudgeBuzzHandlerPlugin.h"
David@0
    26
David@0
    27
#pragma mark Purple Images
David@0
    28
David@0
    29
#pragma mark Conversations
David@0
    30
static void adiumPurpleConvCreate(PurpleConversation *conv)
David@0
    31
{
David@0
    32
	//Pass chats along to the account
David@0
    33
	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
David@0
    34
		
David@0
    35
		AIChat *chat = groupChatLookupFromConv(conv);
David@0
    36
		
zacw@1351
    37
		[accountLookup(purple_conversation_get_account(conv)) addChat:chat];
David@0
    38
	}
David@0
    39
}
David@0
    40
David@0
    41
static void adiumPurpleConvDestroy(PurpleConversation *conv)
David@0
    42
{
David@0
    43
	/* Purple is telling us a conv was destroyed.  We've probably already cleaned up, but be sure in case purple calls this
David@0
    44
	 * when we don't ask it to (for example if we are summarily kicked from a chat room and purple closes the 'window').
David@0
    45
	 */
David@0
    46
	AIChat *chat = (AIChat *)conv->ui_data;
David@0
    47
David@0
    48
	AILogWithSignature(@"%p: %@", conv, chat);
David@0
    49
David@0
    50
	//Chat will be nil if we've already cleaned up, at which point no further action is needed.
David@0
    51
	if (chat) {
David@0
    52
		[accountLookup(purple_conversation_get_account(conv)) chatWasDestroyed:chat];
David@0
    53
David@0
    54
		[chat setIdentifier:nil];
David@0
    55
		[chat release];
David@0
    56
		conv->ui_data = nil;
David@0
    57
	}
David@0
    58
}
David@0
    59
David@0
    60
static void adiumPurpleConvWriteChat(PurpleConversation *conv, const char *who,
David@0
    61
								   const char *message, PurpleMessageFlags flags,
David@0
    62
								   time_t mtime)
David@0
    63
{
David@0
    64
	/* We only care about this if:
David@0
    65
	 *	1) It does not have the PURPLE_MESSAGE_SEND flag, which is set if Purple is sending a sent message back to us -or-
David@0
    66
	 *  2) It is a delayed (history) message from a chat
David@0
    67
	 */
David@0
    68
	if (!(flags & PURPLE_MESSAGE_SEND) || (flags & PURPLE_MESSAGE_DELAYED)) {
David@0
    69
		NSDictionary	*messageDict;
David@0
    70
		NSString		*messageString;
David@0
    71
David@0
    72
		messageString = [NSString stringWithUTF8String:message];
David@0
    73
		AILog(@"Source: %s \t Name: %s \t MyNick: %s : Message %@", 
David@0
    74
			  who,
David@0
    75
			  purple_conversation_get_name(conv),
David@0
    76
			  purple_conv_chat_get_nick(PURPLE_CONV_CHAT(conv)),
David@0
    77
			  messageString);
David@0
    78
David@0
    79
		NSDate				*date = [NSDate dateWithTimeIntervalSince1970:mtime];
zacw@2050
    80
		PurpleAccount		*purpleAccount = purple_conversation_get_account(conv);
David@0
    81
		
zacw@1353
    82
		if ((flags & PURPLE_MESSAGE_SYSTEM) == PURPLE_MESSAGE_SYSTEM || !who) {
zacw@2050
    83
			CBPurpleAccount *account = accountLookup(purpleAccount);
David@0
    84
			
zacw@1353
    85
			[account receivedEventForChat:groupChatLookupFromConv(conv)
zacw@1353
    86
								  message:messageString
zacw@1353
    87
									 date:date
zacw@2848
    88
									flags:[NSNumber numberWithInteger:flags]];
David@0
    89
		} else {
zacw@1353
    90
			NSAttributedString	*attributedMessage = [AIHTMLDecoder decodeHTML:messageString];
sholt@2693
    91
			NSNumber			*purpleMessageFlags = [NSNumber numberWithInteger:flags];
zacw@2050
    92
			NSString			*normalizedUID = get_real_name_for_account_conv_buddy(purpleAccount, conv, (char *)who);
zacw@1353
    93
			
zacw@2050
    94
			if (normalizedUID.length) {
zacw@1353
    95
				messageDict = [NSDictionary dictionaryWithObjectsAndKeys:attributedMessage, @"AttributedMessage",
zacw@2050
    96
							   normalizedUID, @"Source",
zacw@1353
    97
							   purpleMessageFlags, @"PurpleMessageFlags",
zacw@1353
    98
							   date, @"Date",nil];
zacw@1353
    99
				
zacw@1353
   100
			} else {
zacw@1353
   101
				messageDict = [NSDictionary dictionaryWithObjectsAndKeys:attributedMessage, @"AttributedMessage",
zacw@1353
   102
							   purpleMessageFlags, @"PurpleMessageFlags",
zacw@1353
   103
							   date, @"Date",nil];
zacw@1353
   104
			}
zacw@1353
   105
zacw@1353
   106
			[accountLookup(purple_conversation_get_account(conv)) receivedMultiChatMessage:messageDict inChat:groupChatLookupFromConv(conv)];
David@0
   107
		}
David@0
   108
	}
David@0
   109
}
David@0
   110
David@0
   111
static void adiumPurpleConvWriteIm(PurpleConversation *conv, const char *who,
David@0
   112
								 const char *message, PurpleMessageFlags flags,
David@0
   113
								 time_t mtime)
David@0
   114
{
David@0
   115
	//We only care about this if it does not have the PURPLE_MESSAGE_SEND flag, which is set if Purple is sending a sent message back to us
David@0
   116
	if ((flags & PURPLE_MESSAGE_SEND) == 0) {
David@0
   117
		if (flags & PURPLE_MESSAGE_NOTIFY) {
David@0
   118
			// We received a notification (nudge or buzz). Send a notification of such.
David@0
   119
			NSString *type, *messageString = [NSString stringWithUTF8String:message];
David@0
   120
David@0
   121
			// Determine what we're actually notifying about.
David@0
   122
			if ([messageString rangeOfString:@"nudge" options:(NSCaseInsensitiveSearch | NSLiteralSearch)].location != NSNotFound) {
David@0
   123
				type = @"Nudge";
David@0
   124
			} else if ([messageString rangeOfString:@"buzz" options:(NSCaseInsensitiveSearch | NSLiteralSearch)].location != NSNotFound) {
David@0
   125
				type = @"Buzz";
David@0
   126
			} else {
David@0
   127
				// Just call an unknown type a "notification"
David@0
   128
				type = @"notification";
David@0
   129
			}
David@0
   130
David@1109
   131
			[[NSNotificationCenter defaultCenter] postNotificationName:Chat_NudgeBuzzOccured
David@0
   132
																			   object:chatLookupFromConv(conv)
David@0
   133
																			 userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
David@0
   134
																					   type, @"Type",
David@0
   135
																					   nil]];
David@0
   136
		} else {
David@0
   137
			NSDictionary		*messageDict;
David@0
   138
			CBPurpleAccount		*adiumAccount = accountLookup(purple_conversation_get_account(conv));
David@0
   139
			NSString			*messageString;
David@0
   140
			AIChat				*chat;
David@0
   141
			
David@0
   142
			messageString = [NSString stringWithUTF8String:message];
David@0
   143
			chat = chatLookupFromConv(conv);
David@0
   144
			
David@426
   145
			AILog(@"adiumPurpleConvWriteIm: Received %@ from %@", messageString, chat.listObject.UID);
David@0
   146
			
David@0
   147
			//Process any purple imgstore references into real HTML tags pointing to real images
David@0
   148
			messageString = processPurpleImages(messageString, adiumAccount);
David@0
   149
			
David@0
   150
			messageDict = [NSDictionary dictionaryWithObjectsAndKeys:messageString,@"Message",
sholt@2693
   151
						   [NSNumber numberWithInteger:flags],@"PurpleMessageFlags",
David@0
   152
						   [NSDate dateWithTimeIntervalSince1970:mtime],@"Date",nil];
David@0
   153
			
David@0
   154
			[adiumAccount receivedIMChatMessage:messageDict
David@0
   155
										 inChat:chat];
David@0
   156
		}
David@0
   157
	}
David@0
   158
}
David@0
   159
David@0
   160
static void adiumPurpleConvWriteConv(PurpleConversation *conv, const char *who, const char *alias,
David@0
   161
								   const char *message, PurpleMessageFlags flags,
David@0
   162
								   time_t mtime)
David@0
   163
{
David@0
   164
	AILog(@"adiumPurpleConvWriteConv: Received %s from %s [%i]",message,who,flags);
David@0
   165
	AIChat	*chat = chatLookupFromConv(conv);
David@0
   166
zacw@1515
   167
	if (!chat) {
zacw@1515
   168
		return;
zacw@1515
   169
	}
zacw@1515
   170
	
zacw@1515
   171
	NSString			*messageString = [NSString stringWithUTF8String:message];
zacw@1515
   172
	
zacw@1515
   173
	if (!messageString) {
zacw@1515
   174
		AILogWithSignature(@"Received write without message: %@ %d", chat, flags);
zacw@1515
   175
		return;
zacw@1515
   176
	}
zacw@1515
   177
	
zacw@1515
   178
	if (flags & PURPLE_MESSAGE_ERROR) {	
zacw@1515
   179
		if ([messageString rangeOfString:@"User information not available"].location != NSNotFound) {
zacw@1515
   180
			//Ignore user information errors; they are irrelevent
zacw@1515
   181
			//XXX The user info check only works in English; libpurple should be modified to be better about this useless information spamming
zacw@1515
   182
			return;
zacw@1515
   183
		}
zacw@1515
   184
		
zacw@1515
   185
		AIChatErrorType	errorType = AIChatUnknownError;
zacw@1515
   186
		
zacw@1515
   187
		if (([messageString rangeOfString:[NSString stringWithUTF8String:_("Not logged in")]].location != NSNotFound) || 
zacw@1515
   188
			([messageString rangeOfString:[NSString stringWithUTF8String:_("User temporarily unavailable")]].location != NSNotFound)) {
zacw@1515
   189
			errorType = AIChatMessageSendingUserNotAvailable;
zacw@1515
   190
		} else if ([messageString rangeOfString:[NSString stringWithUTF8String:_("In local permit/deny")]].location != NSNotFound) {
zacw@1515
   191
			errorType = AIChatMessageSendingUserIsBlocked;
zacw@1515
   192
		} else if (([messageString rangeOfString:[NSString stringWithUTF8String:_("Reply too big")]].location != NSNotFound) ||
zacw@1515
   193
				   ([messageString rangeOfString:@"message is too large"].location != NSNotFound)) {
zacw@1515
   194
			//XXX - there may be other conditions, but this seems the most common so that's how we'll classify it
zacw@1515
   195
			errorType = AIChatMessageSendingTooLarge;
zacw@1515
   196
		} else if ([messageString rangeOfString:[NSString stringWithUTF8String:_("Command failed")]].location != NSNotFound) {
zacw@1515
   197
			errorType = AIChatCommandFailed;
zacw@1515
   198
		} else if ([messageString rangeOfString:[NSString stringWithUTF8String:_("Wrong number of arguments")]].location != NSNotFound) {
zacw@1515
   199
			errorType = AIChatInvalidNumberOfArguments;
zacw@1515
   200
		} else if ([messageString rangeOfString:[NSString stringWithUTF8String:_("Rate")]].location != NSNotFound) {
zacw@1515
   201
			//XXX Is 'Rate' really a standalone translated string?
zacw@1515
   202
			errorType = AIChatMessageSendingMissedRateLimitExceeded;
zacw@1515
   203
		} else if ([messageString rangeOfString:[NSString stringWithUTF8String:_("Too evil")]].location != NSNotFound) {
zacw@1515
   204
			errorType = AIChatMessageReceivingMissedRemoteIsTooEvil;
zacw@1515
   205
		}
zacw@1515
   206
		/* Another is 'refused by client', which is definitely seen when sending an offline message to an invalid screenname...
zacw@1515
   207
		 * but I don't know when else it is sent. -evands
zacw@1515
   208
		 */
zacw@1515
   209
		
zacw@1515
   210
		/* We will wait until the next run loop, in case this error message was generated by
zacw@1515
   211
		 * the sending of a message. This allows the results of sending the message to be displayed
zacw@1515
   212
		 * first.
zacw@1515
   213
		 */
zacw@1515
   214
		if (errorType != AIChatUnknownError) {
zacw@1515
   215
			[accountLookup(purple_conversation_get_account(conv)) performSelector:@selector(errorForChat:type:)
zacw@2848
   216
																	   withObject:chat
zacw@2848
   217
																	   withObject:[NSNumber numberWithInteger:errorType]
zacw@2848
   218
																	   afterDelay:0];
zacw@1515
   219
		} else {
zacw@1515
   220
			[adium.contentController performSelector:@selector(displayEvent:ofType:inChat:)
zacw@2848
   221
										  withObject:messageString
zacw@2848
   222
										  withObject:@"libpurpleMessage"
zacw@2848
   223
										  withObject:chat
zacw@2848
   224
										  afterDelay:0];
zacw@1515
   225
		}
zacw@1515
   226
		
zacw@1515
   227
		AILog(@"*** Conversation error %@: %@", chat, messageString);
zacw@1515
   228
	} else {
zacw@1515
   229
		BOOL				shouldDisplayMessage = TRUE;
zacw@1515
   230
		if (strcmp(message, _("Direct IM established")) == 0) {
zacw@1515
   231
			[accountLookup(purple_conversation_get_account(conv)) updateContact:chat.listObject
sholt@2693
   232
											   forEvent:[NSNumber numberWithInteger:PURPLE_BUDDY_DIRECTIM_CONNECTED]];
zacw@1515
   233
			shouldDisplayMessage = FALSE;
zacw@1515
   234
			
zacw@1515
   235
		} else {
zacw@1515
   236
			BOOL isClosingDirectIM = FALSE;
zacw@1515
   237
			if ((strcmp(message, _("The remote user has closed the connection.")) == 0) ||
zacw@1515
   238
				(strcmp(message, _("The remote user has declined your request.")) == 0) ||
zacw@1515
   239
				(strcmp(message, _("Received invalid data on connection with remote user.")) == 0) ||
zacw@1515
   240
				(strcmp(message, _("Could not establish a connection with the remote user.")) == 0)) {
zacw@1515
   241
				isClosingDirectIM = TRUE;
zacw@1515
   242
			}
zacw@1515
   243
			
zacw@1515
   244
			if (!isClosingDirectIM) {
zacw@1515
   245
				//Only works in English - XXX fix me!
zacw@1515
   246
				if ([messageString rangeOfString:@"Lost connection with the remote user:"].location != NSNotFound) {
zacw@1515
   247
					isClosingDirectIM = TRUE;
David@0
   248
				}
David@0
   249
			}
zacw@1515
   250
			
zacw@1515
   251
			if (isClosingDirectIM) {
zacw@1515
   252
				if (strcmp(message, _("The remote user has closed the connection.")) != 0) {
zacw@1515
   253
					//Display the message if it's not just the one for the other guy closing it...
zacw@1515
   254
					[adium.contentController displayEvent:messageString
zacw@1515
   255
												   ofType:@"directIMDisconnected"
zacw@1515
   256
												   inChat:chat];
zacw@1515
   257
				}
zacw@1515
   258
				
sholt@2693
   259
				[accountLookup(purple_conversation_get_account(conv)) updateContact:chat.listObject forEvent:[NSNumber numberWithInteger:PURPLE_BUDDY_DIRECTIM_DISCONNECTED]];
zacw@1515
   260
				shouldDisplayMessage = FALSE;
zacw@1515
   261
			}
zacw@1515
   262
		}
David@0
   263
zacw@1515
   264
		if (shouldDisplayMessage) {
zacw@1515
   265
			CBPurpleAccount *account = accountLookup(purple_conversation_get_account(conv));
zacw@1515
   266
			
zacw@2848
   267
			[account performSelector:@selector(receivedEventForChat:message:date:flags:)
zacw@2848
   268
						  withObject:chat
zacw@2848
   269
						  withObject:messageString
zacw@2848
   270
						  withObject:[NSDate dateWithTimeIntervalSince1970:mtime]
zacw@2848
   271
						  withObject:[NSNumber numberWithInteger:flags]
zacw@2848
   272
						  afterDelay:0];
David@0
   273
		}
David@0
   274
	}
David@0
   275
}
David@0
   276
zacw@1460
   277
NSString *get_real_name_for_account_conv_buddy(PurpleAccount *account, PurpleConversation *conv, char *who)
zacw@1460
   278
{
zacw@2050
   279
	g_return_val_if_fail(who != NULL && strlen(who), nil);
zacw@2050
   280
	
zacw@1460
   281
	PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
zacw@1460
   282
	PurplePluginProtocolInfo  *prpl_info = (prpl ? PURPLE_PLUGIN_PROTOCOL_INFO(prpl) : NULL);
zacw@1460
   283
	PurpleConvChat *convChat = purple_conversation_get_chat_data(conv);
zacw@1460
   284
	
zacw@2017
   285
	char *uid = NULL;
zacw@2017
   286
	
zacw@1460
   287
	NSString *normalizedUID;
zacw@1460
   288
	
zacw@1460
   289
	if (prpl_info && prpl_info->get_cb_real_name) {
zacw@1460
   290
		// Get the real name of the buddy for use as a UID, if available.
zacw@2017
   291
		uid = prpl_info->get_cb_real_name(purple_account_get_connection(account),
zacw@2017
   292
										  purple_conv_chat_get_id(convChat),
zacw@2017
   293
										  who);
zacw@1460
   294
	}
zacw@1460
   295
	
zacw@2017
   296
	if (!uid) {
zacw@2017
   297
		// strdup it, mostly so the free below won't have to be cased out.
zacw@2017
   298
		uid = g_strdup(who);
zacw@2017
   299
	}
zacw@2017
   300
		
zacw@2017
   301
	normalizedUID = [NSString stringWithUTF8String:purple_normalize(account, uid)];
zacw@2017
   302
		
zacw@2017
   303
	// We have to free the result of get_cb_real_name.
zacw@2017
   304
	g_free(uid);
zacw@2017
   305
zacw@1460
   306
	return normalizedUID;
zacw@1460
   307
}
zacw@1460
   308
David@0
   309
static void adiumPurpleConvChatAddUsers(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
David@0
   310
{
zacw@1425
   311
	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
zacw@1460
   312
		PurpleAccount *account = purple_conversation_get_account(conv);
zacw@1460
   313
zacw@1460
   314
		NSMutableArray *users = [NSMutableArray array];
zacw@1460
   315
		
zacw@1460
   316
		for (GList *l = cbuddies; l; l = l->next) {
zacw@1460
   317
			PurpleConvChatBuddy *cb = (PurpleConvChatBuddy *)l->data;
zacw@1460
   318
			
zacw@1460
   319
			NSMutableDictionary *user = [NSMutableDictionary dictionary];
zacw@1460
   320
			[user setObject:get_real_name_for_account_conv_buddy(account, conv, cb->name) forKey:@"UID"];
zacw@1460
   321
			[user setObject:[NSNumber numberWithInteger:cb->flags] forKey:@"Flags"];
zacw@1460
   322
			if (cb->alias) {
zacw@1460
   323
				[user setObject:[NSString stringWithUTF8String:cb->alias] forKey:@"Alias"];
zacw@1460
   324
			}
zacw@1460
   325
			
zacw@1460
   326
			[users addObject:user];
zacw@1460
   327
		}
zacw@1460
   328
zacw@1460
   329
		[accountLookup(account) updateUserListForChat:groupChatLookupFromConv(conv)
zacw@1460
   330
												users:users
zacw@1460
   331
										   newlyAdded:new_arrivals];
zacw@1425
   332
	} else
David@0
   333
		AILog(@"adiumPurpleConvChatAddUsers: IM");
David@0
   334
}
David@0
   335
David@0
   336
static void adiumPurpleConvChatRenameUser(PurpleConversation *conv, const char *oldName,
David@0
   337
										const char *newName, const char *newAlias)
David@0
   338
{
David@0
   339
	AILog(@"adiumPurpleConvChatRenameUser: %s: oldName %s, newName %s, newAlias %s",
David@0
   340
			   purple_conversation_get_name(conv),
David@0
   341
			   oldName, newName, newAlias);
zacw@1395
   342
	
David@0
   343
	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
zacw@1395
   344
		PurpleConvChat *convChat = purple_conversation_get_chat_data(conv);
zacw@1395
   345
		PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(convChat, oldName);
zacw@1395
   346
		
zacw@1425
   347
		PurpleAccount *account = purple_conversation_get_account(conv);
zacw@1425
   348
		
zacw@1460
   349
		[accountLookup(purple_conversation_get_account(conv)) renameParticipant:get_real_name_for_account_conv_buddy(account, conv, (char *)oldName)
zacw@1460
   350
																		newName:get_real_name_for_account_conv_buddy(account, conv, (char *)newName)
zacw@1395
   351
																	   newAlias:[NSString stringWithUTF8String:newAlias]
zacw@1460
   352
																		  flags:cb->flags
zacw@1395
   353
																		 inChat:groupChatLookupFromConv(conv)];
David@0
   354
	}
David@0
   355
}
David@0
   356
David@0
   357
static void adiumPurpleConvChatRemoveUsers(PurpleConversation *conv, GList *users)
David@0
   358
{
David@0
   359
	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
David@0
   360
		NSMutableArray	*usersArray = [NSMutableArray array];
zacw@1425
   361
		PurpleAccount	*account = purple_conversation_get_account(conv);
David@0
   362
David@0
   363
		GList *l;
David@0
   364
		for (l = users; l != NULL; l = l->next) {
zacw@1460
   365
			NSString *normalizedUID = get_real_name_for_account_conv_buddy(account, conv, (char *)l->data);
zacw@1460
   366
			[usersArray addObject:normalizedUID];
David@0
   367
		}
David@0
   368
zacw@1425
   369
		[accountLookup(account) removeUsersArray:usersArray
zacw@1425
   370
										fromChat:groupChatLookupFromConv(conv)];
David@0
   371
David@0
   372
	} else {
David@0
   373
		AILog(@"adiumPurpleConvChatRemoveUser: IM");
David@0
   374
	}
David@0
   375
}
David@0
   376
David@0
   377
static void adiumPurpleConvUpdateUser(PurpleConversation *conv, const char *user)
David@0
   378
{
zacw@1425
   379
	PurpleAccount *account = purple_conversation_get_account(conv);
zacw@1425
   380
	CBPurpleAccount *adiumAccount = accountLookup(account);
zacw@1395
   381
	
zacw@1740
   382
	PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), user);
zacw@1740
   383
	
zacw@1740
   384
	NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
zacw@1740
   385
	
zacw@1754
   386
	GList *attribute = purple_conv_chat_cb_get_attribute_keys(cb);
zacw@1754
   387
	
zacw@1754
   388
	for (; attribute != NULL; attribute = g_list_next(attribute)) {
zacw@1740
   389
		[attributes setObject:[NSString stringWithUTF8String:purple_conv_chat_cb_get_attribute(cb, attribute->data)]
zacw@1740
   390
					   forKey:[NSString stringWithUTF8String:attribute->data]];
zacw@1740
   391
	}
zacw@1740
   392
	
zacw@1755
   393
	g_list_free(attribute);
zacw@1754
   394
	
zacw@2231
   395
	NSString *alias = cb->alias ? [NSString stringWithUTF8String:cb->alias] : nil;
zacw@2231
   396
	
zacw@1460
   397
	[adiumAccount updateUser:get_real_name_for_account_conv_buddy(account, conv, (char *)user)
zacw@1425
   398
					 forChat:groupChatLookupFromConv(conv)
zacw@1753
   399
					   flags:cb->flags
zacw@2231
   400
					   alias:alias
zacw@1740
   401
				  attributes:attributes];
David@0
   402
}
David@0
   403
David@0
   404
static void adiumPurpleConvPresent(PurpleConversation *conv)
David@0
   405
{
David@0
   406
	
David@0
   407
}
David@0
   408
David@0
   409
//This isn't a function we want Purple doing anything with, I don't think
David@0
   410
static gboolean adiumPurpleConvHasFocus(PurpleConversation *conv)
David@0
   411
{
David@0
   412
	return NO;
David@0
   413
}
David@0
   414
David@0
   415
static void adiumPurpleConvUpdated(PurpleConversation *conv, PurpleConvUpdateType type)
David@0
   416
{
David@0
   417
	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
David@0
   418
		PurpleConvChat  *chat = purple_conversation_get_chat_data(conv);
David@0
   419
		
David@0
   420
		switch(type) {
David@0
   421
			case PURPLE_CONV_UPDATE_TOPIC:
zacw@1292
   422
			{
zacw@1292
   423
				NSString *who = nil;
zacw@1292
   424
				
zacw@1292
   425
				if (chat->who != NULL) {
zacw@1292
   426
					who = [NSString stringWithUTF8String:chat->who];
zacw@1292
   427
				}
zacw@1292
   428
				
David@0
   429
				[accountLookup(purple_conversation_get_account(conv)) updateTopic:(purple_conv_chat_get_topic(chat) ?
zacw@1292
   430
																				   [NSString stringWithUTF8String:purple_conv_chat_get_topic(chat)] :
zacw@1292
   431
																				   nil)
zacw@1292
   432
																		  forChat:groupChatLookupFromConv(conv)
zacw@1292
   433
																	   withSource:who];
David@0
   434
				break;
zacw@1292
   435
			}
David@0
   436
			case PURPLE_CONV_UPDATE_TITLE:
David@0
   437
				[accountLookup(purple_conversation_get_account(conv)) updateTitle:(purple_conversation_get_title(conv) ?
David@0
   438
														   [NSString stringWithUTF8String:purple_conversation_get_title(conv)] :
David@0
   439
														   nil)
David@0
   440
												  forChat:groupChatLookupFromConv(conv)];
David@0
   441
				
David@0
   442
				AILog(@"Update to title: %s",purple_conversation_get_title(conv));
David@0
   443
				break;
David@0
   444
			case PURPLE_CONV_UPDATE_CHATLEFT:
David@0
   445
				[accountLookup(purple_conversation_get_account(conv)) leftChat:groupChatLookupFromConv(conv)];
David@0
   446
				break;
David@0
   447
			case PURPLE_CONV_UPDATE_ADD:
David@0
   448
			case PURPLE_CONV_UPDATE_REMOVE:
David@0
   449
			case PURPLE_CONV_UPDATE_ACCOUNT:
David@0
   450
			case PURPLE_CONV_UPDATE_TYPING:
David@0
   451
			case PURPLE_CONV_UPDATE_UNSEEN:
David@0
   452
			case PURPLE_CONV_UPDATE_LOGGING:
David@0
   453
			case PURPLE_CONV_ACCOUNT_ONLINE:
David@0
   454
			case PURPLE_CONV_ACCOUNT_OFFLINE:
David@0
   455
			case PURPLE_CONV_UPDATE_AWAY:
David@0
   456
			case PURPLE_CONV_UPDATE_ICON:
David@0
   457
			case PURPLE_CONV_UPDATE_FEATURES:
David@0
   458
David@0
   459
/*				
David@0
   460
				[accountLookup(purple_conversation_get_account(conv)) mainPerformSelector:@selector(convUpdateForChat:type:)
David@0
   461
													   withObject:groupChatLookupFromConv(conv)
David@0
   462
													   withObject:[NSNumber numberWithInt:type]];
David@0
   463
*/				
David@0
   464
			default:
David@0
   465
				break;
David@0
   466
		}
David@0
   467
David@0
   468
	} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
David@0
   469
		PurpleConvIm  *im = purple_conversation_get_im_data(conv);
David@0
   470
		switch (type) {
David@0
   471
			case PURPLE_CONV_UPDATE_TYPING: {
David@0
   472
David@0
   473
				AITypingState typingState;
David@0
   474
David@0
   475
				switch (purple_conv_im_get_typing_state(im)) {
David@0
   476
					case PURPLE_TYPING:
David@0
   477
						typingState = AITyping;
David@0
   478
						break;
David@0
   479
					case PURPLE_TYPED:
David@0
   480
						typingState = AIEnteredText;
David@0
   481
						break;
David@0
   482
					case PURPLE_NOT_TYPING:
David@0
   483
					default:
David@0
   484
						typingState = AINotTyping;
David@0
   485
						break;
David@0
   486
				}
David@0
   487
sholt@2693
   488
				NSNumber	*typingStateNumber = [NSNumber numberWithInteger:typingState];
David@0
   489
David@0
   490
				[accountLookup(purple_conversation_get_account(conv)) typingUpdateForIMChat:imChatLookupFromConv(conv)
David@0
   491
															 typing:typingStateNumber];
David@0
   492
				break;
David@0
   493
			}
David@0
   494
			case PURPLE_CONV_UPDATE_AWAY: {
David@0
   495
				//If the conversation update is UPDATE_AWAY, it seems to suppress the typing state being updated
David@0
   496
				//Reset purple's typing tracking, then update to receive a PURPLE_CONV_UPDATE_TYPING message
David@0
   497
				purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
David@0
   498
				purple_conv_im_update_typing(im);
David@0
   499
				break;
David@0
   500
			}
David@0
   501
			default:
David@0
   502
				break;
David@0
   503
		}
David@0
   504
	}
David@0
   505
}
David@0
   506
David@0
   507
#pragma mark Custom smileys
David@0
   508
gboolean adiumPurpleConvCustomSmileyAdd(PurpleConversation *conv, const char *smile, gboolean remote)
David@0
   509
{
David@0
   510
	AILog(@"%s: Added Custom Smiley %s",purple_conversation_get_name(conv),smile);
David@0
   511
	[accountLookup(purple_conversation_get_account(conv)) chat:chatLookupFromConv(conv)
David@0
   512
			 isWaitingOnCustomEmoticon:[NSString stringWithUTF8String:smile]];
David@0
   513
David@0
   514
	return TRUE;
David@0
   515
}
David@0
   516
David@0
   517
void adiumPurpleConvCustomSmileyWrite(PurpleConversation *conv, const char *smile,
David@0
   518
									const guchar *data, gsize size)
David@0
   519
{
David@0
   520
	AILog(@"%s: Write Custom Smiley %s (%x %i)",purple_conversation_get_name(conv),smile,data,size);
David@0
   521
David@0
   522
	[accountLookup(purple_conversation_get_account(conv)) chat:chatLookupFromConv(conv)
David@0
   523
					 setCustomEmoticon:[NSString stringWithUTF8String:smile]
David@0
   524
						 withImageData:[NSData dataWithBytes:data
David@0
   525
													  length:size]];
David@0
   526
}
David@0
   527
David@0
   528
void adiumPurpleConvCustomSmileyClose(PurpleConversation *conv, const char *smile)
David@0
   529
{
David@0
   530
	AILog(@"%s: Close Custom Smiley %s",purple_conversation_get_name(conv),smile);
David@0
   531
David@0
   532
	[accountLookup(purple_conversation_get_account(conv)) chat:chatLookupFromConv(conv)
David@0
   533
				  closedCustomEmoticon:[NSString stringWithUTF8String:smile]];
David@0
   534
}
David@0
   535
zacw@1690
   536
static gboolean adiumPurpleConvJoin(PurpleConversation *conv, const char *name,
zacw@1690
   537
									PurpleConvChatBuddyFlags flags,
zacw@1690
   538
									GHashTable *users)
zacw@1690
   539
{
zacw@1690
   540
	AIChat *chat = groupChatLookupFromConv(conv);
zacw@1690
   541
zacw@1690
   542
	// We return TRUE if we want to hide it.
zacw@1690
   543
	return !chat.showJoinLeave;
zacw@1690
   544
}
zacw@1690
   545
zacw@1690
   546
static gboolean adiumPurpleConvLeave(PurpleConversation *conv, const char *name,
zacw@1690
   547
									 const char *reason, GHashTable *users)
zacw@1690
   548
{
zacw@1690
   549
	AIChat *chat = groupChatLookupFromConv(conv);
zacw@1690
   550
	
zacw@1690
   551
	// We return TRUE if we want to hide it.
zacw@1690
   552
	return !chat.showJoinLeave;	
zacw@1690
   553
}
zacw@1690
   554
David@0
   555
static PurpleConversationUiOps adiumPurpleConversationOps = {
David@0
   556
	adiumPurpleConvCreate,
David@0
   557
    adiumPurpleConvDestroy,
David@0
   558
    adiumPurpleConvWriteChat,
David@0
   559
    adiumPurpleConvWriteIm,
David@0
   560
    adiumPurpleConvWriteConv,
David@0
   561
    adiumPurpleConvChatAddUsers,
David@0
   562
    adiumPurpleConvChatRenameUser,
David@0
   563
    adiumPurpleConvChatRemoveUsers,
David@0
   564
	adiumPurpleConvUpdateUser,
David@0
   565
	
David@0
   566
	adiumPurpleConvPresent,
David@0
   567
	adiumPurpleConvHasFocus,
David@0
   568
David@0
   569
	/* Custom Smileys */
David@0
   570
	adiumPurpleConvCustomSmileyAdd,
David@0
   571
	adiumPurpleConvCustomSmileyWrite,
David@0
   572
	adiumPurpleConvCustomSmileyClose,
Evan@637
   573
Evan@637
   574
	/* send_confirm */
Evan@637
   575
	NULL
David@0
   576
};
David@0
   577
David@0
   578
PurpleConversationUiOps *adium_purple_conversation_get_ui_ops(void)
David@0
   579
{
David@0
   580
	return &adiumPurpleConversationOps;
David@0
   581
}
David@0
   582
David@0
   583
void adiumPurpleConversation_init(void)
David@0
   584
{	
David@0
   585
	purple_conversations_set_ui_ops(adium_purple_conversation_get_ui_ops());
David@0
   586
David@0
   587
	purple_signal_connect_priority(purple_conversations_get_handle(), "conversation-updated", adium_purple_get_handle(),
David@0
   588
								 PURPLE_CALLBACK(adiumPurpleConvUpdated), NULL,
David@0
   589
								 PURPLE_SIGNAL_PRIORITY_LOWEST);
David@0
   590
	
zacw@1690
   591
	purple_signal_connect(purple_conversations_get_handle(), "chat-buddy-joining", adium_purple_get_handle(),
zacw@1690
   592
						  PURPLE_CALLBACK(adiumPurpleConvJoin), NULL);
zacw@1690
   593
	
zacw@1690
   594
	purple_signal_connect(purple_conversations_get_handle(), "chat-buddy-leaving", adium_purple_get_handle(),
zacw@1690
   595
						  PURPLE_CALLBACK(adiumPurpleConvLeave), NULL);
zacw@1690
   596
	
David@0
   597
}