diff -uNr -x .bzr -x debian devel/protocols/oscar/oscar.c branches/aim-icons/protocols/oscar/oscar.c --- devel/protocols/oscar/oscar.c 2006-06-21 18:32:30.000000000 +0200 +++ branches/aim-icons/protocols/oscar/oscar.c 2006-06-24 21:43:47.000000000 +0200 @@ -1,6 +1,7 @@ /* * gaim * + * Buddy icon hooks Copyright (C) 2006, Kozoru * Some code copyright (C) 2002-2006, Jelmer Vernooij * and the BitlBee team. * Some code copyright (C) 1998-1999, Mark Spencer @@ -29,6 +30,7 @@ #include #include #include +#include #include #include "nogaim.h" #include "bitlbee.h" @@ -45,9 +47,7 @@ #include "chat.h" #include "chatnav.h" -/* constants to identify proto_opts */ -#define USEROPT_AUTH 0 -#define USEROPT_AUTHPORT 1 +#include "http_client.h" #define UC_AOL 0x02 #define UC_ADMIN 0x04 @@ -60,10 +60,13 @@ #define OSCAR_GROUP "Friends" +#define OSCAR_MAX_ICON_SIZE 7168 + /* Don't know if support for UTF8 is really working. For now it's UTF16 here. static int gaim_caps = AIM_CAPS_UTF8; */ -static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | AIM_CAPS_CHAT; +static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | + AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON; static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02}; struct oscar_data { @@ -90,6 +93,11 @@ gboolean killme; gboolean icq; GSList *evilhack; + + char *icon; + guint icon_size; + time_t icon_time; + guint16 icon_sum; struct { guint maxbuddies; /* max users you can watch */ @@ -355,10 +363,12 @@ return FALSE; } +static void oscar_got_http_icon(struct http_request *req); + static void oscar_login(struct aim_user *user) { aim_session_t *sess; aim_conn_t *conn; - char buf[256]; + char buf[256], *server; struct gaim_connection *gc = new_gaim_conn(user); struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1); @@ -389,9 +399,15 @@ return; } - if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 && - g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) { - serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.",user->proto_opt[USEROPT_AUTH]); + server = g_strdup(user->proto_opt[0]); + if ((odata->icon = strchr(server, ':'))) { + *odata->icon = 0; + odata->icon = g_strdup(odata->icon + 1); + } + + if (g_strcasecmp(server, "login.icq.com") != 0 && + g_strcasecmp(server, "login.oscar.aol.com") != 0) { + serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.", server); } g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username); @@ -401,17 +417,100 @@ 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] : AIM_DEFAULT_LOGIN_SERVER, - user->proto_opt[USEROPT_AUTHPORT][0] ? - atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, - oscar_login_connect, gc); + conn->fd = proxy_connect(server, AIM_LOGIN_PORT, oscar_login_connect, gc); + g_free(server); if (conn->fd < 0) { hide_login_progress(gc, _("Couldn't connect to host")); signoff(gc); return; } aim_request_login(sess, conn, gc->username); + + if (odata->icon) { + int fd = -1; + + if (odata->icon[0] == '/') { + struct stat sb; + + fd = open(odata->icon, O_RDONLY); + if (fd == -1) + goto icon_error; + + if (fstat(fd, &sb) == -1) + goto icon_error; + + if (sb.st_size > OSCAR_MAX_ICON_SIZE) { + serv_got_crap(gc, "Couldn't load usericon (%s)", "It's too big"); + goto icon_error_silent; + } + + g_free(odata->icon); + odata->icon_time = sb.st_mtime; + odata->icon_size = sb.st_size; + odata->icon = g_malloc(odata->icon_size); + + if (read(fd, odata->icon, odata->icon_size) != odata->icon_size) + goto icon_error; + + odata->icon_sum = aim_iconsum((guint8*)odata->icon, odata->icon_size); + + serv_got_crap(gc, "Successfully loaded usericon (%d bytes)", odata->icon_size); + + close(fd); + } else { + if (!http_dorequest_url(odata->icon, oscar_got_http_icon, odata)) + goto icon_error; + } + + return; + +icon_error: + serv_got_crap(gc, "Couldn't load usericon (%s)", strerror(errno)); +icon_error_silent: + g_free(odata->icon); + odata->icon = NULL; + odata->icon_size = 0; + + if (fd != -1) + close(fd); + } + + /* DON'T PUT ANY CODE HERE! EVIL CONSTRUCTION INSIDE [tm] ;-) */ +} + +static void oscar_got_http_icon(struct http_request *req) { + struct oscar_data *od = req->data; + struct gaim_connection *gc; + GSList *l; + + for (l = get_connections(); l; l = l->next) + if (((struct gaim_connection*)l->data)->proto_data == od) + break; + + if (!l) + return; + + gc = l->data; + + if (req->status_code != 200) { + serv_got_crap(gc, "Couldn't load usericon (HTTP error %d)", req->status_code); + return; + } + + if (req->body_size > OSCAR_MAX_ICON_SIZE) { + serv_got_crap(gc, "Couldn't load usericon (%s)", "It's too big"); + return; + } + + g_free(od->icon); + od->icon_time = time(NULL); + od->icon_size = req->body_size; + od->icon = g_malloc(od->icon_size); + memcpy(od->icon, req->reply_body, od->icon_size); + + od->icon_sum = aim_iconsum((guint8*)od->icon, od->icon_size); + + serv_got_crap(gc, "Successfully loaded usericon (%d bytes)", od->icon_size); } static void oscar_close(struct gaim_connection *gc) { @@ -444,6 +543,7 @@ b_event_remove(odata->cnpa); if (odata->paspa > 0) b_event_remove(odata->paspa); + g_free(odata->icon); aim_session_kill(odata->sess); g_free(odata->sess); odata->sess = NULL; @@ -490,8 +590,7 @@ 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]) : AIM_LOGIN_PORT, + port = AIM_LOGIN_PORT; va_start(ap, fr); info = va_arg(ap, struct aim_authresp_info *); @@ -870,14 +969,12 @@ va_list ap; struct aim_redirect_data *redir; struct gaim_connection *gc = sess->aux_data; - struct aim_user *user = gc->user; aim_conn_t *tstconn; int i; char *host; int port; - port = user->proto_opt[USEROPT_AUTHPORT][0] ? - atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, + port = AIM_LOGIN_PORT; va_start(ap, fr); redir = va_arg(ap, struct aim_redirect_data *); @@ -1051,8 +1148,14 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) { char *tmp = g_malloc(BUF_LONG + 1); struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; int flags = 0; + /* W000t, they want to see our icon! \o/ */ + if (args->icbmflags & AIM_IMFLAGS_BUDDYREQ && od->icon_size > 0) + aim_send_icon(sess, userinfo->sn, (guint8*) od->icon, od->icon_size, + od->icon_time, od->icon_sum); + if (args->icbmflags & AIM_IMFLAGS_AWAY) flags |= IM_FLAG_AWAY; @@ -1887,6 +1990,17 @@ args.msg = s; args.msglen = len; + if (odata->icon_size > 0) { + /* Set this flag and give more information about the + icon, the client can decide if it wants to see + the icon or not. */ + args.flags |= AIM_IMFLAGS_HASICON; + + args.iconlen = odata->icon_size; + args.iconsum = odata->icon_sum; + args.iconstamp = odata->icon_time; + } + ret = aim_send_im_ext(odata->sess, &args); if (s != message) {