gaim/gaim

Some things here:
oldstatus
2005-01-11, Stu Tomlinson
6d79fa3ebf80
Parents 82548b67fb22
Children c341180d674e
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)
+ * Fix some leaks
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
+ *
+ * gaim
+ *
+ * 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 <tcl.h>
+#include <stdarg.h>
+
+#include "tcl_gaim.h"
+
+#include "internal.h"
+#include "connection.h"
+#include "conversation.h"
+#include "signals.h"
+#include "debug.h"
+#include "value.h"
+#include "core.h"
+
+static GList *tcl_callbacks;
+
+static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler);
+
+void tcl_signal_init()
+{
+ tcl_callbacks = NULL;
+}
+
+void tcl_signal_handler_free(struct tcl_signal_handler *handler)
+{
+ if (handler == NULL)
+ return;
+
+ g_free(handler->signal);
+ if (handler->argnames != NULL)
+ g_free(handler->argnames);
+ Tcl_DecrRefCount(handler->proc);
+ g_free(handler);
+}
+
+void tcl_signal_cleanup(Tcl_Interp *interp)
+{
+ GList *cur;
+ struct tcl_signal_handler *handler;
+
+ for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) {
+ handler = cur->data;
+ if (handler->interp == interp) {
+ tcl_signal_handler_free(handler);
+ cur->data = NULL;
+ }
+ }
+ 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)
+ return FALSE;
+
+ 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))
+ return FALSE;
+
+ tcl_callbacks = g_list_append(tcl_callbacks, (gpointer)handler);
+
+ return TRUE;
+}
+
+void tcl_signal_disconnect(void *instance, const char *signal, Tcl_Interp *interp)
+{
+ GList *cur;
+ struct tcl_signal_handler *handler;
+ gboolean found = FALSE;
+
+ for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) {
+ handler = cur->data;
+ 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);
+ cur->data = NULL;
+ found = TRUE;
+ break;
+ }
+ }
+ if (found)
+ tcl_callbacks = g_list_remove_all(tcl_callbacks, NULL);
+}
+
+static void *tcl_signal_callback(va_list args, struct tcl_signal_handler *handler)
+{
+ struct var {
+ void *val;
+ char *str;
+ } *vars;
+ GString *val, *name;
+ GaimBlistNode *node;
+ int error, i;
+ void *retval = NULL;
+ Tcl_Obj *result;
+
+ 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:
+ case GAIM_TYPE_OBJECT:
+ case GAIM_TYPE_BOXED:
+ /* 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);
+ } else {
+ vars[i].val = va_arg(args, void *);
+ Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val,
+ TCL_LINK_INT|TCL_LINK_READ_ONLY);
+ }
+ break;
+ 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);
+ } else {
+ 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);
+ }
+ break;
+ case GAIM_TYPE_CHAR:
+ case GAIM_TYPE_UCHAR:
+ case GAIM_TYPE_SHORT:
+ case GAIM_TYPE_USHORT:
+ case GAIM_TYPE_INT:
+ case GAIM_TYPE_UINT:
+ case GAIM_TYPE_LONG:
+ case GAIM_TYPE_ULONG:
+ case GAIM_TYPE_ENUM:
+ /* These next two are totally bogus */
+ case GAIM_TYPE_INT64:
+ case GAIM_TYPE_UINT64:
+ /* I should really cast these individually to
+ * preserve as much information as possible ...
+ * but heh */
+ 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);
+ } else {
+ 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);
+ }
+ break;
+ case GAIM_TYPE_STRING:
+ 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);
+ } else {
+ vars[i].str = (char *)ckalloc(1);
+ *vars[i].str = '\0';
+ }
+ Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str, TCL_LINK_STRING);
+ } else {
+ 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);
+ }
+ break;
+ case GAIM_TYPE_SUBTYPE:
+ switch (gaim_value_get_subtype(handler->argtypes[i])) {
+ default:
+ 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:
+ /* pointers again */
+ 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);
+ } else {
+ vars[i].val = va_arg(args, void *);
+ Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].val,
+ TCL_LINK_INT|TCL_LINK_READ_ONLY);
+ }
+ break;
+ 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 **);
+ else
+ node = va_arg(args, GaimBlistNode *);
+ switch (node->type) {
+ case GAIM_BLIST_GROUP_NODE:
+ g_string_printf(val, "group {%s}", ((GaimGroup *)node)->name);
+ break;
+ case GAIM_BLIST_CONTACT_NODE:
+ /* g_string_printf(val, "contact {%s}", Contact Name? ); */
+ break;
+ case GAIM_BLIST_BUDDY_NODE:
+ g_string_printf(val, "buddy {%s} %lu", ((GaimBuddy *)node)->name,
+ (unsigned long)((GaimBuddy *)node)->account);
+ break;
+ case GAIM_BLIST_CHAT_NODE:
+ g_string_printf(val, "chat {%s} %lu", ((GaimChat *)node)->alias,
+ (unsigned long)((GaimChat *)node)->account);
+ break;
+ case GAIM_BLIST_OTHER_NODE:
+ g_string_printf(val, "other");
+ break;
+ }
+ vars[i].str = g_strdup(val->str);
+ Tcl_LinkVar(handler->interp, name->str, (char *)&vars[i].str,
+ TCL_LINK_STRING|TCL_LINK_READ_ONLY);
+ break;
+ }
+ }
+ }
+
+ /* 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)));
+ } else {
+ 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));
+ } else {
+ 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)));
+ retval = NULL;
+ }
+ }
+ }
+ }
+
+ /* 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
+ * on the way out */
+ switch (gaim_value_get_type(handler->argtypes[i])) {
+ case GAIM_TYPE_STRING:
+ 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);
+ }
+ ckfree(vars[i].str);
+ }
+ break;
+ 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_free(vars[i].str);
+ break;
+ default:
+ /* nothing */
+ ;
+ }
+ break;
+ default:
+ /* nothing */
+ ;
+ }
+ }
+
+ g_string_free(name, TRUE);
+ g_string_free(val, TRUE);
+ g_free(vars);
+
+ return retval;
+}
--- 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);
/* Delete the node */
+ g_hash_table_destroy(contact->node.settings);
g_free(contact);
}
}
@@ -1281,6 +1282,7 @@
g_hash_table_destroy(buddy->node.settings);
g_free(buddy->name);
g_free(buddy->alias);
+ g_free(buddy->server_alias);
g_free(buddy);
/* If the contact is empty then remove it */
@@ -1323,6 +1325,7 @@
/* Delete the node */
g_hash_table_destroy(chat->components);
+ g_hash_table_destroy(chat->node.settings);
g_free(chat->alias);
g_free(chat);
}
@@ -1384,6 +1387,7 @@
}
/* Delete the node */
+ g_hash_table_destroy(group->node.settings);
g_free(group->name);
g_free(group);
}
--- /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
+ * @ingroup core
+ *
+ * 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
+ *
+ */
+
+#include <string.h>
+
+#include "account.h"
+#include "util.h"
+#include "cmds.h"
+
+static GList *cmds = NULL;
+static guint next_id = 1;
+
+typedef struct _GaimCmd {
+ GaimCmdId id;
+ gchar *cmd;
+ gchar *args;
+ GaimCmdPriority priority;
+ GaimCmdFlag flags;
+ gchar *prpl_id;
+ GaimCmdFunc func;
+ gchar *help;
+ void *data;
+} GaimCmd;
+
+
+static gint cmds_compare_func(const GaimCmd *a, const GaimCmd *b)
+{
+ if (a->priority > b->priority)
+ return -1;
+ else if (a->priority < b->priority)
+ return 1;
+ else return 0;
+}
+
+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)
+{
+ GaimCmdId id;
+ GaimCmd *c;
+
+ 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);
+
+ id = next_id++;
+
+ c = g_new0(GaimCmd, 1);
+ c->id = id;
+ c->cmd = g_strdup(cmd);
+ c->args = g_strdup(args);
+ c->priority = p;
+ c->flags = f;
+ c->prpl_id = prpl_id ? g_strdup(prpl_id) : NULL;
+ c->func = func;
+ c->help = helpstr ? g_strdup(helpstr) : NULL;
+ c->data = data;
+
+ cmds = g_list_insert_sorted(cmds, c, (GCompareFunc)cmds_compare_func);
+
+ return id;
+}
+
+static void gaim_cmd_free(GaimCmd *c)
+{
+ g_free(c->cmd);
+ g_free(c->args);
+ if (c->prpl_id)
+ g_free(c->prpl_id);
+ if (c->help)
+ g_free(c->help);
+
+ g_free(c);
+}
+
+void gaim_cmd_unregister(GaimCmdId id)
+{
+ GaimCmd *c;
+ GList *l;
+
+ for (l = cmds; l; l = l->next) {
+ c = l->data;
+
+ if (c->id == id) {
+ cmds = g_list_remove(cmds, c);
+ gaim_cmd_free(c);
+ return;
+ }
+ }
+}
+
+static gboolean gaim_cmd_parse_args(GaimCmd *cmd, const gchar *s, const gchar *m, gchar ***args)
+{
+ int i;
+ const char *end, *cur;
+
+ *args = g_new0(char *, strlen(cmd->args) + 1);
+
+ cur = s;
+
+ for (i = 0; cmd->args[i]; i++) {
+ if (!*cur)
+ return (cmd->flags & GAIM_CMD_FLAG_ALLOW_WRONG_ARGS);
+
+ switch (cmd->args[i]) {
+ case 'w':
+ if (!(end = strchr(cur, ' '))) {
+ end = cur + strlen(cur);
+ (*args)[i] = g_strndup(cur, end - cur);
+ cur = end;
+ } else {
+ (*args)[i] = g_strndup(cur, end - cur);
+ cur = end + 1;
+ }
+ break;
+ case 'W':
+ 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));
+ cur = end;
+ } else {
+ (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_pointer_to_offset(s, end));
+ cur = end +1;
+ }
+ break;
+ case 's':
+ (*args)[i] = g_strdup(cur);
+ cur = cur + strlen(cur);
+ break;
+ case 'S':
+ (*args)[i] = gaim_markup_slice(m, g_utf8_pointer_to_offset(s, cur), g_utf8_strlen(cur, -1) + 1);
+ cur = cur + strlen(cur);
+ break;
+ }
+ }
+
+ if (*cur)
+ return (cmd->flags & GAIM_CMD_FLAG_ALLOW_WRONG_ARGS);
+
+ return TRUE;
+}
+
+static void gaim_cmd_free_args(gchar **args)
+{
+ int i;
+
+ for (i = 0; args[i]; i++)
+ g_free(args[i]);
+ g_free(args);
+}
+
+static void gaim_cmd_strip_current_char(gunichar c, char *s, guint len)
+{
+ int bytes;
+
+ 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);
+ char *s = markup;
+
+ while (*s) {
+ gunichar c = g_utf8_get_char(s);
+
+ if (c == '<') {
+ s = strchr(s, '>');
+ if (!s)
+ return;
+ } else if (g_unichar_isspace(c)) {
+ gaim_cmd_strip_current_char(c, s, len - (s - markup));
+ return;
+ } else {
+ gaim_cmd_strip_current_char(c, s, len - (s - markup));
+ continue;
+ }
+ s = g_utf8_next_char(s);
+ }
+}
+
+GaimCmdStatus gaim_cmd_do_command(GaimConversation *conv, const gchar *cmdline,
+ const gchar *markup, gchar **error)
+{
+ GaimCmd *c;
+ GList *l;
+ gchar *err = NULL;
+ gboolean is_im;
+ gboolean found = FALSE, tried_cmd = FALSE, right_type = FALSE, right_prpl = FALSE;
+ const gchar *prpl_id;
+ gchar **args = NULL;
+ gchar *cmd, *rest, *mrest;
+ GaimCmdRet ret = GAIM_CMD_RET_CONTINUE;
+
+ *error = NULL;
+ prpl_id = gaim_account_get_protocol_id(gaim_conversation_get_account(conv));
+
+ if (gaim_conversation_get_type(conv) == GAIM_CONV_IM)
+ is_im = TRUE;
+ else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT)
+ is_im = FALSE;
+ else
+ return GAIM_CMD_STATUS_FAILED;
+
+ rest = strchr(cmdline, ' ');
+ if (rest) {
+ cmd = g_strndup(cmdline, rest - cmdline);
+ rest++;
+ } else {
+ cmd = g_strdup(cmdline);
+ rest = "";
+ }
+
+ mrest = g_strdup(markup);
+ gaim_cmd_strip_cmd_from_markup(mrest);
+
+ for (l = cmds; l; l = l->next) {
+ c = l->data;
+
+ if (strcmp(c->cmd, cmd) != 0)
+ continue;
+
+ found = TRUE;
+
+ if (is_im)
+ if (!(c->flags & GAIM_CMD_FLAG_IM))
+ continue;
+ if (!is_im)
+ if (!(c->flags & GAIM_CMD_FLAG_CHAT))
+ continue;
+
+ right_type = TRUE;
+
+ if ((c->flags & GAIM_CMD_FLAG_PRPL_ONLY) && c->prpl_id &&
+ (strcmp(c->prpl_id, prpl_id) != 0))
+ continue;
+
+ right_prpl = TRUE;
+
+ /* this checks the allow bad args flag for us */
+ if (!gaim_cmd_parse_args(c, rest, mrest, &args)) {
+ gaim_cmd_free_args(args);
+ args = NULL;
+ continue;
+ }
+
+ tried_cmd = TRUE;
+ ret = c->func(conv, cmd, args, &err, c->data);
+ if (ret == GAIM_CMD_RET_CONTINUE) {
+ if (err)
+ g_free(err);
+ err = NULL;
+ gaim_cmd_free_args(args);
+ args = NULL;
+ continue;
+ } else {
+ break;
+ }
+
+ }
+
+ if (args)
+ gaim_cmd_free_args(args);
+ g_free(cmd);
+ g_free(mrest);
+
+ if (!found)
+ return GAIM_CMD_STATUS_NOT_FOUND;
+
+ if (!right_type)
+ return GAIM_CMD_STATUS_WRONG_TYPE;
+ if (!right_prpl)
+ return GAIM_CMD_STATUS_WRONG_PRPL;
+ if (!tried_cmd)
+ return GAIM_CMD_STATUS_WRONG_ARGS;
+
+ if (ret == GAIM_CMD_RET_OK) {
+ return GAIM_CMD_STATUS_OK;
+ } else {
+ *error = err;
+ if (ret == GAIM_CMD_RET_CONTINUE)
+ return GAIM_CMD_STATUS_NOT_FOUND;
+ else
+ return GAIM_CMD_STATUS_FAILED;
+ }
+
+}
+
+
+GList *gaim_cmd_list(GaimConversation *conv)
+{
+ GList *ret = NULL;
+ GaimCmd *c;
+ GList *l;
+
+ for (l = cmds; l; l = l->next) {
+ c = l->data;
+
+ if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_IM))
+ if (!(c->flags & GAIM_CMD_FLAG_IM))
+ continue;
+ if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT))
+ if (!(c->flags & GAIM_CMD_FLAG_CHAT))
+ continue;
+
+ 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))
+ continue;
+
+ ret = g_list_append(ret, c->cmd);
+ }
+
+ return ret;
+}
+
+
+GList *gaim_cmd_help(GaimConversation *conv, const gchar *cmd)
+{
+ GList *ret = NULL;
+ GaimCmd *c;
+ GList *l;
+
+ for (l = cmds; l; l = l->next) {
+ c = l->data;
+
+ if (cmd && (strcmp(cmd, c->cmd) != 0))
+ continue;
+
+ if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_IM))
+ if (!(c->flags & GAIM_CMD_FLAG_IM))
+ continue;
+ if (conv && (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT))
+ if (!(c->flags & GAIM_CMD_FLAG_CHAT))
+ continue;
+
+ 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))
+ continue;
+
+ ret = g_list_append(ret, c->help);
+ }
+
+ return ret;
+}
+
--- 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) {
if (node->data != NULL)
- g_free(node->data);
+ gaim_conv_chat_cb_destroy((GaimConvChatBuddy *)node->data);
node->data = NULL;
}
@@ -1002,6 +1002,9 @@
g_free(conv->u.chat->topic);
conv->u.chat->topic = NULL;
+ if(conv->u.chat->nick)
+ g_free(conv->u.chat->nick);
+
g_free(conv->u.chat);
conv->u.chat = NULL;
--- 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"),
xfer->who, escaped);
+ g_free(escaped);
}
else
{
msg = g_strdup_printf(_("%s canceled the file transfer"), xfer->who);
}
- g_free(escaped);
gaim_xfer_conversation_write(xfer, msg, TRUE);
gaim_xfer_error(gaim_xfer_get_type(xfer), xfer->who, msg);
g_free(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);
return FALSE;
+ }
gtknode = node->ui_data;
@@ -3524,9 +3526,13 @@
if(node->parent)
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 */
+ g_free(node->ui_data);
+ node->ui_data = NULL;
}
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
+ *
+ * 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
+ * source distribution.
+ *
+ * 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 "internal.h"
+#include "gtkgaim.h"
+
+#include "account.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "stock.h"
+#include "util.h"
+
+#include "away.h"
+#include "gtkblist.h"
+#include "gtkdialogs.h"
+#include "gtkutils.h"
+
+/*
+ * The next couple of functions deal with the connection dialog
+ */
+struct signon_meter {
+ GaimAccount *account;
+ GtkWidget *button;
+ GtkWidget *progress;
+ GtkWidget *status;
+};
+
+struct meter_window {
+ GtkWidget *window;
+ GtkWidget *table;
+ gint rows;
+ gint active_count;
+ GSList *meters;
+};
+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)
+ return;
+
+ 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);
+ g_free(meter_win);
+ meter_win = NULL;
+ }
+}
+
+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);
+ } else {
+ kill_meter(meter, _("Done."));
+
+ if (gaim_connections_get_all() == NULL) {
+ gaim_gtkdialogs_destroy_all();
+
+ gaim_blist_destroy();
+
+ show_login();
+ }
+ }
+}
+
+static void cancel_all ()
+{
+ GSList *m = meter_win ? meter_win->meters : NULL;
+ struct signon_meter *meter;
+
+ while (m) {
+ meter = m->data;
+ if (gaim_connection_get_state(meter->account->gc) != GAIM_CONNECTED)
+ cancel_signon(NULL, meter);
+ m = m->next;
+ }
+}
+
+static gint meter_destroy(GtkWidget *window, GdkEvent *evt, struct signon_meter *meter)
+{
+ return TRUE;
+}
+
+static struct signon_meter *find_signon_meter(GaimConnection *gc)
+{
+ GSList *m = meter_win ? meter_win->meters : NULL;
+ struct signon_meter *meter;
+
+ while (m) {
+ meter = m->data;
+ if (meter->account == gaim_connection_get_account(gc))
+ return m->data;
+ m = m->next;
+ }
+
+ return NULL;
+}
+
+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);
+ GtkWidget *image =
+ gtk_image_new_from_pixbuf(scale);
+ g_object_unref(G_OBJECT(pb));
+ g_object_unref(G_OBJECT(scale));
+ return image;
+}
+
+static struct signon_meter *
+new_meter(GaimConnection *gc, GtkWidget *widget,
+ GtkWidget *table, gint *rows)
+{
+ GtkWidget *graphic;
+ GtkWidget *label;
+ GtkWidget *nest_vbox;
+ 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));
+
+ (*rows)++;
+ 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++;
+
+ return meter;
+}
+
+static void gaim_gtk_connection_connect_progress(GaimConnection *gc,
+ const char *text, size_t step, size_t step_count)
+{
+ struct signon_meter *meter;
+
+ if(!meter_win) {
+ GtkWidget *vbox;
+ GtkWidget *cancel_button;
+
+ if(mainwindow)
+ 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),
+ FALSE, FALSE, 0);
+ 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),
+ FALSE, FALSE, 0);
+
+ g_signal_connect(G_OBJECT(meter_win->window), "delete_event",
+ G_CALLBACK(meter_destroy), NULL);
+ }
+
+ meter = find_signon_meter(gc);
+ if(!meter) {
+ meter = new_meter(gc, meter_win->window, meter_win->table,
+ &meter_win->rows);
+
+ 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_setup(gc);
+
+ do_away_menu();
+ gaim_gtk_blist_update_protocol_actions();
+
+ if (meter)
+ kill_meter(meter, _("Done."));
+}
+
+static void gaim_gtk_connection_disconnected(GaimConnection *gc)
+{
+ struct signon_meter *meter = find_signon_meter(gc);
+
+ do_away_menu();
+ gaim_gtk_blist_update_protocol_actions();
+
+ if (meter)
+ kill_meter(meter, _("Done."));
+
+ if (gaim_connections_get_all() != NULL)
+ return;
+
+ gaim_gtkdialogs_destroy_all();
+
+ gaim_blist_destroy();
+
+ show_login();
+}
+
+static void gaim_gtk_connection_notice(GaimConnection *gc,
+ const char *text)
+{
+}
+
+/*
+ * The next couple of functions deal with the disconnected dialog
+ */
+struct disconnect_window {
+ GtkWidget *window;
+ GtkWidget *treeview;
+ GtkWidget *sw;
+ GtkWidget *label;
+ 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)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *sel;
+ char *label_text;
+ GaimAccount *account = NULL;
+
+ if ((disconnect_window == NULL) || (model == NULL))
+ return;
+
+ if (!gtk_tree_model_get_iter_first(model, &iter)) {
+ /* No more accounts being shown. Caloo calay! */
+ disconnect_window_hide();
+ return;
+ }
+
+ /*
+ * 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);
+ } else {
+ 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);
+ else
+ gtk_widget_show(disconnect_window->reconnect_btn);
+ g_free(label_text);
+}
+
+static void disconnect_response_cb(GtkDialog *dialog, gint id, GtkWidget *widget)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *sel = NULL;
+ GtkTreeModel *model = NULL;
+ GaimAccount *account = NULL;
+
+ switch (id) {
+ 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;
+ do {
+ 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));
+
+ /* remove all rows */
+ /* We could just do the following, but we only want to remove accounts
+ * that are going to be reconnected, not accounts that have already
+ * been reconnected.
+ */
+ /* gtk_list_store_clear(GTK_LIST_STORE(model)); */
+ l_del_iter = l_del;
+ 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;
+ }
+ g_list_free(l_del);
+
+ /* 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;
+ }
+ g_list_free(l_accts);
+
+ }
+
+ disconnect_window_update_buttons(model);
+
+ break;
+
+ 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))
+ return;
+ } else {
+ /* There is only one account disconnection, so reconnect to it. */
+ if (!gtk_tree_model_get_iter_first(model, &iter))
+ return;
+ }
+
+ /* 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;
+ do {
+ 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));
+
+ l_del_iter = l_del;
+ 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;
+ }
+ g_list_free(l_del);
+ }
+
+ gaim_account_connect(account);
+ disconnect_window_update_buttons(model);
+
+ break;
+
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CLOSE:
+ disconnect_window_hide();
+ break;
+
+ }
+}
+
+/*
+ * 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);
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GdkPixbuf *icon;
+ GdkPixbuf *scale;
+ GList *l_disc_accts = NULL;
+
+ if (disconnect_window == NULL)
+ return;
+
+ 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);
+ do {
+ 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);
+
+ /* Add */
+ 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),
+ GTK_RESPONSE_APPLY,
+ g_list_length(l_disc_accts) > 1);
+ g_list_free(l_disc_accts);
+
+ if (icon != NULL)
+ g_object_unref(G_OBJECT(icon));
+ if (scale != NULL)
+ g_object_unref(G_OBJECT(scale));
+
+ disconnect_window_update_buttons(model);
+}
+
+static void
+gaim_gtk_connection_report_disconnect(GaimConnection *gc, const char *text)
+{
+ char *label_text = NULL;
+ GtkTreeIter new_iter;
+ 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),
+ _("_Reconnect"),
+ GTK_RESPONSE_ACCEPT);
+
+ disconnect_window->reconnectall_btn = gtk_dialog_add_button(
+ GTK_DIALOG(disconnect_window->window),
+ _("Reconnect _All"),
+ GTK_RESPONSE_APPLY);
+
+ gtk_dialog_add_button(
+ GTK_DIALOG(disconnect_window->window),
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE);
+
+ gtk_widget_show_all(disconnect_window->window);
+
+ /* Tree View */
+ 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);
+ } else
+ 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,
+ 0, NULL,
+ 1, gaim_account_get_username(gaim_connection_get_account(gc)),
+ 2, gaim_date_full(),
+ 3, label_text,
+ 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));
+
+ g_free(label_text);
+}
+/*
+ * 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
+};
+
+GaimConnectionUiOps *
+gaim_gtk_connections_get_ui_ops(void)
+{
+ return &conn_ui_ops;
+}
+
+/*
+ * This function needs to be moved out of here once away messages are
+ * core/UI split.
+ */
+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) {
+ return;
+ }
+
+ if (mesg == NULL)
+ mesg = gaim_prefs_get_string("/core/away/default_message");
+ while (awy) {
+ a = (struct away_message *)awy->data;
+ if (strcmp(a->name, mesg) == 0) {
+ message = a;
+ break;
+ }
+ awy = awy->next;
+ }
+ if (message == NULL) {
+ if(!away_messages)
+ return;
+ 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 @@
GtkTreeIter iter;
GtkTreeModel *model;
GtkTreeSelection *sel;
- const char *name;
+ char *name;
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);
+ g_free(name);
}
static void
@@ -1432,7 +1433,7 @@
}
static GtkWidget *
-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);
gtk_widget_show(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);
gtk_widget_show(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);
gtk_widget_show(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);
gtk_widget_show(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);
gtk_widget_show(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);
gtk_widget_show(button);
}
@@ -1547,6 +1548,7 @@
gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
gaim_gtk_treeview_popup_menu_position_func, widget,
0, GDK_CURRENT_TIME);
+ g_free(who);
return TRUE;
}
@@ -1592,13 +1594,15 @@
if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
chat_do_im(conv, who);
- g_free(who);
} 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);
}
+ g_free(who);
+ gtk_tree_path_free(path);
+
return TRUE;
}
@@ -2421,7 +2425,8 @@
g_object_unref(status);
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 @@
str = g_malloc(1024);
/* 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 @@
}
}
else {
- if (gaim_message_meify(new_message, length)) {
+ if (gaim_message_meify(new_message, -1)) {
str = g_malloc(1024);
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 @@
return;
}
}
+ g_strfreev(links);
break;
case GTK_IMHTML_DRAG_HTML:
{
@@ -2359,6 +2360,8 @@
ws[0] = '\0'; wpos = 0;
gtk_imhtml_toggle_link(imhtml, href);
}
+ if (href)
+ g_free(href);
}
break;
case 46: /* IMG (opt) */
--- /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 @@
+/*
+ * GtkIMHtmlToolbar
+ *
+ * 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
+ * source distribution.
+ *
+ * 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 "internal.h"
+#include "gtkgaim.h"
+
+#include "imgstore.h"
+#include "notify.h"
+#include "request.h"
+#include "stock.h"
+#include "util.h"
+
+#include "gtkdialogs.h"
+#include "gtkimhtmltoolbar.h"
+#include "gtkutils.h"
+
+static GtkVBoxClass *parent_class = NULL;
+
+static void do_bold(GtkWidget *bold, GtkIMHtmlToolbar *toolbar)
+{
+ GObject *object;
+
+ 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,
+ NULL, toolbar);
+ gtk_imhtml_toggle_bold(GTK_IMHTML(toolbar->imhtml));
+ g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+ NULL, toolbar);
+ g_object_unref(object);
+
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+static void
+do_italic(GtkWidget *italic, GtkIMHtmlToolbar *toolbar)
+{
+ GObject *object;
+
+ 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,
+ NULL, toolbar);
+ gtk_imhtml_toggle_italic(GTK_IMHTML(toolbar->imhtml));
+ g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+ NULL, toolbar);
+ g_object_unref(object);
+
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+static void
+do_underline(GtkWidget *underline, GtkIMHtmlToolbar *toolbar)
+{
+ GObject *object;
+
+ 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,
+ NULL, toolbar);
+ gtk_imhtml_toggle_underline(GTK_IMHTML(toolbar->imhtml));
+ g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
+ NULL, toolbar);
+ g_object_unref(object);
+
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+static void
+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);
+}
+
+static void
+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);
+}
+
+static void
+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;
+ }
+}
+
+static void
+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 */
+ char *fontname;
+ char *space;
+ 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)))
+ *space = '\0';
+
+ gtk_imhtml_toggle_fontface(GTK_IMHTML(toolbar->imhtml), fontname);
+
+ g_free(fontname);
+
+ cancel_toolbar_font(NULL, toolbar);
+}
+
+static void
+toggle_font(GtkWidget *font, GtkIMHtmlToolbar *toolbar)
+{
+ char *fontname;
+
+ 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);
+
+ if(fontname) {
+ char fonttif[128];
+ g_snprintf(fonttif, sizeof(fonttif), "%s 12", fontname);
+ gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog),
+ fonttif);
+ g_free(fontname);
+ } else {
+ gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog),
+ DEFAULT_FONT_FACE);
+ }
+
+ 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));
+ } else {
+ cancel_toolbar_font(NULL, toolbar);
+ }
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+static void
+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)
+{
+ GdkColor text_color;
+ GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "gaim_toolbar");
+ char *open_tag;
+
+ open_tag = g_malloc(30);
+ gtk_color_selection_get_current_color(colorsel, &text_color);
+ g_snprintf(open_tag, 23, "#%02X%02X%02X",
+ text_color.red / 256,
+ text_color.green / 256,
+ text_color.blue / 256);
+ gtk_imhtml_toggle_forecolor(GTK_IMHTML(toolbar->imhtml), open_tag);
+
+ g_free(open_tag);
+ cancel_toolbar_fgcolor(NULL, toolbar);
+}
+
+static void
+toggle_fg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar)
+{
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) {
+ GtkWidget *colorsel;
+ GdkColor fgcolor;
+ 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;
+ if (color) {
+ gdk_color_parse(color, &fgcolor);
+ gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &fgcolor);
+ g_free(color);
+ }
+
+ 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);
+ } else {
+ /* gaim_gtk_advance_past(gtkconv, "<FONT COLOR>", "</FONT>"); */
+ }
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+static void
+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;
+ }
+}
+
+static void
+cancel_toolbar_bgcolor(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
+{
+ destroy_toolbar_bgcolor(widget, NULL, toolbar);
+}
+
+static void do_bgcolor(GtkWidget *widget, GtkColorSelection *colorsel)
+{
+ GdkColor text_color;
+ GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "gaim_toolbar");
+ char *open_tag;
+
+ open_tag = g_malloc(30);
+ gtk_color_selection_get_current_color(colorsel, &text_color);
+ g_snprintf(open_tag, 23, "#%02X%02X%02X",
+ text_color.red / 256,
+ text_color.green / 256,
+ text_color.blue / 256);
+ gtk_imhtml_toggle_backcolor(GTK_IMHTML(toolbar->imhtml), open_tag);
+
+ g_free(open_tag);
+ cancel_toolbar_bgcolor(NULL, toolbar);
+}
+
+static void
+toggle_bg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar)
+{
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) {
+ GtkWidget *colorsel;
+ GdkColor bgcolor;
+ 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;
+ if (color) {
+ gdk_color_parse(color, &bgcolor);
+ gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &bgcolor);
+ g_free(color);
+ }
+
+ 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);
+ } else {
+ /* gaim_gtk_advance_past(gtkconv, "<FONT COLOR>", "</FONT>"); */
+ }
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+static void
+cancel_link_cb(GtkIMHtmlToolbar *toolbar, GaimRequestFields *fields)
+{
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE);
+
+ toolbar->link_dialog = NULL;
+}
+
+static void
+close_link_dialog(GtkIMHtmlToolbar *toolbar)
+{
+ if (toolbar->link_dialog != NULL)
+ {
+ gaim_request_close(GAIM_REQUEST_FIELDS, toolbar->link_dialog);
+ toolbar->link_dialog = NULL;
+ }
+}
+
+static void
+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");
+ else
+ description = NULL;
+
+ if (description == NULL)
+ description = url;
+
+ gtk_imhtml_insert_link(GTK_IMHTML(toolbar->imhtml),
+ gtk_text_buffer_get_insert(GTK_IMHTML(toolbar->imhtml)->text_buffer),
+ url, description);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE);
+
+ toolbar->link_dialog = NULL;
+}
+
+static void
+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;
+ char *msg;
+ char *desc = NULL;
+
+ 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"),
+ desc, FALSE);
+ 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 "
+ "is optional."));
+ } else {
+ msg = g_strdup(_("Please enter the URL of the "
+ "link that you want to insert."));
+ }
+
+ toolbar->link_dialog =
+ gaim_request_fields(toolbar, _("Insert Link"),
+ NULL,
+ msg,
+ fields,
+ _("_Insert"), G_CALLBACK(do_insert_link_cb),
+ _("Cancel"), G_CALLBACK(cancel_link_cb),
+ toolbar);
+ g_free(msg);
+ g_free(desc);
+ } else {
+ close_link_dialog(toolbar);
+ }
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+
+static void
+do_insert_image_cb(GtkWidget *widget, int response, GtkIMHtmlToolbar *toolbar)
+{
+ gchar *filename, *name, *buf;
+ char *filedata;
+ size_t size;
+ GError *error = NULL;
+ int id;
+ GtkTextIter iter;
+ GtkTextMark *ins;
+
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+ if (response != GTK_RESPONSE_ACCEPT) {
+#else /* FILECHOOSER */
+ if (response != GTK_RESPONSE_OK) {
+#endif /* FILECHOOSER */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
+ return;
+ }
+
+#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
+#else /* FILECHOOSER */
+ 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);
+ return;
+ }
+
+#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
+ if (gaim_gtk_check_if_dir(filename, GTK_FILE_SELECTION(widget))) {
+ g_free(filename);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
+ return;
+ }
+#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);
+
+ g_error_free(error);
+ g_free(filename);
+
+ return;
+ }
+
+ name = strrchr(filename, G_DIR_SEPARATOR) + 1;
+
+ id = gaim_imgstore_add(filedata, size, name);
+ g_free(filedata);
+
+ if (id == 0) {
+ buf = g_strdup_printf(_("Failed to store image: %s\n"), filename);
+ gaim_notify_error(NULL, NULL, buf, NULL);
+
+ g_free(buf);
+ g_free(filename);
+
+ return;
+ }
+
+ g_free(filename);
+
+ 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)),
+ &iter, ins);
+ gtk_imhtml_insert_image_at_iter(GTK_IMHTML(toolbar->imhtml), id, &iter);
+ gaim_imgstore_unref(id);
+}
+
+
+static void
+insert_image_cb(GtkWidget *save, GtkIMHtmlToolbar *toolbar)
+{
+ GtkWidget *window;
+
+ 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"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ 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);
+#else /* FILECHOOSER */
+ 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;
+ } else {
+ gtk_widget_destroy(toolbar->image_dialog);
+ toolbar->image_dialog = NULL;
+ }
+
+ gtk_widget_grab_focus(toolbar->imhtml);
+}
+
+
+static void
+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;
+ }
+}
+
+
+static void
+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,
+ escaped_smiley);
+
+ 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)
+{
+ GtkWidget *image;
+ GtkWidget *button;
+
+ 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) {
+ while(list) {
+ GtkIMHtmlSmiley *cur = list->data;
+ if(!strcmp(cur->file, smiley->file))
+ return FALSE;
+ list = list->next;
+ }
+ return TRUE;
+}
+
+
+static void
+insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
+{
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smiley))) {
+
+ GtkWidget *dialog;
+ GtkWidget *smiley_table = NULL;
+ GSList *smileys, *unique_smileys = NULL;
+ int width;
+ int row = 0, col = 0;
+
+ if (toolbar->smiley_dialog) {
+ gtk_widget_grab_focus(toolbar->imhtml);
+ return;
+ }
+
+ if (toolbar->sml)
+ smileys = get_proto_smileys(toolbar->sml);
+ else
+ smileys = get_proto_smileys(GAIM_PROTO_DEFAULT);
+
+ while(smileys) {
+ GtkIMHtmlSmiley *smiley = smileys->data;
+ if(!smiley->hidden) {
+ if(smiley_is_unique(unique_smileys, smiley))
+ unique_smileys = g_slist_append(unique_smileys, smiley);
+ }
+ smileys = smileys->next;
+ }
+
+ GAIM_DIALOG(dialog);
+
+ 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);
+
+ /* pack buttons */
+
+ while(unique_smileys) {
+ GtkIMHtmlSmiley *smiley = unique_smileys->data;
+ if(!smiley->hidden) {
+ add_smiley(toolbar, smiley_table, row, col, smiley->file, smiley->smile);
+ if(++col >= width) {
+ col = 0;
+ row++;
+ }
+ }
+ unique_smileys = unique_smileys->next;
+ }
+ }
+ else {
+ 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);
+
+ /* connect signals */
+ 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);
+
+ /* show everything */
+ 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,
+ gboolean is_active,
+ GtkIMHtmlToolbar *toolbar)
+{
+ GObject *object;
+ 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)),
+ toolbar);
+
+ 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)),
+ toolbar);
+
+ 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)),
+ toolbar);
+}
+
+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,
+ toolbar);
+
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic)))
+ toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic),
+ FALSE, toolbar);
+
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline)))
+ toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline),
+ FALSE, toolbar);
+}
+
+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,
+ toolbar);
+
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic)) != italic)
+ toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), italic,
+ toolbar);
+
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline)) != underline)
+ toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline),
+ underline, toolbar);
+}
+
+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))
+ return;
+
+ update_buttons(toolbar);
+}
+
+enum {
+ LAST_SIGNAL
+};
+/* static guint signals [LAST_SIGNAL] = { 0 }; */
+
+static void
+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;
+ }
+
+ if (toolbar->sml)
+ free(toolbar->sml);
+ gtk_object_sink(GTK_OBJECT(toolbar->tooltips));
+
+ G_OBJECT_CLASS(parent_class)->finalize (object);
+}
+
+/* Boring GTK stuff */
+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_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(GtkIMHtmlClass, url_clicked),
+ NULL,
+ 0,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);*/
+ gobject_class->finalize = gtk_imhtmltoolbar_finalize;
+}
+
+static void gtk_imhtmltoolbar_init (GtkIMHtmlToolbar *toolbar)
+{
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *sep;
+ GtkSizeGroup *sg;
+
+ 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);
+ gtk_widget_show(sep);
+
+ hbox = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(toolbar), hbox, FALSE, FALSE, 0);
+
+ /* Bold */
+ 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;
+
+ /* Italic */
+ 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;
+
+ /* Underline */
+ 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 */
+ 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 */
+ sep = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
+
+ /* Font Face */
+
+ 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,
+ _("Font Face"), NULL);
+
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(toggle_font), toolbar);
+
+ toolbar->font = button;
+
+ /* Foreground Color */
+ 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;
+
+ /* Background Color */
+ 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 */
+ sep = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(hbox), sep, FALSE, FALSE, 0);
+
+ /* Insert Link */
+ 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;
+
+ /* Insert IM Image */
+ 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;
+
+ /* Insert Smiley */
+ 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(sep);
+ toolbar->sml = NULL;
+ 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),
+ NULL,
+ NULL,
+ (GClassInitFunc) gtk_imhtmltoolbar_class_init,
+ NULL,
+ NULL,
+ sizeof (GtkIMHtmlToolbar),
+ 0,
+ (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);
+
+ if(bold)
+ toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), bold,
+ toolbar);
+
+ if(italic)
+ toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), italic,
+ toolbar);
+
+ if(underline)
+ toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline),
+ underline, toolbar);
+}
+
+void gtk_imhtmltoolbar_associate_smileys(GtkIMHtmlToolbar *toolbar, const char *proto_id)
+{
+ if (toolbar->sml)
+ g_free(toolbar->sml);
+
+ 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);
+ if (text)
+ lbox = gtk_hbox_new(FALSE, 0);
} else {
bbox = gtk_vbox_new(FALSE, 0);
ibox = gtk_vbox_new(FALSE, 0);
- lbox = gtk_vbox_new(FALSE, 0);
+ if (text)
+ 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);
if (irc->motd)
g_string_free(irc->motd, TRUE);
g_free(irc->server);
--- /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 @@
+/**
+ * @file msgs.c
+ *
+ * gaim
+ *
+ * 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 "internal.h"
+
+#include "conversation.h"
+#include "blist.h"
+#include "notify.h"
+#include "util.h"
+#include "debug.h"
+#include "irc.h"
+
+#include <stdio.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)
+{
+ char *end, *buf;
+
+ end = strchr(mask, '!');
+ if (!end)
+ buf = g_strdup(mask);
+ else
+ buf = g_strndup(mask, end - mask);
+
+ return buf;
+}
+
+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);
+
+ g_free(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)
+{
+ GaimConnection *gc;
+
+ if (!args || !args[1])
+ return;
+
+ 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);
+ return;
+ }
+
+ gc = gaim_account_get_connection(irc->account);
+ if (gc)
+ 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)
+ return;
+
+ 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);
+ char *buf;
+
+ if (!args || !args[1] || !gc)
+ return;
+
+ buf = g_strdup_printf(_("You are banned from %s."), args[1]);
+ gaim_notify_error(gc, _("Banned"), _("Banned"), buf);
+ g_free(buf);
+}
+
+void irc_msg_chanmode(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ GaimConversation *convo;
+ char *buf;
+
+ if (!args || !args[1] || !args[2])
+ return;
+
+ convo = gaim_find_conversation_with_account(args[1], irc->account);
+ if (!convo) /* XXX punt on channels we are not in for now */
+ return;
+
+ 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));
+ g_free(buf);
+
+ return;
+}
+
+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]);
+ return;
+ }
+
+ 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);
+ return;
+ }
+
+ 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")) {
+ irc->whois.ircop = 1;
+ } else if (!strcmp(name, "317")) {
+ irc->whois.idle = atoi(args[2]);
+ if (args[3])
+ 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)
+{
+ GaimConnection *gc;
+ GString *info;
+ char buffer[256];
+ char *str;
+
+ if (!irc->whois.nick) {
+ gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unexpected End of WHOIS for %s\n", args[1]);
+ return;
+ }
+ 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);
+ return;
+ }
+
+ 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>") : "");
+ if (irc->whois.away) {
+ 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);
+ g_free(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);
+ g_free(tmp);
+ }
+ 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);
+ }
+ if (irc->whois.idle) {
+ gchar *timex = gaim_str_seconds_to_string(irc->whois.idle);
+ g_string_append_printf(info, _("<b>Idle for:</b> %s<br>"), timex);
+ g_free(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);
+ g_free(str);
+ memset(&irc->whois, 0, sizeof(irc->whois));
+}
+
+void irc_msg_list(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ if (!irc->roomlist)
+ return;
+
+ if (!strcmp(name, "321")) {
+ gaim_roomlist_set_in_progress(irc->roomlist, TRUE);
+ return;
+ }
+
+ if (!strcmp(name, "323")) {
+ gaim_roomlist_set_in_progress(irc->roomlist, FALSE);
+ gaim_roomlist_unref(irc->roomlist);
+ irc->roomlist = NULL;
+ }
+
+ if (!strcmp(name, "322")) {
+ GaimRoomlistRoom *room;
+
+ if (!args[0] || !args[1] || !args[2] || !args[3])
+ return;
+
+ 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")) {
+ chan = args[0];
+ topic = irc_mirc2txt (args[1]);
+ } else {
+ chan = args[1];
+ topic = irc_mirc2txt (args[2]);
+ }
+
+ convo = gaim_find_conversation_with_account(chan, irc->account);
+ if (!convo) {
+ 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);
+ g_free(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);
+ g_free(nick);
+ gaim_conv_chat_write(GAIM_CONV_CHAT(convo), from, msg, GAIM_MESSAGE_SYSTEM, time(NULL));
+ g_free(msg);
+ } else {
+ 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));
+ g_free(msg);
+ }
+ g_free(tmp2);
+ g_free(topic);
+}
+
+void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ GaimConnection *gc = gaim_account_get_connection(irc->account);
+ char *buf;
+
+ if (!args || !args[1] || !gc)
+ return;
+
+ 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."));
+ g_free(buf);
+}
+
+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);
+ if (!convo) {
+ 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);
+ irc->names = NULL;
+ g_free(irc->nameconv);
+ irc->nameconv = NULL;
+ return;
+ }
+
+ names = cur = g_string_free(irc->names, FALSE);
+ irc->names = NULL;
+ if (irc->nameconv) {
+ 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));
+ else
+ gaim_conv_im_write(GAIM_CONV_IM(convo), "", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
+ g_free(msg);
+ g_free(irc->nameconv);
+ irc->nameconv = NULL;
+ } else {
+ GList *users = NULL;
+ GList *flags = NULL;
+
+ while (*cur) {
+ GaimConvChatBuddyFlags f = GAIM_CBFLAGS_NONE;
+ end = strchr(cur, ' ');
+ if (!end)
+ end = cur + strlen(cur);
+ if (*cur == '@') {
+ f = GAIM_CBFLAGS_OP;
+ cur++;
+ } else if (*cur == '%') {
+ f = GAIM_CBFLAGS_HALFOP;
+ cur++;
+ } else if(*cur == '+') {
+ f = GAIM_CBFLAGS_VOICE;
+ cur++;
+ }
+ tmp = g_strndup(cur, end - cur);
+ users = g_list_append(users, tmp);
+ flags = g_list_append(flags, GINT_TO_POINTER(f));
+ cur = end;
+ if (*cur)
+ cur++;
+ }
+
+ if (users != NULL) {
+ GList *l;
+
+ gaim_conv_chat_add_users(GAIM_CONV_CHAT(convo), users, flags);
+
+ for (l = users; l != NULL; l = l->next)
+ g_free(l->data);
+
+ g_list_free(users);
+ g_list_free(flags);
+ }
+ }
+ g_free(names);
+ } else {
+ if (!irc->names)
+ 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)
+{
+ GaimConnection *gc;
+ if (!strcmp(name, "375")) {
+ gc = gaim_account_get_connection(irc->account);
+ if (gc)
+ gaim_connection_set_display_name(gc, args[0]);
+ }
+
+ if (!irc->motd)
+ 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)
+{
+ GaimConnection *gc;
+
+ gc = gaim_account_get_connection(irc->account);
+ if (!gc)
+ return;
+
+ gaim_connection_set_state(gc, GAIM_CONNECTED);
+ serv_finish_login (gc);
+
+ irc_blist_timeout(irc);
+ if (!irc->timer)
+ 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)
+ return;
+
+ 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)
+{
+ GaimConnection *gc;
+ GaimConversation *convo;
+
+ convo = gaim_find_conversation_with_account(args[1], irc->account);
+ if (convo) {
+ 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));
+ else
+ gaim_conv_im_write(GAIM_CONV_IM(convo), args[1], _("User is not logged in"),
+ GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
+ } else {
+ if ((gc = gaim_account_get_connection(irc->account)) == NULL)
+ return;
+ 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)
+{
+ GaimConnection *gc;
+ GaimConversation *convo;
+
+ convo = gaim_find_conversation_with_account(args[1], irc->account);
+ if (convo) {
+ gaim_conv_chat_write(GAIM_CONV_CHAT(convo), args[1], args[2], GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
+ } else {
+ if ((gc = gaim_account_get_connection(irc->account)) == NULL)
+ return;
+ 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]);
+ if (convo) {
+ /*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])
+ return;
+
+ convo = gaim_find_conversation_with_account(args[1], irc->account);
+ if (!convo)
+ return;
+
+ 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_free(nick);
+ g_hash_table_destroy(components);
+ return;
+ }
+
+ g_hash_table_insert(components, strdup("channel"), strdup(args[1]));
+
+ serv_got_chat_invite(gc, args[1], nick, NULL, components);
+ g_free(nick);
+}
+
+void irc_msg_inviteonly(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ GaimConnection *gc = gaim_account_get_connection(irc->account);
+ char *buf;
+
+ if (!args || !args[1] || !gc)
+ return;
+
+ buf = g_strdup_printf(_("Joining %s requires an invitation."), args[1]);
+ gaim_notify_error(gc, _("Invitation only"), _("Invitation only"), buf);
+ g_free(buf);
+}
+
+void irc_msg_ison(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ char **nicks;
+ struct irc_buddy *ib;
+ int i;
+
+ if (!args || !args[1])
+ return;
+
+ nicks = g_strsplit(args[1], " ", -1);
+
+ for (i = 0; nicks[i]; i++) {
+ if ((ib = g_hash_table_lookup(irc->buddies, (gconstpointer)nicks[i])) == NULL) {
+ continue;
+ }
+ ib->flag = TRUE;
+ }
+
+ g_strfreev(nicks);
+
+ 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 (!gc || !buddy)
+ return;
+
+ if (ib->online && !ib->flag) {
+ serv_got_update(gc, buddy->name, FALSE, 0, 0, 0, 0);
+ ib->online = FALSE;
+ }
+
+ if (!ib->online && ib->flag) {
+ serv_got_update(gc, buddy->name, TRUE, 0, 0, 0, 0);
+ ib->online = TRUE;
+ }
+}
+
+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;
+ struct irc_buddy *ib;
+ static int id = 1;
+
+ if (!gc) {
+ g_free(nick);
+ return;
+ }
+
+ 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]);
+ g_free(nick);
+ return;
+ }
+
+ convo = gaim_find_conversation_with_account(args[0], irc->account);
+ if (convo == NULL) {
+ gaim_debug(GAIM_DEBUG_ERROR, "irc", "JOIN for %s failed\n", args[0]);
+ g_free(nick);
+ return;
+ }
+
+ 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) {
+ ib->flag = TRUE;
+ irc_buddy_status(nick, ib, irc);
+ }
+
+ g_free(userhost);
+ g_free(nick);
+}
+
+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;
+
+ if (!gc) {
+ g_free(nick);
+ return;
+ }
+
+ if (!convo) {
+ gaim_debug(GAIM_DEBUG_ERROR, "irc", "Recieved a KICK for unknown channel %s\n", args[0]);
+ g_free(nick);
+ return;
+ }
+
+ 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));
+ g_free(buf);
+ serv_got_chat_left(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)));
+ } else {
+ buf = g_strdup_printf(_("Kicked by %s (%s)"), nick, args[2]);
+ gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo), args[1], buf);
+ g_free(buf);
+ }
+
+ g_free(nick);
+ return;
+}
+
+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);
+ if (!convo) {
+ gaim_debug(GAIM_DEBUG_ERROR, "irc", "MODE received for %s, which we are not in\n", args[0]);
+ g_free(nick);
+ return;
+ }
+ 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));
+ g_free(buf);
+ if(args[2]) {
+ GaimConvChatBuddyFlags newflag, flags;
+ char *mcur, *cur, *end, *user;
+ gboolean add = FALSE;
+ mcur = args[1];
+ cur = args[2];
+ while (*cur && *mcur) {
+ if ((*mcur == '+') || (*mcur == '-')) {
+ add = (*mcur == '+') ? TRUE : FALSE;
+ mcur++;
+ continue;
+ }
+ end = strchr(cur, ' ');
+ if (!end)
+ 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;
+ if (*mcur == 'o')
+ newflag = GAIM_CBFLAGS_OP;
+ else if (*mcur =='h')
+ newflag = GAIM_CBFLAGS_HALFOP;
+ else if (*mcur == 'v')
+ newflag = GAIM_CBFLAGS_VOICE;
+ if (newflag) {
+ if (add)
+ flags |= newflag;
+ else
+ flags &= ~newflag;
+ gaim_conv_chat_user_set_flags(GAIM_CONV_CHAT(convo), user, flags);
+ }
+ g_free(user);
+ cur = end;
+ if (*cur)
+ cur++;
+ if (*mcur)
+ mcur++;
+ }
+ }
+ } else { /* User */
+ }
+ g_free(nick);
+}
+
+void irc_msg_nick(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ GaimConnection *gc = gaim_account_get_connection(irc->account);
+ GSList *chats;
+ char *nick = irc_mask_nick(from);
+
+ if (!gc) {
+ g_free(nick);
+ return;
+ }
+ chats = gc->buddy_chats;
+
+ if (!gaim_utf8_strcasecmp(nick, gaim_connection_get_display_name(gc))) {
+ gaim_connection_set_display_name(gc, args[0]);
+ }
+
+ while (chats) {
+ GaimConvChat *chat = GAIM_CONV_CHAT(chats->data);
+ /* This is ugly ... */
+ if (gaim_conv_chat_find_user(chat, nick))
+ gaim_conv_chat_rename_user(chat, nick, args[0]);
+ chats = chats->next;
+ }
+ g_free(nick);
+}
+
+void irc_msg_nickused(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ char *newnick, *buf, *end;
+
+ if (!args || !args[1])
+ return;
+
+ newnick = strdup(args[1]);
+ end = newnick + strlen(newnick) - 1;
+ /* try three fallbacks */
+ if (*end == 2) *end = '3';
+ else if (*end == 1) *end = '2';
+ else *end = '1';
+
+ buf = irc_format(irc, "vn", "NICK", newnick);
+ irc_send(irc, buf);
+ g_free(buf);
+ g_free(newnick);
+}
+
+void irc_msg_notice(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ char *newargs[2];
+
+ newargs[0] = " notice "; /* The spaces are magic, leave 'em in! */
+ newargs[1] = args[1];
+ 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);
+ char *msg;
+
+ if (!args || !args[2] || !gc)
+ return;
+
+ msg = g_strdup_printf(_("Could not change nick"));
+ gaim_notify_error(gc, _("Cannot change nick"), msg, args[2]);
+ g_free(msg);
+}
+
+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;
+ char *nick, *msg;
+
+ if (!args || !args[0] || !gc)
+ return;
+
+ convo = gaim_find_conversation_with_account(args[0], irc->account);
+ if (!convo) {
+ gaim_debug(GAIM_DEBUG_INFO, "irc", "Got a PART on %s, which doesn't exist -- probably closed\n", args[0]);
+ return;
+ }
+
+ 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));
+ g_free(msg);
+ serv_got_chat_left(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)));
+ } else {
+ gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo), nick, args[1]);
+ }
+ g_free(nick);
+}
+
+void irc_msg_ping(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ char *buf;
+ if (!args || !args[0])
+ return;
+
+ buf = irc_format(irc, "v:", "PONG", args[0]);
+ irc_send(irc, buf);
+ g_free(buf);
+}
+
+void irc_msg_pong(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ GaimConversation *convo;
+ GaimConnection *gc;
+ char **parts, *msg;
+ time_t oldstamp;
+
+ if (!args || !args[1])
+ return;
+
+ parts = g_strsplit(args[1], " ", 2);
+
+ if (!parts[0] || !parts[1]) {
+ g_strfreev(parts);
+ return;
+ }
+
+ if (sscanf(parts[1], "%lu", &oldstamp) != 1) {
+ msg = g_strdup(_("Error: invalid PONG from server"));
+ } else {
+ msg = g_strdup_printf(_("PING reply -- Lag: %lu seconds"), time(NULL) - oldstamp);
+ }
+
+ convo = gaim_find_conversation_with_account(parts[0], irc->account);
+ g_strfreev(parts);
+ if (convo) {
+ 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));
+ else
+ gaim_conv_im_write(GAIM_CONV_IM(convo), "PONG", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
+ } else {
+ gc = gaim_account_get_connection(irc->account);
+ if (!gc) {
+ g_free(msg);
+ return;
+ }
+ gaim_notify_info(gc, NULL, "PONG", msg);
+ }
+ g_free(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;
+ int notice = 0;
+
+ if (!args || !args[0] || !args[1] || !gc) {
+ g_free(nick);
+ return;
+ }
+
+ notice = !strcmp(args[0], " notice ");
+ tmp = irc_parse_ctcp(irc, nick, args[0], args[1], notice);
+ if (!tmp) {
+ g_free(nick);
+ return;
+ }
+
+ msg = gaim_escape_html(tmp);
+ g_free(tmp);
+
+ tmp = irc_mirc2html(msg);
+ g_free(msg);
+ msg = tmp;
+ if (notice) {
+ tmp = g_strdup_printf("(notice) %s", msg);
+ g_free(msg);
+ msg = tmp;
+ }
+
+ if (!gaim_utf8_strcasecmp(args[0], gaim_connection_get_display_name(gc))) {
+ serv_got_im(gc, nick, msg, 0, time(NULL));
+ } else if (notice) {
+ serv_got_im(gc, nick, msg, 0, time(NULL));
+ } else {
+ convo = gaim_find_conversation_with_account(args[0], irc->account);
+ if (convo)
+ serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)), nick, 0, msg, time(NULL));
+ else
+ gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got a PRIVMSG on %s, which does not exist\n", args[0]);
+ }
+ g_free(msg);
+ g_free(nick);
+}
+
+void irc_msg_regonly(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ GaimConnection *gc = gaim_account_get_connection(irc->account);
+ char *msg;
+
+ if (!args || !args[1] || !args[2] || !gc)
+ return;
+
+ msg = g_strdup_printf(_("Cannot join %s:"), args[1]);
+ gaim_notify_error(gc, _("Cannot join channel"), msg, args[2]);
+ g_free(msg);
+}
+
+void irc_msg_quit(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ GaimConnection *gc = gaim_account_get_connection(irc->account);
+ struct irc_buddy *ib;
+ char *data[2];
+
+ if (!args || !args[0] || !gc)
+ return;
+
+ data[0] = irc_mask_nick(from);
+ data[1] = args[0];
+ /* 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) {
+ ib->flag = FALSE;
+ irc_buddy_status(data[0], ib, irc);
+ }
+ g_free(data[0]);
+
+ return;
+}
+
+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)
+ return;
+
+ nick = irc_mask_nick(from);
+ msg = g_strdup_printf (_("Wallops from %s"), nick);
+ g_free(nick);
+ wallop = g_markup_escape_text(args[0], strlen(args[0]));
+ gaim_notify_info(gc, NULL, msg, wallop);
+ g_free(msg);
+ g_free(wallop);
+}
+
+void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+ return;
+}
--- 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) {
+ g_strfreev(encodings);
return gaim_utf8_salvage(string);
+ }
for (i = 0; encodings[i] != NULL; i++) {
charset = encodings[i];
@@ -255,6 +257,7 @@
return utf8;
}
}
+ g_strfreev(encodings);
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_free(chat->room);
g_free(chat->server);
+ g_free(chat->handle);
+ g_hash_table_destroy(chat->members);
g_free(chat);
}
--- 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;
+ g_free(full_jid);
}
} else {
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);
+
g_free(cmdproc);
}
--- 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 @@
if (httpconn->connected)
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);
+
g_free(httpconn);
}
@@ -669,17 +678,17 @@
if (!wasted)
{
- 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)
g_free(httpconn->host);
httpconn->host = gw_ip;
@@ -689,6 +698,7 @@
MsnServConn *servconn;
/* It's going to die. */
+ /* poor thing */
servconn = httpconn->servconn;
@@ -696,6 +706,7 @@
servconn->wasted = TRUE;
g_free(full_session_id);
+ g_free(session_id);
g_free(gw_ip);
}
}
--- 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 @@
#include "imgstore.h"
#endif
-#define BUDDY_ALIAS_MAXLEN 387
-
typedef struct
{
GaimConnection *gc;
--- 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);
username =
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);
g_strfreev(a);
@@ -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 @@
g_strfreev(elems);
- 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)
+ return;
+
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 @@
{
MsnSlpCall *slpcall;
- gaim_debug_info("msn", "slpcall timeout (%p)\n", slpcall);
+ slpcall = data;
- slpcall = data;
+ 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 @@
{
const char *format;
char *body_str, *body_enc, *pre, *post;
- char *str_reason;
+ const char *str_reason;
#if 0
if (swboard->conv == NULL)
@@ -651,7 +651,10 @@
}
if (str != NULL)
+ {
msn_switchboard_report_user(swboard, GAIM_MESSAGE_SYSTEM, str);
+ g_free(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);
}
MsnGroup *
@@ -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)
+ store_name = who;
+
/* Then request the add to the server. */
list = lists[list_id];
--- /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.
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+/**
+ * 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
+ * to the cache
+ *
+ * @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;
+
+ if (!sess || !cookie)
+ return -EINVAL;
+
+ newcook = aim_checkcookie(sess, cookie->cookie, cookie->type);
+
+ if (newcook == cookie) {
+ newcook->addtime = time(NULL);
+ return 1;
+ } else if (newcook)
+ aim_cookie_free(sess, newcook);
+
+ cookie->addtime = time(NULL);
+
+ cookie->next = sess->msgcookies;
+ sess->msgcookies = cookie;
+
+ return 0;
+}
+
+/**
+ * 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)
+ return NULL;
+
+ for (prev = &sess->msgcookies; (cur = *prev); ) {
+ if ((cur->type == type) &&
+ (memcmp(cur->cookie, cookie, 8) == 0)) {
+ *prev = cur->next;
+ return cur;
+ }
+ prev = &cur->next;
+ }
+
+ return NULL;
+}
+
+/**
+ * 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
+ * cookie on success.
+ */
+faim_internal aim_msgcookie_t *aim_mkcookie(fu8_t *c, int type, void *data)
+{
+ aim_msgcookie_t *cookie;
+
+ if (!c)
+ return NULL;
+
+ if (!(cookie = calloc(1, sizeof(aim_msgcookie_t))))
+ return NULL;
+
+ cookie->data = data;
+ cookie->type = type;
+ memcpy(cookie->cookie, c, 8);
+
+ return cookie;
+}
+
+/**
+ * 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)
+{
+ aim_msgcookie_t *cur;
+
+ for (cur = sess->msgcookies; cur; cur = cur->next) {
+ if ((cur->type == type) &&
+ (memcmp(cur->cookie, cookie, 8) == 0))
+ return cur;
+ }
+
+ return NULL;
+}
+
+#if 0 /* debugging feature */
+faim_internal int aim_dumpcookie(aim_session_t *sess, aim_msgcookie_t *cookie)
+{
+
+ if (!cookie)
+ return -EINVAL;
+
+ faimdprintf(sess, 0, "\tCookie at %p: %d/%s with %p, next %p\n", cookie,
+ cookie->type, cookie->cookie, cookie->data, cookie->next);
+
+ return 0;
+}
+#endif
+
+/**
+ * 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;
+
+ if (!sess || !cookie)
+ return -EINVAL;
+
+ for (prev = &sess->msgcookies; (cur = *prev); ) {
+ if (cur == cookie)
+ *prev = cur->next;
+ else
+ prev = &cur->next;
+ }
+
+ free(cookie->data);
+ free(cookie);
+
+ return 0;
+}
+
+/* XXX I hate switch */
+faim_internal int aim_msgcookie_gettype(int reqclass)
+{
+ /* XXX: hokey-assed. needs fixed. */
+ switch(reqclass) {
+ 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;
+ char *tag = NULL;
size_t size;
int imgid = 0;
@@ -1229,6 +1230,8 @@
if (tag && (data = gaim_strcasestr(binary, tag)))
data += strlen(tag);
+ g_free(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;
char *ret;
- char *to_codeset;
+ const char *to_codeset;
if (yd->jp && utf8 && *utf8)
*utf8 = FALSE;
--- /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
+ *
+ * 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
+ * source distribution.
+ *
+ * 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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "debug.h"
+#include "prpl.h"
+
+#include "conversation.h"
+#include "notify.h"
+#include "util.h"
+#include "internal.h"
+
+#include "yahoo.h"
+#include "yahoochat.h"
+#include "ycht.h"
+
+#define YAHOO_CHAT_ID (1)
+
+/* prototype(s) */
+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;
+
+ if (yd->wm) {
+ ycht_connection_open(gc);
+ return;
+ }
+
+ 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)
+{
+ GList *i;
+
+ for (i = newusers; i; i = i->next) {
+ if (gaim_conv_chat_find_user(chat, i->data))
+ continue;
+ 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))
+ return;
+
+ gaim_conv_chat_add_user(chat, user, reason, GAIM_CBFLAGS_NONE, TRUE);
+}
+
+static GaimConversation *yahoo_find_conference(GaimConnection *gc, const char *name)
+{
+ struct yahoo_data *yd;
+ GSList *l;
+
+ yd = gc->proto_data;
+
+ for (l = yd->confs; l; l = l->next) {
+ GaimConversation *c = l->data;
+ if (!gaim_utf8_strcasecmp(gaim_conversation_get_name(c), name))
+ return c;
+ }
+ return NULL;
+}
+
+
+void yahoo_process_conference_invite(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+ GSList *l;
+ char *room = NULL;
+ char *who = NULL;
+ char *msg = NULL;
+ GString *members = NULL;
+ GHashTable *components;
+
+
+ if (pkt->status == 2)
+ return; /* XXX */
+
+ members = g_string_sized_new(512);
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 1: /* us, but we already know who we are */
+ break;
+ case 57:
+ room = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ case 50: /* inviter */
+ who = pair->value;
+ g_string_append_printf(members, "%s\n", who);
+ break;
+ case 52: /* invitee (me) */
+ case 53: /* members */
+ g_string_append_printf(members, "%s\n", pair->value);
+ break;
+ case 58:
+ msg = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ case 13: /* ? */
+ break;
+ }
+ }
+
+ if (!room) {
+ g_string_free(members, TRUE);
+ return;
+ }
+
+ 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);
+ if (msg)
+ g_hash_table_replace(components, g_strdup("topic"), msg);
+ g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference"));
+ if (members) {
+ 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)
+{
+ GSList *l;
+ char *room = NULL;
+ char *who = NULL;
+ char *msg = NULL;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 57:
+ room = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ case 54:
+ who = pair->value;
+ break;
+ case 14:
+ msg = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ }
+ }
+
+ if (who && room) {
+ /* make sure we're in the room before we process a decline message for it */
+ if(yahoo_find_conference(gc, room)) {
+ char *tmp;
+
+ 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);
+ g_free(tmp);
+ }
+
+ g_free(room);
+ if (msg)
+ g_free(msg);
+ }
+}
+
+void yahoo_process_conference_logon(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+ GSList *l;
+ char *room = NULL;
+ char *who = NULL;
+ GaimConversation *c;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 57:
+ room = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ case 53:
+ who = pair->value;
+ break;
+ }
+ }
+
+ if (who && room) {
+ c = yahoo_find_conference(gc, room);
+ if (c)
+ yahoo_chat_add_user(GAIM_CONV_CHAT(c), who, NULL);
+ g_free(room);
+ }
+}
+
+void yahoo_process_conference_logoff(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+ GSList *l;
+ char *room = NULL;
+ char *who = NULL;
+ GaimConversation *c;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 57:
+ room = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ case 56:
+ who = pair->value;
+ break;
+ }
+ }
+
+ if (who && room) {
+ c = yahoo_find_conference(gc, room);
+ if (c)
+ gaim_conv_chat_remove_user(GAIM_CONV_CHAT(c), who, NULL);
+ g_free(room);
+ }
+}
+
+void yahoo_process_conference_message(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+ GSList *l;
+ char *room = NULL;
+ char *who = NULL;
+ char *msg = NULL;
+ char *msg2;
+ int utf8 = 0;
+ GaimConversation *c;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 57:
+ room = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ case 3:
+ who = pair->value;
+ break;
+ case 14:
+ msg = pair->value;
+ break;
+ case 97:
+ utf8 = strtol(pair->value, NULL, 10);
+ break;
+ }
+ }
+
+ if (room && who && msg) {
+ msg2 = yahoo_string_decode(gc, msg, utf8);
+ c = yahoo_find_conference(gc, room);
+ if (!c)
+ return;
+ 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));
+ g_free(msg);
+ g_free(msg2);
+ }
+ if (room)
+ g_free(room);
+}
+
+
+/* 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;
+
+ if (pkt->status == 1)
+ yd->chat_online = 1;
+}
+
+/* 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;
+ GSList *l;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ if (pair->key == 1)
+ if (g_ascii_strcasecmp(pair->value,
+ gaim_connection_get_display_name(gc)))
+ return;
+ }
+
+ if (pkt->status == 1) {
+ yd->chat_online = 0;
+ if (yd->in_chat)
+ 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;
+ GSList *l;
+ GList *members = NULL;
+ char *room = NULL;
+ char *topic = NULL;
+ char *someid, *someotherid, *somebase64orhashosomething, *somenegativenumber;
+
+ if (pkt->status == -1) {
+ gaim_notify_error(gc, NULL, _("Failed to join chat"), _("Maybe the room is full?"));
+ return;
+ }
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+
+ case 104:
+ room = yahoo_string_decode(gc, pair->value, TRUE);
+ break;
+ case 105:
+ topic = yahoo_string_decode(gc, pair->value, TRUE);
+ break;
+ case 128:
+ someid = pair->value;
+ break;
+ case 108: /* number of joiners */
+ break;
+ case 129:
+ someotherid = pair->value;
+ break;
+ case 130:
+ somebase64orhashosomething = pair->value;
+ break;
+ case 126:
+ somenegativenumber = pair->value;
+ break;
+ case 13: /* this is 1. maybe its the type of room? (normal, user created, private, etc?) */
+ break;
+ case 61: /*this looks similar to 130 */
+ break;
+
+ /* 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);
+ break;
+ case 110: /* age */
+ break;
+ case 141: /* nickname */
+ break;
+ case 142: /* location */
+ break;
+ case 113: /* bitmask */
+ break;
+ }
+ }
+
+
+ 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)))) {
+ int i;
+ GList *flags = NULL;
+ 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 */
+ char *tmpmsg;
+
+ gaim_conversation_set_name(c, room);
+
+ c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
+ if (topic)
+ gaim_conv_chat_set_topic(GAIM_CONV_CHAT(c), NULL, topic);
+ yd->in_chat = 1;
+ 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));
+ g_free(tmpmsg);
+ } else {
+ c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room);
+ if (topic)
+ gaim_conv_chat_set_topic(GAIM_CONV_CHAT(c), NULL, topic);
+ yd->in_chat = 1;
+ yd->chat_name = g_strdup(room);
+ gaim_conv_chat_add_users(GAIM_CONV_CHAT(c), members, flags);
+ }
+ } else if (c) {
+ yahoo_chat_add_users(GAIM_CONV_CHAT(c), members);
+ }
+
+ g_list_free(members);
+ g_free(room);
+ if (topic)
+ g_free(topic);
+}
+
+void yahoo_process_chat_exit(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+ char *who = NULL;
+ char *room = NULL;
+ GSList *l;
+ struct yahoo_data *yd;
+
+ yd = gc->proto_data;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ if (pair->key == 104)
+ room = yahoo_string_decode(gc, pair->value, TRUE);
+ if (pair->key == 109)
+ who = pair->value;
+ }
+
+
+ if (who && room) {
+ 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);
+
+ }
+ if (room)
+ g_free(room);
+}
+
+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;
+ GSList *l;
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+
+ case 97:
+ utf8 = strtol(pair->value, NULL, 10);
+ break;
+ case 104:
+ room = yahoo_string_decode(gc, pair->value, TRUE);
+ break;
+ case 109:
+ who = pair->value;
+ break;
+ case 117:
+ msg = pair->value;
+ break;
+ case 124:
+ msgtype = strtol(pair->value, NULL, 10);
+ break;
+ }
+ }
+
+
+ c = gaim_find_chat(gc, YAHOO_CHAT_ID);
+ if (!who || !c) {
+ if (room)
+ g_free(room);
+ /* we still get messages after we part, funny that */
+ return;
+ }
+
+ if (!msg) {
+ gaim_debug(GAIM_DEBUG_MISC, "yahoo", "Got a message packet with no message.\nThis probably means something important, but we're ignoring it.\n");
+ return;
+ }
+ msg2 = yahoo_string_decode(gc, msg, utf8);
+ msg = yahoo_codes_to_html(msg2);
+ g_free(msg2);
+
+ if (msgtype == 2 || msgtype == 3) {
+ char *tmp;
+ tmp = g_strdup_printf("/me %s", msg);
+ g_free(msg);
+ msg = tmp;
+ }
+
+ serv_got_chat_in(gc, YAHOO_CHAT_ID, who, 0, msg, time(NULL));
+ g_free(msg);
+}
+
+void yahoo_process_chat_addinvite(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+ GSList *l;
+ char *room = NULL;
+ char *msg = NULL;
+ char *who = NULL;
+
+
+ for (l = pkt->hash; l; l = l->next) {
+ struct yahoo_pair *pair = l->data;
+
+ switch (pair->key) {
+ case 104:
+ room = yahoo_string_decode(gc, pair->value, TRUE);
+ break;
+ case 129: /* room id? */
+ break;
+ case 126: /* ??? */
+ break;
+ case 117:
+ msg = yahoo_string_decode(gc, pair->value, FALSE);
+ break;
+ case 119:
+ who = pair->value;
+ break;
+ case 118: /* us */
+ break;
+ }
+ }
+
+ if (room && who) {
+ 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);
+ }
+ if (room)
+ g_free(room);
+ if (msg)
+ g_free(msg);
+}
+
+void yahoo_process_chat_goto(GaimConnection *gc, struct yahoo_packet *pkt)
+{
+ if (pkt->status == -1)
+ 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;
+ GList *w;
+
+ 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;
+ GList *who;
+ char *msg, *msg2;
+ int utf8 = 1;
+
+ 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);
+ if (utf8)
+ yahoo_packet_hash(pkt, 97, "1"); /* utf-8 */
+
+ yahoo_send_packet(yd, pkt);
+
+ yahoo_packet_free(pkt);
+ g_free(msg);
+ g_free(msg2);
+
+ return 0;
+}
+
+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;
+ char **memarr = NULL;
+ int i;
+
+ if (members)
+ 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);
+ if (memarr) {
+ for(i = 0 ; memarr[i]; i++) {
+ if (!strcmp(memarr[i], "") || !strcmp(memarr[i], dn))
+ continue;
+ 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);
+
+ if (memarr)
+ g_strfreev(memarr);
+}
+
+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;
+ GList *members;
+ char *msg2 = NULL;
+
+ if (msg)
+ 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);
+ if (!strcmp(name, dn))
+ continue;
+ yahoo_packet_hash(pkt, 52, name);
+ yahoo_packet_hash(pkt, 53, name);
+ }
+ yahoo_send_packet(yd, pkt);
+
+ yahoo_packet_free(pkt);
+ if (msg)
+ g_free(msg2);
+}
+
+/*
+ * 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;
+ GaimConversation *c;
+ char *eroom;
+ gboolean utf8 = 1;
+
+ if (yd->wm) {
+ g_return_if_fail(yd->ycht != NULL);
+
+ ycht_chat_leave(yd->ycht, room, logout);
+ return;
+ }
+
+ 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);
+
+ yd->in_chat = 0;
+ if (yd->chat_name) {
+ g_free(yd->chat_name);
+ yd->chat_name = NULL;
+ }
+
+ if ((c = gaim_find_chat(gc, YAHOO_CHAT_ID)))
+ serv_got_chat_left(gc, YAHOO_CHAT_ID);
+
+ if (!logout)
+ return;
+
+ 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);
+
+ yd->chat_online = 0;
+ g_free(eroom);
+}
+
+/* borrowed from gtkconv.c */
+static gboolean
+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.
+ */
+ char *c;
+ gboolean inside_html = 0;
+
+ /* Umm.. this would be very bad if this happens. */
+ g_return_val_if_fail(message != NULL, FALSE);
+
+ if (len == -1)
+ len = strlen(message);
+
+ for (c = message; *c != '\0'; c++, len--) {
+ if (inside_html) {
+ if (*c == '>')
+ inside_html = FALSE;
+ }
+ else {
+ if (*c == '<')
+ inside_html = TRUE;
+ else
+ break;
+ }
+ }
+
+ if (*c != '\0' && !g_ascii_strncasecmp(c, "/me ", 4)) {
+ memmove(c, c + 4, len - 3);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+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;
+ int me = 0;
+ char *msg1, *msg2, *room2;
+ gboolean utf8 = TRUE;
+
+ if (yd->wm) {
+ g_return_val_if_fail(yd->ycht != NULL, 1);
+
+ return ycht_chat_send(yd->ycht, room, what);
+ }
+
+ msg1 = g_strdup(what);
+
+ if (meify(msg1, -1))
+ me = 1;
+
+ msg2 = yahoo_html_to_codes(msg1);
+ g_free(msg1);
+ msg1 = yahoo_string_encode(gc, msg2, &utf8);
+ g_free(msg2);
+ 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);
+ if (me)
+ yahoo_packet_hash(pkt, 124, "2");
+ else
+ yahoo_packet_hash(pkt, 124, "1");
+ /* fixme: what about /think? (124=3) */
+ if (utf8)
+ yahoo_packet_hash(pkt, 97, "1");
+
+ yahoo_send_packet(yd, pkt);
+ yahoo_packet_free(pkt);
+ g_free(msg1);
+ g_free(room2);
+
+ return 0;
+}
+
+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;
+ char *room2;
+ gboolean utf8 = TRUE;
+
+ if (yd->wm) {
+ g_return_if_fail(yd->ycht != NULL);
+
+ ycht_chat_join(yd->ycht, room);
+ return;
+ }
+
+ /* 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);
+ g_free(room2);
+}
+
+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;
+ gboolean utf8 = TRUE;
+
+ if (yd->wm) {
+ g_return_if_fail(yd->ycht != NULL);
+
+ ycht_chat_send_invite(yd->ycht, room, buddy, msg);
+ return;
+ }
+
+ room2 = yahoo_string_encode(gc, room, &utf8);
+ if (msg)
+ 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);
+
+ g_free(room2);
+ if (msg2)
+ g_free(msg2);
+}
+
+void yahoo_chat_goto(GaimConnection *gc, const char *name)
+{
+ struct yahoo_data *yd;
+ struct yahoo_packet *pkt;
+
+ yd = gc->proto_data;
+
+ if (yd->wm) {
+ g_return_if_fail(yd->ycht != NULL);
+
+ ycht_chat_goto_user(yd->ycht, name);
+ return;
+ }
+
+ if (!yd->chat_online)
+ yahoo_chat_online(gc);
+
+ 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;
+ GaimConversation *c;
+
+ if (!yd)
+ return;
+
+
+ c = gaim_find_chat(gc, id);
+ if (!c)
+ return;
+
+ 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);
+ } else {
+ 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)
+{
+ GaimConversation *c;
+ int ret;
+ struct yahoo_data *yd;
+
+ yd = (struct yahoo_data *) gc->proto_data;
+ if (!yd)
+ return -1;
+
+ c = gaim_find_chat(gc, id);
+ if (!c)
+ return -1;
+
+ 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);
+ } else {
+ ret = yahoo_chat_send(gc, gaim_connection_get_display_name(gc),
+ gaim_conversation_get_name(c), what);
+ if (!ret)
+ serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(c)),
+ gaim_connection_get_display_name(gc), 0, what, time(NULL));
+ }
+ return ret;
+}
+
+GList *yahoo_c_info(GaimConnection *gc)
+{
+ GList *m = NULL;
+ 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);
+
+ return m;
+}
+
+GHashTable *yahoo_c_info_defaults(GaimConnection *gc, const char *chat_name)
+{
+ GHashTable *defaults;
+
+ defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+
+ if (chat_name != NULL)
+ g_hash_table_insert(defaults, "room", g_strdup(chat_name));
+
+ return defaults;
+}
+
+char *yahoo_get_chat_name(GHashTable *data)
+{
+ return g_strdup(g_hash_table_lookup(data, "room"));
+}
+
+void yahoo_c_join(GaimConnection *gc, GHashTable *data)
+{
+ struct yahoo_data *yd;
+ char *room, *topic, *members, *type;
+ int id;
+ GaimConversation *c;
+
+ yd = (struct yahoo_data *) gc->proto_data;
+ if (!yd)
+ return;
+
+ room = g_hash_table_lookup(data, "room");
+ if (!room)
+ return;
+
+ topic = g_hash_table_lookup(data, "topic");
+ if (!topic)
+ topic = "";
+
+ members = g_hash_table_lookup(data, "members");
+
+
+ if ((type = g_hash_table_lookup(data, "type")) && !strcmp(type, "Conference")) {
+ id = yd->conf_id++;
+ 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);
+ return;
+ } else {
+ if (yd->in_chat)
+ yahoo_chat_leave(gc, room,
+ gaim_connection_get_display_name(gc),
+ FALSE);
+ if (!yd->chat_online)
+ yahoo_chat_online(gc);
+ yahoo_chat_join(gc, gaim_connection_get_display_name(gc), room, topic);
+ return;
+ }
+}
+
+void yahoo_c_invite(GaimConnection *gc, int id, const char *msg, const char *name)
+{
+ GaimConversation *c;
+
+ c = gaim_find_chat(gc, id);
+ if (!c || !c->name)
+ return;
+
+ if (id != YAHOO_CHAT_ID) {
+ yahoo_conf_invite(gc, c, gaim_connection_get_display_name(gc), name,
+ gaim_conversation_get_name(c), msg);
+ } else {
+ yahoo_chat_invite(gc, gaim_connection_get_display_name(gc), name,
+ gaim_conversation_get_name(c), msg);
+ }
+}
+
+
+struct yahoo_roomlist {
+ int fd;
+ int inpa;
+ guchar *rxqueue;
+ int rxlen;
+ gboolean started;
+ char *path;
+ char *host;
+ GaimRoomlist *list;
+ GaimRoomlistRoom *cat;
+ GaimRoomlistRoom *ucat;
+ GMarkupParseContext *parse;
+
+};
+
+static void yahoo_roomlist_destroy(struct yahoo_roomlist *yrl)
+{
+ if (yrl->inpa)
+ gaim_input_remove(yrl->inpa);
+ if (yrl->rxqueue)
+ g_free(yrl->rxqueue);
+ if (yrl->path)
+ g_free(yrl->path);
+ if (yrl->host)
+ g_free(yrl->host);
+ if (yrl->parse)
+ g_markup_parse_context_free(yrl->parse);
+ g_free(yrl);
+}
+
+enum yahoo_room_type {
+ yrt_yahoo,
+ yrt_user,
+};
+
+struct yahoo_chatxml_state {
+ GaimRoomlist *list;
+ struct yahoo_roomlist *yrl;
+ GQueue *q;
+ struct {
+ enum yahoo_room_type type;
+ char *name;
+ char *topic;
+ char *id;
+ int users, voices, webcams;
+ } room;
+};
+
+struct yahoo_lobby {
+ 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);
+
+ s->list = list;
+ s->yrl = yrl;
+ s->q = g_queue_new();
+
+ return s;
+}
+
+static void yahoo_chatxml_state_destroy(struct yahoo_chatxml_state *s)
+{
+ g_queue_free(s->q);
+ if (s->room.name)
+ g_free(s->room.name);
+ if (s->room.topic)
+ g_free(s->room.topic);
+ if (s->room.id)
+ g_free(s->room.id);
+ g_free(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 *r;
+ GaimRoomlistRoom *parent;
+ int i;
+
+ if (!strcmp(ename, "category")) {
+ const gchar *name = NULL, *id = NULL;
+
+ for (i = 0; anames[i]; i++) {
+ if (!strcmp(anames[i], "id"))
+ id = avalues[i];
+ if (!strcmp(anames[i], "name"))
+ name = avalues[i];
+ }
+ if (!name || !id)
+ return;
+
+ 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")) {
+ if (s->room.id)
+ g_free(s->room.id);
+ s->room.id = g_strdup(avalues[i]);
+ } else if (!strcmp(anames[i], "name")) {
+ if (s->room.name)
+ g_free(s->room.name);
+ s->room.name = g_strdup(avalues[i]);
+ } else if (!strcmp(anames[i], "topic")) {
+ if (s->room.topic)
+ g_free(s->room.topic);
+ s->room.topic = g_strdup(avalues[i]);
+ } else if (!strcmp(anames[i], "type")) {
+ if (!strcmp("yahoo", avalues[i]))
+ s->room.type = yrt_yahoo;
+ else
+ 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);
+ else
+ 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);
+
+ g_free(name);
+ g_free(lob);
+ }
+
+ }
+
+}
+
+static GMarkupParser parser = {
+ yahoo_chatlist_start_element,
+ yahoo_chatlist_end_element,
+ NULL,
+ NULL,
+ NULL
+};
+
+static void yahoo_roomlist_cleanup(GaimRoomlist *list, struct yahoo_roomlist *yrl)
+{
+ gaim_roomlist_set_in_progress(list, FALSE);
+
+ if (yrl) {
+ 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;
+ char buf[1024];
+ int len;
+ guchar *start;
+ struct yahoo_chatxml_state *s;
+
+ len = read(yrl->fd, buf, sizeof(buf));
+
+ if (len <= 0) {
+ if (yrl->parse)
+ g_markup_parse_context_end_parse(yrl->parse, NULL);
+ yahoo_roomlist_cleanup(list, yrl);
+ return;
+ }
+
+
+ yrl->rxqueue = g_realloc(yrl->rxqueue, len + yrl->rxlen);
+ memcpy(yrl->rxqueue + yrl->rxlen, buf, len);
+ yrl->rxlen += len;
+
+ if (!yrl->started) {
+ yrl->started = TRUE;
+ start = g_strstr_len(yrl->rxqueue, yrl->rxlen, "\r\n\r\n");
+ if (!start || (start - yrl->rxqueue + 4) >= yrl->rxlen)
+ return;
+ start += 4;
+ } else {
+ start = yrl->rxqueue;
+ }
+
+ 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);
+ return;
+ }
+
+ yrl->rxlen = 0;
+}
+
+static void yahoo_roomlist_got_connected(gpointer data, gint source, GaimInputCondition cond)
+{
+ struct yahoo_roomlist *yrl = data;
+ GaimRoomlist *list = yrl->list;
+ char *buf, *cookie;
+ struct yahoo_data *yd = gaim_account_get_connection(list->account)->proto_data;
+
+ if (source < 0) {
+ gaim_notify_error(gaim_account_get_connection(list->account), NULL, _("Unable to connect"), _("Fetching the room list failed."));
+ yahoo_roomlist_cleanup(list, yrl);
+ return;
+ }
+
+ yrl->fd = source;
+
+ 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));
+ g_free(cookie);
+ g_free(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;
+ GaimRoomlist *rl;
+ char *url;
+ GList *fields = NULL;
+ GaimRoomlistField *f;
+
+ 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));
+ yrl->list = rl;
+
+ gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path), NULL, NULL);
+ g_free(url);
+
+ 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);
+ return NULL;
+ }
+
+ rl->proto_data = g_list_append(rl->proto_data, yrl);
+
+ gaim_roomlist_set_in_progress(rl, TRUE);
+ return rl;
+}
+
+void yahoo_roomlist_cancel(GaimRoomlist *list)
+{
+ GList *l, *k;
+
+ 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);
+ }
+ g_list_free(k);
+}
+
+void yahoo_roomlist_expand_category(GaimRoomlist *list, GaimRoomlistRoom *category)
+{
+ struct yahoo_roomlist *yrl;
+ char *url;
+ char *id;
+
+ if (category->type != GAIM_ROOMLIST_ROOMTYPE_CATEGORY)
+ return;
+
+ if (!(id = g_list_nth_data(category->fields, 1))) {
+ gaim_roomlist_set_in_progress(list, FALSE);
+ return;
+ }
+
+ url = g_strdup_printf("%s?chatroom_%s=0",
+ gaim_account_get_string(
+ list->account,
+ "room_list", YAHOO_ROOMLIST_URL), id);
+
+ yrl = g_new0(struct yahoo_roomlist, 1);
+ yrl->list = list;
+ yrl->cat = category;
+ list->proto_data = g_list_append(list->proto_data, yrl);
+
+ gaim_url_parse(url, &(yrl->host), NULL, &(yrl->path), NULL, NULL);
+ g_free(url);
+
+ 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);
+ return;
+ }
+
+ gaim_roomlist_set_in_progress(list, TRUE);
+ gaim_roomlist_ref(list);
+}