Mercurial > pidgin
diff src/protocols/oscar/oscar.c @ 2086:424a40f12a6c
[gaim-migrate @ 2096]
moving protocols from plugins/ to src/protocols. making it so that you can select which protocols are compiled statically.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Tue, 31 Jul 2001 01:00:39 +0000 |
parents | |
children | b66aca8e8dce |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/oscar/oscar.c Tue Jul 31 01:00:39 2001 +0000 @@ -0,0 +1,3323 @@ +/* + * gaim + * + * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> + * + * 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 <netdb.h> +#include <gtk/gtk.h> +#include <unistd.h> +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include "multi.h" +#include "prpl.h" +#include "gaim.h" +#include "aim.h" +#include "proxy.h" + +#if USE_PIXBUF +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gdk-pixbuf/gdk-pixbuf-loader.h> +#endif + +/*#include "pixmaps/cancel.xpm"*/ +#include "pixmaps/admin_icon.xpm" +#include "pixmaps/aol_icon.xpm" +#include "pixmaps/away_icon.xpm" +#include "pixmaps/dt_icon.xpm" +#include "pixmaps/free_icon.xpm" + +/* constants to identify proto_opts */ +#define USEROPT_AUTH 0 +#define USEROPT_AUTHPORT 1 + +#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3" + +static int gaim_caps = AIM_CAPS_CHAT | +#if USE_PIXBUF + AIM_CAPS_BUDDYICON | +#endif + AIM_CAPS_GETFILE | + AIM_CAPS_IMIMAGE; + +static GtkWidget *join_chat_spin = NULL; +static GtkWidget *join_chat_entry = NULL; + +struct oscar_data { + struct aim_session_t *sess; + struct aim_conn_t *conn; + + guint cnpa; + guint paspa; + + int create_exchange; + char *create_name; + + gboolean conf; + gboolean reqemail; + gboolean chpass; + char *oldp; + char *newp; + + GSList *oscar_chats; + GSList *direct_ims; + GSList *getfiles; + GSList *hasicons; + + gboolean killme; +}; + +struct chat_connection { + char *name; + char *show; /* AOL did something funny to us */ + int exchange; + int fd; /* this is redundant since we have the conn below */ + struct aim_conn_t *conn; + int inpa; + int id; + struct gaim_connection *gc; /* i hate this. */ + struct conversation *cnv; /* bah. */ +}; + +struct direct_im { + struct gaim_connection *gc; + char name[80]; + struct conversation *cnv; + int watcher; + struct aim_conn_t *conn; +}; + +struct ask_direct { + struct gaim_connection *gc; + char *sn; + struct aim_directim_priv *priv; +}; + +struct ask_getfile { + struct gaim_connection *gc; + char *sn; + char *cookie; + char *ip; +}; + +struct getfile_transfer { + struct gaim_connection *gc; + char *receiver; + char *filename; + struct aim_conn_t *conn; + struct aim_fileheader_t *fh; + int gip; + int gop; + FILE *listing; + FILE *file; + GtkWidget *window; + GtkWidget *meter; + GtkWidget *label; + long pos; + long size; +}; + +#if USE_PIXBUF +struct icon_req { + char *user; + time_t timestamp; + unsigned long length; + gpointer data; + gboolean request; + GdkPixbufAnimation *anim; + GdkPixbuf *unanim; + struct conversation *cnv; + GtkWidget *pix; + int curframe; + int timer; +}; +#endif + +static struct direct_im *find_direct_im(struct oscar_data *od, char *who) { + GSList *d = od->direct_ims; + char *n = g_strdup(normalize(who)); + struct direct_im *m = NULL; + + while (d) { + m = (struct direct_im *)d->data; + if (!strcmp(n, normalize(m->name))) + break; + m = NULL; + d = d->next; + } + + g_free(n); + return m; +} + +/* +static struct getfile_transfer *find_getfile_transfer(struct oscar_data *od, struct aim_conn_t *conn) { + GSList *g = od->getfiles; + struct getfile_transfer *n = NULL; + + while (g) { + n = (struct getfile_transfer *)g->data; + if (n->conn == conn) + return n; + n = NULL; + g = g->next; + } + + return n; +} +*/ + +static char *extract_name(char *name) { + char *tmp; + int i, j; + char *x = strchr(name, '-'); + if (!x) return NULL; + x = strchr(++x, '-'); + if (!x) return NULL; + tmp = g_strdup(++x); + + for (i = 0, j = 0; x[i]; i++) { + if (x[i] != '%') + tmp[j++] = x[i]; + else { + char hex[3]; + hex[0] = x[++i]; + hex[1] = x[++i]; + hex[2] = 0; + sscanf(hex, "%x", (int *)&tmp[j++]); + } + } + + tmp[j] = 0; + return tmp; +} + +static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) { + GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats; + struct chat_connection *c = NULL; + if (gc->protocol != PROTO_OSCAR) return NULL; + + while (g) { + c = (struct chat_connection *)g->data; + if (c->id == id) + break; + g = g->next; + c = NULL; + } + + return c; +} + +static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc, + struct aim_conn_t *conn) { + GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats; + struct chat_connection *c = NULL; + + while (g) { + c = (struct chat_connection *)g->data; + if (c->conn == conn) + break; + g = g->next; + c = NULL; + } + + return c; +} + +static int gaim_parse_auth_resp (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_login (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_server_ready (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_handle_redirect (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_info_change (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_account_confirm (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_oncoming (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_offgoing (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_incoming_im(struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_misses (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_user_info (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_motd (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_chatnav_info (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_chat_join (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_chat_leave (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_chat_info_update (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_chat_incoming_msg(struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_msgack (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_ratechange (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_evilnotify (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_searcherror(struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_searchreply(struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_bosrights (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_rateresp (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_reportinterval (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_msgerr (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_buddyrights(struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_locerr (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_parse_genericerr (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_memrequest (struct aim_session_t *, struct command_rx_struct *, ...); + +static int gaim_directim_initiate (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_directim_incoming (struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_directim_disconnect(struct aim_session_t *, struct command_rx_struct *, ...); +static int gaim_directim_typing (struct aim_session_t *, struct command_rx_struct *, ...); + +static char *msgerrreason[] = { + "Invalid error", + "Invalid SNAC", + "Rate to host", + "Rate to client", + "Not logged in", + "Service unavailable", + "Service not defined", + "Obsolete SNAC", + "Not supported by host", + "Not supported by client", + "Refused by client", + "Reply too big", + "Responses lost", + "Request denied", + "Busted SNAC payload", + "Insufficient rights", + "In local permit/deny", + "Too evil (sender)", + "Too evil (receiver)", + "User temporarily unavailable", + "No match", + "List overflow", + "Request ambiguous", + "Queue full", + "Not while on AOL" +}; +static int msgerrreasonlen = 25; + +static void oscar_callback(gpointer data, gint source, + GdkInputCondition condition) { + struct aim_conn_t *conn = (struct aim_conn_t *)data; + struct aim_session_t *sess = aim_conn_getsess(conn); + struct gaim_connection *gc = sess ? sess->aux_data : NULL; + struct oscar_data *odata; + + if (!gc) { + /* gc is null. we return, else we seg SIGSEG on next line. */ + debug_printf("oscar callback for closed connection (1).\n"); + return; + } + + odata = (struct oscar_data *)gc->proto_data; + + if (!g_slist_find(connections, gc)) { + /* oh boy. this is probably bad. i guess the only thing we + * can really do is return? */ + debug_printf("oscar callback for closed connection (2).\n"); + return; + } + + if (condition & GDK_INPUT_EXCEPTION) { + hide_login_progress(gc, _("Disconnected.")); + signoff(gc); + return; + } + if (condition & GDK_INPUT_READ) { + if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { + debug_printf("got information on rendezvous\n"); + if (aim_handlerendconnect(odata->sess, conn) < 0) { + debug_printf(_("connection error (rend)\n")); + } + } else { + if (aim_get_command(odata->sess, conn) >= 0) { + aim_rxdispatch(odata->sess); + if (odata->killme) + signoff(gc); + } else { + if ((conn->type == AIM_CONN_TYPE_BOS) || + !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) { + debug_printf(_("major connection error\n")); + hide_login_progress(gc, _("Disconnected.")); + signoff(gc); + } else if (conn->type == AIM_CONN_TYPE_CHAT) { + struct chat_connection *c = find_oscar_chat_by_conn(gc, conn); + char buf[BUF_LONG]; + debug_printf("disconnected from chat room %s\n", c->name); + c->conn = NULL; + if (c->inpa > 0) + gdk_input_remove(c->inpa); + c->inpa = 0; + c->fd = -1; + aim_conn_kill(odata->sess, &conn); + sprintf(buf, _("You have been disconnected from chat room %s."), c->name); + do_error_dialog(buf, _("Chat Error!")); + } else if (conn->type == AIM_CONN_TYPE_CHATNAV) { + if (odata->cnpa > 0) + gdk_input_remove(odata->cnpa); + odata->cnpa = 0; + debug_printf("removing chatnav input watcher\n"); + if (odata->create_exchange) { + odata->create_exchange = 0; + g_free(odata->create_name); + odata->create_name = NULL; + do_error_dialog(_("Chat is currently unavailable"), + _("Gaim - Chat")); + } + aim_conn_kill(odata->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_AUTH) { + if (odata->paspa > 0) + gdk_input_remove(odata->paspa); + odata->paspa = 0; + debug_printf("removing authconn input watcher\n"); + aim_conn_kill(odata->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) { + debug_printf("No handler for rendezvous disconnect (%d).\n", + source); + aim_conn_kill(odata->sess, &conn); + } else { + debug_printf("holy crap! generic connection error! %d\n", + conn->type); + aim_conn_kill(odata->sess, &conn); + } + } + } + } +} + +static void oscar_debug(struct aim_session_t *sess, int level, const char *format, va_list va) { + char *s = g_strdup_vprintf(format, va); + char buf[256]; + char *t; + struct gaim_connection *gc = sess->aux_data; + + g_snprintf(buf, sizeof(buf), "%s %d: ", gc->username, level); + t = g_strconcat(buf, s, NULL); + debug_printf(t); + if (t[strlen(t)-1] != '\n') + debug_printf("\n"); + g_free(t); + g_free(s); +} + +static void oscar_login_connect(gpointer data, gint source, GdkInputCondition cond) +{ + struct gaim_connection *gc = data; + struct oscar_data *odata; + struct aim_session_t *sess; + struct aim_conn_t *conn; + + if (!g_slist_find(connections, gc)) { + close(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + + if (source < 0) { + hide_login_progress(gc, _("Couldn't connect to host")); + signoff(gc); + return; + } + + aim_conn_completeconnect(sess, conn); + gc->inpa = gdk_input_add(conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, conn); + debug_printf(_("Password sent, waiting for response\n")); +} + +static void oscar_login(struct aim_user *user) { + struct aim_session_t *sess; + struct aim_conn_t *conn; + char buf[256]; + struct gaim_connection *gc = new_gaim_conn(user); + struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1); + odata->create_exchange = 0; + + debug_printf(_("Logging in %s\n"), user->username); + + sess = g_new0(struct aim_session_t, 1); + + aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0); + aim_setdebuggingcb(sess, oscar_debug); + + /* we need an immediate queue because we don't use a while-loop to + * see if things need to be sent. */ + aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL); + odata->sess = sess; + sess->aux_data = gc; + + conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL); + if (conn == NULL) { + debug_printf(_("internal connection error\n")); + hide_login_progress(gc, _("Unable to login to AIM")); + signoff(gc); + return; + } + + g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username); + set_login_progress(gc, 2, buf); + + aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0); + aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); + + conn->status |= AIM_CONN_STATUS_INPROGRESS; + conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ? + user->proto_opt[USEROPT_AUTH] : FAIM_LOGIN_SERVER, + user->proto_opt[USEROPT_AUTHPORT][0] ? + atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT, + oscar_login_connect, gc); + if (conn->fd < 0) { + hide_login_progress(gc, _("Couldn't connect to host")); + signoff(gc); + return; + } + aim_request_login(sess, conn, gc->username); +} + +static void oscar_close(struct gaim_connection *gc) { + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + if (gc->protocol != PROTO_OSCAR) return; + + while (odata->oscar_chats) { + struct chat_connection *n = odata->oscar_chats->data; + if (n->inpa > 0) + gdk_input_remove(n->inpa); + g_free(n->name); + g_free(n->show); + odata->oscar_chats = g_slist_remove(odata->oscar_chats, n); + g_free(n); + } + while (odata->direct_ims) { + struct direct_im *n = odata->direct_ims->data; + if (n->watcher > 0) + gdk_input_remove(n->watcher); + odata->direct_ims = g_slist_remove(odata->direct_ims, n); + g_free(n); + } +#if USE_PIXBUF + while (odata->hasicons) { + struct icon_req *n = odata->hasicons->data; + if (n->anim) + gdk_pixbuf_animation_unref(n->anim); + if (n->unanim) + gdk_pixbuf_unref(n->unanim); + if (n->timer) + gtk_timeout_remove(n->timer); + if (n->cnv && n->pix) + gtk_container_remove(GTK_CONTAINER(n->cnv->bbox), n->pix); + g_free(n->user); + if (n->data) + g_free(n->data); + odata->hasicons = g_slist_remove(odata->hasicons, n); + g_free(n); + } +#endif + if (gc->inpa > 0) + gdk_input_remove(gc->inpa); + if (odata->cnpa > 0) + gdk_input_remove(odata->cnpa); + if (odata->paspa > 0) + gdk_input_remove(odata->paspa); + aim_session_kill(odata->sess); + g_free(odata->sess); + odata->sess = NULL; + g_free(gc->proto_data); + gc->proto_data = NULL; + debug_printf(_("Signed off.\n")); +} + +static void oscar_bos_connect(gpointer data, gint source, GdkInputCondition cond) { + struct gaim_connection *gc = data; + struct oscar_data *odata; + struct aim_session_t *sess; + struct aim_conn_t *bosconn; + + if (!g_slist_find(connections, gc)) { + close(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + bosconn = odata->conn; + + if (source < 0) { + hide_login_progress(gc, _("Could Not Connect")); + signoff(gc); + return; + } + + aim_conn_completeconnect(sess, bosconn); + gc->inpa = gdk_input_add(bosconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, bosconn); + set_login_progress(gc, 4, _("Connection established, cookie sent")); +} + +int gaim_parse_auth_resp(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + struct aim_conn_t *bosconn = NULL; + char *sn = NULL, *bosip = NULL, *errurl = NULL, *email = NULL; + unsigned char *cookie = NULL; + int errorcode = 0, regstatus = 0; + int latestbuild = 0, latestbetabuild = 0; + char *latestrelease = NULL, *latestbeta = NULL; + char *latestreleaseurl = NULL, *latestbetaurl = NULL; + char *latestreleaseinfo = NULL, *latestbetainfo = NULL; + int i; char *host; int port; + struct aim_user *user; + + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; + user = gc->user; + port = user->proto_opt[USEROPT_AUTHPORT][0] ? + atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT, + + va_start(ap, command); + sn = va_arg(ap, char *); + errorcode = va_arg(ap, int); + errurl = va_arg(ap, char *); + regstatus = va_arg(ap, int); + email = va_arg(ap, char *); + bosip = va_arg(ap, char *); + cookie = va_arg(ap, unsigned char *); + + latestrelease = va_arg(ap, char *); + latestbuild = va_arg(ap, int); + latestreleaseurl = va_arg(ap, char *); + latestreleaseinfo = va_arg(ap, char *); + + latestbeta = va_arg(ap, char *); + latestbetabuild = va_arg(ap, int); + latestbetaurl = va_arg(ap, char *); + latestbetainfo = va_arg(ap, char *); + + va_end(ap); + + debug_printf("inside auth_resp (Screen name: %s)\n", sn); + + if (errorcode || !bosip || !cookie) { + switch (errorcode) { + case 0x05: + /* Incorrect nick/password */ + hide_login_progress(gc, _("Incorrect nickname or password.")); + plugin_event(event_error, (void *)980, 0, 0, 0); + break; + case 0x11: + /* Suspended account */ + hide_login_progress(gc, _("Your account is currently suspended.")); + break; + case 0x18: + /* connecting too frequently */ + hide_login_progress(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); + plugin_event(event_error, (void *)983, 0, 0, 0); + break; + case 0x1c: + /* client too old */ + hide_login_progress(gc, _("The client version you are using is too old. Please upgrade at " WEBSITE)); + plugin_event(event_error, (void *)989, 0, 0, 0); + break; + default: + hide_login_progress(gc, _("Authentication Failed")); + break; + } + debug_printf("Login Error Code 0x%04x\n", errorcode); + debug_printf("Error URL: %s\n", errurl); + od->killme = TRUE; + return 1; + } + + + debug_printf("Reg status: %2d\n", regstatus); + if (email) { + debug_printf("Email: %s\n", email); + } else { + debug_printf("Email is NULL\n"); + } + debug_printf("BOSIP: %s\n", bosip); + if (latestbeta) + debug_printf("Latest WinAIM beta version %s, build %d, at %s (%s)\n", + latestbeta, latestbetabuild, latestbetaurl, latestbetainfo); + if (latestrelease) + debug_printf("Latest WinAIM released version %s, build %d, at %s (%s)\n", + latestrelease, latestbuild, latestreleaseurl, latestreleaseinfo); + debug_printf("Closing auth connection...\n"); + aim_conn_kill(sess, &command->conn); + + bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL); + if (bosconn == NULL) { + hide_login_progress(gc, _("Internal Error")); + od->killme = TRUE; + return 0; + } + + aim_conn_addhandler(sess, bosconn, 0x0009, 0x0003, gaim_bosrights, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x0007, gaim_rateresp, 0); /* rate info */ + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, gaim_server_ready, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATEINFO, NULL, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_STS, AIM_CB_STS_SETREPORTINTERVAL, gaim_reportinterval, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_user_info, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0003, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0009, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, 0x0001, 0x001f, gaim_memrequest, 0); + + ((struct oscar_data *)gc->proto_data)->conn = bosconn; + for (i = 0; i < (int)strlen(bosip); i++) { + if (bosip[i] == ':') { + port = atoi(&(bosip[i+1])); + break; + } + } + host = g_strndup(bosip, i); + bosconn->status |= AIM_CONN_STATUS_INPROGRESS; + bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc); + g_free(host); + if (bosconn->fd < 0) { + hide_login_progress(gc, _("Could Not Connect")); + od->killme = TRUE; + return 0; + } + aim_auth_sendcookie(sess, bosconn, cookie); + gdk_input_remove(gc->inpa); + return 1; +} + +struct pieceofcrap { + struct gaim_connection *gc; + unsigned long offset; + unsigned long len; + char *modname; + int fd; + struct aim_conn_t *conn; + unsigned int inpa; +}; + +static void damn_you(gpointer data, gint source, GdkInputCondition c) +{ + struct pieceofcrap *pos = data; + struct oscar_data *od = pos->gc->proto_data; + char in = '\0'; + int x = 0; + unsigned char m[17]; + + while (read(pos->fd, &in, 1) == 1) { + if (in == '\n') + x++; + else if (in != '\r') + x = 0; + if (x == 2) + break; + in = '\0'; + } + if (in != '\n') { + do_error_dialog("Gaim was unable to get a valid hash for logging into AIM." + " You may be disconnected shortly.", "Login Error"); + gdk_input_remove(pos->inpa); + close(pos->fd); + g_free(pos); + return; + } + read(pos->fd, m, 16); + m[16] = '\0'; + debug_printf("Sending hash: "); + for (x = 0; x < 16; x++) + debug_printf("%02x ", (unsigned char)m[x]); + debug_printf("\n"); + gdk_input_remove(pos->inpa); + close(pos->fd); + aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH); + g_free(pos); +} + +static void straight_to_hell(gpointer data, gint source, GdkInputCondition cond) { + struct pieceofcrap *pos = data; + char buf[BUF_LONG]; + + if (source < 0) { + do_error_dialog("Gaim was unable to get a valid hash for logging into AIM." + " You may be disconnected shortly.", "Login Error"); + if (pos->modname) + g_free(pos->modname); + g_free(pos); + return; + } + + g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA + "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n", + pos->offset, pos->len, pos->modname ? pos->modname : ""); + write(pos->fd, buf, strlen(buf)); + if (pos->modname) + g_free(pos->modname); + pos->inpa = gdk_input_add(pos->fd, GDK_INPUT_READ, damn_you, pos); + return; +} + +/* size of icbmui.ocm, the largest module in AIM 3.5 */ +#define AIM_MAX_FILE_SIZE 98304 + +int gaim_memrequest(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + struct pieceofcrap *pos; + unsigned long offset, len; + char *modname; + int fd; + + va_start(ap, command); + offset = va_arg(ap, unsigned long); + len = va_arg(ap, unsigned long); + modname = va_arg(ap, char *); + va_end(ap); + + debug_printf("offset: %d, len: %d, file: %s\n", offset, len, modname ? modname : "aim.exe"); + if (len == 0) { + debug_printf("len is 0, hashing NULL\n"); + aim_sendmemblock(sess, command->conn, offset, len, NULL, + AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + return 1; + } + /* uncomment this when you're convinced it's right. remember, it's been wrong before. + if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) { + char *buf; + int i = 8; + if (modname) + i += strlen(modname); + buf = g_malloc(i); + i = 0; + if (modname) { + memcpy(buf, modname, strlen(modname)); + i += strlen(modname); + } + buf[i++] = offset & 0xff; + buf[i++] = (offset >> 8) & 0xff; + buf[i++] = (offset >> 16) & 0xff; + buf[i++] = (offset >> 24) & 0xff; + buf[i++] = len & 0xff; + buf[i++] = (len >> 8) & 0xff; + buf[i++] = (len >> 16) & 0xff; + buf[i++] = (len >> 24) & 0xff; + debug_printf("len + offset is invalid, hashing request\n"); + aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + g_free(buf); + return 1; + } + */ + + pos = g_new0(struct pieceofcrap, 1); + pos->gc = sess->aux_data; + pos->conn = command->conn; + + pos->offset = offset; + pos->len = len; + pos->modname = modname ? g_strdup(modname) : NULL; + + fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos); + if (fd < 0) { + if (pos->modname) + g_free(pos->modname); + g_free(pos); + do_error_dialog("Gaim was unable to get a valid hash for logging into AIM." + " You may be disconnected shortly.", "Login Error"); + } + pos->fd = fd; + + return 1; +} + +int gaim_parse_login(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { +#if 0 + struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b}; +#else + struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD; +#endif + char *key; + va_list ap; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + key = va_arg(ap, char *); + va_end(ap); + + aim_send_login(sess, command->conn, gc->username, gc->password, &info, key); + return 1; +} + +int gaim_server_ready(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + static int id = 1; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; + struct chat_connection *chatcon; + switch (command->conn->type) { + case AIM_CONN_TYPE_AUTH: + aim_auth_setversions(sess, command->conn); + aim_bos_reqrate(sess, command->conn); + debug_printf("done with AUTH ServerReady\n"); + if (od->chpass) { + debug_printf("changing password\n"); + aim_auth_changepasswd(sess, command->conn, od->newp, od->oldp); + g_free(od->oldp); + g_free(od->newp); + od->chpass = FALSE; + } + if (od->conf) { + debug_printf("confirming account\n"); + aim_auth_reqconfirm(sess, command->conn); + od->conf = FALSE; + } + if (od->reqemail) { + debug_printf("requesting email\n"); + aim_auth_getinfo(sess, command->conn, 0x0011); + od->reqemail = FALSE; + } + break; + case AIM_CONN_TYPE_BOS: + aim_setversions(sess, command->conn); + aim_bos_reqrate(sess, command->conn); /* request rate info */ + debug_printf("done with BOS ServerReady\n"); + break; + case AIM_CONN_TYPE_CHATNAV: + debug_printf("chatnav: got server ready\n"); + aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0); + aim_bos_reqrate(sess, command->conn); + aim_bos_ackrateresp(sess, command->conn); + aim_chatnav_clientready(sess, command->conn); + aim_chatnav_reqrights(sess, command->conn); + break; + case AIM_CONN_TYPE_CHAT: + debug_printf("chat: got server ready\n"); + aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0); + aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0); + aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0); + aim_conn_addhandler(sess, command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0); + aim_bos_reqrate(sess, command->conn); + aim_bos_ackrateresp(sess, command->conn); + aim_chat_clientready(sess, command->conn); + chatcon = find_oscar_chat_by_conn(gc, command->conn); + chatcon->id = id; + chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show); + break; + case AIM_CONN_TYPE_RENDEZVOUS: + break; + default: /* huh? */ + debug_printf("server ready: got unexpected connection type %04x\n", command->conn->type); + break; + } + return 1; +} + +static void oscar_chatnav_connect(gpointer data, gint source, GdkInputCondition cond) +{ + struct gaim_connection *gc = data; + struct oscar_data *odata; + struct aim_session_t *sess; + struct aim_conn_t *tstconn; + + if (!g_slist_find(connections, gc)) { + close(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV); + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + debug_printf("unable to connect to chatnav server\n"); + return; + } + + aim_conn_completeconnect(sess, tstconn); + odata->cnpa = gdk_input_add(tstconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, tstconn); + debug_printf("chatnav: connected\n"); +} + +static void oscar_auth_connect(gpointer data, gint source, GdkInputCondition cond) +{ + struct gaim_connection *gc = data; + struct oscar_data *odata; + struct aim_session_t *sess; + struct aim_conn_t *tstconn; + + if (!g_slist_find(connections, gc)) { + close(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + debug_printf("unable to connect to authorizer\n"); + return; + } + + aim_conn_completeconnect(sess, tstconn); + odata->paspa = gdk_input_add(tstconn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, tstconn); + debug_printf("chatnav: connected\n"); +} + +static void oscar_chat_connect(gpointer data, gint source, GdkInputCondition cond) +{ + struct chat_connection *ccon = data; + struct gaim_connection *gc = ccon->gc; + struct oscar_data *odata; + struct aim_session_t *sess; + struct aim_conn_t *tstconn; + + if (!g_slist_find(connections, gc)) { + close(source); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + tstconn = ccon->conn; + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return; + } + + aim_conn_completeconnect(sess, ccon->conn); + ccon->inpa = gdk_input_add(tstconn->fd, + GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, tstconn); + odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon); + aim_chat_attachname(tstconn, ccon->name); +} + +int gaim_handle_redirect(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + int serviceid; + char *ip; + unsigned char *cookie; + struct gaim_connection *gc = sess->aux_data; + struct aim_user *user = gc->user; + struct aim_conn_t *tstconn; + int i; + char *host; + int port; + + port = user->proto_opt[USEROPT_AUTHPORT][0] ? + atoi(user->proto_opt[USEROPT_AUTHPORT]) : FAIM_LOGIN_PORT, + + va_start(ap, command); + serviceid = va_arg(ap, int); + ip = va_arg(ap, char *); + cookie = va_arg(ap, unsigned char *); + + for (i = 0; i < (int)strlen(ip); i++) { + if (ip[i] == ':') { + port = atoi(&(ip[i+1])); + break; + } + } + host = g_strndup(ip, i); + + switch(serviceid) { + case 0x7: /* Authorizer */ + debug_printf("Reconnecting with authorizor...\n"); + tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL); + if (tstconn == NULL) { + debug_printf("unable to reconnect with authorizer\n"); + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0); + aim_conn_addhandler(sess, tstconn, 0x0001, 0x0007, gaim_rateresp, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0); + aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc); + if (tstconn->fd < 0) { + aim_conn_kill(sess, &tstconn); + debug_printf("unable to reconnect with authorizer\n"); + g_free(host); + return 1; + } + aim_auth_sendcookie(sess, tstconn, cookie); + break; + case 0xd: /* ChatNav */ + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL); + if (tstconn == NULL) { + debug_printf("unable to connect to chatnav server\n"); + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc); + if (tstconn->fd < 0) { + aim_conn_kill(sess, &tstconn); + debug_printf("unable to connect to chatnav server\n"); + g_free(host); + return 1; + } + aim_auth_sendcookie(sess, tstconn, cookie); + break; + case 0xe: /* Chat */ + { + char *roomname = va_arg(ap, char *); + int exchange = va_arg(ap, int); + struct chat_connection *ccon; + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL); + if (tstconn == NULL) { + debug_printf("unable to connect to chat server\n"); + g_free(host); + return 1; + } + + aim_conn_addhandler(sess, tstconn, 0x0001, 0x0003, gaim_server_ready, 0); + ccon = g_new0(struct chat_connection, 1); + ccon->conn = tstconn; + ccon->gc = gc; + ccon->fd = -1; + ccon->name = g_strdup(roomname); + ccon->exchange = exchange; + ccon->show = extract_name(roomname); + + ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS; + ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon); + if (ccon->conn->fd < 0) { + aim_conn_kill(sess, &tstconn); + debug_printf("unable to connect to chat server\n"); + g_free(host); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return 1; + } + aim_auth_sendcookie(sess, tstconn, cookie); + debug_printf("Connected to chat room %s exchange %d\n", roomname, exchange); + } + break; + default: /* huh? */ + debug_printf("got redirect for unknown service 0x%04x\n", serviceid); + break; + } + + va_end(ap); + + g_free(host); + return 1; +} + +int gaim_parse_oncoming(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + struct aim_userinfo_s *info; + time_t time_idle; + int type = 0; + struct gaim_connection *gc = sess->aux_data; + + va_list ap; + va_start(ap, command); + info = va_arg(ap, struct aim_userinfo_s *); + va_end(ap); + + if (info->flags & AIM_FLAG_UNCONFIRMED) + type |= UC_UNCONFIRMED; + else if (info->flags & AIM_FLAG_ADMINISTRATOR) + type |= UC_ADMIN; + else if (info->flags & AIM_FLAG_AOL) + type |= UC_AOL; + else if (info->flags & AIM_FLAG_FREE) + type |= UC_NORMAL; + if (info->flags & AIM_FLAG_AWAY) + type |= UC_UNAVAILABLE; + + if (info->idletime) { + time(&time_idle); + time_idle -= info->idletime*60; + } else + time_idle = 0; + + serv_got_update(gc, info->sn, 1, info->warnlevel/10, info->onlinesince, + time_idle, type, info->capabilities); + + return 1; +} + +int gaim_parse_offgoing(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + struct aim_userinfo_s *info; + va_list ap; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + info = va_arg(ap, struct aim_userinfo_s *); + va_end(ap); + + serv_got_update(gc, info->sn, 0, 0, 0, 0, 0, 0); + + return 1; +} + +static void cancel_direct_im(gpointer w, struct ask_direct *d) { + debug_printf("Freeing DirectIM prompts.\n"); + + g_free(d->sn); + g_free(d); +} + +static void delete_direct_im(gpointer w, struct direct_im *d) { + struct oscar_data *od = (struct oscar_data *)d->gc->proto_data; + + od->direct_ims = g_slist_remove(od->direct_ims, d); + gdk_input_remove(d->watcher); + aim_conn_kill(od->sess, &d->conn); + g_free(d); +} + +static void oscar_directim_callback(gpointer data, gint source, GdkInputCondition condition) { + struct direct_im *dim = data; + struct gaim_connection *gc = dim->gc; + struct oscar_data *od = gc->proto_data; + char buf[256]; + + if (!g_slist_find(connections, gc)) { + g_free(dim); + return; + } + + if (source < 0) { + g_free(dim); + return; + } + + aim_conn_completeconnect(od->sess, dim->conn); + if (!(dim->cnv = find_conversation(dim->name))) dim->cnv = new_conversation(dim->name); + g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name); + write_to_conv(dim->cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL)); + + od->direct_ims = g_slist_append(od->direct_ims, dim); + + gtk_signal_connect(GTK_OBJECT(dim->cnv->window), "destroy", + GTK_SIGNAL_FUNC(delete_direct_im), dim); + + dim->watcher = gdk_input_add(dim->conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, dim->conn); +} + +static int accept_direct_im(gpointer w, struct ask_direct *d) { + struct gaim_connection *gc = d->gc; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct direct_im *dim; + char *host; int port = FAIM_LOGIN_PORT; + int i; + + debug_printf("Accepted DirectIM.\n"); + + dim = find_direct_im(od, d->sn); + if (dim) { + cancel_direct_im(w, d); /* 40 */ + return TRUE; + } + dim = g_new0(struct direct_im, 1); + dim->gc = d->gc; + g_snprintf(dim->name, sizeof dim->name, "%s", d->sn); + + if ((dim->conn = aim_newconn(od->sess, AIM_CONN_TYPE_RENDEZVOUS, NULL)) == NULL) { + g_free(dim); + cancel_direct_im(w, d); + return TRUE; + } + dim->conn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + dim->conn->priv = d->priv; + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, + gaim_directim_incoming, 0); + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT, + gaim_directim_disconnect, 0); + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, + gaim_directim_typing, 0); + + for (i = 0; i < (int)strlen(d->priv->ip); i++) { + if (d->priv->ip[i] == ':') { + port = atoi(&(d->priv->ip[i+1])); + break; + } + } + host = g_strndup(d->priv->ip, i); + dim->conn->status |= AIM_CONN_STATUS_INPROGRESS; + dim->conn->fd = proxy_connect(host, port, oscar_directim_callback, dim); + g_free(host); + if (dim->conn->fd < 0) { + aim_conn_kill(od->sess, &dim->conn); + g_free(dim); + cancel_direct_im(w, d); + return TRUE; + } + + cancel_direct_im(w, d); + + return TRUE; +} + +/* +static void cancel_getfile(gpointer w, struct ask_getfile *g) { + g_free(g->ip); + g_free(g->cookie); + g_free(g->sn); + g_free(g); +} + +static void cancel_getfile_file(GtkObject *obj, struct ask_getfile *g) { + GtkWidget *w = gtk_object_get_user_data(obj); + gtk_widget_destroy(w); + cancel_getfile(w, g); +} + +static void cancel_getfile_cancel(GtkObject *obj, struct ask_getfile *g) { + GtkWidget *w = gtk_object_get_user_data(obj); + gtk_widget_destroy(w); +} + +static void interrupt_getfile(GtkObject *obj, struct getfile_transfer *gt) { + struct gaim_connection *gc = gt->gc; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + + gtk_widget_destroy(gt->window); + gdk_input_remove(gt->gip); + if (gt->gop > 0) + gdk_input_remove(gt->gop); + aim_conn_kill(od->sess, >->conn); + od->getfiles = g_slist_remove(od->getfiles, gt); + g_free(gt->receiver); + g_free(gt->filename); + fclose(gt->listing); + g_free(gt); +} + +static int gaim_getfile_filereq(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct getfile_transfer *gt; + char buf[2048]; + GtkWidget *label; + GtkWidget *button; + + va_list ap; + struct aim_conn_t *oftconn; + struct aim_fileheader_t *fh; + char *cookie; + + va_start(ap, command); + oftconn = va_arg(ap, struct aim_conn_t *); + fh = va_arg(ap, struct aim_fileheader_t *); + cookie = va_arg(ap, char *); + va_end(ap); + + gt = find_getfile_transfer(od, oftconn); + + if (gt->window) + return 1; + + gt->window = gtk_dialog_new(); + gtk_window_set_title(GTK_WINDOW(gt->window), _("Gaim - File Transfer")); + gtk_widget_realize(gt->window); + aol_icon(gt->window->window); + + g_snprintf(buf, sizeof buf, _("Sending %s to %s"), fh->name, gt->receiver); + label = gtk_label_new(buf); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(gt->window)->vbox), label, FALSE, FALSE, 5); + gtk_widget_show(label); + + gt->meter = gtk_progress_bar_new(); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(gt->window)->action_area), gt->meter, FALSE, FALSE, 5); + gtk_widget_show(gt->meter); + + gt->label = gtk_label_new("0 %"); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(gt->window)->action_area), gt->label, FALSE, FALSE, 5); + gtk_widget_show(gt->label); + + button = picture_button(gt->window, _("Cancel"), cancel_xpm); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(gt->window)->action_area), button, FALSE, FALSE, 5); + gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(interrupt_getfile), gt); + + gtk_widget_show(gt->window); + + return 1; +} + +static void getfile_send_callback(gpointer data, gint source, GdkInputCondition condition) { + struct getfile_transfer *gt = (struct getfile_transfer *)data; + int result; + + result = aim_getfile_send_chunk(gt->conn, gt->file, gt->fh, -1, 1024); + gt->pos += result; + if (result == 0) { + gdk_input_remove(gt->gop); gt->gop = 0; + } else if (result == -1) { + do_error_dialog(_("Error in transfer"), "Gaim"); + gdk_input_remove(gt->gop); gt->gop = 0; + interrupt_getfile(NULL, gt); + } +} + +static int gaim_getfile_filesend(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct getfile_transfer *gt; + + va_list ap; + struct aim_conn_t *oftconn; + struct aim_fileheader_t *fh; + char *cookie; + + va_start(ap, command); + oftconn = va_arg(ap, struct aim_conn_t *); + fh = va_arg(ap, struct aim_fileheader_t *); + cookie = va_arg(ap, char *); + va_end(ap); + + gt = find_getfile_transfer(od, oftconn); + + if (gt->gop > 0) { + debug_printf("already have output watcher?\n"); + return 1; + } + + if ((gt->file = fopen(gt->filename, "r")) == NULL) { + interrupt_getfile(NULL, gt); + return 1; + } + gt->pos = 0; + gt->fh = g_memdup(fh, sizeof(struct aim_fileheader_t)); + fseek(gt->file, 0, SEEK_SET); + + gt->gop = gdk_input_add(gt->conn->fd, GDK_INPUT_WRITE, getfile_send_callback, gt); + + return 1; +} + +static int gaim_getfile_complete(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct getfile_transfer *gt; + + va_list ap; + struct aim_conn_t *conn; + struct aim_fileheader_t *fh; + + va_start(ap, command); + conn = va_arg(ap, struct aim_conn_t *); + fh = va_arg(ap, struct aim_fileheader_t *); + va_end(ap); + + gt = find_getfile_transfer(od, conn); + + gtk_widget_destroy(gt->window); + gt->window = NULL; + do_error_dialog(_("Transfer complete."), "Gaim"); + + return 1; +} + +static int gaim_getfile_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct getfile_transfer *gt; + + va_list ap; + struct aim_conn_t *conn; + char *sn; + + va_start(ap, command); + conn = va_arg(ap, struct aim_conn_t *); + sn = va_arg(ap, char *); + va_end(ap); + + gt = find_getfile_transfer(od, conn); + od->getfiles = g_slist_remove(od->getfiles, gt); + gdk_input_remove(gt->gip); + if (gt->gop > 0) + gdk_input_remove(gt->gop); + g_free(gt->receiver); + g_free(gt->filename); + aim_conn_kill(sess, &conn); + fclose(gt->listing); + g_free(gt); + + debug_printf("getfile disconnect\n"); + + return 1; +} + +static void oscar_getfile_callback(gpointer data, gint source, GdkInputCondition condition) { + struct getfile_transfer *gf = data; + struct gaim_connection *gc = gf->gc; + struct oscar_data *od = gc->proto_data; + + gdk_input_remove(gf->gip); + gf->gip = gdk_input_add(source, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, oscar_callback, gf->conn); + + aim_conn_addhandler(od->sess, gf->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ, gaim_getfile_filereq, 0); + aim_conn_addhandler(od->sess, gf->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND, gaim_getfile_filesend, 0); + aim_conn_addhandler(od->sess, gf->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE, gaim_getfile_complete, 0); + aim_conn_addhandler(od->sess, gf->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT, gaim_getfile_disconnect, 0); +} + +static void do_getfile(GtkObject *obj, struct ask_getfile *g) { + GtkWidget *w = gtk_object_get_user_data(obj); + char *filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(w)); + struct gaim_connection *gc = g->gc; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct getfile_transfer *gf; + struct stat st; + struct tm *ft; + char tmppath[256]; + FILE *file; + static int current = 0; + struct aim_conn_t *newconn; + + if (file_is_dir(filename, w)) + return; + + if (stat(filename, &st) != 0) { + gtk_widget_destroy(w); + do_error_dialog(_("Error examining file"), _("GetFile Error")); + cancel_getfile(w, g); + return; + } + + g_snprintf(tmppath, sizeof tmppath, "/%s/gaim%d%d", g_get_tmp_dir(), getpid(), current++); + if ((file = fopen(tmppath, "w+")) == NULL) { + gtk_widget_destroy(w); + do_error_dialog(_("Could not open temporary file, aborting"), _("GetFile Error")); + cancel_getfile(w, g); + return; + } + + gf = g_new0(struct getfile_transfer, 1); + gf->gc = gc; + gf->filename = g_strdup(filename); + gf->listing = file; + gf->receiver = g_strdup(g->sn); + gf->size = st.st_size; + + ft = localtime(&st.st_ctime); + fprintf(file, "%2d/%2d/%4d %2d:%2d %8ld ", + ft->tm_mon + 1, ft->tm_mday, ft->tm_year + 1900, + ft->tm_hour + 1, ft->tm_min + 1, (long)st.st_size); + fprintf(file, "%s\r\n", g_basename(filename)); + rewind(file); + + aim_oft_registerlisting(od->sess, file, ""); + if ((newconn = aim_accepttransfer(od->sess, od->conn, g->sn, g->cookie, g->ip, file, AIM_CAPS_GETFILE)) == NULL) { + od->sess->flags ^= AIM_SESS_FLAGS_NONBLOCKCONNECT; + do_error_dialog(_("Error connecting for transfer"), _("GetFile Error")); + g_free(gf->filename); + fclose(file); + g_free(gf); + gtk_widget_destroy(w); + return; + } + + gtk_widget_destroy(w); + + od->getfiles = g_slist_append(od->getfiles, gf); + gf->conn = newconn; + gf->gip = gdk_input_add(newconn->fd, GDK_INPUT_WRITE, oscar_getfile_callback, gf); +} + +static int accept_getfile(gpointer w, struct ask_getfile *g) { + GtkWidget *window; + window = gtk_file_selection_new(_("Gaim - Send File...")); + gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(window)); + gtk_object_set_user_data(GTK_OBJECT(window), window); + gtk_signal_connect(GTK_OBJECT(window), "destroy", + GTK_SIGNAL_FUNC(cancel_getfile_file), g); + gtk_object_set_user_data(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button), window); + gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button), "clicked", + GTK_SIGNAL_FUNC(do_getfile), g); + gtk_object_set_user_data(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button), window); + gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button), "clicked", + GTK_SIGNAL_FUNC(cancel_getfile_cancel), g); + gtk_widget_show(window); + + return TRUE; +} +*/ + +#if USE_PIXBUF +static gboolean redraw_anim(gpointer data) +{ + int delay; + struct icon_req *ir = data; + GList *frames; + GdkPixbufFrame *frame; + GdkPixbuf *buf; + GdkPixmap *pm; GdkBitmap *bm; + GdkPixmap *src; + GdkGC *gc; + + if (!ir->cnv || !g_list_find(conversations, ir->cnv)) { + debug_printf("I think this is a bug.\n"); + return FALSE; + } + + frames = gdk_pixbuf_animation_get_frames(ir->anim); + frame = g_list_nth_data(frames, ir->curframe); + buf = gdk_pixbuf_frame_get_pixbuf(frame); + switch (gdk_pixbuf_frame_get_action(frame)) { + case GDK_PIXBUF_FRAME_RETAIN: + gdk_pixbuf_render_pixmap_and_mask(buf, &src, NULL, 0); + gtk_pixmap_get(GTK_PIXMAP(ir->pix), &pm, &bm); + gc = gdk_gc_new(pm); + gdk_draw_pixmap(pm, gc, src, 0, 0, + gdk_pixbuf_frame_get_x_offset(frame), + gdk_pixbuf_frame_get_y_offset(frame), + -1, -1); + gdk_pixmap_unref(src); + gtk_widget_queue_draw(ir->pix); + gdk_gc_unref(gc); + break; + case GDK_PIXBUF_FRAME_DISPOSE: + gdk_pixbuf_render_pixmap_and_mask(buf, &pm, &bm, 0); + gtk_pixmap_set(GTK_PIXMAP(ir->pix), pm, bm); + gdk_pixmap_unref(pm); + if (bm) + gdk_bitmap_unref(bm); + break; + case GDK_PIXBUF_FRAME_REVERT: + frame = frames->data; + buf = gdk_pixbuf_frame_get_pixbuf(frame); + gdk_pixbuf_render_pixmap_and_mask(buf, &pm, &bm, 0); + gtk_pixmap_set(GTK_PIXMAP(ir->pix), pm, bm); + gdk_pixmap_unref(pm); + if (bm) + gdk_bitmap_unref(bm); + break; + } + ir->curframe = (ir->curframe + 1) % g_list_length(frames); + delay = MAX(gdk_pixbuf_frame_get_delay_time(frame), 13); + ir->timer = gtk_timeout_add(delay * 10, redraw_anim, ir); + return FALSE; +} +#endif + +int gaim_parse_incoming_im(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + int channel; + struct aim_userinfo_s *userinfo; + va_list ap; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + channel = va_arg(ap, int); + userinfo = va_arg(ap, struct aim_userinfo_s *); + + /* channel 1: standard message */ + if (channel == 1) { + char *tmp = g_malloc(BUF_LONG); + struct aim_incomingim_ch1_args *args; + + args = va_arg(ap, struct aim_incomingim_ch1_args *); + va_end(ap); + +#if USE_PIXBUF + if (args->icbmflags & AIM_IMFLAGS_HASICON) { + struct oscar_data *od = gc->proto_data; + struct icon_req *ir; + GSList *h = od->hasicons; + char *who = normalize(userinfo->sn); + debug_printf("%s has an icon\n", userinfo->sn); + while (h) { + ir = h->data; + if (!strcmp(ir->user, who)) + break; + h = h->next; + } + if (!h) { + ir = g_new0(struct icon_req, 1); + ir->user = g_strdup(who); + od->hasicons = g_slist_append(od->hasicons, ir); + } + if (args->iconstamp > ir->timestamp) + ir->request = TRUE; + ir->timestamp = args->iconstamp; + } +#endif + + /* + * Quickly convert it to eight bit format, replacing + * non-ASCII UNICODE characters with their equivelent + * HTML entity. + */ + if (args->icbmflags & AIM_IMFLAGS_UNICODE) { + int i; + + for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) { + unsigned short uni; + + uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff); + + if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */ + + g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni); + + } else { /* something else, do UNICODE entity */ + g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni); + } + } + } else + g_snprintf(tmp, BUF_LONG, "%s", args->msg); + + serv_got_im(gc, userinfo->sn, tmp, args->icbmflags & AIM_IMFLAGS_AWAY, time(NULL)); + g_free(tmp); + } else if (channel == 2) { + struct aim_incomingim_ch2_args *args; + args = va_arg(ap, struct aim_incomingim_ch2_args *); + va_end(ap); + if (args->reqclass & AIM_CAPS_CHAT) { + char *name = extract_name(args->info.chat.roominfo.name); + serv_got_chat_invite(gc, + name ? name : args->info.chat.roominfo.name, + args->info.chat.roominfo.exchange, + userinfo->sn, + args->info.chat.msg); + if (name) + g_free(name); + } else if (args->reqclass & AIM_CAPS_SENDFILE) { + } else if (args->reqclass & AIM_CAPS_GETFILE) { + /* + char *ip, *cookie; + struct ask_getfile *g = g_new0(struct ask_getfile, 1); + char buf[256]; + + userinfo = va_arg(ap, struct aim_userinfo_s *); + ip = va_arg(ap, char *); + cookie = va_arg(ap, char *); + va_end(ap); + + debug_printf("%s received getfile request from %s (%s), cookie = %s\n", + gc->username, userinfo->sn, ip, cookie); + + g->gc = gc; + g->sn = g_strdup(userinfo->sn); + g->cookie = g_strdup(cookie); + g->ip = g_strdup(ip); + g_snprintf(buf, sizeof buf, "%s has just asked to get a file from %s.", + userinfo->sn, gc->username); + do_ask_dialog(buf, g, accept_getfile, cancel_getfile); + */ + } else if (args->reqclass & AIM_CAPS_VOICE) { + } else if (args->reqclass & AIM_CAPS_BUDDYICON) { +#if USE_PIXBUF + struct oscar_data *od = gc->proto_data; + GSList *h = od->hasicons; + struct icon_req *ir = NULL; + char *who; + struct conversation *c; + + GdkPixbufLoader *load; + GList *frames; + GdkPixbuf *buf; + GdkPixmap *pm; + GdkBitmap *bm; + + who = normalize(userinfo->sn); + + while (h) { + ir = h->data; + if (!strcmp(who, ir->user)) + break; + h = h->next; + + } + + if (!h || ((c = find_conversation(userinfo->sn)) == NULL) || (c->gc != gc)) { + debug_printf("got buddy icon for %s but didn't want it\n", userinfo->sn); + return 1; + } + + if (ir->pix && ir->cnv) + gtk_container_remove(GTK_CONTAINER(ir->cnv->bbox), ir->pix); + ir->pix = NULL; + ir->cnv = NULL; + if (ir->data) + g_free(ir->data); + if (ir->anim) + gdk_pixbuf_animation_unref(ir->anim); + ir->anim = NULL; + if (ir->unanim) + gdk_pixbuf_unref(ir->unanim); + ir->unanim = NULL; + if (ir->timer) + gtk_timeout_remove(ir->timer); + ir->timer = 0; + + ir->length = args->info.icon.length; + + if (!ir->length) + return 1; + + ir->data = g_memdup(args->info.icon.icon, args->info.icon.length); + + load = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(load, ir->data, ir->length); + ir->anim = gdk_pixbuf_loader_get_animation(load); + + if (ir->anim) { + frames = gdk_pixbuf_animation_get_frames(ir->anim); + buf = gdk_pixbuf_frame_get_pixbuf(frames->data); + gdk_pixbuf_render_pixmap_and_mask(buf, &pm, &bm, 0); + + if (gdk_pixbuf_animation_get_num_frames(ir->anim) > 1) { + int delay = + MAX(gdk_pixbuf_frame_get_delay_time(frames->data), 13); + ir->curframe = 1; + ir->timer = gtk_timeout_add(delay * 10, redraw_anim, ir); + } + } else { + ir->unanim = gdk_pixbuf_loader_get_pixbuf(load); + if (!ir->unanim) { + gdk_pixbuf_loader_close(load); + return 1; + } + gdk_pixbuf_render_pixmap_and_mask(ir->unanim, &pm, &bm, 0); + } + + ir->cnv = c; + ir->pix = gtk_pixmap_new(pm, bm); + gtk_box_pack_start(GTK_BOX(c->bbox), ir->pix, FALSE, FALSE, 5); + if (ir->anim && (gdk_pixbuf_animation_get_num_frames(ir->anim) > 1)) + gtk_widget_set_usize(ir->pix, gdk_pixbuf_animation_get_width(ir->anim), + gdk_pixbuf_animation_get_height(ir->anim)); + gtk_widget_show(ir->pix); + gdk_pixmap_unref(pm); + if (bm) + gdk_bitmap_unref(bm); + + gdk_pixbuf_loader_close(load); + +#endif + } else if (args->reqclass & AIM_CAPS_IMIMAGE) { + struct ask_direct *d = g_new0(struct ask_direct, 1); + char buf[256]; + + debug_printf("%s received direct im request from %s (%s)\n", + gc->username, userinfo->sn, args->info.directim->ip); + + d->gc = gc; + d->sn = g_strdup(userinfo->sn); + d->priv = args->info.directim; + g_snprintf(buf, sizeof buf, "%s has just asked to directly connect to %s.", + userinfo->sn, gc->username); + do_ask_dialog(buf, d, accept_direct_im, cancel_direct_im); + } else { + debug_printf("Unknown reqclass %d\n", args->reqclass); + } + } + + return 1; +} + +int gaim_parse_misses(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + unsigned short chan, nummissed, reason; + struct aim_userinfo_s *userinfo; + char buf[1024]; + + va_start(ap, command); + chan = (unsigned short)va_arg(ap, unsigned int); + userinfo = va_arg(ap, struct aim_userinfo_s *); + nummissed = (unsigned short)va_arg(ap, unsigned int); + reason = (unsigned short)va_arg(ap, unsigned int); + va_end(ap); + + switch(reason) { + case 1: + /* message too large */ + sprintf(buf, _("You missed a message from %s because it was too large."), userinfo->sn); + do_error_dialog(buf, _("Gaim - Error")); + plugin_event(event_error, (void *)961, 0, 0, 0); + break; + default: + sprintf(buf, _("You missed a message from %s for unknown reasons."), userinfo->sn); + do_error_dialog(buf, _("Gaim - Error")); + plugin_event(event_error, (void *)970, 0, 0, 0); + break; + } + + return 1; +} + +int gaim_parse_genericerr(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + unsigned short reason; + + va_start(ap, command); + reason = va_arg(ap, int); + va_end(ap); + + debug_printf("snac threw error (reason 0x%04x: %s\n", reason, + (reason < msgerrreasonlen) ? msgerrreason[reason] : "unknown"); + + return 1; +} + +int gaim_parse_msgerr(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + char *destn; + unsigned short reason; + char buf[1024]; + + va_start(ap, command); + reason = (unsigned short)va_arg(ap, unsigned int); + destn = va_arg(ap, char *); + va_end(ap); + + sprintf(buf, _("Your message to %s did not get sent: %s"), destn, + (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown")); + do_error_dialog(buf, _("Gaim - Error")); + + return 1; +} + +int gaim_parse_locerr(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + char *destn; + unsigned short reason; + char buf[1024]; + + va_start(ap, command); + reason = (unsigned short)va_arg(ap, unsigned int); + destn = va_arg(ap, char *); + va_end(ap); + + sprintf(buf, _("User information for %s unavailable: %s"), destn, + (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown")); + do_error_dialog(buf, _("Gaim - Error")); + + return 1; +} + +static char *images(int flags) { + static char buf[1024]; + g_snprintf(buf, sizeof(buf), "%s%s%s%s", + (flags & AIM_FLAG_UNCONFIRMED) ? "<IMG SRC=\"dt_icon.gif\">" : "", + (flags & AIM_FLAG_AOL) ? "<IMG SRC=\"aol_icon.gif\">" : "", + (flags & AIM_FLAG_ADMINISTRATOR) ? "<IMG SRC=\"admin_icon.gif\">" : "", + (flags & AIM_FLAG_FREE) ? "<IMG SRC=\"free_icon.gif\">" : ""); + return buf; +} + +int gaim_parse_user_info(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + struct aim_userinfo_s *info; + char *prof_enc = NULL, *prof = NULL; + unsigned short infotype; + char buf[BUF_LONG]; + struct gaim_connection *gc = sess->aux_data; + va_list ap; + char *asc; + + va_start(ap, command); + info = va_arg(ap, struct aim_userinfo_s *); + prof_enc = va_arg(ap, char *); + prof = va_arg(ap, char *); + infotype = (unsigned short)va_arg(ap, unsigned int); + va_end(ap); + + if (info->membersince) + asc = g_strdup_printf("Member Since : <B>%s</B><BR>\n", + asctime(localtime(&info->membersince))); + else + asc = g_strdup(""); + + g_snprintf(buf, sizeof buf, + _("Username : <B>%s</B> %s <BR>\n" + "%s" + "Warning Level : <B>%d %%</B><BR>\n" + "Online Since : <B>%s</B><BR>\n" + "Idle Minutes : <B>%d</B>\n<BR>\n<HR><BR>\n" + "%s" + "<br><BODY BGCOLOR=WHITE><hr><I>Legend:</I><br><br>" + "<IMG SRC=\"free_icon.gif\"> : Normal AIM User<br>" + "<IMG SRC=\"aol_icon.gif\"> : AOL User <br>" + "<IMG SRC=\"dt_icon.gif\"> : Trial AIM User <br>" + "<IMG SRC=\"admin_icon.gif\"> : Administrator"), + info->sn, images(info->flags), + asc, + info->warnlevel/10, + asctime(localtime(&info->onlinesince)), + info->idletime, + (prof && strlen(prof)) ? + (infotype == AIM_GETINFO_GENERALINFO ? + prof : + away_subs(prof, gc->username)) + : + (infotype == AIM_GETINFO_GENERALINFO ? + _("<i>No Information Provided</i>") : + _("<i>User has no away message</i>"))); + + g_show_info_text(away_subs(buf, gc->username)); + + g_free(asc); + + return 1; +} + +int gaim_parse_motd(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + char *msg; + unsigned short id; + va_list ap; + char buildbuf[150]; + + va_start(ap, command); + id = (unsigned short)va_arg(ap, unsigned int); + msg = va_arg(ap, char *); + va_end(ap); + + aim_getbuildstring(buildbuf, sizeof(buildbuf)); + + debug_printf("MOTD: %s (%d)\n", msg ? msg : "Unknown", id); + debug_printf("Gaim %s / Libfaim %s\n", VERSION, buildbuf); + if (id != 4) + do_error_dialog(_("Your connection may be lost."), + _("AOL error")); + + return 1; +} + +int gaim_chatnav_info(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + unsigned short type; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + + va_start(ap, command); + type = (unsigned short)va_arg(ap, unsigned int); + + switch(type) { + case 0x0002: { + int maxrooms; + struct aim_chat_exchangeinfo *exchanges; + int exchangecount, i = 0; + + maxrooms = (unsigned char)va_arg(ap, unsigned int); + exchangecount = va_arg(ap, int); + exchanges = va_arg(ap, struct aim_chat_exchangeinfo *); + va_end(ap); + + debug_printf("chat info: Chat Rights:\n"); + debug_printf("chat info: \tMax Concurrent Rooms: %d\n", maxrooms); + debug_printf("chat info: \tExchange List: (%d total)\n", exchangecount); + while (i < exchangecount) + debug_printf("chat info: \t\t%d\n", exchanges[i++].number); + if (odata->create_exchange) { + debug_printf("creating room %s\n", odata->create_name); + aim_chatnav_createroom(sess, command->conn, odata->create_name, + odata->create_exchange); + odata->create_exchange = 0; + g_free(odata->create_name); + odata->create_name = NULL; + } + } + break; + case 0x0008: { + char *fqcn, *name, *ck; + unsigned short instance, flags, maxmsglen, maxoccupancy, unknown, exchange; + unsigned char createperms; + unsigned long createtime; + + fqcn = va_arg(ap, char *); + instance = (unsigned short)va_arg(ap, unsigned int); + exchange = (unsigned short)va_arg(ap, unsigned int); + flags = (unsigned short)va_arg(ap, unsigned int); + createtime = va_arg(ap, unsigned long); + maxmsglen = (unsigned short)va_arg(ap, unsigned int); + maxoccupancy = (unsigned short)va_arg(ap, unsigned int); + createperms = (unsigned char)va_arg(ap, int); + unknown = (unsigned short)va_arg(ap, unsigned int); + name = va_arg(ap, char *); + ck = va_arg(ap, char *); + va_end(ap); + + debug_printf("created room: %s %d %d %d %lu %d %d %d %d %s %s\n", + fqcn, + exchange, instance, flags, + createtime, + maxmsglen, maxoccupancy, createperms, unknown, + name, ck); + aim_chat_join(odata->sess, odata->conn, exchange, ck); + } + break; + default: + va_end(ap); + debug_printf("chatnav info: unknown type (%04x)\n", type); + break; + } + return 1; +} + +int gaim_chat_join(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + int count, i = 0; + struct aim_userinfo_s *info; + struct gaim_connection *g = sess->aux_data; + + struct chat_connection *c = NULL; + + va_start(ap, command); + count = va_arg(ap, int); + info = va_arg(ap, struct aim_userinfo_s *); + va_end(ap); + + c = find_oscar_chat_by_conn(g, command->conn); + if (!c) + return 1; + + while (i < count) + add_chat_buddy(c->cnv, info[i++].sn); + + return 1; +} + +int gaim_chat_leave(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + int count, i = 0; + struct aim_userinfo_s *info; + struct gaim_connection *g = sess->aux_data; + + struct chat_connection *c = NULL; + + va_start(ap, command); + count = va_arg(ap, int); + info = va_arg(ap, struct aim_userinfo_s *); + va_end(ap); + + c = find_oscar_chat_by_conn(g, command->conn); + if (!c) + return 1; + + while (i < count) + remove_chat_buddy(c->cnv, info[i++].sn); + + return 1; +} + +int gaim_chat_info_update(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + debug_printf("inside chat_info_update\n"); + return 1; +} + +int gaim_chat_incoming_msg(struct aim_session_t *sess, + struct command_rx_struct *command, ...) { + va_list ap; + struct aim_userinfo_s *info; + char *msg; + struct gaim_connection *gc = sess->aux_data; + struct chat_connection *ccon = find_oscar_chat_by_conn(gc, command->conn); + char *tmp; + + va_start(ap, command); + info = va_arg(ap, struct aim_userinfo_s *); + msg = va_arg(ap, char *); + + tmp = g_malloc(BUF_LONG); + g_snprintf(tmp, BUF_LONG, "%s", msg); + serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL)); + g_free(tmp); + + return 1; +} + + /* + * Recieved in response to an IM sent with the AIM_IMFLAGS_ACK option. + */ +int gaim_parse_msgack(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + unsigned short type; + char *sn = NULL; + + va_start(ap, command); + type = (unsigned short)va_arg(ap, unsigned int); + sn = va_arg(ap, char *); + va_end(ap); + + debug_printf("Sent message to %s.\n", sn); + + return 1; +} + +int gaim_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + static char *codes[5] = {"invalid", + "change", + "warning", + "limit", + "limit cleared"}; + va_list ap; + int code; + unsigned long rateclass, windowsize, clear, alert, limit, disconnect; + unsigned long currentavg, maxavg; + + va_start(ap, command); + code = va_arg(ap, int); + rateclass= va_arg(ap, int); + windowsize = va_arg(ap, unsigned long); + clear = va_arg(ap, unsigned long); + alert = va_arg(ap, unsigned long); + limit = va_arg(ap, unsigned long); + disconnect = va_arg(ap, unsigned long); + currentavg = va_arg(ap, unsigned long); + maxavg = va_arg(ap, unsigned long); + va_end(ap); + + debug_printf("rate %s (paramid 0x%04lx): curavg = %ld, maxavg = %ld, alert at %ld, " + "clear warning at %ld, limit at %ld, disconnect at %ld (window size = %ld)\n", + (code < 5) ? codes[code] : codes[0], + rateclass, + currentavg, maxavg, + alert, clear, + limit, disconnect, + windowsize); + + if (code == AIM_RATE_CODE_CHANGE) { + if (currentavg >= clear) + aim_conn_setlatency(command->conn, 0); + } else if (code == AIM_RATE_CODE_WARNING) { + aim_conn_setlatency(command->conn, windowsize/4); + } else if (code == AIM_RATE_CODE_LIMIT) { + aim_conn_setlatency(command->conn, windowsize/2); + } else if (code == AIM_RATE_CODE_CLEARLIMIT) { + aim_conn_setlatency(command->conn, 0); + } + + return 1; +} + +int gaim_parse_evilnotify(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + int newevil; + struct aim_userinfo_s *userinfo; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + newevil = va_arg(ap, int); + userinfo = va_arg(ap, struct aim_userinfo_s *); + va_end(ap); + + serv_got_eviled(gc, (userinfo && userinfo->sn[0]) ? userinfo->sn : NULL, newevil / 10); + + return 1; +} + +int gaim_rateresp(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + struct gaim_connection *gc = sess->aux_data; + switch (command->conn->type) { + case AIM_CONN_TYPE_BOS: + aim_bos_ackrateresp(sess, command->conn); + aim_bos_reqpersonalinfo(sess, command->conn); + aim_bos_reqlocaterights(sess, command->conn); + aim_bos_setprofile(sess, command->conn, gc->user->user_info, NULL, gaim_caps); + aim_bos_reqbuddyrights(sess, command->conn); + + account_online(gc); + serv_finish_login(gc); + + if (bud_list_cache_exists(gc)) + do_import(NULL, gc); + + debug_printf("buddy list loaded\n"); + + aim_addicbmparam(sess, command->conn); + aim_bos_reqicbmparaminfo(sess, command->conn); + + aim_bos_reqrights(sess, command->conn); + aim_bos_setgroupperm(sess, command->conn, AIM_FLAG_ALLUSERS); + aim_bos_setprivacyflags(sess, command->conn, AIM_PRIVFLAGS_ALLOWIDLE | + AIM_PRIVFLAGS_ALLOWMEMBERSINCE); + + break; + case AIM_CONN_TYPE_AUTH: + aim_bos_ackrateresp(sess, command->conn); + aim_auth_clientready(sess, command->conn); + debug_printf("connected to auth (admin)\n"); + break; + default: + debug_printf("got rate response for unhandled connection type %04x\n", + command->conn->type); + break; + } + + return 1; +} + +int gaim_reportinterval(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + if (command->data) { + debug_printf("minimum report interval: %d (seconds?)\n", aimutil_get16(command->data+10)); + } else + debug_printf("NULL minimum report interval!\n"); + return 1; +} + +int gaim_parse_buddyrights(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + unsigned short maxbuddies, maxwatchers; + + va_start(ap, command); + maxbuddies = (unsigned short)va_arg(ap, unsigned int); + maxwatchers = (unsigned short)va_arg(ap, unsigned int); + va_end(ap); + + debug_printf("buddy list rights: Max buddies = %d / Max watchers = %d\n", maxbuddies, maxwatchers); + + return 1; +} + +int gaim_bosrights(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + unsigned short maxpermits, maxdenies; + va_list ap; + + va_start(ap, command); + maxpermits = (unsigned short)va_arg(ap, unsigned int); + maxdenies = (unsigned short)va_arg(ap, unsigned int); + va_end(ap); + + debug_printf("BOS rights: Max permit = %d / Max deny = %d\n", maxpermits, maxdenies); + + aim_bos_clientready(sess, command->conn); + + aim_bos_reqservice(sess, command->conn, AIM_CONN_TYPE_CHATNAV); + + return 1; +} + +int gaim_parse_searchreply(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + char *address, *SNs; + int i, num; + char *buf; + int at = 0, len; + + va_start(ap, command); + address = va_arg(ap, char *); + num = va_arg(ap, int); + SNs = va_arg(ap, char *); + va_end(ap); + + len = num * (MAXSNLEN + 1) + 1024; + buf = g_malloc(len); + at += g_snprintf(buf + at, len - at, "<B>%s has the following screen names:</B><BR>", address); + for (i = 0; i < num; i++) + at += g_snprintf(buf + at, len - at, "%s<BR>", &SNs[i * (MAXSNLEN + 1)]); + g_show_info_text(buf); + g_free(buf); + + return 1; +} + +int gaim_parse_searcherror(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + char *address; + char buf[BUF_LONG]; + + va_start(ap, command); + address = va_arg(ap, char *); + va_end(ap); + + g_snprintf(buf, sizeof(buf), "No results found for email address %s", address); + do_error_dialog(buf, _("Error")); + + return 1; +} + +int gaim_account_confirm(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + int status; + va_list ap; + char msg[256]; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + status = va_arg(ap, int); /* status code of confirmation request */ + va_end(ap); + + debug_printf("account confirmation returned status 0x%04x (%s)\n", status, + status ? "email sent" : "unknown"); + if (status) { + g_snprintf(msg, sizeof(msg), "You should receive an email asking to confirm %s.", + gc->username); + do_error_dialog(msg, "Confirm"); + } + + return 1; +} + +int gaim_info_change(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + unsigned short change = 0; + int perms, type, length, str; + char *val; + va_list ap; + char buf[BUF_LONG]; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + perms = va_arg(ap, int); + type = va_arg(ap, int); + length = va_arg(ap, int); + val = va_arg(ap, char *); + str = va_arg(ap, int); + va_end(ap); + + if (aimutil_get16(command->data+2) == 0x0005) + change = 1; + + debug_printf("info%s: perms = %d, type = %x, length = %d, val = %s\n", + change ? " change" : "", perms, type, length, str ? val : "(not string)"); + + if ((type == 0x0011) && str) { + g_snprintf(buf, sizeof(buf), "The email address for %s is %s", gc->username, val); + do_error_dialog(buf, "Email"); + } + + return 1; +} + +static void oscar_keepalive(struct gaim_connection *gc) { + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + aim_flap_nop(odata->sess, odata->conn); +} + +static char *oscar_name() { + return "Oscar"; +} + +static void oscar_send_im(struct gaim_connection *gc, char *name, char *message, int away) { + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + struct direct_im *dim = find_direct_im(odata, name); + if (dim) { + aim_send_im_direct(odata->sess, dim->conn, message); + } else { + if (away) + aim_send_im(odata->sess, odata->conn, name, AIM_IMFLAGS_AWAY, message); + else { + int flags = AIM_IMFLAGS_ACK; +#if USE_PIXBUF + GSList *h = odata->hasicons; + struct icon_req *ir; + char *who = normalize(name); + while (h) { + ir = h->data; + if (ir->request && !strcmp(who, ir->user)) + break; + h = h->next; + } + if (h) { + ir->request = FALSE; + flags |= AIM_IMFLAGS_BUDDYREQ; + debug_printf("sending buddy icon request with message\n"); + } +#endif + aim_send_im(odata->sess, odata->conn, name, flags, message); + } + } +} + +static void oscar_get_info(struct gaim_connection *g, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO); +} + +static void oscar_get_away_msg(struct gaim_connection *g, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE); +} + +static void oscar_set_dir(struct gaim_connection *g, char *first, char *middle, char *last, + char *maiden, char *city, char *state, char *country, int web) { + /* FIXME : some of these things are wrong, but i'm lazy */ + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_setdirectoryinfo(odata->sess, odata->conn, first, middle, last, + maiden, NULL, NULL, city, state, NULL, 0, web); +} + + +static void oscar_set_idle(struct gaim_connection *g, int time) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_bos_setidle(odata->sess, odata->conn, time); +} + +static void oscar_set_info(struct gaim_connection *g, char *info) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + char inforeal[1025], away[1025]; + g_snprintf(inforeal, sizeof(inforeal), "%s", info); + if (g->away) + g_snprintf(away, sizeof(away), "%s", g->away); + if (strlen(info) > 1024) + do_error_dialog("Maximum info length (1024) exceeded, truncating", "Info Too Long"); + aim_bos_setprofile(odata->sess, odata->conn, inforeal, g->away ? NULL : "", gaim_caps); +} + +static void oscar_set_away(struct gaim_connection *g, char *state, char *message) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + char info[1025], away[1025]; + g_snprintf(info, sizeof(info), "%s", g->user->user_info); + if (message) + g_snprintf(away, sizeof(away), "%s", message); + aim_bos_setprofile(odata->sess, odata->conn, NULL, message ? away : "", gaim_caps); + if (g->away) + g_free (g->away); + g->away = NULL; + if (message) { + if (strlen(message) > 1024) + do_error_dialog("Maximum away length (1024) exceeded, truncating", + "Info Too Long"); + g->away = g_strdup (message); + } +} + +static void oscar_warn(struct gaim_connection *g, char *name, int anon) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_send_warning(odata->sess, odata->conn, name, anon); +} + +static void oscar_dir_search(struct gaim_connection *g, char *first, char *middle, char *last, + char *maiden, char *city, char *state, char *country, char *email) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + if (strlen(email)) + aim_usersearch_address(odata->sess, odata->conn, email); +} + +static void oscar_add_buddy(struct gaim_connection *g, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_add_buddy(odata->sess, odata->conn, name); +} + +static void oscar_add_buddies(struct gaim_connection *g, GList *buddies) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + char buf[MSG_LEN]; + int n = 0; + while (buddies) { + if (n > MSG_LEN - 18) { + aim_bos_setbuddylist(odata->sess, odata->conn, buf); + n = 0; + } + n += g_snprintf(buf + n, sizeof(buf) - n, "%s&", (char *)buddies->data); + buddies = buddies->next; + } + aim_bos_setbuddylist(odata->sess, odata->conn, buf); +} + +static void oscar_remove_buddy(struct gaim_connection *g, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_remove_buddy(odata->sess, odata->conn, name); +} + +static void oscar_join_chat(struct gaim_connection *g, int exchange, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + struct aim_conn_t *cur = NULL; + if (!name) { + if (!join_chat_entry || !join_chat_spin) + return; + name = gtk_entry_get_text(GTK_ENTRY(join_chat_entry)); + exchange = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(join_chat_spin)); + if (!name || !strlen(name)) + return; + } + debug_printf("Attempting to join chat room %s.\n", name); + if ((cur = aim_getconn_type(odata->sess, AIM_CONN_TYPE_CHATNAV))) { + debug_printf("chatnav exists, creating room\n"); + aim_chatnav_createroom(odata->sess, cur, name, exchange); + } else { + /* this gets tricky */ + debug_printf("chatnav does not exist, opening chatnav\n"); + odata->create_exchange = exchange; + odata->create_name = g_strdup(name); + aim_bos_reqservice(odata->sess, odata->conn, AIM_CONN_TYPE_CHATNAV); + } +} + +static void des_jc() +{ + join_chat_entry = NULL; + join_chat_spin = NULL; +} + +static void oscar_draw_join_chat(struct gaim_connection *gc, GtkWidget *fbox) { + GtkWidget *label; + GtkWidget *rowbox; + GtkObject *adjust; + + rowbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(fbox), rowbox, TRUE, TRUE, 0); + gtk_widget_show(rowbox); + + label = gtk_label_new(_("Join what group:")); + gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); + gtk_signal_connect(GTK_OBJECT(label), "destroy", GTK_SIGNAL_FUNC(des_jc), NULL); + gtk_widget_show(label); + + join_chat_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(rowbox), join_chat_entry, TRUE, TRUE, 0); + gtk_widget_grab_focus(join_chat_entry); + gtk_signal_connect(GTK_OBJECT(join_chat_entry), "activate", GTK_SIGNAL_FUNC(do_join_chat), NULL); + gtk_widget_show(join_chat_entry); + + rowbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(fbox), rowbox, TRUE, TRUE, 0); + gtk_widget_show(rowbox); + + label = gtk_label_new(_("Exchange:")); + gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + adjust = gtk_adjustment_new(4, 4, 20, 1, 10, 10); + join_chat_spin = gtk_spin_button_new(GTK_ADJUSTMENT(adjust), 1, 0); + gtk_widget_set_usize(join_chat_spin, 50, -1); + gtk_box_pack_start(GTK_BOX(rowbox), join_chat_spin, FALSE, FALSE, 0); + gtk_widget_show(join_chat_spin); +} + +static void oscar_chat_invite(struct gaim_connection *g, int id, char *message, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + struct chat_connection *ccon = find_oscar_chat(g, id); + + if (!ccon) + return; + + aim_chat_invite(odata->sess, odata->conn, name, message ? message : "", + ccon->exchange, ccon->name, 0x0); +} + +static void oscar_chat_leave(struct gaim_connection *g, int id) { + struct oscar_data *odata = g ? (struct oscar_data *)g->proto_data : NULL; + GSList *bcs = g->buddy_chats; + struct conversation *b = NULL; + struct chat_connection *c = NULL; + int count = 0; + + while (bcs) { + count++; + b = (struct conversation *)bcs->data; + if (id == b->id) + break; + bcs = bcs->next; + b = NULL; + } + + if (!b) + return; + + debug_printf("Attempting to leave room %s (currently in %d rooms)\n", b->name, count); + + c = find_oscar_chat(g, b->id); + if (c != NULL) { + if (odata) + odata->oscar_chats = g_slist_remove(odata->oscar_chats, c); + if (c->inpa > 0) + gdk_input_remove(c->inpa); + if (g && odata->sess) + aim_conn_kill(odata->sess, &c->conn); + g_free(c->name); + g_free(c->show); + g_free(c); + } + /* we do this because with Oscar it doesn't tell us we left */ + serv_got_chat_left(g, b->id); +} + +static void oscar_chat_send(struct gaim_connection *g, int id, char *message) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + GSList *bcs = g->buddy_chats; + struct conversation *b = NULL; + struct chat_connection *c = NULL; + char *buf; + int i, j; + + while (bcs) { + b = (struct conversation *)bcs->data; + if (id == b->id) + break; + bcs = bcs->next; + b = NULL; + } + if (!b) + return; + + bcs = odata->oscar_chats; + while (bcs) { + c = (struct chat_connection *)bcs->data; + if (b == c->cnv) + break; + bcs = bcs->next; + c = NULL; + } + if (!c) + return; + + buf = g_malloc(strlen(message) * 4 + 1); + for (i = 0, j = 0; i < strlen(message); i++) { + if (message[i] == '\n') { + buf[j++] = '<'; + buf[j++] = 'B'; + buf[j++] = 'R'; + buf[j++] = '>'; + } else { + buf[j++] = message[i]; + } + } + buf[j] = '\0'; + aim_chat_send_im(odata->sess, c->conn, 0, buf, strlen(buf)); + g_free(buf); +} + +static char **oscar_list_icon(int uc) { + if (uc & UC_UNAVAILABLE) + return (char **)away_icon_xpm; + if (uc & UC_AOL) + return (char **)aol_icon_xpm; + if (uc & UC_NORMAL) + return (char **)free_icon_xpm; + if (uc & UC_ADMIN) + return (char **)admin_icon_xpm; + if (uc & UC_UNCONFIRMED) + return (char **)dt_icon_xpm; + return NULL; +} + +static void oscar_info(GtkObject *obj, char *who) { + struct gaim_connection *gc = (struct gaim_connection *)gtk_object_get_user_data(obj); + serv_get_info(gc, who); +} + +static void oscar_away_msg(GtkObject *obj, char *who) { + struct gaim_connection *gc = (struct gaim_connection *)gtk_object_get_user_data(obj); + serv_get_away_msg(gc, who); +} + +static int gaim_directim_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct aim_directim_priv *priv; + struct aim_conn_t *newconn; + struct direct_im *dim; + char buf[256]; + + va_start(ap, command); + newconn = va_arg(ap, struct aim_conn_t *); + va_end(ap); + + priv = (struct aim_directim_priv *)newconn->priv; + + debug_printf("DirectIM: initiate success to %s\n", priv->sn); + dim = find_direct_im(od, priv->sn); + + dim->cnv = find_conversation(priv->sn); + if (!dim->cnv) dim->cnv = new_conversation(priv->sn); + gtk_signal_connect(GTK_OBJECT(dim->cnv->window), "destroy", + GTK_SIGNAL_FUNC(delete_direct_im), dim); + gdk_input_remove(dim->watcher); + dim->conn = newconn; + dim->watcher = gdk_input_add(dim->conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, dim->conn); + g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), priv->sn); + write_to_conv(dim->cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL)); + + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, + gaim_directim_incoming, 0); + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMDISCONNECT, + gaim_directim_disconnect, 0); + aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, + gaim_directim_typing, 0); + + return 1; +} + +static int gaim_directim_incoming(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + char *msg = NULL; + struct aim_conn_t *conn; + struct aim_directim_priv *priv; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, command); + conn = va_arg(ap, struct aim_conn_t *); + msg = va_arg(ap, char *); + va_end(ap); + + if (!(priv = conn->priv)) { + return -1; + } + + debug_printf("Got DirectIM message from %s\n", priv->sn); + + serv_got_im(gc, priv->sn, msg, 0, time((time_t)NULL)); + + return 1; +} + +static int gaim_directim_disconnect(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + struct aim_conn_t *conn; + char *sn; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct direct_im *dim; + char buf[256]; + + va_start(ap, command); + conn = va_arg(ap, struct aim_conn_t *); + sn = va_arg(ap, char *); + va_end(ap); + + debug_printf("%s disconnected Direct IM.\n", sn); + + dim = find_direct_im(od, sn); + od->direct_ims = g_slist_remove(od->direct_ims, dim); + gdk_input_remove(dim->watcher); + gtk_signal_disconnect_by_data(GTK_OBJECT(dim->cnv->window), dim); + + g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn); + if (dim->cnv) + write_to_conv(dim->cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL)); + + aim_conn_kill(sess, &conn); + + return 1; +} + +static int gaim_directim_typing(struct aim_session_t *sess, struct command_rx_struct *command, ...) { + va_list ap; + struct aim_conn_t *conn; + struct aim_directim_priv *priv; + + va_start(ap, command); + conn = va_arg(ap, struct aim_conn_t *); + va_end(ap); + + if (!(priv = conn->priv)) { + return -1; + } + + /* I had to leave this. It's just too funny. It reminds me of my sister. */ + debug_printf("ohmigod! %s has started typing (DirectIM). He's going to send you a message! *squeal*\n", priv->sn); + + return 1; +} + +struct ask_do_dir_im { + char *who; + struct gaim_connection *gc; +}; + +static void oscar_cancel_direct_im(GtkObject *obj, struct ask_do_dir_im *data) { + g_free(data); +} + +static void oscar_direct_im(GtkObject *obj, struct ask_do_dir_im *data) { + struct gaim_connection *gc = data->gc; + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + struct direct_im *dim; + + dim = find_direct_im(od, data->who); + if (dim) { + do_error_dialog("Direct IM request already pending.", "Unable"); + return; + } + dim = g_new0(struct direct_im, 1); + dim->gc = gc; + g_snprintf(dim->name, sizeof dim->name, "%s", data->who); + + dim->conn = aim_directim_initiate(od->sess, od->conn, NULL, data->who); + if (dim->conn != NULL) { + od->direct_ims = g_slist_append(od->direct_ims, dim); + dim->watcher = gdk_input_add(dim->conn->fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION, + oscar_callback, dim->conn); + aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE, + gaim_directim_initiate, 0); + } else { + do_error_dialog(_("Unable to open Direct IM"), _("Error")); + g_free(dim); + } +} + +static void oscar_ask_direct_im(GtkObject *m, gchar *who) { + char buf[BUF_LONG]; + struct ask_do_dir_im *data = g_new0(struct ask_do_dir_im, 1); + data->who = who; + data->gc = gtk_object_get_user_data(m); + g_snprintf(buf, sizeof(buf), _("You have selected to open a Direct IM connection with %s." + " Doing this will let them see your IP address, and may be" + " a security risk. Do you wish to continue?"), who); + do_ask_dialog(buf, data, oscar_direct_im, oscar_cancel_direct_im); +} + +static void oscar_buddy_menu(GtkWidget *menu, struct gaim_connection *gc, char *who) { + GtkWidget *button; + char *n = g_strdup(normalize(gc->username)); + + button = gtk_menu_item_new_with_label(_("Get Info")); + gtk_signal_connect(GTK_OBJECT(button), "activate", + GTK_SIGNAL_FUNC(oscar_info), who); + gtk_object_set_user_data(GTK_OBJECT(button), gc); + gtk_menu_append(GTK_MENU(menu), button); + gtk_widget_show(button); + + button = gtk_menu_item_new_with_label(_("Get Away Msg")); + gtk_signal_connect(GTK_OBJECT(button), "activate", + GTK_SIGNAL_FUNC(oscar_away_msg), who); + gtk_object_set_user_data(GTK_OBJECT(button), gc); + gtk_menu_append(GTK_MENU(menu), button); + gtk_widget_show(button); + + if (strcmp(n, normalize(who))) { + button = gtk_menu_item_new_with_label(_("Direct IM")); + gtk_signal_connect(GTK_OBJECT(button), "activate", + GTK_SIGNAL_FUNC(oscar_ask_direct_im), who); + gtk_object_set_user_data(GTK_OBJECT(button), gc); + gtk_menu_append(GTK_MENU(menu), button); + gtk_widget_show(button); + } + g_free(n); +} + + +/* weeee */ +static void oscar_print_option(GtkEntry *entry, struct aim_user *user) +{ + int entrynum; + + entrynum = (int)gtk_object_get_user_data(GTK_OBJECT(entry)); + + if (entrynum == USEROPT_AUTH) { + g_snprintf(user->proto_opt[USEROPT_AUTH], + sizeof(user->proto_opt[USEROPT_AUTH]), "%s", gtk_entry_get_text(entry)); + } else if (entrynum == USEROPT_AUTHPORT) { + g_snprintf(user->proto_opt[USEROPT_AUTHPORT], + sizeof(user->proto_opt[USEROPT_AUTHPORT]), "%s", gtk_entry_get_text(entry)); + } +} + +static void oscar_user_opts(GtkWidget *book, struct aim_user *user) +{ + /* so here, we create the new notebook page */ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *entry; + + vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 5); + gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, gtk_label_new("Oscar Options")); + gtk_widget_show(vbox); + + hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + + label = gtk_label_new("Auth Host:"); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + entry = gtk_entry_new(); + gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 0); + gtk_object_set_user_data(GTK_OBJECT(entry), (void *)USEROPT_AUTH); + gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(oscar_print_option), user); + if (user->proto_opt[USEROPT_AUTH][0]) { + debug_printf("setting text %s\n", user->proto_opt[USEROPT_AUTH]); + gtk_entry_set_text(GTK_ENTRY(entry), user->proto_opt[USEROPT_AUTH]); + } else + gtk_entry_set_text(GTK_ENTRY(entry), "login.oscar.aol.com"); + gtk_widget_show(entry); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show(hbox); + + label = gtk_label_new("Auth Port:"); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + entry = gtk_entry_new(); + gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 0); + gtk_object_set_user_data(GTK_OBJECT(entry), (void *)USEROPT_AUTHPORT); + gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(oscar_print_option), user); + if (user->proto_opt[USEROPT_AUTHPORT][0]) { + debug_printf("setting text %s\n", user->proto_opt[USEROPT_AUTHPORT]); + gtk_entry_set_text(GTK_ENTRY(entry), user->proto_opt[USEROPT_AUTHPORT]); + } else + gtk_entry_set_text(GTK_ENTRY(entry), "5190"); + + gtk_widget_show(entry); +} + +static void oscar_set_permit_deny(struct gaim_connection *gc) { + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + GSList *list; + char buf[MAXMSGLEN]; + int at; + + switch(gc->permdeny) { + case 1: + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username); + break; + case 2: + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username); + break; + case 3: + list = gc->permit; + at = 0; + while (list) { + at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data); + list = list->next; + } + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf); + break; + case 4: + list = gc->deny; + at = 0; + while (list) { + at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data); + list = list->next; + } + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf); + break; + default: + break; + } +} + +static void oscar_add_permit(struct gaim_connection *gc, char *who) { + if (gc->permdeny != 3) return; + oscar_set_permit_deny(gc); +} + +static void oscar_add_deny(struct gaim_connection *gc, char *who) { + if (gc->permdeny != 4) return; + oscar_set_permit_deny(gc); +} + +static void oscar_rem_permit(struct gaim_connection *gc, char *who) { + if (gc->permdeny != 3) return; + oscar_set_permit_deny(gc); +} + +static void oscar_rem_deny(struct gaim_connection *gc, char *who) { + if (gc->permdeny != 4) return; + oscar_set_permit_deny(gc); +} + +static void oscar_draw_new_user(GtkWidget *box) +{ + GtkWidget *label; + + label = gtk_label_new(_("Unfortunately, currently Oscar only allows new user registration by " + "going to http://aim.aol.com/aimnew/Aim/register.adp?promo=106723&pageset=Aim&client=no" + ". Clicking the Register button will open the URL for you.")); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 5); + gtk_widget_show(label); +} + +static void oscar_do_new_user() +{ + open_url(NULL, "http://aim.aol.com/aimnew/Aim/register.adp?promo=106723&pageset=Aim&client=no"); +} + +static GList *oscar_away_states() +{ + return g_list_append(NULL, GAIM_AWAY_CUSTOM); +} + +static void oscar_do_action(struct gaim_connection *gc, char *act) +{ + struct oscar_data *od = gc->proto_data; + struct aim_conn_t *conn = aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH); + + if (!strcmp(act, "Set User Info")) { + show_set_info(gc); + } else if (!strcmp(act, "Change Password")) { + show_change_passwd(gc); + } else if (!strcmp(act, "Confirm Account")) { + if (!conn) { + od->conf = TRUE; + aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } else + aim_auth_reqconfirm(od->sess, conn); + } else if (!strcmp(act, "Change Email")) { + } else if (!strcmp(act, "Display Current Registered Address")) { + if (!conn) { + od->reqemail = TRUE; + aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } else + aim_auth_getinfo(od->sess, conn, 0x11); + } else if (!strcmp(act, "Search for Buddy by Email")) { + show_find_email(gc); + } +} + +static GList *oscar_actions() +{ + GList *m = NULL; + + m = g_list_append(m, "Set User Info"); + m = g_list_append(m, NULL); + m = g_list_append(m, "Change Password"); + m = g_list_append(m, "Confirm Account"); + /* + m = g_list_append(m, "Change Email"); + */ + m = g_list_append(m, "Display Current Registered Address"); + m = g_list_append(m, NULL); + m = g_list_append(m, "Search for Buddy by Email"); + + return m; +} + +static void oscar_change_passwd(struct gaim_connection *gc, char *old, char *new) +{ + struct oscar_data *od = gc->proto_data; + if (!aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH)) { + od->chpass = TRUE; + od->oldp = g_strdup(old); + od->newp = g_strdup(new); + aim_bos_reqservice(od->sess, od->conn, AIM_CONN_TYPE_AUTH); + } else { + aim_auth_changepasswd(od->sess, aim_getconn_type(od->sess, AIM_CONN_TYPE_AUTH), + new, old); + } +} + +static void oscar_insert_convo(struct gaim_connection *gc, struct conversation *c) +{ +#if USE_PIXBUF + struct oscar_data *od = gc->proto_data; + GSList *h = od->hasicons; + struct icon_req *ir = NULL; + char *who = normalize(c->name); + + GdkPixbufLoader *load; + GList *frames; + GdkPixbuf *buf; + GdkPixmap *pm; + GdkBitmap *bm; + + while (h) { + ir = h->data; + if (!strcmp(who, ir->user)) + break; + h = h->next; + } + if (!h || !ir->data) + return; + + ir->cnv = c; + + load = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(load, ir->data, ir->length); + ir->anim = gdk_pixbuf_loader_get_animation(load); + + if (ir->anim) { + frames = gdk_pixbuf_animation_get_frames(ir->anim); + buf = gdk_pixbuf_frame_get_pixbuf(frames->data); + gdk_pixbuf_render_pixmap_and_mask(buf, &pm, &bm, 0); + + if (gdk_pixbuf_animation_get_num_frames(ir->anim) > 1) { + int delay = MAX(gdk_pixbuf_frame_get_delay_time(frames->data), 13); + ir->curframe = 1; + ir->timer = gtk_timeout_add(delay * 10, redraw_anim, ir); + } + } else { + ir->unanim = gdk_pixbuf_loader_get_pixbuf(load); + if (!ir->unanim) { + gdk_pixbuf_loader_close(load); + return; + } + gdk_pixbuf_render_pixmap_and_mask(ir->unanim, &pm, &bm, 0); + } + + ir->pix = gtk_pixmap_new(pm, bm); + gtk_box_pack_start(GTK_BOX(c->bbox), ir->pix, FALSE, FALSE, 5); + if (ir->anim && (gdk_pixbuf_animation_get_num_frames(ir->anim) > 1)) + gtk_widget_set_usize(ir->pix, gdk_pixbuf_animation_get_width(ir->anim), + gdk_pixbuf_animation_get_height(ir->anim)); + gtk_widget_show(ir->pix); + gdk_pixmap_unref(pm); + if (bm) + gdk_bitmap_unref(bm); + + gdk_pixbuf_loader_close(load); +#endif +} + +static void oscar_remove_convo(struct gaim_connection *gc, struct conversation *c) +{ +#if USE_PIXBUF + struct oscar_data *od = gc->proto_data; + GSList *h = od->hasicons; + struct icon_req *ir = NULL; + char *who = normalize(c->name); + + while (h) { + ir = h->data; + if (!strcmp(who, ir->user)) + break; + h = h->next; + } + if (!h || !ir->data) + return; + + if (ir->cnv && ir->pix) { + gtk_container_remove(GTK_CONTAINER(ir->cnv->bbox), ir->pix); + ir->pix = NULL; + ir->cnv = NULL; + } + + if (ir->anim) { + gdk_pixbuf_animation_unref(ir->anim); + ir->anim = NULL; + } else if (ir->unanim) { + gdk_pixbuf_unref(ir->unanim); + ir->unanim = NULL; + } + + ir->curframe = 0; + + if (ir->timer) + gtk_timeout_remove(ir->timer); + ir->timer = 0; +#endif +} + +static struct prpl *my_protocol = NULL; + +void oscar_init(struct prpl *ret) { + ret->protocol = PROTO_OSCAR; + ret->options = OPT_PROTO_HTML | OPT_PROTO_CORRECT_TIME; + ret->name = oscar_name; + ret->list_icon = oscar_list_icon; + ret->away_states = oscar_away_states; + ret->actions = oscar_actions; + ret->do_action = oscar_do_action; + ret->buddy_menu = oscar_buddy_menu; + ret->user_opts = oscar_user_opts; + ret->draw_new_user = oscar_draw_new_user; + ret->do_new_user = oscar_do_new_user; + ret->insert_convo = oscar_insert_convo; + ret->remove_convo = oscar_remove_convo; + ret->login = oscar_login; + ret->close = oscar_close; + ret->send_im = oscar_send_im; + ret->set_info = oscar_set_info; + ret->get_info = oscar_get_info; + ret->set_away = oscar_set_away; + ret->get_away_msg = oscar_get_away_msg; + ret->set_dir = oscar_set_dir; + ret->get_dir = NULL; /* Oscar really doesn't have this */ + ret->dir_search = oscar_dir_search; + ret->set_idle = oscar_set_idle; + ret->change_passwd = oscar_change_passwd; + ret->add_buddy = oscar_add_buddy; + ret->add_buddies = oscar_add_buddies; + ret->remove_buddy = oscar_remove_buddy; + ret->add_permit = oscar_add_permit; + ret->add_deny = oscar_add_deny; + ret->rem_permit = oscar_rem_permit; + ret->rem_deny = oscar_rem_deny; + ret->set_permit_deny = oscar_set_permit_deny; + ret->warn = oscar_warn; + ret->accept_chat = NULL; /* oscar doesn't have accept, it just joins */ + ret->join_chat = oscar_join_chat; + ret->draw_join_chat = oscar_draw_join_chat; + ret->chat_invite = oscar_chat_invite; + ret->chat_leave = oscar_chat_leave; + ret->chat_whisper = NULL; + ret->chat_send = oscar_chat_send; + ret->keepalive = oscar_keepalive; + + my_protocol = ret; +} + +#ifndef STATIC + +char *gaim_plugin_init(GModule *handle) +{ + load_protocol(oscar_init, sizeof(struct prpl)); + return NULL; +} + +void gaim_plugin_remove() +{ + struct prpl *p = find_prpl(PROTO_OSCAR); + if (p == my_protocol) + unload_protocol(p); +} + +char *name() +{ + return "Oscar"; +} + +char *description() +{ + return "Allows gaim to use the Oscar protocol."; +} + +#endif