[gaim-migrate @ 5891]
[pidgin-git.git] / src / server.c
blob055c9044e76bfb18019bed2675f1eccdcc82e133
1 /*
2 * gaim
4 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <time.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/time.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include "gtkimhtml.h"
35 #include "prpl.h"
36 #include "multi.h"
37 #include "gaim.h"
38 #include "sound.h"
39 #include "pounce.h"
40 #include "notify.h"
42 void serv_login(struct gaim_account *account)
44 GaimPlugin *p = gaim_find_prpl(account->protocol);
45 GaimPluginProtocolInfo *prpl_info = NULL;
47 if (account->gc != NULL || p == NULL)
48 return;
50 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(p);
52 if (prpl_info->login) {
53 if (!strlen(account->password) && !(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
54 !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) {
55 gaim_notify_error(NULL, NULL,
56 _("Please enter your password"), NULL);
57 return;
60 gaim_debug(GAIM_DEBUG_INFO, "server",
61 PACKAGE " " VERSION " logging in %s using %s\n",
62 account->username, p->info->name);
63 account->connecting = TRUE;
64 connecting_count++;
65 gaim_debug(GAIM_DEBUG_MISC, "server",
66 "connection count: %d\n", connecting_count);
67 gaim_event_broadcast(event_connecting, account);
68 prpl_info->login(account);
72 static gboolean send_keepalive(gpointer d)
74 struct gaim_connection *gc = d;
75 GaimPluginProtocolInfo *prpl_info = NULL;
77 if (gc != NULL && gc->prpl != NULL)
78 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
80 if (prpl_info && prpl_info->keepalive)
81 prpl_info->keepalive(gc);
83 return TRUE;
86 static void update_keepalive(struct gaim_connection *gc, gboolean on)
88 if (on && !gc->keepalive) {
89 gaim_debug(GAIM_DEBUG_INFO, "server", "allowing NOP\n");
90 gc->keepalive = g_timeout_add(60000, send_keepalive, gc);
91 } else if (!on && gc->keepalive > 0) {
92 gaim_debug(GAIM_DEBUG_INFO, "server", "removing NOP\n");
93 g_source_remove(gc->keepalive);
94 gc->keepalive = 0;
98 void serv_close(struct gaim_connection *gc)
100 GaimPlugin *prpl;
101 GaimPluginProtocolInfo *prpl_info = NULL;
103 while (gc->buddy_chats) {
104 struct gaim_conversation *b = gc->buddy_chats->data;
106 gc->buddy_chats = g_slist_remove(gc->buddy_chats, b);
108 /* TODO: Nuke the UI-specific code here. */
109 if (GAIM_IS_GTK_CONVERSATION(b))
110 gaim_gtkconv_update_buttons_by_protocol(b);
113 if (gc->idle_timer > 0)
114 g_source_remove(gc->idle_timer);
115 gc->idle_timer = 0;
117 update_keepalive(gc, FALSE);
119 if (gc->prpl != NULL) {
120 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
122 if (prpl_info->close)
123 prpl_info->close(gc);
126 prpl = gc->prpl;
127 account_offline(gc);
128 destroy_gaim_conn(gc);
131 void serv_touch_idle(struct gaim_connection *gc)
133 /* Are we idle? If so, not anymore */
134 if (gc->is_idle > 0) {
135 gc->is_idle = 0;
136 serv_set_idle(gc, 0);
138 time(&gc->lastsent);
139 if (gc->is_auto_away)
140 check_idle(gc);
143 void serv_finish_login(struct gaim_connection *gc)
145 GaimPluginProtocolInfo *prpl_info = NULL;
147 if (gc != NULL && gc->prpl != NULL)
148 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
150 if (strlen(gc->account->user_info)) {
151 /* g_malloc(strlen(gc->user->user_info) * 4);
152 strncpy_withhtml(buf, gc->user->user_info, strlen(gc->user->user_info) * 4); */
153 serv_set_info(gc, gc->account->user_info);
154 /* g_free(buf); */
157 if (gc->idle_timer > 0)
158 g_source_remove(gc->idle_timer);
160 gc->idle_timer = g_timeout_add(20000, check_idle, gc);
161 serv_touch_idle(gc);
163 if (prpl_info->options & OPT_PROTO_CORRECT_TIME)
164 serv_add_buddy(gc, gc->username);
166 update_keepalive(gc, TRUE);
169 /* This should return the elapsed time in seconds in which Gaim will not send
170 * typing notifications.
171 * if it returns zero, it will not send any more typing notifications
172 * typing is a flag - TRUE for typing, FALSE for stopped typing */
173 int serv_send_typing(struct gaim_connection *g, char *name, int typing) {
174 GaimPluginProtocolInfo *prpl_info = NULL;
176 if (g != NULL && g->prpl != NULL)
177 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
179 if (g && prpl_info && prpl_info->send_typing)
180 return prpl_info->send_typing(g, name, typing);
182 return 0;
185 struct queued_away_response {
186 char name[80];
187 time_t sent_away;
190 struct queued_away_response *find_queued_away_response_by_name(char *name);
192 int serv_send_im(struct gaim_connection *gc, char *name, char *message,
193 int len, int flags)
195 struct gaim_conversation *c;
196 int val = -EINVAL;
197 GaimPluginProtocolInfo *prpl_info = NULL;
199 if (gc != NULL && gc->prpl != NULL)
200 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
202 c = gaim_find_conversation(name);
204 if (prpl_info && prpl_info->send_im)
205 val = prpl_info->send_im(gc, name, message, len, flags);
207 if (!(flags & IM_FLAG_AWAY))
208 serv_touch_idle(gc);
210 if (gc->away && away_options & OPT_AWAY_DELAY_IN_USE &&
211 !(away_options & OPT_AWAY_NO_AUTO_RESP)) {
212 time_t t;
213 struct queued_away_response *qar;
214 time(&t);
215 qar = find_queued_away_response_by_name(name);
216 if (!qar) {
217 qar = (struct queued_away_response *)g_new0(struct queued_away_response, 1);
218 g_snprintf(qar->name, sizeof(qar->name), "%s", name);
219 qar->sent_away = 0;
220 away_time_queue = g_slist_append(away_time_queue, qar);
222 qar->sent_away = t;
225 if (c && gaim_im_get_type_again_timeout(GAIM_IM(c)))
226 gaim_im_stop_type_again_timeout(GAIM_IM(c));
228 return val;
231 void serv_get_info(struct gaim_connection *g, char *name)
233 GaimPluginProtocolInfo *prpl_info = NULL;
235 if (g != NULL && g->prpl != NULL)
236 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
238 if (g && prpl_info && prpl_info->get_info)
239 prpl_info->get_info(g, name);
242 void serv_get_away(struct gaim_connection *g, const char *name)
244 GaimPluginProtocolInfo *prpl_info = NULL;
246 if (g != NULL && g->prpl != NULL)
247 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
249 if (g && prpl_info && prpl_info->get_away)
250 prpl_info->get_away(g, name);
253 void serv_get_dir(struct gaim_connection *g, char *name)
255 GaimPluginProtocolInfo *prpl_info = NULL;
257 if (g != NULL && g->prpl != NULL)
258 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
260 if (prpl_info && g_slist_find(connections, g) && prpl_info->get_dir)
261 prpl_info->get_dir(g, name);
264 void serv_set_dir(struct gaim_connection *g, const char *first,
265 const char *middle, const char *last, const char *maiden,
266 const char *city, const char *state, const char *country,
267 int web)
269 GaimPluginProtocolInfo *prpl_info = NULL;
271 if (g != NULL && g->prpl != NULL)
272 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
274 if (prpl_info && g_slist_find(connections, g) && prpl_info->set_dir)
275 prpl_info->set_dir(g, first, middle, last, maiden, city, state,
276 country, web);
279 void serv_dir_search(struct gaim_connection *g, const char *first,
280 const char *middle, const char *last, const char *maiden,
281 const char *city, const char *state, const char *country,
282 const char *email)
284 GaimPluginProtocolInfo *prpl_info = NULL;
286 if (g != NULL && g->prpl != NULL)
287 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
289 if (prpl_info && g_slist_find(connections, g) && prpl_info->dir_search)
290 prpl_info->dir_search(g, first, middle, last, maiden, city, state,
291 country, email);
295 void serv_set_away(struct gaim_connection *gc, char *state, char *message)
297 GaimPluginProtocolInfo *prpl_info = NULL;
299 if (gc->away_state == NULL && state == NULL &&
300 gc->away == NULL && message == NULL) {
302 return;
305 if ((gc->away_state != NULL && state != NULL &&
306 !strcmp(gc->away_state, state) &&
307 !strcmp(gc->away_state, GAIM_AWAY_CUSTOM)) &&
308 (gc->away != NULL && message != NULL && !strcmp(gc->away, message))) {
310 return;
313 if (gc != NULL && gc->prpl != NULL)
314 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
316 if (prpl_info && prpl_info->set_away) {
317 char *buf = NULL;
319 if (gc->away_state) {
320 g_free(gc->away_state);
321 gc->away_state = NULL;
324 if (message) {
325 buf = g_malloc(strlen(message) * 4 + 1);
326 if (gc->flags & OPT_CONN_HTML)
327 strncpy_withhtml(buf, message, strlen(message) * 4 + 1);
328 else
329 strncpy_nohtml(buf, message, strlen(message) + 1);
332 prpl_info->set_away(gc, state, buf);
334 if (gc->away && state) {
335 gc->away_state = g_strdup(state);
338 gaim_event_broadcast(event_away, gc, state, buf);
340 if (buf)
341 g_free(buf);
344 system_log(log_away, gc, NULL, OPT_LOG_BUDDY_AWAY | OPT_LOG_MY_SIGNON);
347 void serv_set_away_all(char *message)
349 GSList *c;
350 struct gaim_connection *g;
352 for (c = connections; c != NULL; c = c->next) {
353 g = (struct gaim_connection *)c->data;
355 serv_set_away(g, GAIM_AWAY_CUSTOM, message);
359 void serv_set_info(struct gaim_connection *g, char *info)
361 GaimPluginProtocolInfo *prpl_info = NULL;
363 if (g != NULL && g->prpl != NULL)
364 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
366 if (prpl_info && g_slist_find(connections, g) && prpl_info->set_info) {
367 if (gaim_event_broadcast(event_set_info, g, info))
368 return;
370 prpl_info->set_info(g, info);
374 void serv_change_passwd(struct gaim_connection *g, const char *orig, const char *new)
376 GaimPluginProtocolInfo *prpl_info = NULL;
378 if (g != NULL && g->prpl != NULL)
379 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
381 if (prpl_info && g_slist_find(connections, g) && prpl_info->change_passwd)
382 prpl_info->change_passwd(g, orig, new);
385 void serv_add_buddy(struct gaim_connection *g, const char *name)
387 GaimPluginProtocolInfo *prpl_info = NULL;
389 if (g != NULL && g->prpl != NULL)
390 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
392 if (prpl_info && g_slist_find(connections, g) && prpl_info->add_buddy)
393 prpl_info->add_buddy(g, name);
396 void serv_add_buddies(struct gaim_connection *g, GList *buddies)
398 GaimPluginProtocolInfo *prpl_info = NULL;
400 if (g != NULL && g->prpl != NULL)
401 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
403 if (prpl_info && g_slist_find(connections, g)) {
404 if (prpl_info->add_buddies)
405 prpl_info->add_buddies(g, buddies);
406 else if (prpl_info->add_buddy) {
407 while (buddies) {
408 prpl_info->add_buddy(g, buddies->data);
409 buddies = buddies->next;
416 void serv_remove_buddy(struct gaim_connection *g, char *name, char *group)
418 GaimPluginProtocolInfo *prpl_info = NULL;
420 if (g != NULL && g->prpl != NULL)
421 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
423 if (prpl_info && g_slist_find(connections, g) && prpl_info->remove_buddy)
424 prpl_info->remove_buddy(g, name, group);
427 void serv_remove_buddies(struct gaim_connection *gc, GList *g, char *group)
429 GaimPluginProtocolInfo *prpl_info = NULL;
431 if (!g_slist_find(connections, gc))
432 return;
434 if (!gc->prpl)
435 return; /* how the hell did that happen? */
437 if (gc != NULL && gc->prpl != NULL)
438 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
440 if (prpl_info->remove_buddies)
441 prpl_info->remove_buddies(gc, g, group);
442 else {
443 while (g) {
444 serv_remove_buddy(gc, g->data, group);
445 g = g->next;
451 * Set buddy's alias on server roster/list
453 void serv_alias_buddy(struct buddy *b)
455 GaimPluginProtocolInfo *prpl_info = NULL;
457 if (b != NULL && b->account->gc->prpl != NULL)
458 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl);
460 if (b && prpl_info && prpl_info->alias_buddy) {
461 prpl_info->alias_buddy(b->account->gc, b->name, b->alias);
465 void serv_got_alias(struct gaim_connection *gc, char *who, char *alias) {
466 struct buddy *b = gaim_find_buddy(gc->account, who);
467 if(!b)
468 return;
470 if (b->server_alias)
471 g_free(b->server_alias);
473 if(alias)
474 b->server_alias = g_strdup(alias);
475 else
476 b->server_alias = NULL;
478 gaim_blist_update_buddy_status(b, b->uc);
482 * Move a buddy from one group to another on server.
484 * Note: For now we'll not deal with changing gc's at the same time, but
485 * it should be possible. Probably needs to be done, someday.
487 void serv_move_buddy(struct buddy *b, struct group *og, struct group *ng)
489 GaimPluginProtocolInfo *prpl_info = NULL;
491 if (b->account->gc != NULL && b->account->gc->prpl != NULL)
492 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(b->account->gc->prpl);
494 if (b && b->account->gc && og && ng) {
495 if (prpl_info && prpl_info->group_buddy) {
496 prpl_info->group_buddy(b->account->gc, b->name, og->name, ng->name);
502 * Rename a group on server roster/list.
504 void serv_rename_group(struct gaim_connection *g, struct group *old_group,
505 const char *new_name)
507 GaimPluginProtocolInfo *prpl_info = NULL;
509 if (g != NULL && g->prpl != NULL)
510 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
512 if (prpl_info && old_group && new_name) {
513 GList *tobemoved = NULL;
514 GaimBlistNode *b = ((GaimBlistNode*)old_group)->child;
516 while (b) {
517 if(GAIM_BLIST_NODE_IS_BUDDY(b)) {
518 struct buddy *bd = (struct buddy *)b;
519 if (bd->account == g->account)
520 tobemoved = g_list_append(tobemoved, bd->name);
522 b = b->next;
525 if (prpl_info->rename_group) {
526 /* prpl's might need to check if the group already
527 * exists or not, and handle that differently */
528 prpl_info->rename_group(g, old_group->name, new_name, tobemoved);
529 } else {
530 serv_remove_buddies(g, tobemoved, old_group->name);
531 serv_add_buddies(g, tobemoved);
534 g_list_free(tobemoved);
538 void serv_add_permit(struct gaim_connection *g, const char *name)
540 GaimPluginProtocolInfo *prpl_info = NULL;
542 if (g != NULL && g->prpl != NULL)
543 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
545 if (prpl_info && g_slist_find(connections, g) && prpl_info->add_permit)
546 prpl_info->add_permit(g, name);
549 void serv_add_deny(struct gaim_connection *g, const char *name)
551 GaimPluginProtocolInfo *prpl_info = NULL;
553 if (g != NULL && g->prpl != NULL)
554 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
556 if (prpl_info && g_slist_find(connections, g) && prpl_info->add_deny)
557 prpl_info->add_deny(g, name);
560 void serv_rem_permit(struct gaim_connection *g, const char *name)
562 GaimPluginProtocolInfo *prpl_info = NULL;
564 if (g != NULL && g->prpl != NULL)
565 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
567 if (prpl_info && g_slist_find(connections, g) && prpl_info->rem_permit)
568 prpl_info->rem_permit(g, name);
571 void serv_rem_deny(struct gaim_connection *g, const char *name)
573 GaimPluginProtocolInfo *prpl_info = NULL;
575 if (g != NULL && g->prpl != NULL)
576 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
578 if (prpl_info && g_slist_find(connections, g) && prpl_info->rem_deny)
579 prpl_info->rem_deny(g, name);
582 void serv_set_permit_deny(struct gaim_connection *g)
584 GaimPluginProtocolInfo *prpl_info = NULL;
586 if (g != NULL && g->prpl != NULL)
587 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
590 * this is called when either you import a buddy list, and make lots
591 * of changes that way, or when the user toggles the permit/deny mode
592 * in the prefs. In either case you should probably be resetting and
593 * resending the permit/deny info when you get this.
595 if (prpl_info && g_slist_find(connections, g) && prpl_info->set_permit_deny)
596 prpl_info->set_permit_deny(g);
600 void serv_set_idle(struct gaim_connection *g, int time)
602 GaimPluginProtocolInfo *prpl_info = NULL;
604 if (g != NULL && g->prpl != NULL)
605 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
607 if (prpl_info && g_slist_find(connections, g) && prpl_info->set_idle)
608 prpl_info->set_idle(g, time);
611 void serv_warn(struct gaim_connection *g, char *name, int anon)
613 GaimPluginProtocolInfo *prpl_info = NULL;
615 if (g != NULL && g->prpl != NULL)
616 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
618 if (prpl_info && g_slist_find(connections, g) && prpl_info->warn)
619 prpl_info->warn(g, name, anon);
622 void serv_join_chat(struct gaim_connection *g, GHashTable *data)
624 GaimPluginProtocolInfo *prpl_info = NULL;
626 if (g != NULL && g->prpl != NULL)
627 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
629 if (prpl_info && g_slist_find(connections, g) && prpl_info->join_chat)
630 prpl_info->join_chat(g, data);
633 void serv_chat_invite(struct gaim_connection *g, int id, const char *message, const char *name)
635 GaimPluginProtocolInfo *prpl_info = NULL;
636 char *buffy = message && *message ? g_strdup(message) : NULL;
638 if (g != NULL && g->prpl != NULL)
639 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
641 gaim_event_broadcast(event_chat_send_invite, g, (void *)id, name, &buffy);
643 if (prpl_info && g_slist_find(connections, g) && prpl_info->chat_invite)
644 prpl_info->chat_invite(g, id, buffy, name);
646 if (buffy)
647 g_free(buffy);
650 void serv_chat_leave(struct gaim_connection *g, int id)
652 GaimPluginProtocolInfo *prpl_info = NULL;
654 if (!g_slist_find(connections, g))
655 return;
657 if (g->prpl != NULL)
658 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
660 if (prpl_info && prpl_info->chat_leave)
661 prpl_info->chat_leave(g, id);
664 void serv_chat_whisper(struct gaim_connection *g, int id, char *who, char *message)
666 GaimPluginProtocolInfo *prpl_info = NULL;
668 if (g != NULL && g->prpl != NULL)
669 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
671 if (prpl_info && prpl_info->chat_whisper)
672 prpl_info->chat_whisper(g, id, who, message);
675 int serv_chat_send(struct gaim_connection *g, int id, char *message)
677 int val = -EINVAL;
678 GaimPluginProtocolInfo *prpl_info = NULL;
680 if (g->prpl != NULL)
681 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(g->prpl);
683 if (prpl_info && prpl_info->chat_send)
684 val = prpl_info->chat_send(g, id, message);
686 serv_touch_idle(g);
688 return val;
691 int find_queue_row_by_name(char *name)
693 gchar *temp;
694 gint i = 0;
695 gboolean valid;
696 GtkTreeIter iter;
698 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(awayqueuestore), &iter);
699 while(valid) {
700 gtk_tree_model_get(GTK_TREE_MODEL(awayqueuestore), &iter, 0, &temp, -1);
701 if(!strcmp(name, temp))
702 return i;
703 g_free(temp);
705 i++;
706 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(awayqueuestore), &iter);
709 return -1;
712 int find_queue_total_by_name(char *name)
714 GSList *templist;
715 int i = 0;
717 templist = message_queue;
719 while (templist) {
720 struct queued_message *qm = (struct queued_message *)templist->data;
721 if ((qm->flags & WFLAG_RECV) && !strcmp(name, qm->name))
722 i++;
724 templist = templist->next;
727 return i;
730 struct queued_away_response *find_queued_away_response_by_name(char *name)
732 GSList *templist;
733 struct queued_away_response *qar;
735 templist = away_time_queue;
737 while (templist) {
738 qar = (struct queued_away_response *)templist->data;
740 if (!strcmp(name, qar->name))
741 return qar;
743 templist = templist->next;
746 return NULL;
750 * woo. i'm actually going to comment this function. isn't that fun. make
751 * sure to follow along, kids
753 void serv_got_im(struct gaim_connection *gc, const char *who, const char *msg,
754 guint32 flags, time_t mtime, gint len)
756 char *buffy;
757 char *angel;
758 int plugin_return;
759 int away = 0;
761 struct gaim_conversation *cnv;
763 char *message, *name;
766 * Pay no attention to the man behind the curtain.
768 * The reason i feel okay with this is because it's useful to some
769 * plugins. Gaim doesn't ever use it itself. Besides, it's not entirely
770 * accurate; it's possible to have false negatives with most protocols.
771 * Also with some it's easy to have false positives as well. So if you're
772 * a plugin author, don't rely on this, still do your own checks. But uh.
773 * It's a start.
776 if (flags & IM_FLAG_GAIMUSER)
777 gaim_debug(GAIM_DEBUG_MISC, "server", "%s is a gaim user.\n", who);
780 * We should update the conversation window buttons and menu,
781 * if it exists.
783 cnv = gaim_find_conversation_with_account(who, gc->account);
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
788 * those instead. It's basically just to avoid segfaults. Of course,
789 * if the data is binary, plugins don't see it. Bitch all you want;
790 * I really don't want you to be dealing with it.
792 if (len < 0) {
793 buffy = g_malloc(MAX(strlen(msg) + 1, BUF_LONG));
794 strcpy(buffy, msg);
795 angel = g_strdup(who);
796 plugin_return = gaim_event_broadcast(event_im_recv, gc, &angel, &buffy, &flags);
798 if (!buffy || !angel || plugin_return) {
799 if (buffy)
800 g_free(buffy);
801 if (angel)
802 g_free(angel);
803 return;
805 name = angel;
806 message = buffy;
807 } else {
808 name = g_strdup(who);
809 message = g_memdup(msg, len);
813 * If you can't figure this out, stop reading right now.
814 * "We're not worthy! We're not worthy!"
816 if ((len < 0) && (convo_options & OPT_CONVO_SEND_LINKS)) {
817 buffy = linkify_text(message);
818 g_free(message);
819 message = buffy;
823 * Um. When we call gaim_conversation_write with the message we received,
824 * it's nice to pass whether or not it was an auto-response. So if it
825 * was an auto-response, we set the appropriate flag. This is just so
826 * prpls don't have to know about WFLAG_* (though some do anyway)
828 if (flags & IM_FLAG_AWAY)
829 away = WFLAG_AUTO;
832 * Alright. Two cases for how to handle this. Either we're away or
833 * we're not. If we're not, then it's easy. If we are, then there
834 * are three or four different ways of handling it and different
835 * things we have to do for each.
837 if (gc->away) {
838 time_t t;
839 char *tmpmsg;
840 struct buddy *b = gaim_find_buddy(gc->account, name);
841 char *alias = b ? gaim_get_buddy_alias(b) : name;
842 int row;
843 struct queued_away_response *qar;
845 time(&t);
848 * Either we're going to queue it or not. Because of the way
849 * awayness currently works, this is fucked up. It's possible
850 * for an account to be away without the imaway dialog being
851 * shown. In fact, it's possible for *all* the accounts to be
852 * away without the imaway dialog being shown. So in order for
853 * this to be queued properly, we have to make sure that the
854 * imaway dialog actually exists, first.
856 if (!cnv && awayqueue && (away_options & OPT_AWAY_QUEUE)) {
858 * Alright, so we're going to queue it. Neat, eh? :)
859 * So first we create something to store the message, and add
860 * it to our queue. Then we update the away dialog to indicate
861 * that we've queued something.
863 struct queued_message *qm;
864 GtkTreeIter iter;
865 gchar path[10];
867 qm = g_new0(struct queued_message, 1);
868 g_snprintf(qm->name, sizeof(qm->name), "%s", name);
869 qm->message = g_memdup(message, len == -1 ? strlen(message) + 1 : len);
870 qm->account = gc->account;
871 qm->tm = mtime;
872 qm->flags = WFLAG_RECV | away;
873 qm->len = len;
874 message_queue = g_slist_append(message_queue, qm);
876 row = find_queue_row_by_name(qm->name);
877 if (row >= 0) {
878 char number[32];
879 int qtotal;
881 qtotal = find_queue_total_by_name(qm->name);
882 g_snprintf(number, 32, _("(%d messages)"), qtotal);
883 g_snprintf(path, 10, "%d", row);
884 gtk_tree_model_get_iter_from_string(
885 GTK_TREE_MODEL(awayqueuestore), &iter, path);
886 gtk_list_store_set(awayqueuestore, &iter,
887 1, number, -1);
888 } else {
889 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(awayqueuestore),
890 &iter);
891 gtk_list_store_append(awayqueuestore, &iter);
892 gtk_list_store_set(awayqueuestore, &iter,
893 0, qm->name,
894 1, _("(1 message)"),
895 -1);
897 } else {
899 * Make sure the conversation
900 * exists and is updated (partly handled above already), play
901 * the receive sound (sound.c will take care of not playing
902 * while away), and then write it to the convo window.
904 if (cnv == NULL)
905 cnv = gaim_conversation_new(GAIM_CONV_IM, gc->account, name);
907 gaim_im_write(GAIM_IM(cnv), NULL, message, len,
908 away | WFLAG_RECV, mtime);
912 * Regardless of whether we queue it or not, we should send an
913 * auto-response. That is, of course, unless the horse.... no wait.
914 * Don't autorespond if:
916 * - it's not supported on this connection
917 * - or it's disabled
918 * - or the away message is empty
919 * - or we're not idle and the 'only auto respond if idle' pref
920 * is set
922 if (!(gc->flags & OPT_CONN_AUTO_RESP) ||
923 (away_options & OPT_AWAY_NO_AUTO_RESP) || !strlen(gc->away) ||
924 ((away_options & OPT_AWAY_IDLE_RESP) && !gc->is_idle)) {
926 g_free(name);
927 g_free(message);
928 return;
932 * This used to be based on the conversation window. But um, if
933 * you went away, and someone sent you a message and got your
934 * auto-response, and then you closed the window, and then the
935 * sent you another one, they'd get the auto-response back too
936 * soon. Besides that, we need to keep track of this even if we've
937 * got a queue. So the rest of this block is just the auto-response,
938 * if necessary
940 qar = find_queued_away_response_by_name(name);
941 if (!qar) {
942 qar = (struct queued_away_response *)g_new0(struct queued_away_response, 1);
943 g_snprintf(qar->name, sizeof(qar->name), "%s", name);
944 qar->sent_away = 0;
945 away_time_queue = g_slist_append(away_time_queue, qar);
947 if ((t - qar->sent_away) < away_resend) {
948 g_free(name);
949 g_free(message);
950 return;
952 qar->sent_away = t;
954 /* apply default fonts and colors */
955 tmpmsg = stylize(gc->away, MSG_LEN);
956 serv_send_im(gc, name, away_subs(tmpmsg, alias), -1, IM_FLAG_AWAY);
957 if (!cnv && awayqueue && (away_options & OPT_AWAY_QUEUE)) {
958 struct queued_message *qm;
959 qm = g_new0(struct queued_message, 1);
960 g_snprintf(qm->name, sizeof(qm->name), "%s", name);
961 qm->message = g_strdup(away_subs(tmpmsg, alias));
962 qm->account = gc->account;
963 qm->tm = mtime;
964 qm->flags = WFLAG_SEND | WFLAG_AUTO;
965 qm->len = -1;
966 message_queue = g_slist_append(message_queue, qm);
967 } else if (cnv != NULL)
968 gaim_im_write(GAIM_IM(cnv), NULL, away_subs(tmpmsg, alias),
969 len, WFLAG_SEND | WFLAG_AUTO, mtime);
971 g_free(tmpmsg);
972 } else {
974 * We're not away. This is easy. If the convo window doesn't
975 * exist, create and update it (if it does exist it was updated
976 * earlier), then play a sound indicating we've received it and
977 * then display it. Easy.
979 if (away_options & OPT_AWAY_QUEUE_UNREAD &&
980 !gaim_find_conversation(name) && docklet_count) {
983 * We're gonna queue it up and wait for the user to ask for
984 * it... probably by clicking the docklet or windows tray icon.
986 struct queued_message *qm;
987 qm = g_new0(struct queued_message, 1);
988 g_snprintf(qm->name, sizeof(qm->name), "%s", name);
989 qm->message = g_strdup(message);
990 qm->account = gc->account;
991 qm->tm = mtime;
992 qm->flags = away | WFLAG_RECV;
993 qm->len = len;
994 unread_message_queue = g_slist_append(unread_message_queue, qm);
995 } else {
996 if (cnv == NULL)
997 cnv = gaim_conversation_new(GAIM_CONV_IM, gc->account, name);
999 /* CONV XXX gaim_conversation_set_name(cnv, name); */
1001 gaim_im_write(GAIM_IM(cnv), NULL, message, len,
1002 away | WFLAG_RECV, mtime);
1003 gaim_window_flash(gaim_conversation_get_window(cnv));
1007 gaim_event_broadcast(event_im_displayed_rcvd, gc, name, message, flags, mtime);
1008 g_free(name);
1009 g_free(message);
1014 void serv_got_update(struct gaim_connection *gc, char *name, int loggedin,
1015 int evil, time_t signon, time_t idle, int type)
1017 struct buddy *b = gaim_find_buddy(gc->account, name);
1019 if (signon && (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->options &
1020 OPT_PROTO_CORRECT_TIME)) {
1022 char *tmp = g_strdup(normalize(name));
1023 if (!gaim_utf8_strcasecmp(tmp, normalize(gc->username))) {
1024 gc->evil = evil;
1025 gc->login_time_official = signon;
1026 /*update_idle_times();*/
1028 g_free(tmp);
1031 if (!b) {
1032 gaim_debug(GAIM_DEBUG_ERROR, "server",
1033 "No such buddy: %s\n", name);
1034 return;
1037 /* This code will 'align' the name from the TOC */
1038 /* server with what's in our record. We want to */
1039 /* store things how THEY want it... */
1040 if (strcmp(name, b->name)) {
1041 gaim_blist_rename_buddy(b, name);
1042 gaim_blist_save();
1045 if (!b->idle && idle) {
1046 gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_IDLE);
1047 gaim_event_broadcast(event_buddy_idle, gc, b->name);
1048 system_log(log_idle, gc, b, OPT_LOG_BUDDY_IDLE);
1050 if (b->idle && !idle) {
1051 gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_IDLE_RETURN);
1052 gaim_event_broadcast(event_buddy_unidle, gc, b->name);
1053 system_log(log_unidle, gc, b, OPT_LOG_BUDDY_IDLE);
1056 gaim_blist_update_buddy_idle(b, idle);
1057 gaim_blist_update_buddy_evil(b, evil);
1059 if ((b->uc & UC_UNAVAILABLE) && !(type & UC_UNAVAILABLE)) {
1060 gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_AWAY_RETURN);
1061 system_log(log_back, gc, b, OPT_LOG_BUDDY_AWAY);
1062 } else if (!(b->uc & UC_UNAVAILABLE) && (type & UC_UNAVAILABLE)) {
1063 gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_AWAY);
1064 system_log(log_away, gc, b, OPT_LOG_BUDDY_AWAY);
1067 gaim_blist_update_buddy_status(b, type);
1070 if (loggedin) {
1071 if (!GAIM_BUDDY_IS_ONLINE(b)) {
1072 struct gaim_conversation *c = gaim_find_conversation(b->name);
1073 if (c && (im_options & OPT_IM_LOGON)) {
1074 char *tmp = g_strdup_printf(_("%s logged in."), gaim_get_buddy_alias(b));
1075 gaim_conversation_write(c, NULL, tmp, -1,
1076 WFLAG_SYSTEM, time(NULL));
1077 g_free(tmp);
1078 } else if (awayqueue && find_queue_total_by_name(b->name)) {
1079 struct queued_message *qm = g_new0(struct queued_message, 1);
1080 g_snprintf(qm->name, sizeof(qm->name), "%s", b->name);
1081 qm->message = g_strdup_printf(_("%s logged in."),
1082 gaim_get_buddy_alias(b));
1083 qm->account = gc->account;
1084 qm->tm = time(NULL);
1085 qm->flags = WFLAG_SYSTEM;
1086 qm->len = -1;
1087 message_queue = g_slist_append(message_queue, qm);
1089 gaim_sound_play_event(GAIM_SOUND_BUDDY_ARRIVE);
1090 gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_SIGNON);
1091 system_log(log_signon, gc, b, OPT_LOG_BUDDY_SIGNON);
1093 } else {
1094 if (GAIM_BUDDY_IS_ONLINE(b)) {
1095 struct gaim_conversation *c = gaim_find_conversation(b->name);
1096 if (c && (im_options & OPT_IM_LOGON)) {
1097 char *tmp = g_strdup_printf(_("%s logged out."), gaim_get_buddy_alias(b));
1098 gaim_conversation_write(c, NULL, tmp, -1,
1099 WFLAG_SYSTEM, time(NULL));
1100 g_free(tmp);
1101 } else if (awayqueue && find_queue_total_by_name(b->name)) {
1102 struct queued_message *qm = g_new0(struct queued_message, 1);
1103 g_snprintf(qm->name, sizeof(qm->name), "%s", b->name);
1104 qm->message = g_strdup_printf(_("%s logged out."),
1105 gaim_get_buddy_alias(b));
1106 qm->account = gc->account;
1107 qm->tm = time(NULL);
1108 qm->flags = WFLAG_SYSTEM;
1109 qm->len = -1;
1110 message_queue = g_slist_append(message_queue, qm);
1112 serv_got_typing_stopped(gc, name); /* obviously not typing */
1113 gaim_sound_play_event(GAIM_SOUND_BUDDY_LEAVE);
1114 gaim_pounce_execute(gc->account, b->name, GAIM_POUNCE_SIGNOFF);
1115 system_log(log_signoff, gc, b, OPT_LOG_BUDDY_SIGNON);
1119 gaim_blist_update_buddy_presence(b, loggedin);
1124 void serv_got_eviled(struct gaim_connection *gc, char *name, int lev)
1126 char buf2[1024];
1128 gaim_event_broadcast(event_warned, gc, name, lev);
1130 if (gc->evil >= lev) {
1131 gc->evil = lev;
1132 return;
1135 gc->evil = lev;
1137 g_snprintf(buf2, sizeof(buf2),
1138 _("%s has just been warned by %s.\n"
1139 "Your new warning level is %d%%"),
1140 gc->username,
1141 ((name == NULL)? _("an anonymous person") : name), lev);
1143 gaim_notify_info(NULL, NULL, buf2, NULL);
1146 void serv_got_typing(struct gaim_connection *gc, char *name, int timeout,
1147 int state) {
1149 struct buddy *b;
1150 struct gaim_conversation *cnv = gaim_find_conversation(name);
1151 struct gaim_im *im;
1153 if (!cnv)
1154 return;
1156 im = GAIM_IM(cnv);
1158 gaim_conversation_set_account(cnv, gc->account);
1159 gaim_im_set_typing_state(im, state);
1160 gaim_im_update_typing(im);
1162 b = gaim_find_buddy(gc->account, name);
1164 gaim_event_broadcast(event_got_typing, gc, name);
1166 if (b != NULL)
1167 gaim_pounce_execute(gc->account, name, GAIM_POUNCE_TYPING);
1169 if (timeout > 0)
1170 gaim_im_start_typing_timeout(im, timeout);
1173 void serv_got_typing_stopped(struct gaim_connection *gc, char *name) {
1175 struct gaim_conversation *c = gaim_find_conversation(name);
1176 struct gaim_im *im;
1177 struct buddy *b;
1179 if (!c)
1180 return;
1182 im = GAIM_IM(c);
1184 if (im->typing_state == NOT_TYPING)
1185 return;
1187 gaim_im_stop_typing_timeout(im);
1188 gaim_im_set_typing_state(im, NOT_TYPING);
1189 gaim_im_update_typing(im);
1191 b = gaim_find_buddy(gc->account, name);
1193 if (b != NULL)
1194 gaim_pounce_execute(gc->account, name, GAIM_POUNCE_TYPING_STOPPED);
1197 struct chat_invite_data {
1198 struct gaim_connection *gc;
1199 GHashTable *components;
1202 static void chat_invite_data_free(struct chat_invite_data *cid)
1204 if (cid->components)
1205 g_hash_table_destroy(cid->components);
1206 g_free(cid);
1209 static void chat_invite_accept(struct chat_invite_data *cid)
1211 serv_join_chat(cid->gc, cid->components);
1213 chat_invite_data_free(cid);
1218 void serv_got_chat_invite(struct gaim_connection *gc, char *name,
1219 char *who, char *message, GHashTable *data)
1221 char buf2[BUF_LONG];
1222 struct chat_invite_data *cid = g_new0(struct chat_invite_data, 1);
1225 gaim_event_broadcast(event_chat_invited, gc, who, name, message);
1227 if (message)
1228 g_snprintf(buf2, sizeof(buf2),
1229 _("User '%s' invites %s to buddy chat room: '%s'\n%s"),
1230 who, gc->username, name, message);
1231 else
1232 g_snprintf(buf2, sizeof(buf2),
1233 _("User '%s' invites %s to buddy chat room: '%s'\n"),
1234 who, gc->username, name);
1236 cid->gc = gc;
1237 cid->components = data;
1239 do_ask_dialog(_("Buddy Chat Invite"), buf2, cid, _("Accept"), chat_invite_accept, _("Cancel"), chat_invite_data_free, NULL, FALSE);
1242 struct gaim_conversation *serv_got_joined_chat(struct gaim_connection *gc,
1243 int id, char *name)
1245 struct gaim_conversation *b;
1246 struct gaim_chat *chat;
1248 b = gaim_conversation_new(GAIM_CONV_CHAT, gc->account, name);
1249 chat = GAIM_CHAT(b);
1251 gc->buddy_chats = g_slist_append(gc->buddy_chats, b);
1253 gaim_chat_set_id(chat, id);
1255 if ((logging_options & OPT_LOG_CHATS) ||
1256 find_log_info(gaim_conversation_get_name(b))) {
1258 FILE *fd;
1259 char *filename;
1261 filename = (char *)malloc(100);
1262 g_snprintf(filename, 100, "%s.chat", gaim_conversation_get_name(b));
1264 fd = open_log_file(filename, TRUE);
1266 if (fd) {
1267 if (!(logging_options & OPT_LOG_STRIP_HTML))
1268 fprintf(fd,
1269 "<HR><BR><H3 Align=Center> ---- New Conversation @ %s ----</H3><BR>\n",
1270 full_date());
1271 else
1272 fprintf(fd, "---- New Conversation @ %s ----\n", full_date());
1274 fclose(fd);
1276 free(filename);
1279 gaim_window_show(gaim_conversation_get_window(b));
1280 gaim_window_switch_conversation(gaim_conversation_get_window(b),
1281 gaim_conversation_get_index(b));
1283 gaim_event_broadcast(event_chat_join, gc, id, name);
1285 return b;
1288 void serv_got_chat_left(struct gaim_connection *g, int id)
1290 GSList *bcs;
1291 struct gaim_conversation *conv = NULL;
1292 struct gaim_chat *chat = NULL;
1294 for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
1295 conv = (struct gaim_conversation *)bcs->data;
1297 chat = GAIM_CHAT(conv);
1299 if (gaim_chat_get_id(chat) == id)
1300 break;
1302 conv = NULL;
1305 if (!conv)
1306 return;
1308 gaim_event_broadcast(event_chat_leave, g, gaim_chat_get_id(chat));
1310 gaim_debug(GAIM_DEBUG_INFO, "server", "Leaving room: %s\n",
1311 gaim_conversation_get_name(conv));
1313 g->buddy_chats = g_slist_remove(g->buddy_chats, conv);
1315 gaim_conversation_destroy(conv);
1318 void serv_got_chat_in(struct gaim_connection *g, int id, char *who,
1319 int whisper, char *message, time_t mtime)
1321 int w;
1322 GSList *bcs;
1323 struct gaim_conversation *conv = NULL;
1324 struct gaim_chat *chat = NULL;
1325 char *buf;
1326 char *buffy, *angel;
1327 int plugin_return;
1329 for (bcs = g->buddy_chats; bcs != NULL; bcs = bcs->next) {
1330 conv = (struct gaim_conversation *)bcs->data;
1332 chat = GAIM_CHAT(conv);
1334 if (gaim_chat_get_id(chat) == id)
1335 break;
1337 conv = NULL;
1340 if (!conv)
1341 return;
1344 * Plugin stuff. We pass a char ** but we don't want to pass what's
1345 * been given us by the prpls. so we create temp holders and pass those
1346 * instead. It's basically just to avoid segfaults. Of course, if the
1347 * data is binary, plugins don't see it. Bitch all you want; i really
1348 * don't want you to be dealing with it.
1351 buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG));
1352 strcpy(buffy, message);
1353 angel = g_strdup(who);
1354 plugin_return = gaim_event_broadcast(event_chat_recv, g, gaim_chat_get_id(chat),
1355 &angel, &buffy);
1357 if (!buffy || !angel || plugin_return) {
1358 if (buffy)
1359 g_free(buffy);
1360 if (angel)
1361 g_free(angel);
1362 return;
1364 who = angel;
1365 message = buffy;
1369 if (convo_options & OPT_CONVO_SEND_LINKS)
1370 buf = linkify_text(message);
1371 else
1372 buf = g_strdup(message);
1374 if (whisper)
1375 w = WFLAG_WHISPER;
1376 else
1377 w = 0;
1379 gaim_chat_write(chat, who, buf, w, mtime);
1381 g_free(who);
1382 g_free(message);
1383 g_free(buf);
1386 static void des_popup(GtkWidget *w, GtkWidget *window)
1388 if (w == window) {
1389 char *u = g_object_get_data(G_OBJECT(window), "url");
1390 g_free(u);
1392 gtk_widget_destroy(window);
1395 void serv_got_popup(char *msg, char *u, int wid, int hei)
1397 GtkWidget *window;
1398 GtkWidget *vbox;
1399 GtkWidget *sw;
1400 GtkWidget *text;
1401 GtkWidget *hbox;
1402 GtkWidget *button;
1403 char *url = g_strdup(u);
1405 GAIM_DIALOG(window);
1406 gtk_window_set_role(GTK_WINDOW(window), "popup");
1407 gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
1408 gtk_window_set_title(GTK_WINDOW(window), _("Gaim - Popup"));
1409 gtk_container_set_border_width(GTK_CONTAINER(window), 5);
1410 g_signal_connect(G_OBJECT(window), "destroy",
1411 G_CALLBACK(des_popup), window);
1412 g_object_set_data(G_OBJECT(window), "url", url);
1413 gtk_widget_realize(window);
1415 vbox = gtk_vbox_new(FALSE, 5);
1416 gtk_container_add(GTK_CONTAINER(window), vbox);
1418 sw = gtk_scrolled_window_new(NULL, NULL);
1419 gtk_widget_set_size_request(sw, wid, hei);
1420 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
1421 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);
1423 text = gtk_imhtml_new(NULL, NULL);
1424 gtk_container_add(GTK_CONTAINER(sw), text);
1425 gaim_setup_imhtml(text);
1427 hbox = gtk_hbox_new(FALSE, 5);
1428 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1430 button = gaim_pixbuf_button_from_stock(_("Close"), GTK_STOCK_CLOSE, GAIM_BUTTON_HORIZONTAL);
1431 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 5);
1432 g_signal_connect(G_OBJECT(button), "clicked",
1433 G_CALLBACK(des_popup), window);
1435 button = gaim_pixbuf_button_from_stock(_("More Info"), GTK_STOCK_FIND, GAIM_BUTTON_HORIZONTAL);
1436 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 5);
1437 g_signal_connect(G_OBJECT(button), "clicked",
1438 G_CALLBACK(open_url), url);
1440 gtk_widget_show_all(window);
1442 gtk_imhtml_append_text(GTK_IMHTML(text), msg, -1, GTK_IMHTML_NO_NEWLINE);