Mercurial > pidgin
annotate libgaim/server.c @ 15222:26357b7f117e
[gaim-migrate @ 18012]
* Get rid of an assertion failure that I think was happening when
you added a new account without setting a custom icon for it, and
you still had the Accounts window open
* A little code-reuse in some buddy icon scaling code
* An minor memleak that could happen when unable open a new file in
$HOME/.gaim/icons/
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sun, 17 Dec 2006 05:04:23 +0000 |
parents | 0a9c44ce9a4a |
children | a6aad36ca735 |
rev | line source |
---|---|
14192 | 1 /* |
2 * gaim | |
3 * | |
4 * Gaim is the legal property of its developers, whose names are too numerous | |
5 * to list here. Please refer to the COPYRIGHT file distributed with this | |
6 * source distribution. | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 * | |
22 */ | |
23 #include "internal.h" | |
24 #include "blist.h" | |
25 #include "conversation.h" | |
26 #include "debug.h" | |
27 #include "log.h" | |
28 #include "notify.h" | |
29 #include "prefs.h" | |
30 #include "privacy.h" | |
31 #include "prpl.h" | |
32 #include "request.h" | |
33 #include "signals.h" | |
34 #include "server.h" | |
35 #include "status.h" | |
36 #include "util.h" | |
37 | |
38 #define SECS_BEFORE_RESENDING_AUTORESPONSE 600 | |
39 #define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married" | |
40 | |
41 unsigned int | |
42 serv_send_typing(GaimConnection *gc, const char *name, GaimTypingState state) | |
43 { | |
44 GaimPluginProtocolInfo *prpl_info = NULL; | |
45 | |
46 if (gc != NULL && gc->prpl != NULL) | |
47 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
48 | |
49 if (prpl_info && prpl_info->send_typing) | |
50 return prpl_info->send_typing(gc, name, state); | |
51 | |
52 return 0; | |
53 } | |
54 | |
55 static GSList *last_auto_responses = NULL; | |
56 struct last_auto_response { | |
57 GaimConnection *gc; | |
58 char name[80]; | |
59 time_t sent; | |
60 }; | |
61 | |
62 static gboolean | |
63 expire_last_auto_responses(gpointer data) | |
64 { | |
65 GSList *tmp, *cur; | |
66 struct last_auto_response *lar; | |
67 | |
68 tmp = last_auto_responses; | |
69 | |
70 while (tmp) { | |
71 cur = tmp; | |
72 tmp = tmp->next; | |
73 lar = (struct last_auto_response *)cur->data; | |
74 | |
75 if ((time(NULL) - lar->sent) > SECS_BEFORE_RESENDING_AUTORESPONSE) { | |
76 last_auto_responses = g_slist_remove(last_auto_responses, lar); | |
77 g_free(lar); | |
78 } | |
79 } | |
80 | |
81 return FALSE; /* do not run again */ | |
82 } | |
83 | |
84 static struct last_auto_response * | |
85 get_last_auto_response(GaimConnection *gc, const char *name) | |
86 { | |
87 GSList *tmp; | |
88 struct last_auto_response *lar; | |
89 | |
90 /* because we're modifying or creating a lar, schedule the | |
91 * function to expire them as the pref dictates */ | |
92 gaim_timeout_add((SECS_BEFORE_RESENDING_AUTORESPONSE + 1) * 1000, expire_last_auto_responses, NULL); | |
93 | |
94 tmp = last_auto_responses; | |
95 | |
96 while (tmp) { | |
97 lar = (struct last_auto_response *)tmp->data; | |
98 | |
99 if (gc == lar->gc && !strncmp(name, lar->name, sizeof(lar->name))) | |
100 return lar; | |
101 | |
102 tmp = tmp->next; | |
103 } | |
104 | |
105 lar = (struct last_auto_response *)g_new0(struct last_auto_response, 1); | |
106 g_snprintf(lar->name, sizeof(lar->name), "%s", name); | |
107 lar->gc = gc; | |
108 lar->sent = 0; | |
109 last_auto_responses = g_slist_prepend(last_auto_responses, lar); | |
110 | |
111 return lar; | |
112 } | |
113 | |
114 int serv_send_im(GaimConnection *gc, const char *name, const char *message, | |
115 GaimMessageFlags flags) | |
116 { | |
117 GaimConversation *conv; | |
118 GaimAccount *account; | |
119 GaimPresence *presence; | |
120 GaimPluginProtocolInfo *prpl_info; | |
121 int val = -EINVAL; | |
122 const gchar *auto_reply_pref; | |
123 | |
124 g_return_val_if_fail(gc != NULL, val); | |
125 g_return_val_if_fail(gc->prpl != NULL, val); | |
126 | |
127 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
128 | |
129 account = gaim_connection_get_account(gc); | |
130 presence = gaim_account_get_presence(account); | |
131 | |
132 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, gc->account); | |
133 | |
134 if (prpl_info && prpl_info->send_im) | |
135 val = prpl_info->send_im(gc, name, message, flags); | |
136 | |
137 /* | |
138 * XXX - If "only auto-reply when away & idle" is set, then shouldn't | |
139 * this only reset lar->sent if we're away AND idle? | |
140 */ | |
141 auto_reply_pref = gaim_prefs_get_string("/core/away/auto_reply"); | |
142 if ((gc->flags & GAIM_CONNECTION_AUTO_RESP) && | |
143 !gaim_presence_is_available(presence) && | |
144 strcmp(auto_reply_pref, "never")) { | |
145 | |
146 struct last_auto_response *lar; | |
147 lar = get_last_auto_response(gc, name); | |
148 lar->sent = time(NULL); | |
149 } | |
150 | |
151 if (conv && gaim_conv_im_get_send_typed_timeout(GAIM_CONV_IM(conv))) | |
152 gaim_conv_im_stop_send_typed_timeout(GAIM_CONV_IM(conv)); | |
153 | |
154 return val; | |
155 } | |
156 | |
157 void serv_get_info(GaimConnection *gc, const char *name) | |
158 { | |
159 GaimPluginProtocolInfo *prpl_info = NULL; | |
160 | |
161 if (gc != NULL && gc->prpl != NULL) | |
162 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
163 | |
164 if (gc && prpl_info && prpl_info->get_info) | |
165 prpl_info->get_info(gc, name); | |
166 } | |
167 | |
168 void serv_set_info(GaimConnection *gc, const char *info) | |
169 { | |
170 GaimPluginProtocolInfo *prpl_info = NULL; | |
171 GaimAccount *account; | |
172 | |
173 if (gc != NULL && gc->prpl != NULL) | |
174 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
175 | |
14607 | 176 if (prpl_info && prpl_info->set_info) { |
14192 | 177 |
178 account = gaim_connection_get_account(gc); | |
179 | |
180 if (gaim_signal_emit_return_1(gaim_accounts_get_handle(), | |
181 "account-setting-info", account, info)) | |
182 return; | |
183 | |
184 prpl_info->set_info(gc, info); | |
185 | |
186 gaim_signal_emit(gaim_accounts_get_handle(), | |
187 "account-set-info", account, info); | |
188 } | |
189 } | |
190 | |
191 /* | |
192 * Set buddy's alias on server roster/list | |
193 */ | |
194 void serv_alias_buddy(GaimBuddy *b) | |
195 { | |
196 GaimPluginProtocolInfo *prpl_info = NULL; | |
197 | |
198 if (b != NULL && b->account->gc->prpl != NULL) | |
199 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl); | |
200 | |
201 if (b && prpl_info && prpl_info->alias_buddy) { | |
202 prpl_info->alias_buddy(b->account->gc, b->name, b->alias); | |
203 } | |
204 } | |
205 | |
206 void | |
207 serv_got_alias(GaimConnection *gc, const char *who, const char *alias) | |
208 { | |
209 GaimAccount *account = gaim_connection_get_account(gc); | |
210 GSList *buds, *buddies = gaim_find_buddies(account, who); | |
211 GaimBuddy *b; | |
212 GaimConversation *conv; | |
213 | |
214 for (buds = buddies; buds; buds = buds->next) | |
215 { | |
216 b = buds->data; | |
14496
aee74d84816c
[gaim-migrate @ 17215]
Richard Laager <rlaager@wiktel.com>
parents:
14484
diff
changeset
|
217 if ((b->server_alias == NULL && alias == NULL) || |
aee74d84816c
[gaim-migrate @ 17215]
Richard Laager <rlaager@wiktel.com>
parents:
14484
diff
changeset
|
218 (b->server_alias && alias && !strcmp(b->server_alias, alias))) |
aee74d84816c
[gaim-migrate @ 17215]
Richard Laager <rlaager@wiktel.com>
parents:
14484
diff
changeset
|
219 { |
14484
1f81919515ae
[gaim-migrate @ 17203]
Richard Laager <rlaager@wiktel.com>
parents:
14192
diff
changeset
|
220 continue; |
14496
aee74d84816c
[gaim-migrate @ 17215]
Richard Laager <rlaager@wiktel.com>
parents:
14484
diff
changeset
|
221 } |
14192 | 222 gaim_blist_server_alias_buddy(b, alias); |
223 | |
224 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, b->name, account); | |
225 | |
14496
aee74d84816c
[gaim-migrate @ 17215]
Richard Laager <rlaager@wiktel.com>
parents:
14484
diff
changeset
|
226 if (conv != NULL && alias != NULL) |
14192 | 227 { |
228 char *tmp = g_strdup_printf(_("%s is now known as %s.\n"), | |
229 who, alias); | |
230 | |
231 gaim_conversation_write(conv, NULL, tmp, GAIM_MESSAGE_SYSTEM, | |
232 time(NULL)); | |
233 | |
234 g_free(tmp); | |
235 } | |
236 } | |
237 g_slist_free(buddies); | |
238 } | |
239 | |
240 /* | |
241 * Move a buddy from one group to another on server. | |
242 * | |
243 * Note: For now we'll not deal with changing gc's at the same time, but | |
244 * it should be possible. Probably needs to be done, someday. Although, | |
245 * the UI for that would be difficult, because groups are Gaim-wide. | |
246 */ | |
247 void serv_move_buddy(GaimBuddy *b, GaimGroup *og, GaimGroup *ng) | |
248 { | |
249 GaimPluginProtocolInfo *prpl_info = NULL; | |
250 | |
251 g_return_if_fail(b != NULL); | |
252 g_return_if_fail(og != NULL); | |
253 g_return_if_fail(ng != NULL); | |
254 | |
255 if (b->account->gc != NULL && b->account->gc->prpl != NULL) | |
256 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl); | |
257 | |
258 if (b->account->gc && og && ng) { | |
259 if (prpl_info && prpl_info->group_buddy) { | |
260 prpl_info->group_buddy(b->account->gc, b->name, og->name, ng->name); | |
261 } | |
262 } | |
263 } | |
264 | |
265 void serv_add_permit(GaimConnection *g, const char *name) | |
266 { | |
267 GaimPluginProtocolInfo *prpl_info = NULL; | |
268 | |
269 if (g != NULL && g->prpl != NULL) | |
270 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
271 | |
14607 | 272 if (prpl_info && prpl_info->add_permit) |
14192 | 273 prpl_info->add_permit(g, name); |
274 } | |
275 | |
276 void serv_add_deny(GaimConnection *g, const char *name) | |
277 { | |
278 GaimPluginProtocolInfo *prpl_info = NULL; | |
279 | |
280 if (g != NULL && g->prpl != NULL) | |
281 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
282 | |
14607 | 283 if (prpl_info && prpl_info->add_deny) |
14192 | 284 prpl_info->add_deny(g, name); |
285 } | |
286 | |
287 void serv_rem_permit(GaimConnection *g, const char *name) | |
288 { | |
289 GaimPluginProtocolInfo *prpl_info = NULL; | |
290 | |
291 if (g != NULL && g->prpl != NULL) | |
292 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
293 | |
14607 | 294 if (prpl_info && prpl_info->rem_permit) |
14192 | 295 prpl_info->rem_permit(g, name); |
296 } | |
297 | |
298 void serv_rem_deny(GaimConnection *g, const char *name) | |
299 { | |
300 GaimPluginProtocolInfo *prpl_info = NULL; | |
301 | |
302 if (g != NULL && g->prpl != NULL) | |
303 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
304 | |
14607 | 305 if (prpl_info && prpl_info->rem_deny) |
14192 | 306 prpl_info->rem_deny(g, name); |
307 } | |
308 | |
309 void serv_set_permit_deny(GaimConnection *g) | |
310 { | |
311 GaimPluginProtocolInfo *prpl_info = NULL; | |
312 | |
313 if (g != NULL && g->prpl != NULL) | |
314 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
315 | |
316 /* | |
317 * this is called when either you import a buddy list, and make lots | |
318 * of changes that way, or when the user toggles the permit/deny mode | |
319 * in the prefs. In either case you should probably be resetting and | |
320 * resending the permit/deny info when you get this. | |
321 */ | |
14607 | 322 if (prpl_info && prpl_info->set_permit_deny) |
14192 | 323 prpl_info->set_permit_deny(g); |
324 } | |
325 | |
326 void serv_join_chat(GaimConnection *g, GHashTable *data) | |
327 { | |
328 GaimPluginProtocolInfo *prpl_info = NULL; | |
329 | |
330 if (g != NULL && g->prpl != NULL) | |
331 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
332 | |
14607 | 333 if (prpl_info && prpl_info->join_chat) |
14192 | 334 prpl_info->join_chat(g, data); |
335 } | |
336 | |
337 | |
338 void serv_reject_chat(GaimConnection *g, GHashTable *data) | |
339 { | |
340 GaimPluginProtocolInfo *prpl_info = NULL; | |
341 | |
342 if (g != NULL && g->prpl != NULL) | |
343 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
344 | |
14607 | 345 if (prpl_info && prpl_info->reject_chat) |
14192 | 346 prpl_info->reject_chat(g, data); |
347 } | |
348 | |
349 void serv_chat_invite(GaimConnection *g, int id, const char *message, const char *name) | |
350 { | |
351 GaimPluginProtocolInfo *prpl_info = NULL; | |
352 GaimConversation *conv; | |
353 char *buffy = message && *message ? g_strdup(message) : NULL; | |
354 | |
355 conv = gaim_find_chat(g, id); | |
356 | |
357 if (conv == NULL) | |
358 return; | |
359 | |
360 if (g != NULL && g->prpl != NULL) | |
361 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
362 | |
363 gaim_signal_emit(gaim_conversations_get_handle(), "chat-inviting-user", | |
364 conv, name, &buffy); | |
365 | |
14607 | 366 if (prpl_info && prpl_info->chat_invite) |
14192 | 367 prpl_info->chat_invite(g, id, buffy, name); |
368 | |
369 gaim_signal_emit(gaim_conversations_get_handle(), "chat-invited-user", | |
370 conv, name, buffy); | |
371 | |
372 g_free(buffy); | |
373 } | |
374 | |
375 /* Ya know, nothing uses this except gaim_conversation_destroy(), | |
376 * I think I'll just merge it into that later... | |
377 * Then again, something might want to use this, from outside prpl-land | |
378 * to leave a chat without destroying the conversation. | |
379 */ | |
380 | |
381 void serv_chat_leave(GaimConnection *g, int id) | |
382 { | |
383 GaimPluginProtocolInfo *prpl_info = NULL; | |
384 | |
385 if (g->prpl != NULL) | |
386 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
387 | |
388 if (prpl_info && prpl_info->chat_leave) | |
389 prpl_info->chat_leave(g, id); | |
390 } | |
391 | |
392 void serv_chat_whisper(GaimConnection *g, int id, const char *who, const char *message) | |
393 { | |
394 GaimPluginProtocolInfo *prpl_info = NULL; | |
395 | |
396 if (g != NULL && g->prpl != NULL) | |
397 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
398 | |
399 if (prpl_info && prpl_info->chat_whisper) | |
400 prpl_info->chat_whisper(g, id, who, message); | |
401 } | |
402 | |
403 int serv_chat_send(GaimConnection *gc, int id, const char *message, GaimMessageFlags flags) | |
404 { | |
405 int val = -EINVAL; | |
406 GaimPluginProtocolInfo *prpl_info = NULL; | |
407 | |
408 if (gc->prpl != NULL) | |
409 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
410 | |
411 if (prpl_info && prpl_info->chat_send) | |
412 val = prpl_info->chat_send(gc, id, message, flags); | |
413 | |
414 return val; | |
415 } | |
416 | |
417 /* | |
418 * woo. i'm actually going to comment this function. isn't that fun. make | |
419 * sure to follow along, kids | |
420 */ | |
421 void serv_got_im(GaimConnection *gc, const char *who, const char *msg, | |
422 GaimMessageFlags flags, time_t mtime) | |
423 { | |
424 GaimAccount *account; | |
425 GaimConversation *cnv; | |
426 char *message, *name; | |
427 char *angel, *buffy; | |
428 int plugin_return; | |
429 | |
430 g_return_if_fail(msg != NULL); | |
431 | |
432 account = gaim_connection_get_account(gc); | |
433 | |
434 if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->set_permit_deny == NULL) { | |
435 /* protocol does not support privacy, handle it ourselves */ | |
436 if (!gaim_privacy_check(account, who)) | |
437 return; | |
438 } | |
439 | |
440 /* | |
441 * We should update the conversation window buttons and menu, | |
442 * if it exists. | |
443 */ | |
444 cnv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, who, gc->account); | |
445 | |
446 /* | |
447 * Plugin stuff. we pass a char ** but we don't want to pass what's | |
448 * been given us by the prpls. So we create temp holders and pass | |
449 * those instead. It's basically just to avoid segfaults. | |
450 */ | |
451 buffy = g_malloc(MAX(strlen(msg) + 1, BUF_LONG)); | |
452 strcpy(buffy, msg); | |
453 angel = g_strdup(who); | |
454 | |
455 plugin_return = GPOINTER_TO_INT( | |
456 gaim_signal_emit_return_1(gaim_conversations_get_handle(), | |
457 "receiving-im-msg", gc->account, | |
458 &angel, &buffy, cnv, &flags)); | |
459 | |
460 if (!buffy || !angel || plugin_return) { | |
461 g_free(buffy); | |
462 g_free(angel); | |
463 return; | |
464 } | |
465 | |
466 name = angel; | |
467 message = buffy; | |
468 | |
469 gaim_signal_emit(gaim_conversations_get_handle(), "received-im-msg", gc->account, | |
470 name, message, cnv, flags); | |
471 | |
472 /* search for conversation again in case it was created by received-im-msg handler */ | |
473 if (cnv == NULL) | |
474 cnv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, gc->account); | |
475 | |
476 /* Make sure URLs are clickable */ | |
477 buffy = gaim_markup_linkify(message); | |
478 g_free(message); | |
479 message = buffy; | |
480 | |
481 /* | |
482 * XXX: Should we be setting this here, or relying on prpls to set it? | |
483 */ | |
484 flags |= GAIM_MESSAGE_RECV; | |
485 | |
486 if (cnv == NULL) | |
487 cnv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, name); | |
488 | |
489 gaim_conv_im_write(GAIM_CONV_IM(cnv), NULL, message, flags, mtime); | |
490 g_free(message); | |
491 | |
492 /* | |
493 * Don't autorespond if: | |
494 * | |
495 * - it's not supported on this connection | |
496 * - we are available | |
497 * - or it's disabled | |
498 * - or we're not idle and the 'only auto respond if idle' pref | |
499 * is set | |
500 */ | |
501 if (gc->flags & GAIM_CONNECTION_AUTO_RESP) | |
502 { | |
503 GaimPresence *presence; | |
504 GaimStatus *status; | |
505 GaimStatusType *status_type; | |
506 GaimStatusPrimitive primitive; | |
507 const gchar *auto_reply_pref; | |
508 const char *away_msg = NULL; | |
509 | |
510 auto_reply_pref = gaim_prefs_get_string("/core/away/auto_reply"); | |
511 | |
512 presence = gaim_account_get_presence(account); | |
513 status = gaim_presence_get_active_status(presence); | |
514 status_type = gaim_status_get_type(status); | |
515 primitive = gaim_status_type_get_primitive(status_type); | |
516 if ((primitive == GAIM_STATUS_AVAILABLE) || | |
517 (primitive == GAIM_STATUS_INVISIBLE) || | |
518 (primitive == GAIM_STATUS_MOBILE) || | |
519 !strcmp(auto_reply_pref, "never") || | |
520 (!gaim_presence_is_idle(presence) && !strcmp(auto_reply_pref, "awayidle"))) | |
521 { | |
522 g_free(name); | |
523 return; | |
524 } | |
525 | |
526 away_msg = gaim_value_get_string( | |
527 gaim_status_get_attr_value(status, "message")); | |
528 | |
529 if ((away_msg != NULL) && (*away_msg != '\0')) { | |
530 struct last_auto_response *lar; | |
531 time_t now = time(NULL); | |
532 | |
533 /* | |
534 * This used to be based on the conversation window. But um, if | |
535 * you went away, and someone sent you a message and got your | |
536 * auto-response, and then you closed the window, and then they | |
537 * sent you another one, they'd get the auto-response back too | |
538 * soon. Besides that, we need to keep track of this even if we've | |
539 * got a queue. So the rest of this block is just the auto-response, | |
540 * if necessary. | |
541 */ | |
542 lar = get_last_auto_response(gc, name); | |
543 if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE) | |
544 { | |
545 /* | |
546 * We don't want to send an autoresponse in response to the other user's | |
547 * autoresponse. We do, however, not want to then send one in response to the | |
548 * _next_ message, so we still set lar->sent to now. | |
549 */ | |
550 lar->sent = now; | |
551 | |
552 if (!(flags & GAIM_MESSAGE_AUTO_RESP)) | |
553 { | |
554 serv_send_im(gc, name, away_msg, GAIM_MESSAGE_AUTO_RESP); | |
555 | |
556 gaim_conv_im_write(GAIM_CONV_IM(cnv), NULL, away_msg, | |
557 GAIM_MESSAGE_SEND | GAIM_MESSAGE_AUTO_RESP, | |
558 mtime); | |
559 } | |
560 } | |
561 } | |
562 } | |
563 | |
564 g_free(name); | |
565 } | |
566 | |
567 void serv_got_typing(GaimConnection *gc, const char *name, int timeout, | |
568 GaimTypingState state) { | |
569 GaimConversation *conv; | |
570 GaimConvIm *im = NULL; | |
571 | |
572 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, gc->account); | |
573 if (conv != NULL) { | |
574 im = GAIM_CONV_IM(conv); | |
575 | |
576 gaim_conv_im_set_typing_state(im, state); | |
577 gaim_conv_im_update_typing(im); | |
578 } else { | |
579 if (state == GAIM_TYPING) | |
580 { | |
581 gaim_signal_emit(gaim_conversations_get_handle(), | |
582 "buddy-typing", gc->account, name); | |
583 } | |
584 else | |
585 { | |
586 gaim_signal_emit(gaim_conversations_get_handle(), | |
587 "buddy-typed", gc->account, name); | |
588 } | |
589 } | |
590 | |
591 if (conv != NULL && timeout > 0) | |
592 gaim_conv_im_start_typing_timeout(im, timeout); | |
593 } | |
594 | |
595 void serv_got_typing_stopped(GaimConnection *gc, const char *name) { | |
596 | |
597 GaimConversation *conv; | |
598 GaimConvIm *im; | |
599 | |
600 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, gc->account); | |
601 if (conv != NULL) | |
602 { | |
603 im = GAIM_CONV_IM(conv); | |
604 | |
605 if (im->typing_state == GAIM_NOT_TYPING) | |
606 return; | |
607 | |
608 gaim_conv_im_stop_typing_timeout(im); | |
609 gaim_conv_im_set_typing_state(im, GAIM_NOT_TYPING); | |
610 gaim_conv_im_update_typing(im); | |
611 } | |
612 else | |
613 { | |
614 gaim_signal_emit(gaim_conversations_get_handle(), | |
615 "buddy-typing-stopped", gc->account, name); | |
616 } | |
617 } | |
618 | |
619 struct chat_invite_data { | |
620 GaimConnection *gc; | |
621 GHashTable *components; | |
622 }; | |
623 | |
624 static void chat_invite_data_free(struct chat_invite_data *cid) | |
625 { | |
626 if (cid->components) | |
627 g_hash_table_destroy(cid->components); | |
628 g_free(cid); | |
629 } | |
630 | |
631 | |
632 static void chat_invite_reject(struct chat_invite_data *cid) | |
633 { | |
634 serv_reject_chat(cid->gc, cid->components); | |
635 chat_invite_data_free(cid); | |
636 } | |
637 | |
638 | |
639 static void chat_invite_accept(struct chat_invite_data *cid) | |
640 { | |
641 serv_join_chat(cid->gc, cid->components); | |
642 chat_invite_data_free(cid); | |
643 } | |
644 | |
645 | |
646 | |
647 void serv_got_chat_invite(GaimConnection *gc, const char *name, | |
648 const char *who, const char *message, GHashTable *data) | |
649 { | |
650 GaimAccount *account; | |
651 char buf2[BUF_LONG]; | |
652 struct chat_invite_data *cid = g_new0(struct chat_invite_data, 1); | |
653 int plugin_return; | |
654 | |
655 account = gaim_connection_get_account(gc); | |
656 if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->set_permit_deny == NULL) { | |
657 /* protocol does not support privacy, handle it ourselves */ | |
658 if (!gaim_privacy_check(account, who)) | |
659 return; | |
660 } | |
661 | |
662 plugin_return = GPOINTER_TO_INT(gaim_signal_emit_return_1( | |
663 gaim_conversations_get_handle(), | |
664 "chat-invited", account, who, name, message, data)); | |
665 | |
666 cid->gc = gc; | |
667 cid->components = data; | |
668 | |
669 if (plugin_return == 0) | |
670 { | |
671 if (message != NULL) | |
672 { | |
673 g_snprintf(buf2, sizeof(buf2), | |
674 _("%s has invited %s to the chat room %s:\n%s"), | |
675 who, gaim_account_get_username(account), name, message); | |
676 } | |
677 else | |
678 g_snprintf(buf2, sizeof(buf2), | |
679 _("%s has invited %s to the chat room %s\n"), | |
680 who, gaim_account_get_username(account), name); | |
681 | |
682 | |
683 gaim_request_accept_cancel(gc, NULL, _("Accept chat invitation?"), buf2, | |
684 GAIM_DEFAULT_ACTION_NONE, cid, | |
685 G_CALLBACK(chat_invite_accept), | |
686 G_CALLBACK(chat_invite_reject)); | |
687 } | |
688 else if (plugin_return > 0) | |
689 chat_invite_accept(cid); | |
690 else | |
691 chat_invite_reject(cid); | |
692 } | |
693 | |
694 GaimConversation *serv_got_joined_chat(GaimConnection *gc, | |
695 int id, const char *name) | |
696 { | |
697 GaimConversation *conv; | |
698 GaimConvChat *chat; | |
699 GaimAccount *account; | |
700 | |
701 account = gaim_connection_get_account(gc); | |
702 | |
703 conv = gaim_conversation_new(GAIM_CONV_TYPE_CHAT, account, name); | |
704 chat = GAIM_CONV_CHAT(conv); | |
705 | |
706 if (!g_slist_find(gc->buddy_chats, conv)) | |
707 gc->buddy_chats = g_slist_append(gc->buddy_chats, conv); | |
708 | |
709 gaim_conv_chat_set_id(chat, id); | |
710 | |
711 gaim_signal_emit(gaim_conversations_get_handle(), "chat-joined", conv); | |
712 | |
713 return conv; | |
714 } | |
715 | |
716 void serv_got_chat_left(GaimConnection *g, int id) | |
717 { | |
718 GSList *bcs; | |
719 GaimConversation *conv = NULL; | |
720 GaimConvChat *chat = NULL; | |
721 | |
722 for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) { | |
723 conv = (GaimConversation *)bcs->data; | |
724 | |
725 chat = GAIM_CONV_CHAT(conv); | |
726 | |
727 if (gaim_conv_chat_get_id(chat) == id) | |
728 break; | |
729 | |
730 conv = NULL; | |
731 } | |
732 | |
733 if (!conv) | |
734 return; | |
735 | |
736 gaim_debug(GAIM_DEBUG_INFO, "server", "Leaving room: %s\n", | |
737 gaim_conversation_get_name(conv)); | |
738 | |
739 g->buddy_chats = g_slist_remove(g->buddy_chats, conv); | |
740 | |
741 gaim_conv_chat_left(GAIM_CONV_CHAT(conv)); | |
742 | |
743 gaim_signal_emit(gaim_conversations_get_handle(), "chat-left", conv); | |
744 } | |
745 | |
746 void serv_got_chat_in(GaimConnection *g, int id, const char *who, | |
747 GaimMessageFlags flags, const char *message, time_t mtime) | |
748 { | |
749 GSList *bcs; | |
750 GaimConversation *conv = NULL; | |
751 GaimConvChat *chat = NULL; | |
752 char *buf; | |
753 char *buffy, *angel; | |
754 int plugin_return; | |
755 | |
756 g_return_if_fail(who != NULL); | |
757 g_return_if_fail(message != NULL); | |
758 | |
759 for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) { | |
760 conv = (GaimConversation *)bcs->data; | |
761 | |
762 chat = GAIM_CONV_CHAT(conv); | |
763 | |
764 if (gaim_conv_chat_get_id(chat) == id) | |
765 break; | |
766 | |
767 conv = NULL; | |
768 } | |
769 | |
770 if (!conv) | |
771 return; | |
772 | |
773 /* | |
774 * Plugin stuff. We pass a char ** but we don't want to pass what's | |
775 * been given us by the prpls. so we create temp holders and pass those | |
776 * instead. It's basically just to avoid segfaults. Of course, if the | |
777 * data is binary, plugins don't see it. Bitch all you want; i really | |
778 * don't want you to be dealing with it. | |
779 */ | |
780 | |
781 buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG)); | |
782 strcpy(buffy, message); | |
783 angel = g_strdup(who); | |
784 | |
785 plugin_return = GPOINTER_TO_INT( | |
786 gaim_signal_emit_return_1(gaim_conversations_get_handle(), | |
787 "receiving-chat-msg", g->account, | |
788 &angel, &buffy, conv, &flags)); | |
789 | |
790 if (!buffy || !angel || plugin_return) { | |
791 g_free(buffy); | |
792 g_free(angel); | |
793 return; | |
794 } | |
795 who = angel; | |
796 message = buffy; | |
797 | |
798 gaim_signal_emit(gaim_conversations_get_handle(), "received-chat-msg", g->account, | |
799 who, message, conv, flags); | |
800 | |
801 /* Make sure URLs are clickable */ | |
802 buf = gaim_markup_linkify(message); | |
803 | |
804 gaim_conv_chat_write(chat, who, buf, flags, mtime); | |
805 | |
806 g_free(angel); | |
807 g_free(buf); | |
808 g_free(buffy); | |
809 } | |
810 | |
811 void serv_send_file(GaimConnection *gc, const char *who, const char *file) | |
812 { | |
813 GaimPluginProtocolInfo *prpl_info = NULL; | |
814 | |
815 if (gc != NULL && gc->prpl != NULL) | |
816 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
817 | |
818 if (prpl_info && prpl_info->send_file) { | |
819 if (!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who)) { | |
820 prpl_info->send_file(gc, who, file); | |
821 } | |
822 } | |
823 } |