Some things here:
- Several memory leak fixes
- A few invalid memory access fixes
- Fix MSN notification server transfer to be quiet about it
- Fix MSN blist sync if user has insane friendly name
- 1 typo fix :)
... and quite possibly something else I forgot.
--- a/ChangeLog Sun Jan 09 14:16:11 2005 -0500
+++ b/ChangeLog Tue Jan 11 12:27:29 2005 -0500
@@ -9,6 +9,7 @@
* Fix configuration of Jabber chat rooms on some servers
* More MSN bug fixes (Felipe Contreras)
* Fix queue messages to Docklet when not globally away (Robert McQueen)
version 1.1.1 (12/28/2004):
* Allow SILC authentication via public key if your key is password
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/tcl/tcl_signals.c Tue Jan 11 12:27:29 2005 -0500
@@ -0,0 +1,317 @@
+ * @file tcl_signals.c Gaim Tcl signal API + * Copyright (C) 2003 Ethan Blanton <eblanton@cs.purdue.edu> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#include "conversation.h" +static GList *tcl_callbacks; +static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler); +void tcl_signal_handler_free(struct tcl_signal_handler *handler) + g_free(handler->signal); + if (handler->argnames != NULL) + g_free(handler->argnames); + Tcl_DecrRefCount(handler->proc); +void tcl_signal_cleanup(Tcl_Interp *interp) + struct tcl_signal_handler *handler; + for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { + if (handler->interp == interp) { + tcl_signal_handler_free(handler); + tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); +gboolean tcl_signal_connect(struct tcl_signal_handler *handler) + gaim_signal_get_values(handler->instance, handler->signal, &handler->returntype, + &handler->nargs, &handler->argtypes); + if (handler->nargs != handler->nnames) + tcl_signal_disconnect(handler->interp, handler->signal, handler->interp); + if (!gaim_signal_connect_vargs(handler->instance, handler->signal, (void *)handler->interp, + GAIM_CALLBACK(tcl_signal_callback), (void *)handler)) + tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler); +void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp) + struct tcl_signal_handler *handler; + gboolean found = FALSE; + for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) { + if (handler->interp == interp && handler->instance == instance + && !strcmp(signal, handler->signal)) { + gaim_signal_disconnect(instance, signal, handler->interp, + GAIM_CALLBACK(tcl_signal_callback)); + tcl_signal_handler_free(handler); + tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL); +static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler) + vars = g_new0(struct var, handler->nargs); + val = g_string_sized_new(32); + name = g_string_sized_new(32); + for (i = 0; i < handler->nargs; i++) { + g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]); + switch(gaim_value_get_type(handler->argtypes[i])) { + default: /* Yes, at the top */ + case GAIM_TYPE_UNKNOWN: /* What? I guess just pass the word ... */ + /* treat this as a pointer, but complain first */ + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "unknown GaimValue type %d\n", + gaim_value_get_type(handler->argtypes[i])); + case GAIM_TYPE_POINTER: + /* These are all "pointer" types to us */ + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vars[i].val = va_arg(args, void **); + Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); + vars[i].val = va_arg(args, void *); + Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, + TCL_LINK_INT|TCL_LINK_READ_ONLY); + case GAIM_TYPE_BOOLEAN: + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vars[i].val = va_arg(args, gboolean *); + Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_BOOLEAN); + vars[i].val = (void *)va_arg(args, gboolean); + Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, + TCL_LINK_BOOLEAN|TCL_LINK_READ_ONLY); + /* These next two are totally bogus */ + /* I should really cast these individually to + * preserve as much information as possible ... + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vars[i].val = (void *)va_arg(args, int *); + Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); + vars[i].val = (void *)va_arg(args, int); + Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, + TCL_LINK_INT|TCL_LINK_READ_ONLY); + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vars[i].val = (void *)va_arg(args, char **); + if (vars[i].val != NULL && *(char **)vars[i].val != NULL) { + vars[i].str = (char *)ckalloc(strlen(*(char **)vars[i].val) + 1); + strcpy(vars[i].str, *(char **)vars[i].val); + vars[i].str = (char *)ckalloc(1); + Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, TCL_LINK_STRING); + vars[i].val = (void *)va_arg(args, char *); + Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, + TCL_LINK_STRING|TCL_LINK_READ_ONLY); + case GAIM_TYPE_SUBTYPE: + switch (gaim_value_get_subtype(handler->argtypes[i])) { + case GAIM_SUBTYPE_UNKNOWN: + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "subtype unknown\n"); + case GAIM_SUBTYPE_ACCOUNT: + case GAIM_SUBTYPE_CONNECTION: + case GAIM_SUBTYPE_CONVERSATION: + case GAIM_SUBTYPE_CONV_WINDOW: + case GAIM_SUBTYPE_PLUGIN: + if (gaim_value_is_outgoing(handler->argtypes[i])) { + vars[i].val = va_arg(args, void **); + Tcl_LinkVar(handler->interp, name->str, vars[i].val, TCL_LINK_INT); + vars[i].val = va_arg(args, void *); + Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val, + TCL_LINK_INT|TCL_LINK_READ_ONLY); + case GAIM_SUBTYPE_BLIST: + case GAIM_SUBTYPE_BLIST_BUDDY: + case GAIM_SUBTYPE_BLIST_GROUP: + case GAIM_SUBTYPE_BLIST_CHAT: + /* We're going to switch again for code-deduping */ + if (gaim_value_is_outgoing(handler->argtypes[i])) + node = *va_arg(args, GaimBlistNode **); + node = va_arg(args, GaimBlistNode *); + case GAIM_BLIST_GROUP_NODE: + g_string_printf(val, "group {%s}", ((GaimGroup *)node)->name); + case GAIM_BLIST_CONTACT_NODE: + /* g_string_printf(val, "contact {%s}", Contact Name? ); */ + case GAIM_BLIST_BUDDY_NODE: + g_string_printf(val, "buddy {%s} %lu", ((GaimBuddy *)node)->name, + (unsigned long)((GaimBuddy *)node)->account); + case GAIM_BLIST_CHAT_NODE: + g_string_printf(val, "chat {%s} %lu", ((GaimChat *)node)->alias, + (unsigned long)((GaimChat *)node)->account); + case GAIM_BLIST_OTHER_NODE: + g_string_printf(val, "other"); + vars[i].str = g_strdup(val->str); + Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, + TCL_LINK_STRING|TCL_LINK_READ_ONLY); + /* Call the friggin' procedure already */ + if ((error = Tcl_EvalObjEx(handler->interp, handler->proc, TCL_EVAL_GLOBAL)) != TCL_OK) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n", + Tcl_GetString(Tcl_GetObjResult(handler->interp))); + result = Tcl_GetObjResult(handler->interp); + /* handle return values -- strings and words only */ + if (handler->returntype) { + if (gaim_value_get_type(handler->returntype) == GAIM_TYPE_STRING) { + retval = (void *)g_strdup(Tcl_GetString(result)); + if ((error = Tcl_GetIntFromObj(handler->interp, result, (int *)&retval)) != TCL_OK) { + gaim_debug(GAIM_DEBUG_ERROR, "tcl", "Error retrieving procedure result: %s\n", + Tcl_GetString(Tcl_GetObjResult(handler->interp))); + /* And finally clean up */ + for (i = 0; i < handler->nargs; i++) { + g_string_printf(name, "::gaim::_callback::%s", handler->argnames[i]); + Tcl_UnlinkVar(handler->interp, name->str); + /* We basically only have to deal with strings and buddies + switch (gaim_value_get_type(handler->argtypes[i])) { + if (gaim_value_is_outgoing(handler->argtypes[i])) { + if (vars[i].val != NULL && *(char **)vars[i].val != NULL) { + g_free(*(char **)vars[i].val); + *(char **)vars[i].val = g_strdup(vars[i].str); + case GAIM_TYPE_SUBTYPE: + switch(gaim_value_get_subtype(handler->argtypes[i])) { + case GAIM_SUBTYPE_BLIST: + case GAIM_SUBTYPE_BLIST_BUDDY: + case GAIM_SUBTYPE_BLIST_GROUP: + case GAIM_SUBTYPE_BLIST_CHAT: + g_string_free(name, TRUE); + g_string_free(val, TRUE); --- a/src/blist.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/blist.c Tue Jan 11 12:27:29 2005 -0500
@@ -1212,6 +1212,7 @@
ops->remove(gaimbuddylist, node);
+ g_hash_table_destroy(contact->node.settings); @@ -1281,6 +1282,7 @@
g_hash_table_destroy(buddy->node.settings);
+ g_free(buddy->server_alias); /* If the contact is empty then remove it */
@@ -1323,6 +1325,7 @@
g_hash_table_destroy(chat->components);
+ g_hash_table_destroy(chat->node.settings); @@ -1384,6 +1387,7 @@
+ g_hash_table_destroy(group->node.settings); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cmds.c Tue Jan 11 12:27:29 2005 -0500
@@ -0,0 +1,366 @@
+ * @file cmds.c Commands API + * Copyright (C) 2003-2004 Timothy Ringenbach <omarvo@hotmail.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +static GList *cmds = NULL; +static guint next_id = 1; +typedef struct _GaimCmd { + GaimCmdPriority priority; +static gint cmds_compare_func(const GaimCmd *a, const GaimCmd *b) + if (a->priority > b->priority) + else if (a->priority < b->priority) +GaimCmdId gaim_cmd_register(const gchar *cmd, const gchar *args, + GaimCmdPriority p, GaimCmdFlag f, + const gchar *prpl_id, GaimCmdFunc func, + const gchar *helpstr, void *data) + g_return_val_if_fail(cmd != NULL && *cmd != '\0', 0); + g_return_val_if_fail(args != NULL, 0); + g_return_val_if_fail(func != NULL, 0); + c = g_new0(GaimCmd, 1); + c->cmd = g_strdup(cmd); + c->args = g_strdup(args); + c->prpl_id = prpl_id ? g_strdup(prpl_id) : NULL; + c->help = helpstr ? g_strdup(helpstr) : NULL; + cmds = g_list_insert_sorted(cmds, c, (GCompareFunc)cmds_compare_func); +static void gaim_cmd_free(GaimCmd *c) +void gaim_cmd_unregister(GaimCmdId id) + for (l = cmds; l; l = l->next) { + cmds = g_list_remove(cmds, c); +static gboolean gaim_cmd_parse_args(GaimCmd *cmd, const gchar *s, const gchar *m, gchar ***args) + *args = g_new0(char *, strlen(cmd->args) + 1); + for (i = 0; cmd->args[i]; i++) { + return (cmd->flags & GAIM_CMD_FLAG_ALLOW_WRONG_ARGS); + switch (cmd->args[i]) { + if (!(end = strchr(cur, ' '))) { + end = cur + strlen(cur); + (*args)[i] = g_strndup(cur, end - cur); + (*args)[i] = g_strndup(cur, end - cur); + if (!(end = strchr(cur, ' '))) { + end = cur + strlen(cur); + (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_pointer_to_offset(s, end)); + (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_pointer_to_offset(s, end)); + (*args)[i] = g_strdup(cur); + cur = cur + strlen(cur); + (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_strlen(cur, -1) + 1); + cur = cur + strlen(cur); + return (cmd->flags & GAIM_CMD_FLAG_ALLOW_WRONG_ARGS); +static void gaim_cmd_free_args(gchar **args) + for (i = 0; args[i]; i++) +static void gaim_cmd_strip_current_char(gunichar c, char *s, guint len) + bytes = g_unichar_to_utf8(c, NULL); + memmove(s, s + bytes, len + 1 - bytes); +static void gaim_cmd_strip_cmd_from_markup(char *markup) + guint len = strlen(markup); + gunichar c = g_utf8_get_char(s); + } else if (g_unichar_isspace(c)) { + gaim_cmd_strip_current_char(c, s, len - (s - markup)); + gaim_cmd_strip_current_char(c, s, len - (s - markup)); + s = g_utf8_next_char(s); +GaimCmdStatus gaim_cmd_do_command(GaimConversation *conv, const gchar *cmdline, + const gchar *markup, gchar **error) + gboolean found = FALSE, tried_cmd = FALSE, right_type = FALSE, right_prpl = FALSE; + gchar *cmd, *rest, *mrest; + GaimCmdRet ret = GAIM_CMD_RET_CONTINUE; + prpl_id = gaim_account_get_protocol_id(gaim_conversation_get_account(conv)); + if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) + else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) + return GAIM_CMD_STATUS_FAILED; + rest = strchr(cmdline, ' '); + cmd = g_strndup(cmdline, rest - cmdline); + cmd = g_strdup(cmdline); + mrest = g_strdup(markup); + gaim_cmd_strip_cmd_from_markup(mrest); + for (l = cmds; l; l = l->next) { + if (strcmp(c->cmd, cmd) != 0) + if (!(c->flags & GAIM_CMD_FLAG_IM)) + if (!(c->flags & GAIM_CMD_FLAG_CHAT)) + if ((c->flags & GAIM_CMD_FLAG_PRPL_ONLY) && c->prpl_id && + (strcmp(c->prpl_id, prpl_id) != 0)) + /* this checks the allow bad args flag for us */ + if (!gaim_cmd_parse_args(c, rest, mrest, &args)) { + gaim_cmd_free_args(args); + ret = c->func(conv, cmd, args, &err, c->data); + if (ret == GAIM_CMD_RET_CONTINUE) { + gaim_cmd_free_args(args); + gaim_cmd_free_args(args); + return GAIM_CMD_STATUS_NOT_FOUND; + return GAIM_CMD_STATUS_WRONG_TYPE; + return GAIM_CMD_STATUS_WRONG_PRPL; + return GAIM_CMD_STATUS_WRONG_ARGS; + if (ret == GAIM_CMD_RET_OK) { + return GAIM_CMD_STATUS_OK; + if (ret == GAIM_CMD_RET_CONTINUE) + return GAIM_CMD_STATUS_NOT_FOUND; + return GAIM_CMD_STATUS_FAILED; +GList *gaim_cmd_list(GaimConversation *conv) + for (l = cmds; l; l = l->next) { + if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_IM)) + if (!(c->flags & GAIM_CMD_FLAG_IM)) + if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT)) + if (!(c->flags & GAIM_CMD_FLAG_CHAT)) + if (conv && (c->flags & GAIM_CMD_FLAG_PRPL_ONLY) && c->prpl_id && + (strcmp(c->prpl_id, gaim_account_get_protocol_id(gaim_conversation_get_account(conv))) != 0)) + ret = g_list_append(ret, c->cmd); +GList *gaim_cmd_help(GaimConversation *conv, const gchar *cmd) + for (l = cmds; l; l = l->next) { + if (cmd && (strcmp(cmd, c->cmd) != 0)) + if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_IM)) + if (!(c->flags & GAIM_CMD_FLAG_IM)) + if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT)) + if (!(c->flags & GAIM_CMD_FLAG_CHAT)) + if (conv && (c->flags & GAIM_CMD_FLAG_PRPL_ONLY) && c->prpl_id && + (strcmp(c->prpl_id, gaim_account_get_protocol_id(gaim_conversation_get_account(conv))) != 0)) + ret = g_list_append(ret, c->help); --- a/src/conversation.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/conversation.c Tue Jan 11 12:27:29 2005 -0500
@@ -978,7 +978,7 @@
for (node = conv->u.chat->in_room; node != NULL; node = node->next) {
+ gaim_conv_chat_cb_destroy((GaimConvChatBuddy *)node->data); @@ -1002,6 +1002,9 @@
g_free(conv->u.chat->topic);
conv->u.chat->topic = NULL;
+ g_free(conv->u.chat->nick); --- a/src/ft.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/ft.c Tue Jan 11 12:27:29 2005 -0500
@@ -984,12 +984,12 @@
escaped = g_markup_escape_text(gaim_xfer_get_filename(xfer), -1);
msg = g_strdup_printf(_("%s canceled the transfer of %s"),
msg = g_strdup_printf(_("%s canceled the file transfer"), xfer->who);
gaim_xfer_conversation_write(xfer, msg, TRUE);
gaim_xfer_error(gaim_xfer_get_type(xfer), xfer->who, msg);
--- a/src/gtkblist.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/gtkblist.c Tue Jan 11 12:27:29 2005 -0500
@@ -2162,8 +2162,10 @@
node = g_value_get_pointer(&val);
if(!GAIM_BLIST_NODE_IS_CONTACT(node) && !GAIM_BLIST_NODE_IS_BUDDY(node)
- && !GAIM_BLIST_NODE_IS_CHAT(node))
+ && !GAIM_BLIST_NODE_IS_CHAT(node)) { + gtk_tree_path_free(path); @@ -3524,9 +3526,13 @@
gaim_gtk_blist_update(list, node->parent);
- /* There's something I don't understand here */
- /* g_free(node->ui_data);
- node->ui_data = NULL; */
+ /* There's something I don't understand here - Ethan */ + /* Ethan said that back in 2003, but this g_free has been left commented + * out ever since. I can't find any reason at all why this is bad and + * valgrind found several reasons why it's good. If this causes problems + * comment it out again. Stu */ static gboolean do_selection_changed(GaimBlistNode *new_selection)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkconn.c Tue Jan 11 12:27:29 2005 -0500
@@ -0,0 +1,717 @@
+ * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * The next couple of functions deal with the connection dialog +struct meter_window *meter_win = NULL; +static void kill_meter(struct signon_meter *meter, const char *text) + if(gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(meter->progress)) == 1) + gtk_widget_set_sensitive(meter->button, FALSE); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(meter->progress), 1); + gtk_label_set_text(GTK_LABEL(meter->status), text); + meter_win->active_count--; + if (meter_win->active_count == 0) { + gtk_widget_destroy(meter_win->window); +static void cancel_signon(GtkWidget *button, struct signon_meter *meter) + if (meter->account->gc != NULL) { + meter->account->gc->wants_to_die = TRUE; + gaim_connection_destroy(meter->account->gc); + kill_meter(meter, _("Done.")); + if (gaim_connections_get_all() == NULL) { + gaim_gtkdialogs_destroy_all(); +static void cancel_all () + GSList *m = meter_win ? meter_win->meters : NULL; + struct signon_meter *meter; + if (gaim_connection_get_state(meter->account->gc) != GAIM_CONNECTED) + cancel_signon(NULL, meter); +static gint meter_destroy(GtkWidget *window, GdkEvent *evt, struct signon_meter *meter) +static struct signon_meter *find_signon_meter(GaimConnection *gc) + GSList *m = meter_win ? meter_win->meters : NULL; + struct signon_meter *meter; + if (meter->account == gaim_connection_get_account(gc)) +static GtkWidget* create_meter_pixmap (GaimConnection *gc) + GdkPixbuf *pb = create_prpl_icon(gc->account); + GdkPixbuf *scale = gdk_pixbuf_scale_simple(pb, 30,30,GDK_INTERP_BILINEAR); + gtk_image_new_from_pixbuf(scale); + g_object_unref(G_OBJECT(pb)); + g_object_unref(G_OBJECT(scale)); +static struct signon_meter * +new_meter(GaimConnection *gc, GtkWidget *widget, + GtkWidget *table, gint *rows) + GString *name_to_print; + struct signon_meter *meter; + meter = g_new0(struct signon_meter, 1); + meter->account = gaim_connection_get_account(gc); + name_to_print = g_string_new(gaim_account_get_username(meter->account)); + gtk_table_resize (GTK_TABLE(table), *rows, 4); + graphic = create_meter_pixmap(gc); + nest_vbox = gtk_vbox_new (FALSE, 0); + g_string_prepend(name_to_print, _("Signon: ")); + label = gtk_label_new (name_to_print->str); + g_string_free(name_to_print, TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + meter->status = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(meter->status), 0, 0.5); + gtk_widget_set_size_request(meter->status, 250, -1); + meter->progress = gtk_progress_bar_new (); + meter->button = gaim_pixbuf_button_from_stock (_("Cancel"), GTK_STOCK_CANCEL, GAIM_BUTTON_HORIZONTAL); + g_signal_connect(G_OBJECT (meter->button), "clicked", + G_CALLBACK (cancel_signon), meter); + gtk_table_attach (GTK_TABLE (table), graphic, 0, 1, *rows, *rows+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); + gtk_table_attach (GTK_TABLE (table), nest_vbox, 1, 2, *rows, *rows+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); + gtk_box_pack_start (GTK_BOX (nest_vbox), GTK_WIDGET (label), FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (nest_vbox), GTK_WIDGET (meter->status), FALSE, FALSE, 0); + gtk_table_attach (GTK_TABLE (table), meter->progress, 2, 3, *rows, *rows+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); + gtk_table_attach (GTK_TABLE (table), meter->button, 3, 4, *rows, *rows+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); + gtk_widget_show_all (GTK_WIDGET (meter_win->window)); + meter_win->active_count++; +static void gaim_gtk_connection_connect_progress(GaimConnection *gc, + const char *text, size_t step, size_t step_count) + struct signon_meter *meter; + GtkWidget *cancel_button; + gtk_widget_hide(mainwindow); + meter_win = g_new0(struct meter_window, 1); + meter_win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_resizable(GTK_WINDOW(meter_win->window), FALSE); + gtk_window_set_role(GTK_WINDOW(meter_win->window), "signon"); + gtk_container_set_border_width(GTK_CONTAINER(meter_win->window), 5); + gtk_window_set_title(GTK_WINDOW(meter_win->window), _("Signon")); + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_add(GTK_CONTAINER(meter_win->window), vbox); + meter_win->table = gtk_table_new(1, 4, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(meter_win->table), + gtk_container_set_border_width(GTK_CONTAINER(meter_win->table), 5); + gtk_table_set_row_spacings(GTK_TABLE(meter_win->table), 5); + gtk_table_set_col_spacings(GTK_TABLE(meter_win->table), 10); + cancel_button = gaim_pixbuf_button_from_stock(_("Cancel All"), + GTK_STOCK_QUIT, GAIM_BUTTON_HORIZONTAL); + g_signal_connect_swapped(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(cancel_all), NULL); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(cancel_button), + g_signal_connect(G_OBJECT(meter_win->window), "delete_event", + G_CALLBACK(meter_destroy), NULL); + meter = find_signon_meter(gc); + meter = new_meter(gc, meter_win->window, meter_win->table, + meter_win->meters = g_slist_append(meter_win->meters, meter); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(meter->progress), + (float)step / (float)step_count); + gtk_label_set_text(GTK_LABEL(meter->status), text); +static void gaim_gtk_connection_connected(GaimConnection *gc) + struct signon_meter *meter = find_signon_meter(gc); + gaim_gtk_blist_update_protocol_actions(); + kill_meter(meter, _("Done.")); +static void gaim_gtk_connection_disconnected(GaimConnection *gc) + struct signon_meter *meter = find_signon_meter(gc); + gaim_gtk_blist_update_protocol_actions(); + kill_meter(meter, _("Done.")); + if (gaim_connections_get_all() != NULL) + gaim_gtkdialogs_destroy_all(); +static void gaim_gtk_connection_notice(GaimConnection *gc, + * The next couple of functions deal with the disconnected dialog +struct disconnect_window { + GtkWidget *reconnect_btn; + GtkWidget *reconnectall_btn; +struct disconnect_window *disconnect_window = NULL; +static void disconnect_connection_change_cb(GaimConnection *gc, void *data); + * Destroy the dialog and remove the signals associated with it. +static void disconnect_window_hide() + gaim_signal_disconnect(gaim_connections_get_handle(), "signed-on", + disconnect_window, GAIM_CALLBACK(disconnect_connection_change_cb)); + gaim_signal_disconnect(gaim_connections_get_handle(), "signed-off", + disconnect_window, GAIM_CALLBACK(disconnect_connection_change_cb)); + gtk_widget_destroy(disconnect_window->window); + g_free(disconnect_window); + disconnect_window = NULL; + * Make sure the Reconnect and Reconnect All buttons are correctly + * shown or hidden. Also make sure the label on the Reconnect + * button is correctly set to either Reconnect or Remove. If there + * is more than one account then make sure the GtkTreeView is shown. + * If there are no accounts disconnected then hide the dialog. +static void disconnect_window_update_buttons(GtkTreeModel *model) + GaimAccount *account = NULL; + if ((disconnect_window == NULL) || (model == NULL)) + if (!gtk_tree_model_get_iter_first(model, &iter)) { + /* No more accounts being shown. Caloo calay! */ + disconnect_window_hide(); + * If we have more than one disconnected account then show the + * GtkTreeView and the "Reconnect All" button + if (gtk_tree_model_iter_next(model, &iter)) { + gtk_widget_show_all(disconnect_window->sw); + gtk_widget_show(disconnect_window->reconnectall_btn); + gtk_widget_hide_all(disconnect_window->sw); + gtk_widget_hide(disconnect_window->reconnectall_btn); + * Make sure one of the accounts is selected. + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(disconnect_window->treeview)); + if (!gtk_tree_selection_get_selected(sel, &model, &iter)) { + gtk_tree_model_get_iter_first(model, &iter); + gtk_tree_selection_select_iter(sel, &iter); + * Update the Reconnect/Remove button appropriately and set the + * label in the dialog to what it should be. If there is only + * one account in the tree model, and that account is connected, + * then we don't show the remove button. + gtk_tree_model_get(model, &iter, 3, &label_text, 4, &account, -1); + gtk_button_set_label(GTK_BUTTON(disconnect_window->reconnect_btn), + gaim_account_is_connected(account) ? _("_Remove") : _("_Reconnect")); + gtk_label_set_markup(GTK_LABEL(disconnect_window->label), label_text); + gtk_dialog_set_response_sensitive(GTK_DIALOG(disconnect_window->window), GTK_RESPONSE_ACCEPT, TRUE); + gtk_tree_model_get_iter_first(model, &iter); + if (gaim_account_is_connected(account) && !(gtk_tree_model_iter_next(model, &iter))) + gtk_widget_hide(disconnect_window->reconnect_btn); + gtk_widget_show(disconnect_window->reconnect_btn); +static void disconnect_response_cb(GtkDialog *dialog, gint id, GtkWidget *widget) + GtkTreeSelection *sel = NULL; + GtkTreeModel *model = NULL; + GaimAccount *account = NULL; + case GTK_RESPONSE_APPLY: /* Reconnect All */ + model = gtk_tree_view_get_model(GTK_TREE_VIEW(disconnect_window->treeview)); + if (gtk_tree_model_get_iter_first(model, &iter)) { + /* tree rows to be deleted */ + GList *l_del = NULL, *l_del_iter = NULL; + /* accounts to be connected */ + GList *l_accts = NULL, *l_accts_iter = NULL; + GtkTreePath *path = gtk_tree_model_get_path(model, &iter); + GtkTreeRowReference* del_row = gtk_tree_row_reference_new(model, path); + l_del = g_list_append(l_del, del_row); + gtk_tree_path_free(path); + gtk_tree_model_get(model, &iter, 4, &account, -1); + if (!gaim_account_is_connected(account) && g_list_find(l_accts, account) == NULL) + l_accts = g_list_append(l_accts, account); + } while (gtk_tree_model_iter_next(model, &iter)); + /* We could just do the following, but we only want to remove accounts + * that are going to be reconnected, not accounts that have already + /* gtk_list_store_clear(GTK_LIST_STORE(model)); */ + while (l_del_iter != NULL) { + GtkTreeRowReference* del_row = l_del_iter->data; + GtkTreePath *path = gtk_tree_row_reference_get_path(del_row); + if (gtk_tree_model_get_iter(model, &iter, path)) + gtk_list_store_remove(GTK_LIST_STORE(model), &iter); + gtk_tree_path_free(path); + gtk_tree_row_reference_free(del_row); + l_del_iter = l_del_iter->next; + /* reconnect disconnected accounts */ + l_accts_iter = l_accts; + while (l_accts_iter != NULL) { + account = l_accts_iter->data; + gaim_account_connect(account); + l_accts_iter = l_accts_iter->next; + disconnect_window_update_buttons(model); + case GTK_RESPONSE_ACCEPT: /* Reconnect Selected */ + model = gtk_tree_view_get_model(GTK_TREE_VIEW(disconnect_window->treeview)); + * If we have more than one account disconnection displayed, then + * the scroll window is visible and we should use the selected + * account to reconnect. + if (GTK_WIDGET_VISIBLE(disconnect_window->sw)) { + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(disconnect_window->treeview)); + if (!gtk_tree_selection_get_selected(sel, &model, &iter)) + /* There is only one account disconnection, so reconnect to it. */ + if (!gtk_tree_model_get_iter_first(model, &iter)) + /* remove all disconnections of the account to be reconnected */ + gtk_tree_model_get(model, &iter, 4, &account, -1); + if (gtk_tree_model_get_iter_first(model, &iter)) { + GList *l_del = NULL, *l_del_iter = NULL; + GaimAccount *account2 = NULL; + gtk_tree_model_get(model, &iter, 4, &account2, -1); + if (account2 == account) { + GtkTreePath *path = gtk_tree_model_get_path(model, &iter); + GtkTreeRowReference* del_row = gtk_tree_row_reference_new(model, path); + l_del = g_list_append(l_del, del_row); + gtk_tree_path_free(path); + } while (gtk_tree_model_iter_next(model, &iter)); + while (l_del_iter != NULL) { + GtkTreeRowReference* del_row = l_del_iter->data; + GtkTreePath *path = gtk_tree_row_reference_get_path(del_row); + if (gtk_tree_model_get_iter(model, &iter, path)) + gtk_list_store_remove(GTK_LIST_STORE(model), &iter); + gtk_tree_path_free(path); + gtk_tree_row_reference_free(del_row); + l_del_iter = l_del_iter->next; + gaim_account_connect(account); + disconnect_window_update_buttons(model); + case GTK_RESPONSE_DELETE_EVENT: + case GTK_RESPONSE_CLOSE: + disconnect_window_hide(); + * Called whenever a different account is selected in the GtkListWhatever. +static void disconnect_tree_cb(GtkTreeSelection *sel, GtkTreeModel *model) + disconnect_window_update_buttons(model); + * Update the icon next to the account in the disconnect dialog, and + * gray the Reconnect All button if there is only 1 disconnected account. +static void disconnect_connection_change_cb(GaimConnection *gc, void *data) { + GaimAccount *account = gaim_connection_get_account(gc); + GList *l_disc_accts = NULL; + if (disconnect_window == NULL) + model = gtk_tree_view_get_model(GTK_TREE_VIEW(disconnect_window->treeview)); + icon = create_prpl_icon(account); + scale = gdk_pixbuf_scale_simple(icon, 16, 16, GDK_INTERP_BILINEAR); + /* Mark all disconnections w/ the account type disconnected /w grey icon */ + if (!gaim_account_is_connected(account)) + gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); + gtk_tree_model_get_iter_first(model, &iter); + GaimAccount *account2 = NULL; + /* Gray out the icon if this row is for this account */ + gtk_tree_model_get(model, &iter, 4, &account2, -1); + if (account2 == account) + gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, scale, -1); + if (!gaim_account_is_connected(account2) + && g_list_find(l_disc_accts, account2) == NULL) + l_disc_accts = g_list_append(l_disc_accts, account2); + } while (gtk_tree_model_iter_next(model, &iter)); + gtk_dialog_set_response_sensitive( + GTK_DIALOG(disconnect_window->window), + g_list_length(l_disc_accts) > 1); + g_list_free(l_disc_accts); + g_object_unref(G_OBJECT(icon)); + g_object_unref(G_OBJECT(scale)); + disconnect_window_update_buttons(model); +gaim_gtk_connection_report_disconnect(GaimConnection *gc, const char *text) + char *label_text = NULL; + GtkListStore *list_store; + GtkTreeViewColumn *col; + GtkTreeSelection *sel = NULL; + label_text = g_strdup_printf(_("<span weight=\"bold\" size=\"larger\">%s has been disconnected.</span>\n\n%s\n%s"), + gaim_account_get_username(gaim_connection_get_account(gc)), gaim_date_full(), + text ? text : _("Reason Unknown.")); + /* Build the window if it isn't there yet */ + if (!disconnect_window) { + GtkWidget *hbox, *vbox, *img; + GtkCellRenderer *rend, *rend2; + disconnect_window = g_new0(struct disconnect_window, 1); + disconnect_window->window = gtk_dialog_new_with_buttons(GAIM_ALERT_TITLE, NULL, GTK_DIALOG_NO_SEPARATOR, NULL); + g_signal_connect(G_OBJECT(disconnect_window->window), "response", G_CALLBACK(disconnect_response_cb), disconnect_window); + gtk_container_set_border_width(GTK_CONTAINER(disconnect_window->window), 6); + gtk_window_set_resizable(GTK_WINDOW(disconnect_window->window), FALSE); + gtk_dialog_set_has_separator(GTK_DIALOG(disconnect_window->window), FALSE); + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(disconnect_window->window)->vbox), 12); + gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(disconnect_window->window)->vbox), 6); + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(disconnect_window->window)->vbox), hbox); + img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment(GTK_MISC(img), 0, 0); + gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); + vbox = gtk_vbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + disconnect_window->label = gtk_label_new(label_text); + gtk_label_set_line_wrap(GTK_LABEL(disconnect_window->label), TRUE); + gtk_misc_set_alignment(GTK_MISC(disconnect_window->label), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), disconnect_window->label, FALSE, FALSE, 0); + disconnect_window->reconnect_btn = gtk_dialog_add_button( + GTK_DIALOG(disconnect_window->window), + disconnect_window->reconnectall_btn = gtk_dialog_add_button( + GTK_DIALOG(disconnect_window->window), + GTK_DIALOG(disconnect_window->window), + gtk_widget_show_all(disconnect_window->window); + disconnect_window->sw = gtk_scrolled_window_new(NULL,NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(disconnect_window->sw), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(disconnect_window->sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(vbox), disconnect_window->sw, TRUE, TRUE, 0); + list_store = gtk_list_store_new(5, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); + disconnect_window->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store)); + rend = gtk_cell_renderer_pixbuf_new(); + rend2 = gtk_cell_renderer_text_new(); + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(col, _("Account")); + gtk_tree_view_column_pack_start(col, rend, FALSE); + gtk_tree_view_column_pack_start(col, rend2, FALSE); + gtk_tree_view_column_set_attributes(col, rend, "pixbuf", 0, NULL); + gtk_tree_view_column_set_attributes(col, rend2, "text", 1, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(disconnect_window->treeview), col); + rend = gtk_cell_renderer_text_new(); + col = gtk_tree_view_column_new_with_attributes (_("Time"), + rend, "text", 2, NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW(disconnect_window->treeview), col); + g_object_unref(G_OBJECT(list_store)); + gtk_container_add(GTK_CONTAINER(disconnect_window->sw), disconnect_window->treeview); + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (disconnect_window->treeview)); + gtk_widget_set_size_request(disconnect_window->treeview, -1, 96); + g_signal_connect (G_OBJECT (sel), "changed", + G_CALLBACK (disconnect_tree_cb), list_store); + gaim_signal_connect(gaim_connections_get_handle(), "signed-on", + disconnect_window, GAIM_CALLBACK(disconnect_connection_change_cb), NULL); + gaim_signal_connect(gaim_connections_get_handle(), "signed-off", + disconnect_window, GAIM_CALLBACK(disconnect_connection_change_cb), NULL); + list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(disconnect_window->treeview))); + /* Add this account to our list of disconnected accounts */ + gtk_list_store_append(list_store, &new_iter); + gtk_list_store_set(list_store, &new_iter, + 1, gaim_account_get_username(gaim_connection_get_account(gc)), + 4, gaim_connection_get_account(gc), -1); + /* Make sure the newly disconnected account is selected */ + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(disconnect_window->treeview)); + gtk_tree_selection_select_iter(sel, &new_iter); + disconnect_window_update_buttons(GTK_TREE_MODEL(list_store)); + * End of disconnected dialog +static GaimConnectionUiOps conn_ui_ops = + gaim_gtk_connection_connect_progress, + gaim_gtk_connection_connected, + gaim_gtk_connection_disconnected, + gaim_gtk_connection_notice, + gaim_gtk_connection_report_disconnect +gaim_gtk_connections_get_ui_ops(void) + * This function needs to be moved out of here once away messages are +void away_on_login(const char *mesg) + GSList *awy = away_messages; + struct away_message *a, *message = NULL; + GaimGtkBuddyList *gtkblist; + gtkblist = GAIM_GTK_BLIST(gaim_get_blist()); + if (!gtkblist->window) { + mesg = gaim_prefs_get_string("/core/away/default_message"); + a = (struct away_message *)awy->data; + if (strcmp(a->name, mesg) == 0) { + message = away_messages->data; + do_away_message(NULL, message); --- a/src/gtkconv.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/gtkconv.c Tue Jan 11 12:27:29 2005 -0500
@@ -1336,7 +1336,7 @@
chat = GAIM_CONV_CHAT(conv);
gtkconv = GAIM_GTK_CONVERSATION(conv);
@@ -1358,6 +1358,7 @@
gaim_conv_chat_ignore(chat, name);
add_chat_buddy_common(conv, name);
@@ -1432,7 +1433,7 @@
-create_chat_menu(GaimConversation *conv, gchar *who,
+create_chat_menu(GaimConversation *conv, const char *who, GaimPluginProtocolInfo *prpl_info, GaimConnection *gc)
static GtkWidget *menu = NULL;
@@ -1450,7 +1451,7 @@
button = gtk_menu_item_new_with_label(_("IM"));
g_signal_connect(G_OBJECT(button), "activate",
G_CALLBACK(menu_chat_im_cb), conv);
- g_object_set_data(G_OBJECT(button), "user_data", who);
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
@@ -1459,7 +1460,7 @@
button = gtk_menu_item_new_with_label(_("Send File"));
g_signal_connect(G_OBJECT(button), "activate",
G_CALLBACK(menu_chat_send_file_cb), conv);
- g_object_set_data(G_OBJECT(button), "user_data", who);
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
@@ -1471,7 +1472,7 @@
g_signal_connect(G_OBJECT(button), "activate",
G_CALLBACK(ignore_cb), conv);
- g_object_set_data(G_OBJECT(button), "user_data", who);
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
@@ -1479,7 +1480,7 @@
button = gtk_menu_item_new_with_label(_("Info"));
g_signal_connect(G_OBJECT(button), "activate",
G_CALLBACK(menu_chat_info_cb), conv);
- g_object_set_data(G_OBJECT(button), "user_data", who);
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
@@ -1488,7 +1489,7 @@
button = gtk_menu_item_new_with_label(_("Get Away Msg"));
g_signal_connect(G_OBJECT(button), "activate",
G_CALLBACK(menu_chat_get_away_cb), conv);
- g_object_set_data(G_OBJECT(button), "user_data", who);
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
@@ -1504,7 +1505,7 @@
g_signal_connect(G_OBJECT(button), "activate",
G_CALLBACK(menu_chat_add_remove_cb), conv);
- g_object_set_data(G_OBJECT(button), "user_data", who);
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
@@ -1547,6 +1548,7 @@
gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
gaim_gtk_treeview_popup_menu_position_func, widget,
@@ -1592,13 +1594,15 @@
if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
} else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
GtkWidget *menu = create_chat_menu (conv, who, prpl_info, gc);
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
event->button, event->time);
+ gtk_tree_path_free(path); @@ -2421,7 +2425,8 @@
if (gaim_conv_window_get_active_conversation(win) == conv &&
- gtkconv->u.im->anim == NULL)
+ (gaim_conversation_get_type(conv) != GAIM_CONV_IM || + gtkconv->u.im->anim == NULL)) status = get_tab_icon(conv, FALSE);
@@ -5197,7 +5202,7 @@
/* If we're whispering, it's not an autoresponse. */
- if (gaim_message_meify(new_message, length)) {
+ if (gaim_message_meify(new_message, -1)) { g_snprintf(str, 1024, "***%s", who_escaped);
strcpy(color, "#6C2585");
@@ -5207,7 +5212,7 @@
- if (gaim_message_meify(new_message, length)) {
+ if (gaim_message_meify(new_message, -1)) { if (flags & GAIM_MESSAGE_AUTO_RESP)
--- a/src/gtkimhtml.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/gtkimhtml.c Tue Jan 11 12:27:29 2005 -0500
@@ -1372,6 +1372,7 @@
case GTK_IMHTML_DRAG_HTML:
@@ -2359,6 +2360,8 @@
gtk_imhtml_toggle_link(imhtml, href);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gtkimhtmltoolbar.c Tue Jan 11 12:27:29 2005 -0500
@@ -0,0 +1,1113 @@
+ * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#include "gtkimhtmltoolbar.h" +static GtkVBoxClass *parent_class = NULL; +static void do_bold(GtkWidget *bold, GtkIMHtmlToolbar *toolbar) + g_return_if_fail(toolbar); + /* block the format_function_toggle handler */ + object = g_object_ref(G_OBJECT(GTK_IMHTML(toolbar->imhtml))); + g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + gtk_imhtml_toggle_bold(GTK_IMHTML(toolbar->imhtml)); + g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + g_object_unref(object); + gtk_widget_grab_focus(toolbar->imhtml); +do_italic(GtkWidget *italic, GtkIMHtmlToolbar *toolbar) + g_return_if_fail(toolbar); + /* block the format_function_toggle handler */ + object = g_object_ref(G_OBJECT(GTK_IMHTML(toolbar->imhtml))); + g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + gtk_imhtml_toggle_italic(GTK_IMHTML(toolbar->imhtml)); + g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + g_object_unref(object); + gtk_widget_grab_focus(toolbar->imhtml); +do_underline(GtkWidget *underline, GtkIMHtmlToolbar *toolbar) + g_return_if_fail(toolbar); + /* block the format_function_toggle handler */ + object = g_object_ref(G_OBJECT(GTK_IMHTML(toolbar->imhtml))); + g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + gtk_imhtml_toggle_underline(GTK_IMHTML(toolbar->imhtml)); + g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, + g_object_unref(object); + gtk_widget_grab_focus(toolbar->imhtml); +do_small(GtkWidget *smalltb, GtkIMHtmlToolbar *toolbar) + g_return_if_fail(toolbar); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->smaller_size))) + gtk_imhtml_font_shrink(GTK_IMHTML(toolbar->imhtml)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smaller_size), FALSE); + gtk_widget_grab_focus(toolbar->imhtml); +do_big(GtkWidget *large, GtkIMHtmlToolbar *toolbar) + g_return_if_fail(toolbar); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->larger_size))) + gtk_imhtml_font_grow(GTK_IMHTML(toolbar->imhtml)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->larger_size), FALSE); + gtk_widget_grab_focus(toolbar->imhtml); +destroy_toolbar_font(GtkWidget *widget, GdkEvent *event, + GtkIMHtmlToolbar *toolbar) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->font), FALSE); + if (toolbar->font_dialog != NULL) + gtk_widget_destroy(toolbar->font_dialog); + toolbar->font_dialog = NULL; +cancel_toolbar_font(GtkWidget *widget, GtkIMHtmlToolbar *toolbar) + destroy_toolbar_font(widget, NULL, toolbar); +static void apply_font(GtkWidget *widget, GtkFontSelection *fontsel) + /* this could be expanded to include font size, weight, etc. + but for now only works with font face */ + GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(fontsel), "gaim_toolbar"); + fontname = gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(fontsel)); + space = strrchr(fontname, ' '); + if(space && isdigit(*(space+1))) + gtk_imhtml_toggle_fontface(GTK_IMHTML(toolbar->imhtml), fontname); + cancel_toolbar_font(NULL, toolbar); +toggle_font(GtkWidget *font, GtkIMHtmlToolbar *toolbar) + g_return_if_fail(toolbar); + fontname = gtk_imhtml_get_current_fontface(GTK_IMHTML(toolbar->imhtml)); + if (!toolbar->font_dialog) { + toolbar->font_dialog = gtk_font_selection_dialog_new(_("Select Font")); + g_object_set_data(G_OBJECT(toolbar->font_dialog), "gaim_toolbar", toolbar); + g_snprintf(fonttif, sizeof(fonttif), "%s 12", fontname); + gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog), + gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog), + g_signal_connect(G_OBJECT(toolbar->font_dialog), "delete_event", + G_CALLBACK(destroy_toolbar_font), toolbar); + g_signal_connect(G_OBJECT(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog)->ok_button), "clicked", + G_CALLBACK(apply_font), toolbar->font_dialog); + g_signal_connect(G_OBJECT(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog)->cancel_button), "clicked", + G_CALLBACK(cancel_toolbar_font), toolbar); + gtk_window_present(GTK_WINDOW(toolbar->font_dialog)); + cancel_toolbar_font(NULL, toolbar); + gtk_widget_grab_focus(toolbar->imhtml); +destroy_toolbar_fgcolor(GtkWidget *widget, GdkEvent *event, + GtkIMHtmlToolbar *toolbar) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->fgcolor), FALSE); + if (toolbar->fgcolor_dialog != NULL) + gtk_widget_destroy(toolbar->fgcolor_dialog); + toolbar->fgcolor_dialog = NULL; +static void cancel_toolbar_fgcolor(GtkWidget *widget, + GtkIMHtmlToolbar *toolbar) + destroy_toolbar_fgcolor(widget, NULL, toolbar); +static void do_fgcolor(GtkWidget *widget, GtkColorSelection *colorsel) + GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "gaim_toolbar"); + open_tag = g_malloc(30); + gtk_color_selection_get_current_color(colorsel, &text_color); + g_snprintf(open_tag, 23, "#%02X%02X%02X", + text_color.green / 256, + text_color.blue / 256); + gtk_imhtml_toggle_forecolor(GTK_IMHTML(toolbar->imhtml), open_tag); + cancel_toolbar_fgcolor(NULL, toolbar); +toggle_fg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) { + char *color = gtk_imhtml_get_current_forecolor(GTK_IMHTML(toolbar->imhtml)); + if (!toolbar->fgcolor_dialog) { + toolbar->fgcolor_dialog = gtk_color_selection_dialog_new(_("Select Text Color")); + colorsel = GTK_COLOR_SELECTION_DIALOG(toolbar->fgcolor_dialog)->colorsel; + gdk_color_parse(color, &fgcolor); + gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &fgcolor); + g_object_set_data(G_OBJECT(colorsel), "gaim_toolbar", toolbar); + g_signal_connect(G_OBJECT(toolbar->fgcolor_dialog), "delete_event", + G_CALLBACK(destroy_toolbar_fgcolor), toolbar); + g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(toolbar->fgcolor_dialog)->ok_button), "clicked", + G_CALLBACK(do_fgcolor), colorsel); + g_signal_connect(G_OBJECT (GTK_COLOR_SELECTION_DIALOG(toolbar->fgcolor_dialog)->cancel_button), "clicked", + G_CALLBACK(cancel_toolbar_fgcolor), toolbar); + gtk_window_present(GTK_WINDOW(toolbar->fgcolor_dialog)); + } else if (toolbar->fgcolor_dialog != NULL) { + cancel_toolbar_fgcolor(color, toolbar); + /* gaim_gtk_advance_past(gtkconv, "<FONT COLOR>", "</FONT>"); */ + gtk_widget_grab_focus(toolbar->imhtml); +destroy_toolbar_bgcolor(GtkWidget *widget, GdkEvent *event, + GtkIMHtmlToolbar *toolbar) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->bgcolor), FALSE); + if (toolbar->bgcolor_dialog != NULL) + gtk_widget_destroy(toolbar->bgcolor_dialog); + toolbar->bgcolor_dialog = NULL; +cancel_toolbar_bgcolor(GtkWidget *widget, GtkIMHtmlToolbar *toolbar) + destroy_toolbar_bgcolor(widget, NULL, toolbar); +static void do_bgcolor(GtkWidget *widget, GtkColorSelection *colorsel) + GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "gaim_toolbar"); + open_tag = g_malloc(30); + gtk_color_selection_get_current_color(colorsel, &text_color); + g_snprintf(open_tag, 23, "#%02X%02X%02X", + text_color.green / 256, + text_color.blue / 256); + gtk_imhtml_toggle_backcolor(GTK_IMHTML(toolbar->imhtml), open_tag); + cancel_toolbar_bgcolor(NULL, toolbar); +toggle_bg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) { + char *color = gtk_imhtml_get_current_backcolor(GTK_IMHTML(toolbar->imhtml)); + if (!toolbar->bgcolor_dialog) { + toolbar->bgcolor_dialog = gtk_color_selection_dialog_new(_("Select Background Color")); + colorsel = GTK_COLOR_SELECTION_DIALOG(toolbar->bgcolor_dialog)->colorsel; + gdk_color_parse(color, &bgcolor); + gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &bgcolor); + g_object_set_data(G_OBJECT(colorsel), "gaim_toolbar", toolbar); + g_signal_connect(G_OBJECT(toolbar->bgcolor_dialog), "delete_event", + G_CALLBACK(destroy_toolbar_bgcolor), toolbar); + g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(toolbar->bgcolor_dialog)->ok_button), "clicked", + G_CALLBACK(do_bgcolor), colorsel); + g_signal_connect(G_OBJECT(GTK_COLOR_SELECTION_DIALOG(toolbar->bgcolor_dialog)->cancel_button), "clicked", + G_CALLBACK(cancel_toolbar_bgcolor), toolbar); + gtk_window_present(GTK_WINDOW(toolbar->bgcolor_dialog)); + } else if (toolbar->bgcolor_dialog != NULL) { + cancel_toolbar_bgcolor(color, toolbar); + /* gaim_gtk_advance_past(gtkconv, "<FONT COLOR>", "</FONT>"); */ + gtk_widget_grab_focus(toolbar->imhtml); +cancel_link_cb(GtkIMHtmlToolbar *toolbar, GaimRequestFields *fields) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE); + toolbar->link_dialog = NULL; +close_link_dialog(GtkIMHtmlToolbar *toolbar) + if (toolbar->link_dialog != NULL) + gaim_request_close(GAIM_REQUEST_FIELDS, toolbar->link_dialog); + toolbar->link_dialog = NULL; +do_insert_link_cb(GtkIMHtmlToolbar *toolbar, GaimRequestFields *fields) + const char *url, *description; + url = gaim_request_fields_get_string(fields, "url"); + if (GTK_IMHTML(toolbar->imhtml)->format_functions & GTK_IMHTML_LINKDESC) + description = gaim_request_fields_get_string(fields, "description"); + if (description == NULL) + gtk_imhtml_insert_link(GTK_IMHTML(toolbar->imhtml), + gtk_text_buffer_get_insert(GTK_IMHTML(toolbar->imhtml)->text_buffer), + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE); + toolbar->link_dialog = NULL; +insert_link_cb(GtkWidget *w, GtkIMHtmlToolbar *toolbar) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link))) { + GaimRequestFields *fields; + GaimRequestFieldGroup *group; + GaimRequestField *field; + GtkTextIter start, end; + fields = gaim_request_fields_new(); + group = gaim_request_field_group_new(NULL); + gaim_request_fields_add_group(fields, group); + field = gaim_request_field_string_new("url", _("_URL"), NULL, FALSE); + gaim_request_field_set_required(field, TRUE); + gaim_request_field_group_add_field(group, field); + if(GTK_IMHTML(toolbar->imhtml)->format_functions & GTK_IMHTML_LINKDESC) { + if (gtk_text_buffer_get_selection_bounds(GTK_IMHTML(toolbar->imhtml)->text_buffer, &start, &end)) { + desc = gtk_imhtml_get_text(GTK_IMHTML(toolbar->imhtml), &start, &end); + field = gaim_request_field_string_new("description", _("_Description"), + gaim_request_field_group_add_field(group, field); + msg = g_strdup(_("Please enter the URL and description of the " + "link that you want to insert. The description " + msg = g_strdup(_("Please enter the URL of the " + "link that you want to insert.")); + gaim_request_fields(toolbar, _("Insert Link"), + _("_Insert"), G_CALLBACK(do_insert_link_cb), + _("Cancel"), G_CALLBACK(cancel_link_cb), + close_link_dialog(toolbar); + gtk_widget_grab_focus(toolbar->imhtml); +do_insert_image_cb(GtkWidget *widget, int response, GtkIMHtmlToolbar *toolbar) + gchar *filename, *name, *buf; +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + if (response != GTK_RESPONSE_ACCEPT) { + if (response != GTK_RESPONSE_OK) { +#endif /* FILECHOOSER */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE); +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); + filename = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(widget))); +#endif /* FILECHOOSER */ + if (filename == NULL) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE); +#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + if (gaim_gtk_check_if_dir(filename, GTK_FILE_SELECTION(widget))) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE); +#endif /* FILECHOOSER */ + /* The following triggers a callback that closes the widget */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE); + if (!g_file_get_contents(filename, &filedata, &size, &error)) { + gaim_notify_error(NULL, NULL, error->message, NULL); + name = strrchr(filename, G_DIR_SEPARATOR) + 1; + id = gaim_imgstore_add(filedata, size, name); + buf = g_strdup_printf(_("Failed to store image: %s\n"), filename); + gaim_notify_error(NULL, NULL, buf, NULL); + ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml))); + gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), + gtk_imhtml_insert_image_at_iter(GTK_IMHTML(toolbar->imhtml), id, &iter); + gaim_imgstore_unref(id); +insert_image_cb(GtkWidget *save, GtkIMHtmlToolbar *toolbar) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image))) { +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + window = gtk_file_chooser_dialog_new(_("Insert Image"), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT); + g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(window)), + "response", G_CALLBACK(do_insert_image_cb), toolbar); + window = gtk_file_selection_new(_("Insert Image")); + gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_OK); + g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(window)), + "response", G_CALLBACK(do_insert_image_cb), toolbar); +#endif /* FILECHOOSER */ + gtk_widget_show(window); + toolbar->image_dialog = window; + gtk_widget_destroy(toolbar->image_dialog); + toolbar->image_dialog = NULL; + gtk_widget_grab_focus(toolbar->imhtml); +close_smiley_dialog(GtkWidget *widget, GdkEvent *event, + GtkIMHtmlToolbar *toolbar) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smiley), FALSE); + if (toolbar->smiley_dialog != NULL) + gtk_widget_destroy(toolbar->smiley_dialog); + toolbar->smiley_dialog = NULL; +insert_smiley_text(GtkWidget *widget, GtkIMHtmlToolbar *toolbar) + char *smiley_text, *escaped_smiley; + smiley_text = g_object_get_data(G_OBJECT(widget), "smiley_text"); + escaped_smiley = gaim_escape_html(smiley_text); + gtk_imhtml_insert_smiley(GTK_IMHTML(toolbar->imhtml), + GTK_IMHTML(toolbar->imhtml)->protocol_name, + g_free(escaped_smiley); + close_smiley_dialog(NULL, NULL, toolbar); +static void add_smiley(GtkIMHtmlToolbar *toolbar, GtkWidget *table, int row, int col, char *filename, char *face) + image = gtk_image_new_from_file(filename); + button = gtk_button_new(); + gtk_container_add(GTK_CONTAINER(button), image); + g_object_set_data(G_OBJECT(button), "smiley_text", face); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(insert_smiley_text), toolbar); + gtk_tooltips_set_tip(toolbar->tooltips, button, face, NULL); + gtk_table_attach_defaults(GTK_TABLE(table), button, col, col+1, row, row+1); + /* these look really weird with borders */ + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_widget_show(button); +static gboolean smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley) { + GtkIMHtmlSmiley *cur = list->data; + if(!strcmp(cur->file, smiley->file)) +insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smiley))) { + GtkWidget *smiley_table = NULL; + GSList *smileys, *unique_smileys = NULL; + if (toolbar->smiley_dialog) { + gtk_widget_grab_focus(toolbar->imhtml); + smileys = get_proto_smileys(toolbar->sml); + smileys = get_proto_smileys(GAIM_PROTO_DEFAULT); + GtkIMHtmlSmiley *smiley = smileys->data; + if(smiley_is_unique(unique_smileys, smiley)) + unique_smileys = g_slist_append(unique_smileys, smiley); + smileys = smileys->next; + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + gtk_window_set_role(GTK_WINDOW(dialog), "smiley_dialog"); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); + if(g_slist_length(unique_smileys)) { + width = floor(sqrt(g_slist_length(unique_smileys))); + smiley_table = gtk_table_new(width, width, TRUE); + while(unique_smileys) { + GtkIMHtmlSmiley *smiley = unique_smileys->data; + add_smiley(toolbar, smiley_table, row, col, smiley->file, smiley->smile); + unique_smileys = unique_smileys->next; + smiley_table = gtk_label_new(_("This theme has no available smileys.")); + gtk_container_add(GTK_CONTAINER(dialog), smiley_table); + gtk_widget_show(smiley_table); + gtk_container_set_border_width(GTK_CONTAINER(dialog), 5); + g_object_set_data(G_OBJECT(dialog), "dialog_type", "smiley dialog"); + g_signal_connect(G_OBJECT(dialog), "delete_event", + G_CALLBACK(close_smiley_dialog), toolbar); + gtk_window_set_title(GTK_WINDOW(dialog), _("Smile!")); + gtk_widget_show_all(dialog); + toolbar->smiley_dialog = dialog; + } else if (toolbar->smiley_dialog) { + close_smiley_dialog(smiley, NULL, toolbar); + gtk_widget_grab_focus(toolbar->imhtml); +static void update_buttons_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar) + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bold), buttons & GTK_IMHTML_BOLD); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->italic), buttons & GTK_IMHTML_ITALIC); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->underline), buttons & GTK_IMHTML_UNDERLINE); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->larger_size), buttons & GTK_IMHTML_GROW); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smaller_size), buttons & GTK_IMHTML_SHRINK); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->font), buttons & GTK_IMHTML_FACE); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->fgcolor), buttons & GTK_IMHTML_FORECOLOR); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bgcolor), buttons & GTK_IMHTML_BACKCOLOR); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->image), buttons & GTK_IMHTML_IMAGE); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->link), buttons & GTK_IMHTML_LINK); + gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smiley), buttons & GTK_IMHTML_SMILEY); +/* we call this when we want to _set_active the toggle button, it'll + * block the callback thats connected to the button so we don't have to + * do the double toggling hack +static void toggle_button_set_active_block(GtkToggleButton *button, + GtkIMHtmlToolbar *toolbar) + g_return_if_fail(toolbar); + object = g_object_ref(button); + g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, toolbar); + gtk_toggle_button_set_active(button, is_active); + g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, toolbar); + g_object_unref(object); +static void toggle_button_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar) + if (buttons & GTK_IMHTML_BOLD) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->bold)), + if (buttons & GTK_IMHTML_ITALIC) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic)), + if (buttons & GTK_IMHTML_UNDERLINE) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline), + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline)), +static void reset_buttons_cb(GtkIMHtml *imhtml, GtkIMHtmlToolbar *toolbar) + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->bold))) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), FALSE, + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic))) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline))) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline), +static void update_buttons(GtkIMHtmlToolbar *toolbar) { + gboolean bold, italic, underline; + bold = italic = underline = FALSE; + gtk_imhtml_get_current_format(GTK_IMHTML(toolbar->imhtml), + &bold, &italic, &underline); + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->bold)) != bold) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), bold, + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic)) != italic) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), italic, + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline)) != underline) + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline), +static void update_format_cb(GtkIMHtml *imhtml, GtkIMHtmlToolbar *toolbar) { + update_buttons(toolbar); +static void mark_set_cb(GtkTextBuffer *buffer, GtkTextIter *location, + GtkTextMark *mark, GtkIMHtmlToolbar *toolbar) + if(mark != gtk_text_buffer_get_insert(buffer)) + update_buttons(toolbar); +/* static guint signals [LAST_SIGNAL] = { 0 }; */ +gtk_imhtmltoolbar_finalize (GObject *object) + GtkIMHtmlToolbar *toolbar = GTK_IMHTMLTOOLBAR(object); + if (toolbar->image_dialog != NULL) + gtk_widget_destroy(toolbar->image_dialog); + toolbar->image_dialog = NULL; + if (toolbar->font_dialog != NULL) + gtk_widget_destroy(toolbar->font_dialog); + toolbar->font_dialog = NULL; + if (toolbar->smiley_dialog != NULL) + gtk_widget_destroy(toolbar->smiley_dialog); + toolbar->smiley_dialog = NULL; + if (toolbar->bgcolor_dialog != NULL) + gtk_widget_destroy(toolbar->bgcolor_dialog); + toolbar->bgcolor_dialog = NULL; + if (toolbar->fgcolor_dialog != NULL) + gtk_widget_destroy(toolbar->fgcolor_dialog); + toolbar->fgcolor_dialog = NULL; + if (toolbar->link_dialog != NULL) + gaim_request_close(GAIM_REQUEST_FIELDS, toolbar->link_dialog); + toolbar->link_dialog = NULL; + gtk_object_sink(GTK_OBJECT(toolbar->tooltips)); + G_OBJECT_CLASS(parent_class)->finalize (object); +static void gtk_imhtmltoolbar_class_init (GtkIMHtmlToolbarClass *class) + GtkObjectClass *object_class; + GObjectClass *gobject_class; + object_class = (GtkObjectClass*) class; + gobject_class = (GObjectClass*) class; + parent_class = gtk_type_class(GTK_TYPE_VBOX); + /* signals[URL_CLICKED] = g_signal_new(url_clicked", + G_TYPE_FROM_CLASS(gobject_class), + G_STRUCT_OFFSET(GtkIMHtmlClass, url_clicked), + g_cclosure_marshal_VOID__POINTER, + gobject_class->finalize = gtk_imhtmltoolbar_finalize; +static void gtk_imhtmltoolbar_init (GtkIMHtmlToolbar *toolbar) + toolbar->imhtml = NULL; + toolbar->font_dialog = NULL; + toolbar->fgcolor_dialog = NULL; + toolbar->bgcolor_dialog = NULL; + toolbar->link_dialog = NULL; + toolbar->smiley_dialog = NULL; + toolbar->image_dialog = NULL; + toolbar->tooltips = gtk_tooltips_new(); + sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); + sep = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(toolbar), sep, FALSE, FALSE, 0); + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(toolbar), hbox, FALSE, FALSE, 0); + button = gaim_pixbuf_toolbar_button_from_stock(GTK_STOCK_BOLD); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, _("Bold"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(do_bold), toolbar); + toolbar->bold = button; + button = gaim_pixbuf_toolbar_button_from_stock(GTK_STOCK_ITALIC); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, _("Italic"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(do_italic), toolbar); + toolbar->italic = button; + button = gaim_pixbuf_toolbar_button_from_stock(GTK_STOCK_UNDERLINE); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, _("Underline"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(do_underline), toolbar); + toolbar->underline = button; + sep = gtk_vseparator_new(); + gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0); + /* Increase font size */ + button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_BIGGER); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, + _("Larger font size"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(do_big), toolbar); + toolbar->larger_size = button; + /* Decrease font size */ + button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_SMALLER); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, + _("Smaller font size"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(do_small), toolbar); + toolbar->smaller_size = button; + sep = gtk_vseparator_new(); + gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0); + button = gaim_pixbuf_toolbar_button_from_stock(GTK_STOCK_SELECT_FONT); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(toggle_font), toolbar); + toolbar->font = button; + button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_FGCOLOR); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, + _("Foreground font color"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(toggle_fg_color), toolbar); + toolbar->fgcolor = button; + button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_BGCOLOR); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, + _("Background color"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(toggle_bg_color), toolbar); + toolbar->bgcolor = button; + sep = gtk_vseparator_new(); + gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0); + button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_LINK); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert link"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(insert_link_cb), toolbar); + toolbar->link = button; + button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_IMAGE); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert image"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(insert_image_cb), toolbar); + toolbar->image = button; + button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_SMILEY); + gtk_size_group_add_widget(sg, button); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_tooltips_set_tip(toolbar->tooltips, button, _("Insert smiley"), NULL); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(insert_smiley_cb), toolbar); + toolbar->smiley = button; + sep = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(toolbar), sep, FALSE, FALSE, 0); + gtk_widget_show_all(hbox); +GtkWidget *gtk_imhtmltoolbar_new() + return GTK_WIDGET(g_object_new(gtk_imhtmltoolbar_get_type(), NULL)); +GType gtk_imhtmltoolbar_get_type() + static GType imhtmltoolbar_type = 0; + if (!imhtmltoolbar_type) { + static const GTypeInfo imhtmltoolbar_info = { + sizeof(GtkIMHtmlToolbarClass), + (GClassInitFunc) gtk_imhtmltoolbar_class_init, + sizeof (GtkIMHtmlToolbar), + (GInstanceInitFunc) gtk_imhtmltoolbar_init + imhtmltoolbar_type = g_type_register_static(GTK_TYPE_VBOX, + "GtkIMHtmlToolbar", &imhtmltoolbar_info, 0); + return imhtmltoolbar_type; +void gtk_imhtmltoolbar_attach(GtkIMHtmlToolbar *toolbar, GtkWidget *imhtml) + GtkIMHtmlButtons buttons; + gboolean bold, italic, underline; + g_return_if_fail(toolbar != NULL); + g_return_if_fail(GTK_IS_IMHTMLTOOLBAR(toolbar)); + g_return_if_fail(imhtml != NULL); + g_return_if_fail(GTK_IS_IMHTML(imhtml)); + toolbar->imhtml = imhtml; + g_signal_connect(G_OBJECT(imhtml), "format_buttons_update", G_CALLBACK(update_buttons_cb), toolbar); + g_signal_connect(G_OBJECT(imhtml), "format_function_toggle", G_CALLBACK(toggle_button_cb), toolbar); + g_signal_connect(G_OBJECT(imhtml), "format_function_clear", G_CALLBACK(reset_buttons_cb), toolbar); + g_signal_connect(G_OBJECT(imhtml), "format_function_update", G_CALLBACK(update_format_cb), toolbar); + g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set", G_CALLBACK(mark_set_cb), toolbar); + buttons = gtk_imhtml_get_format_functions(GTK_IMHTML(imhtml)); + update_buttons_cb(GTK_IMHTML(imhtml), buttons, toolbar); + bold = italic = underline = FALSE; + gtk_imhtml_get_current_format(GTK_IMHTML(imhtml), &bold, &italic, &underline); + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), bold, + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), italic, + toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline), +void gtk_imhtmltoolbar_associate_smileys(GtkIMHtmlToolbar *toolbar, const char *proto_id) + toolbar->sml = g_strdup(proto_id); --- a/src/gtkutils.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/gtkutils.c Tue Jan 11 12:27:29 2005 -0500
@@ -228,18 +228,20 @@
gaim_pixbuf_button_from_stock(const char *text, const char *icon,
GaimButtonOrientation style)
- GtkWidget *button, *image, *label, *bbox, *ibox, *lbox;
+ GtkWidget *button, *image, *label, *bbox, *ibox, *lbox = NULL; button = gtk_button_new();
if (style == GAIM_BUTTON_HORIZONTAL) {
bbox = gtk_hbox_new(FALSE, 0);
ibox = gtk_hbox_new(FALSE, 0);
- lbox = gtk_hbox_new(FALSE, 0);
+ lbox = gtk_hbox_new(FALSE, 0); bbox = gtk_vbox_new(FALSE, 0);
ibox = gtk_vbox_new(FALSE, 0);
- lbox = gtk_vbox_new(FALSE, 0);
+ lbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(button), bbox);
--- a/src/protocols/irc/irc.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/irc/irc.c Tue Jan 11 12:27:29 2005 -0500
@@ -314,6 +314,7 @@
gaim_timeout_remove(irc->timer);
g_hash_table_destroy(irc->cmds);
g_hash_table_destroy(irc->msgs);
+ g_hash_table_destroy(irc->buddies); g_string_free(irc->motd, TRUE);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/irc/msgs.c Tue Jan 11 12:27:29 2005 -0500
@@ -0,0 +1,949 @@
+ * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#include "conversation.h" +static char *irc_mask_nick(const char *mask); +static char *irc_mask_userhost(const char *mask); +static void irc_chat_remove_buddy(GaimConversation *convo, char *data[2]); +static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc); +static char *irc_mask_nick(const char *mask) + end = strchr(mask, '!'); + buf = g_strndup(mask, end - mask); +static char *irc_mask_userhost(const char *mask) + return g_strdup(strchr(mask, '!') + 1); +static void irc_chat_remove_buddy(GaimConversation *convo, char *data[2]) + char *message = g_strdup_printf("quit: %s", data[1]); + if (gaim_conv_chat_find_user(GAIM_CONV_CHAT(convo), data[0])) + gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo), data[0], message); +void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args) + gaim_debug(GAIM_DEBUG_INFO, "irc", "Unrecognized message: %s\n", args[0]); +void irc_msg_away(struct irc_conn *irc, const char *name, const char *from, char **args) + if (irc->whois.nick && !gaim_utf8_strcasecmp(irc->whois.nick, args[1])) { + /* We're doing a whois, show this in the whois dialog */ + irc_msg_whois(irc, name, from, args); + gc = gaim_account_get_connection(irc->account); + serv_got_im(gc, args[1], args[2], GAIM_CONV_IM_AUTO_RESP, time(NULL)); +void irc_msg_badmode(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (!args || !args[1] || !gc) + gaim_notify_error(gc, NULL, _("Bad mode"), args[1]); +void irc_msg_banned(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (!args || !args[1] || !gc) + buf = g_strdup_printf(_("You are banned from %s."), args[1]); + gaim_notify_error(gc, _("Banned"), _("Banned"), buf); +void irc_msg_chanmode(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConversation *convo; + if (!args || !args[1] || !args[2]) + convo = gaim_find_conversation_with_account(args[1], irc->account); + if (!convo) /* XXX punt on channels we are not in for now */ + buf = g_strdup_printf("mode for %s: %s %s", args[1], args[2], args[3] ? args[3] : ""); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), "", buf, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); +void irc_msg_whois(struct irc_conn *irc, const char *name, const char *from, char **args) + if (!irc->whois.nick) { + gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unexpected WHOIS reply for %s\n", args[1]); + if (gaim_utf8_strcasecmp(irc->whois.nick, args[1])) { + gaim_debug(GAIM_DEBUG_WARNING, "irc", "Got WHOIS reply for %s while waiting for %s\n", args[1], irc->whois.nick); + if (!strcmp(name, "301")) { + irc->whois.away = g_strdup(args[2]); + } else if (!strcmp(name, "311")) { + irc->whois.userhost = g_strdup_printf("%s@%s", args[2], args[3]); + irc->whois.name = g_strdup(args[5]); + } else if (!strcmp(name, "312")) { + irc->whois.server = g_strdup(args[2]); + irc->whois.serverinfo = g_strdup(args[3]); + } else if (!strcmp(name, "313")) { + } else if (!strcmp(name, "317")) { + irc->whois.idle = atoi(args[2]); + irc->whois.signon = (time_t)atoi(args[3]); + } else if (!strcmp(name, "319")) { + irc->whois.channels = g_strdup(args[2]); + } else if (!strcmp(name, "320")) { + irc->whois.identified = 1; +void irc_msg_endwhois(struct irc_conn *irc, const char *name, const char *from, char **args) + if (!irc->whois.nick) { + gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unexpected End of WHOIS for %s\n", args[1]); + if (gaim_utf8_strcasecmp(irc->whois.nick, args[1])) { + gaim_debug(GAIM_DEBUG_WARNING, "irc", "Received end of WHOIS for %s, expecting %s\n", args[1], irc->whois.nick); + info = g_string_new(""); + g_string_append_printf(info, _("<b>%s:</b> %s"), _("Nick"), args[1]); + g_string_append_printf(info, "%s%s<br>", + irc->whois.ircop ? _(" <i>(ircop)</i>") : "", + irc->whois.identified ? _(" <i>(identified)</i>") : ""); + char *tmp = g_markup_escape_text(irc->whois.away, strlen(irc->whois.away)); + g_free(irc->whois.away); + g_string_append_printf(info, _("<b>%s:</b> %s<br>"), _("Away"), tmp); + if (irc->whois.userhost) { + char *tmp = g_markup_escape_text(irc->whois.name, strlen(irc->whois.name)); + g_free(irc->whois.name); + g_string_append_printf(info, _("<b>%s:</b> %s<br>"), _("Username"), irc->whois.userhost); + g_string_append_printf(info, _("<b>%s:</b> %s<br>"), _("Realname"), tmp); + g_free(irc->whois.userhost); + if (irc->whois.server) { + g_string_append_printf(info, _("<b>%s:</b> %s"), _("Server"), irc->whois.server); + g_string_append_printf(info, " (%s)<br>", irc->whois.serverinfo); + g_free(irc->whois.server); + g_free(irc->whois.serverinfo); + if (irc->whois.channels) { + g_string_append_printf(info, _("<b>%s:</b> %s<br>"), _("Currently on"), irc->whois.channels); + g_free(irc->whois.channels); + gchar *timex = gaim_str_seconds_to_string(irc->whois.idle); + g_string_append_printf(info, _("<b>Idle for:</b> %s<br>"), timex); + g_string_append_printf(info, _("<b>%s:</b> %s"), _("Online since"), ctime(&irc->whois.signon)); + if (!strcmp(irc->whois.nick, "Paco-Paco")) { + g_string_append_printf(info, _("<br><b>Defining adjective:</b> Glorious<br>")); + gc = gaim_account_get_connection(irc->account); + str = g_string_free(info, FALSE); + g_snprintf(buffer, sizeof(buffer), + _("Buddy Information for %s"), irc->whois.nick); + gaim_notify_userinfo(gc, irc->whois.nick, NULL, buffer, NULL, str, NULL, NULL); + g_free(irc->whois.nick); + memset(&irc->whois, 0, sizeof(irc->whois)); +void irc_msg_list(struct irc_conn *irc, const char *name, const char *from, char **args) + if (!strcmp(name, "321")) { + gaim_roomlist_set_in_progress(irc->roomlist, TRUE); + if (!strcmp(name, "323")) { + gaim_roomlist_set_in_progress(irc->roomlist, FALSE); + gaim_roomlist_unref(irc->roomlist); + if (!strcmp(name, "322")) { + GaimRoomlistRoom *room; + if (!args[0] || !args[1] || !args[2] || !args[3]) + room = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_ROOM, args[1], NULL); + gaim_roomlist_room_add_field(irc->roomlist, room, args[1]); + gaim_roomlist_room_add_field(irc->roomlist, room, GINT_TO_POINTER(strtol(args[2], NULL, 10))); + gaim_roomlist_room_add_field(irc->roomlist, room, args[3]); + gaim_roomlist_room_add(irc->roomlist, room); +void irc_msg_topic(struct irc_conn *irc, const char *name, const char *from, char **args) + char *chan, *topic, *msg, *nick, *tmp, *tmp2; + GaimConversation *convo; + if (!strcmp(name, "topic")) { + topic = irc_mirc2txt (args[1]); + topic = irc_mirc2txt (args[2]); + convo = gaim_find_conversation_with_account(chan, irc->account); + gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got a topic for %s, which doesn't exist\n", chan); + /* If this is an interactive update, print it out */ + tmp = gaim_escape_html(topic); + tmp2 = gaim_markup_linkify(tmp); + if (!strcmp(name, "topic")) { + nick = irc_mask_nick(from); + gaim_conv_chat_set_topic(GAIM_CONV_CHAT(convo), nick, topic); + msg = g_strdup_printf(_("%s has changed the topic to: %s"), nick, tmp2); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), from, msg, GAIM_MESSAGE_SYSTEM, time(NULL)); + msg = g_strdup_printf(_("The topic for %s is: %s"), chan, tmp2); + gaim_conv_chat_set_topic(GAIM_CONV_CHAT(convo), NULL, topic); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), "", msg, GAIM_MESSAGE_SYSTEM, time(NULL)); +void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (!args || !args[1] || !gc) + buf = g_strdup_printf(_("Unknown message '%s'"), args[1]); + gaim_notify_error(gc, _("Unknown message"), buf, _("Gaim has sent a message the IRC server did not understand.")); +void irc_msg_names(struct irc_conn *irc, const char *name, const char *from, char **args) + char *names, *cur, *end, *tmp, *msg; + GaimConversation *convo; + if (!strcmp(name, "366")) { + convo = gaim_find_conversation_with_account(irc->nameconv ? irc->nameconv : args[1], irc->account); + gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got a NAMES list for %s, which doesn't exist\n", args[2]); + g_string_free(irc->names, TRUE); + names = cur = g_string_free(irc->names, FALSE); + msg = g_strdup_printf(_("Users on %s: %s"), args[1], names); + if (gaim_conversation_get_type(convo) == GAIM_CONV_CHAT) + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), "", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + gaim_conv_im_write(GAIM_CONV_IM(convo), "", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + GaimConvChatBuddyFlags f = GAIM_CBFLAGS_NONE; + end = strchr(cur, ' '); + end = cur + strlen(cur); + } else if (*cur == '%') { + f = GAIM_CBFLAGS_HALFOP; + } else if(*cur == '+') { + f = GAIM_CBFLAGS_VOICE; + tmp = g_strndup(cur, end - cur); + users = g_list_append(users, tmp); + flags = g_list_append(flags, GINT_TO_POINTER(f)); + gaim_conv_chat_add_users(GAIM_CONV_CHAT(convo), users, flags); + for (l = users; l != NULL; l = l->next) + irc->names = g_string_new(""); + irc->names = g_string_append(irc->names, args[3]); +void irc_msg_motd(struct irc_conn *irc, const char *name, const char *from, char **args) + if (!strcmp(name, "375")) { + gc = gaim_account_get_connection(irc->account); + gaim_connection_set_display_name(gc, args[0]); + irc->motd = g_string_new(""); + g_string_append_printf(irc->motd, "%s<br>", args[1]); +void irc_msg_endmotd(struct irc_conn *irc, const char *name, const char *from, char **args) + gc = gaim_account_get_connection(irc->account); + gaim_connection_set_state(gc, GAIM_CONNECTED); + serv_finish_login (gc); + irc_blist_timeout(irc); + irc->timer = gaim_timeout_add(45000, (GSourceFunc)irc_blist_timeout, (gpointer)irc); +void irc_msg_nochan(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (gc == NULL || args == NULL || args[1] == NULL) + gaim_notify_error(gc, NULL, _("No such channel"), args[1]); +void irc_msg_nonick(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConversation *convo; + convo = gaim_find_conversation_with_account(args[1], irc->account); + if (gaim_conversation_get_type(convo) == GAIM_CONV_CHAT) /* does this happen? */ + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), args[1], _("no such channel"), + GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + gaim_conv_im_write(GAIM_CONV_IM(convo), args[1], _("User is not logged in"), + GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + if ((gc = gaim_account_get_connection(irc->account)) == NULL) + gaim_notify_error(gc, NULL, _("No such nick or channel"), args[1]); + if (irc->whois.nick && !gaim_utf8_strcasecmp(irc->whois.nick, args[1])) { + g_free(irc->whois.nick); + irc->whois.nick = NULL; +void irc_msg_nosend(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConversation *convo; + convo = gaim_find_conversation_with_account(args[1], irc->account); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), args[1], args[2], GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + if ((gc = gaim_account_get_connection(irc->account)) == NULL) + gaim_notify_error(gc, NULL, _("Could not send"), args[2]); +void irc_msg_notinchan(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConversation *convo = gaim_find_conversation_with_account(args[1], irc->account); + gaim_debug(GAIM_DEBUG_INFO, "irc", "We're apparently not in %s, but tried to use it\n", args[1]); + /*g_slist_remove(irc->gc->buddy_chats, convo); + gaim_conversation_set_account(convo, NULL);*/ + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), args[1], args[2], GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); +void irc_msg_notop(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConversation *convo; + if (!args || !args[1] || !args[2]) + convo = gaim_find_conversation_with_account(args[1], irc->account); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), "", args[2], GAIM_MESSAGE_SYSTEM, time(NULL)); +void irc_msg_invite(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + GHashTable *components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + char *nick = irc_mask_nick(from); + if (!args || !args[1] || !gc) { + g_hash_table_destroy(components); + g_hash_table_insert(components, strdup("channel"), strdup(args[1])); + serv_got_chat_invite(gc, args[1], nick, NULL, components); +void irc_msg_inviteonly(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (!args || !args[1] || !gc) + buf = g_strdup_printf(_("Joining %s requires an invitation."), args[1]); + gaim_notify_error(gc, _("Invitation only"), _("Invitation only"), buf); +void irc_msg_ison(struct irc_conn *irc, const char *name, const char *from, char **args) + nicks = g_strsplit(args[1], " ", -1); + for (i = 0; nicks[i]; i++) { + if ((ib = g_hash_table_lookup(irc->buddies, (gconstpointer)nicks[i])) == NULL) { + g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_status, (gpointer)irc); +static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc) + GaimConnection *gc = gaim_account_get_connection(irc->account); + GaimBuddy *buddy = gaim_find_buddy(irc->account, name); + if (ib->online && !ib->flag) { + serv_got_update(gc, buddy->name, FALSE, 0, 0, 0, 0); + if (!ib->online && ib->flag) { + serv_got_update(gc, buddy->name, TRUE, 0, 0, 0, 0); +void irc_msg_join(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + GaimConversation *convo; + char *nick = irc_mask_nick(from), *userhost; + if (!gaim_utf8_strcasecmp(nick, gaim_connection_get_display_name(gc))) { + /* We are joining a channel for the first time */ + serv_got_joined_chat(gc, id++, args[0]); + convo = gaim_find_conversation_with_account(args[0], irc->account); + gaim_debug(GAIM_DEBUG_ERROR, "irc", "JOIN for %s failed\n", args[0]); + userhost = irc_mask_userhost(from); + gaim_conv_chat_add_user(GAIM_CONV_CHAT(convo), nick, userhost, GAIM_CBFLAGS_NONE, TRUE); + if ((ib = g_hash_table_lookup(irc->buddies, nick)) != NULL) { + irc_buddy_status(nick, ib, irc); +void irc_msg_kick(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + GaimConversation *convo = gaim_find_conversation_with_account(args[0], irc->account); + char *nick = irc_mask_nick(from), *buf; + gaim_debug(GAIM_DEBUG_ERROR, "irc", "Recieved a KICK for unknown channel %s\n", args[0]); + if (!gaim_utf8_strcasecmp(gaim_connection_get_display_name(gc), args[1])) { + buf = g_strdup_printf(_("You have been kicked by %s: (%s)"), nick, args[2]); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), args[0], buf, GAIM_MESSAGE_SYSTEM, time(NULL)); + serv_got_chat_left(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo))); + buf = g_strdup_printf(_("Kicked by %s (%s)"), nick, args[2]); + gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo), args[1], buf); +void irc_msg_mode(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConversation *convo; + char *nick = irc_mask_nick(from), *buf; + if (*args[0] == '#' || *args[0] == '&') { /* Channel */ + convo = gaim_find_conversation_with_account(args[0], irc->account); + gaim_debug(GAIM_DEBUG_ERROR, "irc", "MODE received for %s, which we are not in\n", args[0]); + buf = g_strdup_printf(_("mode (%s %s) by %s"), args[1], args[2] ? args[2] : "", nick); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), args[0], buf, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + GaimConvChatBuddyFlags newflag, flags; + char *mcur, *cur, *end, *user; + while (*cur && *mcur) { + if ((*mcur == '+') || (*mcur == '-')) { + add = (*mcur == '+') ? TRUE : FALSE; + end = strchr(cur, ' '); + end = cur + strlen(cur); + user = g_strndup(cur, end - cur); + flags = gaim_conv_chat_user_get_flags(GAIM_CONV_CHAT(convo), user); + newflag = GAIM_CBFLAGS_NONE; + newflag = GAIM_CBFLAGS_OP; + newflag = GAIM_CBFLAGS_HALFOP; + newflag = GAIM_CBFLAGS_VOICE; + gaim_conv_chat_user_set_flags(GAIM_CONV_CHAT(convo), user, flags); +void irc_msg_nick(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + char *nick = irc_mask_nick(from); + chats = gc->buddy_chats; + if (!gaim_utf8_strcasecmp(nick, gaim_connection_get_display_name(gc))) { + gaim_connection_set_display_name(gc, args[0]); + GaimConvChat *chat = GAIM_CONV_CHAT(chats->data); + if (gaim_conv_chat_find_user(chat, nick)) + gaim_conv_chat_rename_user(chat, nick, args[0]); +void irc_msg_nickused(struct irc_conn *irc, const char *name, const char *from, char **args) + char *newnick, *buf, *end; + newnick = strdup(args[1]); + end = newnick + strlen(newnick) - 1; + /* try three fallbacks */ + if (*end == 2) *end = '3'; + else if (*end == 1) *end = '2'; + buf = irc_format(irc, "vn", "NICK", newnick); +void irc_msg_notice(struct irc_conn *irc, const char *name, const char *from, char **args) + newargs[0] = " notice "; /* The spaces are magic, leave 'em in! */ + irc_msg_privmsg(irc, name, from, newargs); +void irc_msg_nochangenick(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (!args || !args[2] || !gc) + msg = g_strdup_printf(_("Could not change nick")); + gaim_notify_error(gc, _("Cannot change nick"), msg, args[2]); +void irc_msg_part(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + GaimConversation *convo; + if (!args || !args[0] || !gc) + convo = gaim_find_conversation_with_account(args[0], irc->account); + gaim_debug(GAIM_DEBUG_INFO, "irc", "Got a PART on %s, which doesn't exist -- probably closed\n", args[0]); + nick = irc_mask_nick(from); + if (!gaim_utf8_strcasecmp(nick, gaim_connection_get_display_name(gc))) { + msg = g_strdup_printf(_("You have parted the channel%s%s"), + (args[1] && *args[1]) ? ": " : "", args[1]); + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), args[0], msg, GAIM_MESSAGE_SYSTEM, time(NULL)); + serv_got_chat_left(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo))); + gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo), nick, args[1]); +void irc_msg_ping(struct irc_conn *irc, const char *name, const char *from, char **args) + buf = irc_format(irc, "v:", "PONG", args[0]); +void irc_msg_pong(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConversation *convo; + parts = g_strsplit(args[1], " ", 2); + if (!parts[0] || !parts[1]) { + if (sscanf(parts[1], "%lu", &oldstamp) != 1) { + msg = g_strdup(_("Error: invalid PONG from server")); + msg = g_strdup_printf(_("PING reply -- Lag: %lu seconds"), time(NULL) - oldstamp); + convo = gaim_find_conversation_with_account(parts[0], irc->account); + if (gaim_conversation_get_type (convo) == GAIM_CONV_CHAT) + gaim_conv_chat_write(GAIM_CONV_CHAT(convo), "PONG", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + gaim_conv_im_write(GAIM_CONV_IM(convo), "PONG", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL)); + gc = gaim_account_get_connection(irc->account); + gaim_notify_info(gc, NULL, "PONG", msg); +void irc_msg_privmsg(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + GaimConversation *convo; + char *nick = irc_mask_nick(from), *tmp, *msg; + if (!args || !args[0] || !args[1] || !gc) { + notice = !strcmp(args[0], " notice "); + tmp = irc_parse_ctcp(irc, nick, args[0], args[1], notice); + msg = gaim_escape_html(tmp); + tmp = irc_mirc2html(msg); + tmp = g_strdup_printf("(notice) %s", msg); + if (!gaim_utf8_strcasecmp(args[0], gaim_connection_get_display_name(gc))) { + serv_got_im(gc, nick, msg, 0, time(NULL)); + serv_got_im(gc, nick, msg, 0, time(NULL)); + convo = gaim_find_conversation_with_account(args[0], irc->account); + serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)), nick, 0, msg, time(NULL)); + gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got a PRIVMSG on %s, which does not exist\n", args[0]); +void irc_msg_regonly(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (!args || !args[1] || !args[2] || !gc) + msg = g_strdup_printf(_("Cannot join %s:"), args[1]); + gaim_notify_error(gc, _("Cannot join channel"), msg, args[2]); +void irc_msg_quit(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + if (!args || !args[0] || !gc) + data[0] = irc_mask_nick(from); + /* XXX this should have an API, I shouldn't grab this directly */ + g_slist_foreach(gc->buddy_chats, (GFunc)irc_chat_remove_buddy, data); + if ((ib = g_hash_table_lookup(irc->buddies, data[0])) != NULL) { + irc_buddy_status(data[0], ib, irc); +void irc_msg_wallops(struct irc_conn *irc, const char *name, const char *from, char **args) + GaimConnection *gc = gaim_account_get_connection(irc->account); + char *nick, *msg, *wallop; + if (!args || !args[0] || !gc) + nick = irc_mask_nick(from); + msg = g_strdup_printf (_("Wallops from %s"), nick); + wallop = g_markup_escape_text(args[0], strlen(args[0])); + gaim_notify_info(gc, NULL, msg, wallop); +void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args) --- a/src/protocols/irc/parse.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/irc/parse.c Tue Jan 11 12:27:29 2005 -0500
@@ -235,8 +235,10 @@
enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET);
encodings = g_strsplit(enclist, ",", -1);
- if (encodings[0] == NULL)
+ if (encodings[0] == NULL) { return gaim_utf8_salvage(string);
for (i = 0; encodings[i] != NULL; i++) {
@@ -255,6 +257,7 @@
return gaim_utf8_salvage(string);
--- a/src/protocols/jabber/chat.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/jabber/chat.c Tue Jan 11 12:27:29 2005 -0500
@@ -292,6 +292,8 @@
+ g_hash_table_destroy(chat->members); --- a/src/protocols/jabber/jabber.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/jabber/jabber.c Tue Jan 11 12:27:29 2005 -0500
@@ -109,6 +109,7 @@
if((my_jb = jabber_buddy_find(js, full_jid, TRUE)))
my_jb->subscription |= JABBER_SUB_BOTH;
char *msg = jabber_parse_error(js, packet);
--- a/src/protocols/msn/cmdproc.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/cmdproc.c Tue Jan 11 12:27:29 2005 -0500
@@ -49,6 +49,10 @@
g_queue_free(cmdproc->txqueue);
msn_history_destroy(cmdproc->history);
+ if (cmdproc->last_cmd != NULL) + msn_command_destroy(cmdproc->last_cmd); --- a/src/protocols/msn/httpconn.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/httpconn.c Tue Jan 11 12:27:29 2005 -0500
@@ -68,6 +68,15 @@
msn_httpconn_disconnect(httpconn);
+ if (httpconn->full_session_id != NULL) + g_free(httpconn->full_session_id); + if (httpconn->session_id != NULL) + g_free(httpconn->session_id); + if (httpconn->host != NULL) + g_free(httpconn->host); @@ -669,17 +678,17 @@
- if (httpconn->full_session_id != NULL);
+ if (httpconn->full_session_id != NULL) g_free(httpconn->full_session_id);
httpconn->full_session_id = full_session_id;
- if (httpconn->session_id != NULL);
+ if (httpconn->session_id != NULL) g_free(httpconn->session_id);
httpconn->session_id = session_id;
- if (httpconn->host != NULL);
+ if (httpconn->host != NULL) @@ -689,6 +698,7 @@
servconn = httpconn->servconn;
@@ -696,6 +706,7 @@
--- a/src/protocols/msn/msn.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/msn.c Tue Jan 11 12:27:29 2005 -0500
@@ -47,8 +47,6 @@
-#define BUDDY_ALIAS_MAXLEN 387
--- a/src/protocols/msn/msn.h Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/msn.h Tue Jan 11 12:27:29 2005 -0500
@@ -69,6 +69,7 @@
#define USEROPT_HOTMAIL 0
+#define BUDDY_ALIAS_MAXLEN 387 #define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}"
--- a/src/protocols/msn/nexus.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/nexus.c Tue Jan 11 12:27:29 2005 -0500
@@ -132,7 +132,8 @@
session = nexus->session;
g_return_if_fail(session != NULL);
- msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
+ if (!session->logged_in) + msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE); g_strdup(gaim_url_encode(gaim_account_get_username(session->account)));
@@ -312,7 +313,8 @@
session = nexus->session;
g_return_if_fail(session != NULL);
- msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH);
+ if (!session->logged_in) + msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH); request_str = g_strdup_printf("GET /rdr/pprdr.asp\r\n\r\n");
--- a/src/protocols/msn/notification.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/notification.c Tue Jan 11 12:27:29 2005 -0500
@@ -111,7 +111,8 @@
vers = g_strjoinv(" ", a);
- msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE);
+ if (!session->logged_in) + msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE); msn_cmdproc_send(cmdproc, "VER", "%s", vers);
@@ -199,7 +200,8 @@
cmdproc = session->notification->cmdproc;
- msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
+ if (!session->logged_in) + msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END); msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);
@@ -233,7 +235,8 @@
gaim_connection_set_display_name(gc, friendly);
- msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN);
+ if (!session->logged_in) + msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN); msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
@@ -258,7 +261,8 @@
- msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START);
+ if (!session->logged_in) + msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START); msn_nexus_connect(session->nexus);
--- a/src/protocols/msn/session.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/session.c Tue Jan 11 12:27:29 2005 -0500
@@ -366,6 +366,9 @@
account = session->account;
gc = gaim_account_get_connection(account);
+ if (session->logged_in) msn_user_set_buddy_icon(session->user,
gaim_account_get_buddy_icon(session->account));
--- a/src/protocols/msn/slpcall.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/slpcall.c Tue Jan 11 12:27:29 2005 -0500
@@ -192,9 +192,9 @@
- gaim_debug_info("msn", "slpcall timeout (%p)\n", slpcall);
+ gaim_debug_info("msn", "slpcall timeout (%p)\n", slpcall); if (!slpcall->pending && !slpcall->progress)
--- a/src/protocols/msn/switchboard.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/switchboard.c Tue Jan 11 12:27:29 2005 -0500
@@ -352,7 +352,7 @@
char *body_str, *body_enc, *pre, *post;
+ const char *str_reason; if (swboard->conv == NULL)
@@ -651,7 +651,10 @@
msn_switchboard_report_user(swboard, GAIM_MESSAGE_SYSTEM, str);
msn_switchboard_destroy(swboard);
--- a/src/protocols/msn/userlist.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/msn/userlist.c Tue Jan 11 12:27:29 2005 -0500
@@ -467,6 +467,7 @@
msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group)
userlist->groups = g_list_remove(userlist->groups, group);
+ msn_group_destroy(group); @@ -630,6 +631,13 @@
store_name = (user != NULL) ? get_store_name(user) : who;
+ /* this might be a bit of a hack, but it should prevent notification server + * disconnections for people who have buddies with insane friendly names + * who added you to their buddy list from being disconnected. Stu. */ + /* ... No, that sentence didn't parse for me either. Stu. */ + if (strlen(store_name) > BUDDY_ALIAS_MAXLEN) /* Then request the add to the server. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/msgcookie.c Tue Jan 11 12:27:29 2005 -0500
@@ -0,0 +1,190 @@
+ * Cookie Caching stuff. Adam wrote this, apparently just some + * derivatives of n's SNAC work. I cleaned it up, added comments. + * I'm assuming that cookies are type-specific. that is, we can have + * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we + * lose some error checking. if we assume cookies are not type-specific and are + * wrong, we get quirky behavior when cookies step on each others' toes. + * aim_cachecookie - appends a cookie to the cookie list + * if cookie->cookie for type cookie->type is found, updates the + * ->addtime of the found structure; otherwise adds the given cookie + * @param sess session to add to + * @param cookie pointer to struct to append + * @return returns -1 on error, 0 on append, 1 on update. the cookie you pass + * in may be free'd, so don't count on its value after calling this! +faim_internal int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie) + aim_msgcookie_t *newcook; + newcook = aim_checkcookie(sess, cookie->cookie, cookie->type); + if (newcook == cookie) { + newcook->addtime = time(NULL); + aim_cookie_free(sess, newcook); + cookie->addtime = time(NULL); + cookie->next = sess->msgcookies; + sess->msgcookies = cookie; + * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list) + * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process. + * @param sess session to grab cookie from + * @param cookie cookie string to look for + * @param type cookie type to look for + * @return if found, returns the struct; if none found (or on error), returns NULL: +faim_internal aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, fu8_t *cookie, int type) + aim_msgcookie_t *cur, **prev; + if (!cookie || !sess->msgcookies) + for (prev = &sess->msgcookies; (cur = *prev); ) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) { + * aim_mkcookie - generate an aim_msgcookie_t *struct from a cookie string, a type, and a data pointer. + * @param c pointer to the cookie string array + * @param type cookie type to use + * @param data data to be cached with the cookie + * @return returns NULL on error, a pointer to the newly-allocated +faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *c, int type, void *data) + aim_msgcookie_t *cookie; + if (!(cookie = calloc(1, sizeof(aim_msgcookie_t)))) + memcpy(cookie->cookie, c, 8); + * aim_checkcookie - check to see if a cookietuple has been cached + * @param sess session to check for the cookie in + * @param cookie pointer to the cookie string array + * @param type type of the cookie to look for + * @return returns a pointer to the cookie struct (still in the list) + * on success; returns NULL on error/not found +faim_internal aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const fu8_t *cookie, int type) + for (cur = sess->msgcookies; cur; cur = cur->next) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) +#if 0 /* debugging feature */ +faim_internal int aim_dumpcookie(aim_session_t *sess, aim_msgcookie_t *cookie) + faimdprintf(sess, 0, "\tCookie at %p: %d/%s with %p, next %p\n", cookie, + cookie->type, cookie->cookie, cookie->data, cookie->next); + * aim_cookie_free - free an aim_msgcookie_t struct + * this function removes the cookie *cookie from the list of cookies + * in sess, and then frees all memory associated with it. including + * its data! if you want to use the private data after calling this, + * make sure you copy it first. + * @param sess session to remove the cookie from + * @param cookie the address of a pointer to the cookie struct to remove + * @return returns -1 on error, 0 on success. +faim_internal int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie) + aim_msgcookie_t *cur, **prev; + for (prev = &sess->msgcookies; (cur = *prev); ) { +faim_internal int aim_msgcookie_gettype(int reqclass) + /* XXX: hokey-assed. needs fixed. */ + case AIM_CAPS_BUDDYICON: return AIM_COOKIETYPE_OFTICON; + case AIM_CAPS_TALK: return AIM_COOKIETYPE_OFTVOICE; + case AIM_CAPS_DIRECTIM: return AIM_COOKIETYPE_OFTIMAGE; + case AIM_CAPS_CHAT: return AIM_COOKIETYPE_CHAT; + case AIM_CAPS_GETFILE: return AIM_COOKIETYPE_OFTGET; + case AIM_CAPS_SENDFILE: return AIM_COOKIETYPE_OFTSEND; + default: return AIM_COOKIETYPE_UNKNOWN; --- a/src/protocols/oscar/oscar.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/oscar/oscar.c Tue Jan 11 12:27:29 2005 -0500
@@ -1209,7 +1209,8 @@
/* for each valid image tag... */
while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
const char *id, *src, *datasize;
- const char *tag = NULL, *data = NULL;
+ const char *data = NULL; @@ -1229,6 +1230,8 @@
if (tag && (data = gaim_strcasestr(binary, tag)))
/* check the data is here and store it */
if (data + (size = atoi(datasize)) <= msgend)
imgid = gaim_imgstore_add(data, size, src);
--- a/src/protocols/yahoo/util.c Sun Jan 09 14:16:11 2005 -0500
+++ b/src/protocols/yahoo/util.c Tue Jan 11 12:27:29 2005 -0500
@@ -46,7 +46,7 @@
struct yahoo_data *yd = gc->proto_data;
+ const char *to_codeset; if (yd->jp && utf8 && *utf8)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/yahoo/yahoochat.c Tue Jan 11 12:27:29 2005 -0500
@@ -0,0 +1,1484 @@
+ * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * Some code copyright 2003 Tim Ringenbach <omarvo@hotmail.com> + * (marv on irc.freenode.net) + * Some code borrowed from libyahoo2, copyright (C) 2002, Philip + * S Tellis <philip . tellis AT gmx . net> + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#include "conversation.h" +#define YAHOO_CHAT_ID (1) +static void yahoo_chat_leave(GaimConnection *gc, const char *room, const char *dn, gboolean logout); +/* special function to log us on to the yahoo chat service */ +static void yahoo_chat_online(GaimConnection *gc) + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + ycht_connection_open(gc); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE,0); + yahoo_packet_hash(pkt, 1, gaim_connection_get_display_name(gc)); + yahoo_packet_hash(pkt, 109, gaim_connection_get_display_name(gc)); + yahoo_packet_hash(pkt, 6, "abcde"); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +/* this is slow, and different from the gaim_* version in that it (hopefully) won't add a user twice */ +void yahoo_chat_add_users(GaimConvChat *chat, GList *newusers) + for (i = newusers; i; i = i->next) { + if (gaim_conv_chat_find_user(chat, i->data)) + gaim_conv_chat_add_user(chat, i->data, NULL, GAIM_CBFLAGS_NONE, TRUE); +void yahoo_chat_add_user(GaimConvChat *chat, const char *user, const char *reason) + if (gaim_conv_chat_find_user(chat, user)) + gaim_conv_chat_add_user(chat, user, reason, GAIM_CBFLAGS_NONE, TRUE); +static GaimConversation *yahoo_find_conference(GaimConnection *gc, const char *name) + for (l = yd->confs; l; l = l->next) { + GaimConversation *c = l->data; + if (!gaim_utf8_strcasecmp(gaim_conversation_get_name(c), name)) +void yahoo_process_conference_invite(GaimConnection *gc, struct yahoo_packet *pkt) + GString *members = NULL; + GHashTable *components; + members = g_string_sized_new(512); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + case 1: /* us, but we already know who we are */ + room = yahoo_string_decode(gc, pair->value, FALSE); + g_string_append_printf(members, "%s\n", who); + case 52: /* invitee (me) */ + g_string_append_printf(members, "%s\n", pair->value); + msg = yahoo_string_decode(gc, pair->value, FALSE); + g_string_free(members, TRUE); + components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_replace(components, g_strdup("room"), room); + g_hash_table_replace(components, g_strdup("topic"), msg); + g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference")); + g_hash_table_replace(components, g_strdup("members"), g_strdup(members->str)); + serv_got_chat_invite(gc, room, who, msg, components); + g_string_free(members, TRUE); +void yahoo_process_conference_decline(GaimConnection *gc, struct yahoo_packet *pkt) + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + room = yahoo_string_decode(gc, pair->value, FALSE); + msg = yahoo_string_decode(gc, pair->value, FALSE); + /* make sure we're in the room before we process a decline message for it */ + if(yahoo_find_conference(gc, room)) { + tmp = g_strdup_printf(_("%s declined your conference invitation to room \"%s\" because \"%s\"."), + who, room, msg?msg:""); + gaim_notify_info(gc, NULL, _("Invitation Rejected"), tmp); +void yahoo_process_conference_logon(GaimConnection *gc, struct yahoo_packet *pkt) + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + room = yahoo_string_decode(gc, pair->value, FALSE); + c = yahoo_find_conference(gc, room); + yahoo_chat_add_user(GAIM_CONV_CHAT(c), who, NULL); +void yahoo_process_conference_logoff(GaimConnection *gc, struct yahoo_packet *pkt) + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + room = yahoo_string_decode(gc, pair->value, FALSE); + c = yahoo_find_conference(gc, room); + gaim_conv_chat_remove_user(GAIM_CONV_CHAT(c), who, NULL); +void yahoo_process_conference_message(GaimConnection *gc, struct yahoo_packet *pkt) + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + room = yahoo_string_decode(gc, pair->value, FALSE); + utf8 = strtol(pair->value, NULL, 10); + if (room && who && msg) { + msg2 = yahoo_string_decode(gc, msg, utf8); + c = yahoo_find_conference(gc, room); + msg = yahoo_codes_to_html(msg2); + serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(c)), who, 0, msg, time(NULL)); +/* this is a confirmation of yahoo_chat_online(); */ +void yahoo_process_chat_online(GaimConnection *gc, struct yahoo_packet *pkt) + struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data; +/* this is basicly the opposite of chat_online */ +void yahoo_process_chat_logout(GaimConnection *gc, struct yahoo_packet *pkt) + struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (g_ascii_strcasecmp(pair->value, + gaim_connection_get_display_name(gc))) + if (pkt->status == 1) { + yahoo_c_leave(gc, YAHOO_CHAT_ID); +void yahoo_process_chat_join(GaimConnection *gc, struct yahoo_packet *pkt) + struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data; + GaimConversation *c = NULL; + char *someid, *someotherid, *somebase64orhashosomething, *somenegativenumber; + if (pkt->status == -1) { + gaim_notify_error(gc, NULL, _("Failed to join chat"), _("Maybe the room is full?")); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + room = yahoo_string_decode(gc, pair->value, TRUE); + topic = yahoo_string_decode(gc, pair->value, TRUE); + case 108: /* number of joiners */ + someotherid = pair->value; + somebase64orhashosomething = pair->value; + somenegativenumber = pair->value; + case 13: /* this is 1. maybe its the type of room? (normal, user created, private, etc?) */ + case 61: /*this looks similar to 130 */ + /* the previous section was just room info. this next section is + info about individual room members, (including us) */ + case 109: /* the yahoo id */ + members = g_list_append(members, pair->value); + case 141: /* nickname */ + case 142: /* location */ + case 113: /* bitmask */ + if (room && yd->chat_name && gaim_utf8_strcasecmp(room, yd->chat_name)) + yahoo_chat_leave(gc, room, + gaim_connection_get_display_name(gc), FALSE); + c = gaim_find_chat(gc, YAHOO_CHAT_ID); + if (room && (!c || gaim_conv_chat_has_left(GAIM_CONV_CHAT(c))) && members && + ((g_list_length(members) > 1) || + !g_ascii_strcasecmp(members->data, gaim_connection_get_display_name(gc)))) { + for (i = 0; i < g_list_length(members); i++) + flags = g_list_append(flags, GINT_TO_POINTER(GAIM_CBFLAGS_NONE)); + if (c && gaim_conv_chat_has_left(GAIM_CONV_CHAT(c))) { + /* this might be a hack, but oh well, it should nicely */ + gaim_conversation_set_name(c, room); + c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room); + gaim_conv_chat_set_topic(GAIM_CONV_CHAT(c), NULL, topic); + yd->chat_name = g_strdup(room); + gaim_conv_chat_add_users(GAIM_CONV_CHAT(c), members, flags); + tmpmsg = g_strdup_printf(_("You are now chatting in %s."), room); + gaim_conv_chat_write(GAIM_CONV_CHAT(c), "", tmpmsg, GAIM_MESSAGE_SYSTEM, time(NULL)); + c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room); + gaim_conv_chat_set_topic(GAIM_CONV_CHAT(c), NULL, topic); + yd->chat_name = g_strdup(room); + gaim_conv_chat_add_users(GAIM_CONV_CHAT(c), members, flags); + yahoo_chat_add_users(GAIM_CONV_CHAT(c), members); +void yahoo_process_chat_exit(GaimConnection *gc, struct yahoo_packet *pkt) + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + room = yahoo_string_decode(gc, pair->value, TRUE); + GaimConversation *c = gaim_find_chat(gc, YAHOO_CHAT_ID); + if (c && !gaim_utf8_strcasecmp(gaim_conversation_get_name(c), room)) + gaim_conv_chat_remove_user(GAIM_CONV_CHAT(c), who, NULL); +void yahoo_process_chat_message(GaimConnection *gc, struct yahoo_packet *pkt) + char *room = NULL, *who = NULL, *msg = NULL, *msg2; + int msgtype = 1, utf8 = 1; /* default to utf8 */ + GaimConversation *c = NULL; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + utf8 = strtol(pair->value, NULL, 10); + room = yahoo_string_decode(gc, pair->value, TRUE); + msgtype = strtol(pair->value, NULL, 10); + c = gaim_find_chat(gc, YAHOO_CHAT_ID); + /* we still get messages after we part, funny that */ + gaim_debug(GAIM_DEBUG_MISC, "yahoo", "Got a message packet with no message.\nThis probably means something important, but we're ignoring it.\n"); + msg2 = yahoo_string_decode(gc, msg, utf8); + msg = yahoo_codes_to_html(msg2); + if (msgtype == 2 || msgtype == 3) { + tmp = g_strdup_printf("/me %s", msg); + serv_got_chat_in(gc, YAHOO_CHAT_ID, who, 0, msg, time(NULL)); +void yahoo_process_chat_addinvite(GaimConnection *gc, struct yahoo_packet *pkt) + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + room = yahoo_string_decode(gc, pair->value, TRUE); + case 129: /* room id? */ + msg = yahoo_string_decode(gc, pair->value, FALSE); + GHashTable *components; + components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_replace(components, g_strdup("room"), g_strdup(room)); + serv_got_chat_invite(gc, room, who, msg, components); +void yahoo_process_chat_goto(GaimConnection *gc, struct yahoo_packet *pkt) + gaim_notify_error(gc, NULL, _("Failed to join buddy in chat"), + _("Maybe they're not in a chat?")); + * Functions dealing with conferences + * I think conference names are always ascii. +void yahoo_conf_leave(struct yahoo_data *yd, const char *room, const char *dn, GList *who) + struct yahoo_packet *pkt; + gaim_debug_misc("yahoo", "leaving conference %s\n", room); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, dn); + for (w = who; w; w = w->next) { + const char *name = gaim_conv_chat_cb_get_name(w->data); + yahoo_packet_hash(pkt, 3, name); + yahoo_packet_hash(pkt, 57, room); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +static int yahoo_conf_send(GaimConnection *gc, const char *dn, const char *room, + GList *members, const char *what) + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + msg = yahoo_html_to_codes(what); + msg2 = yahoo_string_encode(gc, msg, &utf8); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, dn); + for (who = members; who; who = who->next) { + const char *name = gaim_conv_chat_cb_get_name(who->data); + yahoo_packet_hash(pkt, 53, name); + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 14, msg2); + yahoo_packet_hash(pkt, 97, "1"); /* utf-8 */ + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +static void yahoo_conf_join(struct yahoo_data *yd, GaimConversation *c, const char *dn, const char *room, + const char *topic, const char *members) + struct yahoo_packet *pkt; + memarr = g_strsplit(members, "\n", 0); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, dn); + yahoo_packet_hash(pkt, 3, dn); + yahoo_packet_hash(pkt, 57, room); + for(i = 0 ; memarr[i]; i++) { + if (!strcmp(memarr[i], "") || !strcmp(memarr[i], dn)) + yahoo_packet_hash(pkt, 3, memarr[i]); + gaim_conv_chat_add_user(GAIM_CONV_CHAT(c), memarr[i], NULL, GAIM_CBFLAGS_NONE, TRUE); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +static void yahoo_conf_invite(GaimConnection *gc, GaimConversation *c, + const char *dn, const char *buddy, const char *room, const char *msg) + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + msg2 = yahoo_string_encode(gc, msg, NULL); + members = gaim_conv_chat_get_users(GAIM_CONV_CHAT(c)); + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, dn); + yahoo_packet_hash(pkt, 51, buddy); + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 58, msg?msg2:""); + yahoo_packet_hash(pkt, 13, "0"); + for(; members; members = members->next) { + const char *name = gaim_conv_chat_cb_get_name(members->data); + yahoo_packet_hash(pkt, 52, name); + yahoo_packet_hash(pkt, 53, name); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); + * Functions dealing with chats +static void yahoo_chat_leave(GaimConnection *gc, const char *room, const char *dn, gboolean logout) + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + g_return_if_fail(yd->ycht != NULL); + ycht_chat_leave(yd->ycht, room, logout); + eroom = yahoo_string_encode(gc, room, &utf8); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATEXIT, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 104, eroom); + yahoo_packet_hash(pkt, 109, dn); + yahoo_packet_hash(pkt, 108, "1"); + yahoo_packet_hash(pkt, 112, "0"); /* what does this one mean? */ + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); + if ((c = gaim_find_chat(gc, YAHOO_CHAT_ID))) + serv_got_chat_left(gc, YAHOO_CHAT_ID); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, + YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, dn); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +/* borrowed from gtkconv.c */ +meify(char *message, size_t len) + * Read /me-ify: If the message (post-HTML) starts with /me, + * remove the "/me " part of it (including that space) and return TRUE. + gboolean inside_html = 0; + /* Umm.. this would be very bad if this happens. */ + g_return_val_if_fail(message != NULL, FALSE); + for (c = message; *c != '\0'; c++, len--) { + if (*c != '\0' && !g_ascii_strncasecmp(c, "/me ", 4)) { + memmove(c, c + 4, len - 3); +static int yahoo_chat_send(GaimConnection *gc, const char *dn, const char *room, const char *what) + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + char *msg1, *msg2, *room2; + g_return_val_if_fail(yd->ycht != NULL, 1); + return ycht_chat_send(yd->ycht, room, what); + msg2 = yahoo_html_to_codes(msg1); + msg1 = yahoo_string_encode(gc, msg2, &utf8); + room2 = yahoo_string_encode(gc, room, NULL); + pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, dn); + yahoo_packet_hash(pkt, 104, room2); + yahoo_packet_hash(pkt, 117, msg1); + yahoo_packet_hash(pkt, 124, "2"); + yahoo_packet_hash(pkt, 124, "1"); + /* fixme: what about /think? (124=3) */ + yahoo_packet_hash(pkt, 97, "1"); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +static void yahoo_chat_join(GaimConnection *gc, const char *dn, const char *room, const char *topic) + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + g_return_if_fail(yd->ycht != NULL); + ycht_chat_join(yd->ycht, room); + /* apparently room names are always utf8, or else always not utf8, + * so we don't have to actually pass the flag in the packet. Or something. */ + room2 = yahoo_string_encode(gc, room, &utf8); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, gaim_connection_get_display_name(gc)); + yahoo_packet_hash(pkt, 62, "2"); + yahoo_packet_hash(pkt, 104, room2); + yahoo_packet_hash(pkt, 129, "0"); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +static void yahoo_chat_invite(GaimConnection *gc, const char *dn, const char *buddy, + const char *room, const char *msg) + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + char *room2, *msg2 = NULL; + g_return_if_fail(yd->ycht != NULL); + ycht_chat_send_invite(yd->ycht, room, buddy, msg); + room2 = yahoo_string_encode(gc, room, &utf8); + msg2 = yahoo_string_encode(gc, msg, NULL); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATADDINVITE, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, dn); + yahoo_packet_hash(pkt, 118, buddy); + yahoo_packet_hash(pkt, 104, room2); + yahoo_packet_hash(pkt, 117, (msg2?msg2:"")); + yahoo_packet_hash(pkt, 129, "0"); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +void yahoo_chat_goto(GaimConnection *gc, const char *name) + struct yahoo_packet *pkt; + g_return_if_fail(yd->ycht != NULL); + ycht_chat_goto_user(yd->ycht, name); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATGOTO, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 109, name); + yahoo_packet_hash(pkt, 1, gaim_connection_get_display_name(gc)); + yahoo_packet_hash(pkt, 62, "2"); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); + * These are the functions registered with the core + * which get called for both chats and conferences. +void yahoo_c_leave(GaimConnection *gc, int id) + struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data; + c = gaim_find_chat(gc, id); + if (id != YAHOO_CHAT_ID) { + yahoo_conf_leave(yd, gaim_conversation_get_name(c), + gaim_connection_get_display_name(gc), gaim_conv_chat_get_users(GAIM_CONV_CHAT(c))); + yd->confs = g_slist_remove(yd->confs, c); + yahoo_chat_leave(gc, gaim_conversation_get_name(c), gaim_connection_get_display_name(gc), TRUE); + serv_got_chat_left(gc, id); +int yahoo_c_send(GaimConnection *gc, int id, const char *what) + yd = (struct yahoo_data *) gc->proto_data; + c = gaim_find_chat(gc, id); + if (id != YAHOO_CHAT_ID) { + ret = yahoo_conf_send(gc, gaim_connection_get_display_name(gc), + gaim_conversation_get_name(c), gaim_conv_chat_get_users(GAIM_CONV_CHAT(c)), what); + ret = yahoo_chat_send(gc, gaim_connection_get_display_name(gc), + gaim_conversation_get_name(c), what); + serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(c)), + gaim_connection_get_display_name(gc), 0, what, time(NULL)); +GList *yahoo_c_info(GaimConnection *gc) + struct proto_chat_entry *pce; + pce = g_new0(struct proto_chat_entry, 1); + pce->label = _("_Room:"); + pce->identifier = "room"; + m = g_list_append(m, pce); +GHashTable *yahoo_c_info_defaults(GaimConnection *gc, const char *chat_name) + defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); + g_hash_table_insert(defaults, "room", g_strdup(chat_name)); +char *yahoo_get_chat_name(GHashTable *data) + return g_strdup(g_hash_table_lookup(data, "room")); +void yahoo_c_join(GaimConnection *gc, GHashTable *data) + char *room, *topic, *members, *type; + yd = (struct yahoo_data *) gc->proto_data; + room = g_hash_table_lookup(data, "room"); + topic = g_hash_table_lookup(data, "topic"); + members = g_hash_table_lookup(data, "members"); + if ((type = g_hash_table_lookup(data, "type")) && !strcmp(type, "Conference")) { + c = serv_got_joined_chat(gc, id, room); + yd->confs = g_slist_prepend(yd->confs, c); + gaim_conv_chat_set_topic(GAIM_CONV_CHAT(c), gaim_connection_get_display_name(gc), topic); + yahoo_conf_join(yd, c, gaim_connection_get_display_name(gc), room, topic, members); + yahoo_chat_leave(gc, room, + gaim_connection_get_display_name(gc), + yahoo_chat_join(gc, gaim_connection_get_display_name(gc), room, topic); +void yahoo_c_invite(GaimConnection *gc, int id, const char *msg, const char *name) + c = gaim_find_chat(gc, id); + if (id != YAHOO_CHAT_ID) { + yahoo_conf_invite(gc, c, gaim_connection_get_display_name(gc), name, + gaim_conversation_get_name(c), msg); + yahoo_chat_invite(gc, gaim_connection_get_display_name(gc), name, + gaim_conversation_get_name(c), msg); + GaimRoomlistRoom *ucat; + GMarkupParseContext *parse; +static void yahoo_roomlist_destroy(struct yahoo_roomlist *yrl) + gaim_input_remove(yrl->inpa); + g_markup_parse_context_free(yrl->parse); +struct yahoo_chatxml_state { + struct yahoo_roomlist *yrl; + enum yahoo_room_type type; + int users, voices, webcams; + int count, users, voices, webcams; +static struct yahoo_chatxml_state *yahoo_chatxml_state_new(GaimRoomlist *list, struct yahoo_roomlist *yrl) + struct yahoo_chatxml_state *s; + s = g_new0(struct yahoo_chatxml_state, 1); +static void yahoo_chatxml_state_destroy(struct yahoo_chatxml_state *s) +static void yahoo_chatlist_start_element(GMarkupParseContext *context, const gchar *ename, + const gchar **anames, const gchar **avalues, + gpointer user_data, GError **error) + struct yahoo_chatxml_state *s = user_data; + GaimRoomlist *list = s->list; + GaimRoomlistRoom *parent; + if (!strcmp(ename, "category")) { + const gchar *name = NULL, *id = NULL; + for (i = 0; anames[i]; i++) { + if (!strcmp(anames[i], "id")) + if (!strcmp(anames[i], "name")) + parent = g_queue_peek_head(s->q); + r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATEGORY, name, parent); + gaim_roomlist_room_add_field(list, r, (gpointer)name); + gaim_roomlist_room_add_field(list, r, (gpointer)id); + gaim_roomlist_room_add(list, r); + g_queue_push_head(s->q, r); + } else if (!strcmp(ename, "room")) { + s->room.users = s->room.voices = s->room.webcams = 0; + for (i = 0; anames[i]; i++) { + if (!strcmp(anames[i], "id")) { + s->room.id = g_strdup(avalues[i]); + } else if (!strcmp(anames[i], "name")) { + s->room.name = g_strdup(avalues[i]); + } else if (!strcmp(anames[i], "topic")) { + s->room.topic = g_strdup(avalues[i]); + } else if (!strcmp(anames[i], "type")) { + if (!strcmp("yahoo", avalues[i])) + s->room.type = yrt_yahoo; + s->room.type = yrt_user; + } else if (!strcmp(ename, "lobby")) { + struct yahoo_lobby *lob = g_new0(struct yahoo_lobby, 1); + for (i = 0; anames[i]; i++) { + if (!strcmp(anames[i], "count")) { + lob->count = strtol(avalues[i], NULL, 10); + } else if (!strcmp(anames[i], "users")) { + s->room.users += lob->users = strtol(avalues[i], NULL, 10); + } else if (!strcmp(anames[i], "voices")) { + s->room.voices += lob->voices = strtol(avalues[i], NULL, 10); + } else if (!strcmp(anames[i], "webcams")) { + s->room.webcams += lob->webcams = strtol(avalues[i], NULL, 10); + g_queue_push_head(s->q, lob); +static void yahoo_chatlist_end_element(GMarkupParseContext *context, const gchar *ename, + gpointer user_data, GError **error) + struct yahoo_chatxml_state *s = user_data; + if (!strcmp(ename, "category")) { + g_queue_pop_head(s->q); + } else if (!strcmp(ename, "room")) { + struct yahoo_lobby *lob; + GaimRoomlistRoom *r, *l; + if (s->room.type == yrt_yahoo) + r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATEGORY|GAIM_ROOMLIST_ROOMTYPE_ROOM, + s->room.name, s->yrl->cat); + r = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATEGORY|GAIM_ROOMLIST_ROOMTYPE_ROOM, + s->room.name, s->yrl->ucat); + gaim_roomlist_room_add_field(s->list, r, s->room.name); + gaim_roomlist_room_add_field(s->list, r, s->room.id); + gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.users)); + gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.voices)); + gaim_roomlist_room_add_field(s->list, r, GINT_TO_POINTER(s->room.webcams)); + gaim_roomlist_room_add_field(s->list, r, s->room.topic); + gaim_roomlist_room_add(s->list, r); + while ((lob = g_queue_pop_head(s->q))) { + char *name = g_strdup_printf("%s:%d", s->room.name, lob->count); + l = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_ROOM, name, r); + gaim_roomlist_room_add_field(s->list, l, name); + gaim_roomlist_room_add_field(s->list, l, s->room.id); + gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->users)); + gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->voices)); + gaim_roomlist_room_add_field(s->list, l, GINT_TO_POINTER(lob->webcams)); + gaim_roomlist_room_add_field(s->list, l, s->room.topic); + gaim_roomlist_room_add(s->list, l); +static GMarkupParser parser = { + yahoo_chatlist_start_element, + yahoo_chatlist_end_element, +static void yahoo_roomlist_cleanup(GaimRoomlist *list, struct yahoo_roomlist *yrl) + gaim_roomlist_set_in_progress(list, FALSE); + list->proto_data = g_list_remove(list->proto_data, yrl); + yahoo_roomlist_destroy(yrl); + gaim_roomlist_unref(list); +static void yahoo_roomlist_pending(gpointer data, gint source, GaimInputCondition cond) + struct yahoo_roomlist *yrl = data; + GaimRoomlist *list = yrl->list; + struct yahoo_chatxml_state *s; + len = read(yrl->fd, buf, sizeof(buf)); + g_markup_parse_context_end_parse(yrl->parse, NULL); + yahoo_roomlist_cleanup(list, yrl); + yrl->rxqueue = g_realloc(yrl->rxqueue, len + yrl->rxlen); + memcpy(yrl->rxqueue + yrl->rxlen, buf, len); + start = g_strstr_len(yrl->rxqueue, yrl->rxlen, "\r\n\r\n"); + if (!start || (start - yrl->rxqueue + 4) >= yrl->rxlen) + if (yrl->parse == NULL) { + s = yahoo_chatxml_state_new(list, yrl); + yrl->parse = g_markup_parse_context_new(&parser, 0, s, + (GDestroyNotify)yahoo_chatxml_state_destroy); + if (!g_markup_parse_context_parse(yrl->parse, start, (yrl->rxlen - (start - yrl->rxqueue)), NULL)) { + yahoo_roomlist_cleanup(list, yrl); +static void yahoo_roomlist_got_connected(gpointer data, gint source, GaimInputCondition cond) + struct yahoo_roomlist *yrl = data; + GaimRoomlist *list = yrl->list; + struct yahoo_data *yd = gaim_account_get_connection(list->account)->proto_data; + gaim_notify_error(gaim_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed.")); + yahoo_roomlist_cleanup(list, yrl); + cookie = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + buf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\nCookie: %s\r\n\r\n", yrl->path, yrl->host, cookie); + write(yrl->fd, buf, strlen(buf)); + yrl->inpa = gaim_input_add(yrl->fd, GAIM_INPUT_READ, yahoo_roomlist_pending, yrl); +GaimRoomlist *yahoo_roomlist_get_list(GaimConnection *gc) + struct yahoo_roomlist *yrl; + url = g_strdup_printf("%s?chatcat=0", + gaim_account_get_string( + gaim_connection_get_account(gc), + "room_list", YAHOO_ROOMLIST_URL)); + yrl = g_new0(struct yahoo_roomlist, 1); + rl = gaim_roomlist_new(gaim_connection_get_account(gc)); + gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path), NULL, NULL); + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "room", TRUE); + fields = g_list_append(fields, f); + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "id", TRUE); + fields = g_list_append(fields, f); + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE); + fields = g_list_append(fields, f); + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Voices"), "voices", FALSE); + fields = g_list_append(fields, f); + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Webcams"), "webcams", FALSE); + fields = g_list_append(fields, f); + f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, _("Topic"), "topic", FALSE); + fields = g_list_append(fields, f); + gaim_roomlist_set_fields(rl, fields); + if (gaim_proxy_connect(gaim_connection_get_account(gc), + yrl->host, 80, yahoo_roomlist_got_connected, yrl) != 0) + gaim_notify_error(gc, NULL, _("Connection problem"), _("Unable to fetch room list.")); + yahoo_roomlist_cleanup(rl, yrl); + rl->proto_data = g_list_append(rl->proto_data, yrl); + gaim_roomlist_set_in_progress(rl, TRUE); +void yahoo_roomlist_cancel(GaimRoomlist *list) + k = l = list->proto_data; + list->proto_data = NULL; + gaim_roomlist_set_in_progress(list, FALSE); + for (; l; l = l->next) { + yahoo_roomlist_destroy(l->data); + gaim_roomlist_unref(l->data); +void yahoo_roomlist_expand_category(GaimRoomlist *list, GaimRoomlistRoom *category) + struct yahoo_roomlist *yrl; + if (category->type != GAIM_ROOMLIST_ROOMTYPE_CATEGORY) + if (!(id = g_list_nth_data(category->fields, 1))) { + gaim_roomlist_set_in_progress(list, FALSE); + url = g_strdup_printf("%s?chatroom_%s=0", + gaim_account_get_string( + "room_list", YAHOO_ROOMLIST_URL), id); + yrl = g_new0(struct yahoo_roomlist, 1); + list->proto_data = g_list_append(list->proto_data, yrl); + gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path), NULL, NULL); + yrl->ucat = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_CATEGORY, _("User Rooms"), yrl->cat); + gaim_roomlist_room_add(list, yrl->ucat); + if (gaim_proxy_connect(list->account, + yrl->host, 80, yahoo_roomlist_got_connected, yrl) != 0) + gaim_notify_error(gaim_account_get_connection(list->account), + NULL, _("Connection problem"), _("Unable to fetch room list.")); + yahoo_roomlist_cleanup(list, yrl); + gaim_roomlist_set_in_progress(list, TRUE); + gaim_roomlist_ref(list);