Mercurial > pidgin.yaz
comparison libgaim/server.c @ 14192:60b1bc8dbf37
[gaim-migrate @ 16863]
Renamed 'core' to 'libgaim'
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Sat, 19 Aug 2006 01:50:10 +0000 |
parents | |
children | 1f81919515ae |
comparison
equal
deleted
inserted
replaced
14191:009db0b357b5 | 14192:60b1bc8dbf37 |
---|---|
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 | |
176 if (prpl_info && g_list_find(gaim_connections_get_all(), gc) && | |
177 prpl_info->set_info) { | |
178 | |
179 account = gaim_connection_get_account(gc); | |
180 | |
181 if (gaim_signal_emit_return_1(gaim_accounts_get_handle(), | |
182 "account-setting-info", account, info)) | |
183 return; | |
184 | |
185 prpl_info->set_info(gc, info); | |
186 | |
187 gaim_signal_emit(gaim_accounts_get_handle(), | |
188 "account-set-info", account, info); | |
189 } | |
190 } | |
191 | |
192 /* | |
193 * Set buddy's alias on server roster/list | |
194 */ | |
195 void serv_alias_buddy(GaimBuddy *b) | |
196 { | |
197 GaimPluginProtocolInfo *prpl_info = NULL; | |
198 | |
199 if (b != NULL && b->account->gc->prpl != NULL) | |
200 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl); | |
201 | |
202 if (b && prpl_info && prpl_info->alias_buddy) { | |
203 prpl_info->alias_buddy(b->account->gc, b->name, b->alias); | |
204 } | |
205 } | |
206 | |
207 void | |
208 serv_got_alias(GaimConnection *gc, const char *who, const char *alias) | |
209 { | |
210 GaimAccount *account = gaim_connection_get_account(gc); | |
211 GSList *buds, *buddies = gaim_find_buddies(account, who); | |
212 GaimBuddy *b; | |
213 GaimConversation *conv; | |
214 | |
215 for (buds = buddies; buds; buds = buds->next) | |
216 { | |
217 b = buds->data; | |
218 gaim_blist_server_alias_buddy(b, alias); | |
219 | |
220 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, b->name, account); | |
221 | |
222 if (conv != NULL && b->server_alias != NULL && | |
223 strcmp(b->server_alias, alias)) | |
224 { | |
225 char *tmp = g_strdup_printf(_("%s is now known as %s.\n"), | |
226 who, alias); | |
227 | |
228 gaim_conversation_write(conv, NULL, tmp, GAIM_MESSAGE_SYSTEM, | |
229 time(NULL)); | |
230 | |
231 g_free(tmp); | |
232 } | |
233 } | |
234 g_slist_free(buddies); | |
235 } | |
236 | |
237 /* | |
238 * Move a buddy from one group to another on server. | |
239 * | |
240 * Note: For now we'll not deal with changing gc's at the same time, but | |
241 * it should be possible. Probably needs to be done, someday. Although, | |
242 * the UI for that would be difficult, because groups are Gaim-wide. | |
243 */ | |
244 void serv_move_buddy(GaimBuddy *b, GaimGroup *og, GaimGroup *ng) | |
245 { | |
246 GaimPluginProtocolInfo *prpl_info = NULL; | |
247 | |
248 g_return_if_fail(b != NULL); | |
249 g_return_if_fail(og != NULL); | |
250 g_return_if_fail(ng != NULL); | |
251 | |
252 if (b->account->gc != NULL && b->account->gc->prpl != NULL) | |
253 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl); | |
254 | |
255 if (b->account->gc && og && ng) { | |
256 if (prpl_info && prpl_info->group_buddy) { | |
257 prpl_info->group_buddy(b->account->gc, b->name, og->name, ng->name); | |
258 } | |
259 } | |
260 } | |
261 | |
262 void serv_add_permit(GaimConnection *g, const char *name) | |
263 { | |
264 GaimPluginProtocolInfo *prpl_info = NULL; | |
265 | |
266 if (g != NULL && g->prpl != NULL) | |
267 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
268 | |
269 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->add_permit) | |
270 prpl_info->add_permit(g, name); | |
271 } | |
272 | |
273 void serv_add_deny(GaimConnection *g, const char *name) | |
274 { | |
275 GaimPluginProtocolInfo *prpl_info = NULL; | |
276 | |
277 if (g != NULL && g->prpl != NULL) | |
278 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
279 | |
280 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->add_deny) | |
281 prpl_info->add_deny(g, name); | |
282 } | |
283 | |
284 void serv_rem_permit(GaimConnection *g, const char *name) | |
285 { | |
286 GaimPluginProtocolInfo *prpl_info = NULL; | |
287 | |
288 if (g != NULL && g->prpl != NULL) | |
289 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
290 | |
291 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->rem_permit) | |
292 prpl_info->rem_permit(g, name); | |
293 } | |
294 | |
295 void serv_rem_deny(GaimConnection *g, const char *name) | |
296 { | |
297 GaimPluginProtocolInfo *prpl_info = NULL; | |
298 | |
299 if (g != NULL && g->prpl != NULL) | |
300 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
301 | |
302 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->rem_deny) | |
303 prpl_info->rem_deny(g, name); | |
304 } | |
305 | |
306 void serv_set_permit_deny(GaimConnection *g) | |
307 { | |
308 GaimPluginProtocolInfo *prpl_info = NULL; | |
309 | |
310 if (g != NULL && g->prpl != NULL) | |
311 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
312 | |
313 /* | |
314 * this is called when either you import a buddy list, and make lots | |
315 * of changes that way, or when the user toggles the permit/deny mode | |
316 * in the prefs. In either case you should probably be resetting and | |
317 * resending the permit/deny info when you get this. | |
318 */ | |
319 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->set_permit_deny) | |
320 prpl_info->set_permit_deny(g); | |
321 } | |
322 | |
323 void serv_join_chat(GaimConnection *g, GHashTable *data) | |
324 { | |
325 GaimPluginProtocolInfo *prpl_info = NULL; | |
326 | |
327 if (g != NULL && g->prpl != NULL) | |
328 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
329 | |
330 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->join_chat) | |
331 prpl_info->join_chat(g, data); | |
332 } | |
333 | |
334 | |
335 void serv_reject_chat(GaimConnection *g, GHashTable *data) | |
336 { | |
337 GaimPluginProtocolInfo *prpl_info = NULL; | |
338 | |
339 if (g != NULL && g->prpl != NULL) | |
340 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
341 | |
342 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->reject_chat) | |
343 prpl_info->reject_chat(g, data); | |
344 } | |
345 | |
346 void serv_chat_invite(GaimConnection *g, int id, const char *message, const char *name) | |
347 { | |
348 GaimPluginProtocolInfo *prpl_info = NULL; | |
349 GaimConversation *conv; | |
350 char *buffy = message && *message ? g_strdup(message) : NULL; | |
351 | |
352 conv = gaim_find_chat(g, id); | |
353 | |
354 if (conv == NULL) | |
355 return; | |
356 | |
357 if (g != NULL && g->prpl != NULL) | |
358 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl); | |
359 | |
360 gaim_signal_emit(gaim_conversations_get_handle(), "chat-inviting-user", | |
361 conv, name, &buffy); | |
362 | |
363 if (prpl_info && g_list_find(gaim_connections_get_all(), g) && prpl_info->chat_invite) | |
364 prpl_info->chat_invite(g, id, buffy, name); | |
365 | |
366 gaim_signal_emit(gaim_conversations_get_handle(), "chat-invited-user", | |
367 conv, name, buffy); | |
368 | |
369 g_free(buffy); | |
370 } | |
371 | |
372 /* Ya know, nothing uses this except gaim_conversation_destroy(), | |
373 * I think I'll just merge it into that later... | |
374 * Then again, something might want to use this, from outside prpl-land | |
375 * to leave a chat without destroying the conversation. | |
376 */ | |
377 | |
378 void serv_chat_leave(GaimConnection *g, int id) | |
379 { | |
380 GaimPluginProtocolInfo *prpl_info = NULL; | |
381 | |
382 if (!g_list_find(gaim_connections_get_all(), g)) | |
383 return; | |
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 void serv_set_buddyicon(GaimConnection *gc, const char *filename) | |
418 { | |
419 GaimPluginProtocolInfo *prpl_info = NULL; | |
420 | |
421 if (gc->prpl != NULL) | |
422 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
423 | |
424 if (prpl_info && prpl_info->set_buddy_icon) | |
425 prpl_info->set_buddy_icon(gc, filename); | |
426 | |
427 } | |
428 | |
429 /* | |
430 * woo. i'm actually going to comment this function. isn't that fun. make | |
431 * sure to follow along, kids | |
432 */ | |
433 void serv_got_im(GaimConnection *gc, const char *who, const char *msg, | |
434 GaimMessageFlags flags, time_t mtime) | |
435 { | |
436 GaimAccount *account; | |
437 GaimConversation *cnv; | |
438 char *message, *name; | |
439 char *angel, *buffy; | |
440 int plugin_return; | |
441 | |
442 g_return_if_fail(msg != NULL); | |
443 | |
444 account = gaim_connection_get_account(gc); | |
445 | |
446 if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->set_permit_deny == NULL) { | |
447 /* protocol does not support privacy, handle it ourselves */ | |
448 if (!gaim_privacy_check(account, who)) | |
449 return; | |
450 } | |
451 | |
452 /* | |
453 * We should update the conversation window buttons and menu, | |
454 * if it exists. | |
455 */ | |
456 cnv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, who, gc->account); | |
457 | |
458 /* | |
459 * Plugin stuff. we pass a char ** but we don't want to pass what's | |
460 * been given us by the prpls. So we create temp holders and pass | |
461 * those instead. It's basically just to avoid segfaults. | |
462 */ | |
463 buffy = g_malloc(MAX(strlen(msg) + 1, BUF_LONG)); | |
464 strcpy(buffy, msg); | |
465 angel = g_strdup(who); | |
466 | |
467 plugin_return = GPOINTER_TO_INT( | |
468 gaim_signal_emit_return_1(gaim_conversations_get_handle(), | |
469 "receiving-im-msg", gc->account, | |
470 &angel, &buffy, cnv, &flags)); | |
471 | |
472 if (!buffy || !angel || plugin_return) { | |
473 g_free(buffy); | |
474 g_free(angel); | |
475 return; | |
476 } | |
477 | |
478 name = angel; | |
479 message = buffy; | |
480 | |
481 gaim_signal_emit(gaim_conversations_get_handle(), "received-im-msg", gc->account, | |
482 name, message, cnv, flags); | |
483 | |
484 /* search for conversation again in case it was created by received-im-msg handler */ | |
485 if (cnv == NULL) | |
486 cnv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, gc->account); | |
487 | |
488 /* Make sure URLs are clickable */ | |
489 buffy = gaim_markup_linkify(message); | |
490 g_free(message); | |
491 message = buffy; | |
492 | |
493 /* | |
494 * XXX: Should we be setting this here, or relying on prpls to set it? | |
495 */ | |
496 flags |= GAIM_MESSAGE_RECV; | |
497 | |
498 if (cnv == NULL) | |
499 cnv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, name); | |
500 | |
501 gaim_conv_im_write(GAIM_CONV_IM(cnv), NULL, message, flags, mtime); | |
502 g_free(message); | |
503 | |
504 /* | |
505 * Don't autorespond if: | |
506 * | |
507 * - it's not supported on this connection | |
508 * - we are available | |
509 * - or it's disabled | |
510 * - or we're not idle and the 'only auto respond if idle' pref | |
511 * is set | |
512 */ | |
513 if (gc->flags & GAIM_CONNECTION_AUTO_RESP) | |
514 { | |
515 GaimPresence *presence; | |
516 GaimStatus *status; | |
517 GaimStatusType *status_type; | |
518 GaimStatusPrimitive primitive; | |
519 const gchar *auto_reply_pref; | |
520 const char *away_msg = NULL; | |
521 | |
522 auto_reply_pref = gaim_prefs_get_string("/core/away/auto_reply"); | |
523 | |
524 presence = gaim_account_get_presence(account); | |
525 status = gaim_presence_get_active_status(presence); | |
526 status_type = gaim_status_get_type(status); | |
527 primitive = gaim_status_type_get_primitive(status_type); | |
528 if ((primitive == GAIM_STATUS_AVAILABLE) || | |
529 (primitive == GAIM_STATUS_INVISIBLE) || | |
530 (primitive == GAIM_STATUS_MOBILE) || | |
531 !strcmp(auto_reply_pref, "never") || | |
532 (!gaim_presence_is_idle(presence) && !strcmp(auto_reply_pref, "awayidle"))) | |
533 { | |
534 g_free(name); | |
535 return; | |
536 } | |
537 | |
538 away_msg = gaim_value_get_string( | |
539 gaim_status_get_attr_value(status, "message")); | |
540 | |
541 if ((away_msg != NULL) && (*away_msg != '\0')) { | |
542 struct last_auto_response *lar; | |
543 time_t now = time(NULL); | |
544 | |
545 /* | |
546 * This used to be based on the conversation window. But um, if | |
547 * you went away, and someone sent you a message and got your | |
548 * auto-response, and then you closed the window, and then they | |
549 * sent you another one, they'd get the auto-response back too | |
550 * soon. Besides that, we need to keep track of this even if we've | |
551 * got a queue. So the rest of this block is just the auto-response, | |
552 * if necessary. | |
553 */ | |
554 lar = get_last_auto_response(gc, name); | |
555 if ((now - lar->sent) >= SECS_BEFORE_RESENDING_AUTORESPONSE) | |
556 { | |
557 /* | |
558 * We don't want to send an autoresponse in response to the other user's | |
559 * autoresponse. We do, however, not want to then send one in response to the | |
560 * _next_ message, so we still set lar->sent to now. | |
561 */ | |
562 lar->sent = now; | |
563 | |
564 if (!(flags & GAIM_MESSAGE_AUTO_RESP)) | |
565 { | |
566 serv_send_im(gc, name, away_msg, GAIM_MESSAGE_AUTO_RESP); | |
567 | |
568 gaim_conv_im_write(GAIM_CONV_IM(cnv), NULL, away_msg, | |
569 GAIM_MESSAGE_SEND | GAIM_MESSAGE_AUTO_RESP, | |
570 mtime); | |
571 } | |
572 } | |
573 } | |
574 } | |
575 | |
576 g_free(name); | |
577 } | |
578 | |
579 void serv_got_typing(GaimConnection *gc, const char *name, int timeout, | |
580 GaimTypingState state) { | |
581 GaimConversation *conv; | |
582 GaimConvIm *im = NULL; | |
583 | |
584 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, gc->account); | |
585 if (conv != NULL) { | |
586 im = GAIM_CONV_IM(conv); | |
587 | |
588 gaim_conv_im_set_typing_state(im, state); | |
589 gaim_conv_im_update_typing(im); | |
590 } else { | |
591 if (state == GAIM_TYPING) | |
592 { | |
593 gaim_signal_emit(gaim_conversations_get_handle(), | |
594 "buddy-typing", gc->account, name); | |
595 } | |
596 else | |
597 { | |
598 gaim_signal_emit(gaim_conversations_get_handle(), | |
599 "buddy-typed", gc->account, name); | |
600 } | |
601 } | |
602 | |
603 if (conv != NULL && timeout > 0) | |
604 gaim_conv_im_start_typing_timeout(im, timeout); | |
605 } | |
606 | |
607 void serv_got_typing_stopped(GaimConnection *gc, const char *name) { | |
608 | |
609 GaimConversation *conv; | |
610 GaimConvIm *im; | |
611 | |
612 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, gc->account); | |
613 if (conv != NULL) | |
614 { | |
615 im = GAIM_CONV_IM(conv); | |
616 | |
617 if (im->typing_state == GAIM_NOT_TYPING) | |
618 return; | |
619 | |
620 gaim_conv_im_stop_typing_timeout(im); | |
621 gaim_conv_im_set_typing_state(im, GAIM_NOT_TYPING); | |
622 gaim_conv_im_update_typing(im); | |
623 } | |
624 else | |
625 { | |
626 gaim_signal_emit(gaim_conversations_get_handle(), | |
627 "buddy-typing-stopped", gc->account, name); | |
628 } | |
629 } | |
630 | |
631 struct chat_invite_data { | |
632 GaimConnection *gc; | |
633 GHashTable *components; | |
634 }; | |
635 | |
636 static void chat_invite_data_free(struct chat_invite_data *cid) | |
637 { | |
638 if (cid->components) | |
639 g_hash_table_destroy(cid->components); | |
640 g_free(cid); | |
641 } | |
642 | |
643 | |
644 static void chat_invite_reject(struct chat_invite_data *cid) | |
645 { | |
646 serv_reject_chat(cid->gc, cid->components); | |
647 chat_invite_data_free(cid); | |
648 } | |
649 | |
650 | |
651 static void chat_invite_accept(struct chat_invite_data *cid) | |
652 { | |
653 serv_join_chat(cid->gc, cid->components); | |
654 chat_invite_data_free(cid); | |
655 } | |
656 | |
657 | |
658 | |
659 void serv_got_chat_invite(GaimConnection *gc, const char *name, | |
660 const char *who, const char *message, GHashTable *data) | |
661 { | |
662 GaimAccount *account; | |
663 char buf2[BUF_LONG]; | |
664 struct chat_invite_data *cid = g_new0(struct chat_invite_data, 1); | |
665 int plugin_return; | |
666 | |
667 account = gaim_connection_get_account(gc); | |
668 if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->set_permit_deny == NULL) { | |
669 /* protocol does not support privacy, handle it ourselves */ | |
670 if (!gaim_privacy_check(account, who)) | |
671 return; | |
672 } | |
673 | |
674 plugin_return = GPOINTER_TO_INT(gaim_signal_emit_return_1( | |
675 gaim_conversations_get_handle(), | |
676 "chat-invited", account, who, name, message, data)); | |
677 | |
678 cid->gc = gc; | |
679 cid->components = data; | |
680 | |
681 if (plugin_return == 0) | |
682 { | |
683 if (message != NULL) | |
684 { | |
685 g_snprintf(buf2, sizeof(buf2), | |
686 _("%s has invited %s to the chat room %s:\n%s"), | |
687 who, gaim_account_get_username(account), name, message); | |
688 } | |
689 else | |
690 g_snprintf(buf2, sizeof(buf2), | |
691 _("%s has invited %s to the chat room %s\n"), | |
692 who, gaim_account_get_username(account), name); | |
693 | |
694 | |
695 gaim_request_accept_cancel(gc, NULL, _("Accept chat invitation?"), buf2, | |
696 GAIM_DEFAULT_ACTION_NONE, cid, | |
697 G_CALLBACK(chat_invite_accept), | |
698 G_CALLBACK(chat_invite_reject)); | |
699 } | |
700 else if (plugin_return > 0) | |
701 chat_invite_accept(cid); | |
702 else | |
703 chat_invite_reject(cid); | |
704 } | |
705 | |
706 GaimConversation *serv_got_joined_chat(GaimConnection *gc, | |
707 int id, const char *name) | |
708 { | |
709 GaimConversation *conv; | |
710 GaimConvChat *chat; | |
711 GaimAccount *account; | |
712 | |
713 account = gaim_connection_get_account(gc); | |
714 | |
715 conv = gaim_conversation_new(GAIM_CONV_TYPE_CHAT, account, name); | |
716 chat = GAIM_CONV_CHAT(conv); | |
717 | |
718 if (!g_slist_find(gc->buddy_chats, conv)) | |
719 gc->buddy_chats = g_slist_append(gc->buddy_chats, conv); | |
720 | |
721 gaim_conv_chat_set_id(chat, id); | |
722 | |
723 gaim_signal_emit(gaim_conversations_get_handle(), "chat-joined", conv); | |
724 | |
725 return conv; | |
726 } | |
727 | |
728 void serv_got_chat_left(GaimConnection *g, int id) | |
729 { | |
730 GSList *bcs; | |
731 GaimConversation *conv = NULL; | |
732 GaimConvChat *chat = NULL; | |
733 | |
734 for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) { | |
735 conv = (GaimConversation *)bcs->data; | |
736 | |
737 chat = GAIM_CONV_CHAT(conv); | |
738 | |
739 if (gaim_conv_chat_get_id(chat) == id) | |
740 break; | |
741 | |
742 conv = NULL; | |
743 } | |
744 | |
745 if (!conv) | |
746 return; | |
747 | |
748 gaim_debug(GAIM_DEBUG_INFO, "server", "Leaving room: %s\n", | |
749 gaim_conversation_get_name(conv)); | |
750 | |
751 g->buddy_chats = g_slist_remove(g->buddy_chats, conv); | |
752 | |
753 gaim_conv_chat_left(GAIM_CONV_CHAT(conv)); | |
754 | |
755 gaim_signal_emit(gaim_conversations_get_handle(), "chat-left", conv); | |
756 } | |
757 | |
758 void serv_got_chat_in(GaimConnection *g, int id, const char *who, | |
759 GaimMessageFlags flags, const char *message, time_t mtime) | |
760 { | |
761 GSList *bcs; | |
762 GaimConversation *conv = NULL; | |
763 GaimConvChat *chat = NULL; | |
764 char *buf; | |
765 char *buffy, *angel; | |
766 int plugin_return; | |
767 | |
768 g_return_if_fail(who != NULL); | |
769 g_return_if_fail(message != NULL); | |
770 | |
771 for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) { | |
772 conv = (GaimConversation *)bcs->data; | |
773 | |
774 chat = GAIM_CONV_CHAT(conv); | |
775 | |
776 if (gaim_conv_chat_get_id(chat) == id) | |
777 break; | |
778 | |
779 conv = NULL; | |
780 } | |
781 | |
782 if (!conv) | |
783 return; | |
784 | |
785 /* | |
786 * Plugin stuff. We pass a char ** but we don't want to pass what's | |
787 * been given us by the prpls. so we create temp holders and pass those | |
788 * instead. It's basically just to avoid segfaults. Of course, if the | |
789 * data is binary, plugins don't see it. Bitch all you want; i really | |
790 * don't want you to be dealing with it. | |
791 */ | |
792 | |
793 buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG)); | |
794 strcpy(buffy, message); | |
795 angel = g_strdup(who); | |
796 | |
797 plugin_return = GPOINTER_TO_INT( | |
798 gaim_signal_emit_return_1(gaim_conversations_get_handle(), | |
799 "receiving-chat-msg", g->account, | |
800 &angel, &buffy, conv, &flags)); | |
801 | |
802 if (!buffy || !angel || plugin_return) { | |
803 g_free(buffy); | |
804 g_free(angel); | |
805 return; | |
806 } | |
807 who = angel; | |
808 message = buffy; | |
809 | |
810 gaim_signal_emit(gaim_conversations_get_handle(), "received-chat-msg", g->account, | |
811 who, message, conv, flags); | |
812 | |
813 /* Make sure URLs are clickable */ | |
814 buf = gaim_markup_linkify(message); | |
815 | |
816 gaim_conv_chat_write(chat, who, buf, flags, mtime); | |
817 | |
818 g_free(angel); | |
819 g_free(buf); | |
820 g_free(buffy); | |
821 } | |
822 | |
823 void serv_send_file(GaimConnection *gc, const char *who, const char *file) | |
824 { | |
825 GaimPluginProtocolInfo *prpl_info = NULL; | |
826 | |
827 if (gc != NULL && gc->prpl != NULL) | |
828 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
829 | |
830 if (prpl_info && prpl_info->send_file) { | |
831 if (!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who)) { | |
832 prpl_info->send_file(gc, who, file); | |
833 } | |
834 } | |
835 } |