William Bowling is sharing code with you

Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.

Don't show this again

wbowling / adium (fork of adium / adium)

Fork of Adium for patches/improvements

Clone this repository (size: 338.7 MB): HTTPS / SSH
hg clone https://bitbucket.org/wbowling/adium
hg clone ssh://hg@bitbucket.org/wbowling/adium

adium / Plugins / Purple Service / adiumPurpleConversation.m

commit
bfdbbb6fd385
parent
f7650158eeb5
branch
default

Display the unsent message that is passed from libpurple instead of ignoring it. Fixes #13190.

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