# HG changeset patch # User Sean Egan # Date 1020625328 0 # Node ID 56746b0868db56b391180f88ff2dbb956e734524 # Parent 3ef95e625bbca1ba2ed5dd7be14b1995be61d9b5 [gaim-migrate @ 3229] More OSCAR stuff from KingAnt! committer: Tailor Script diff -r 3ef95e625bbc -r 56746b0868db ChangeLog --- a/ChangeLog Sun May 05 18:43:20 2002 +0000 +++ b/ChangeLog Sun May 05 19:02:08 2002 +0000 @@ -15,7 +15,8 @@ * Better selection in GtkIMHtml (Thanks Ben Miller) * A warning when your OSCAR buddy list is too long (Thanks, Mark Doliner) - + * ICQ status messages in OSCAR (Thanks, Mark Doliner) + version 0.57 (04/25/2002): * New authorization method for Yahoo! * Polish translation updated (Thanks Przemyslaw Sulek) diff -r 3ef95e625bbc -r 56746b0868db src/protocols/oscar/aim.h --- a/src/protocols/oscar/aim.h Sun May 05 18:43:20 2002 +0000 +++ b/src/protocols/oscar/aim.h Sun May 05 19:02:08 2002 +0000 @@ -854,6 +854,7 @@ faim_export aim_conn_t *aim_directim_initiate(aim_session_t *, const char *destsn); faim_export aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const fu8_t *cookie); +faim_export int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type); faim_export aim_conn_t *aim_sendfile_initiate(aim_session_t *, const char *destsn, const char *filename, fu16_t numfiles, fu32_t totsize); faim_export int aim_send_im_ch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message); diff -r 3ef95e625bbc -r 56746b0868db src/protocols/oscar/aim_cbtypes.h --- a/src/protocols/oscar/aim_cbtypes.h Sun May 05 18:43:20 2002 +0000 +++ b/src/protocols/oscar/aim_cbtypes.h Sun May 05 19:02:08 2002 +0000 @@ -97,7 +97,7 @@ #define AIM_CB_MSG_INCOMING 0x0007 #define AIM_CB_MSG_EVIL 0x0009 #define AIM_CB_MSG_MISSEDCALL 0x000a -#define AIM_CB_MSG_CLIENTERROR 0x000b +#define AIM_CB_MSG_CLIENTAUTORESP 0x000b #define AIM_CB_MSG_ACK 0x000c #define AIM_CB_MSG_DEFAULT 0xffff diff -r 3ef95e625bbc -r 56746b0868db src/protocols/oscar/im.c --- a/src/protocols/oscar/im.c Sun May 05 18:43:20 2002 +0000 +++ b/src/protocols/oscar/im.c Sun May 05 19:02:08 2002 +0000 @@ -732,16 +732,127 @@ return 0; } -/* +/** + * Request the status message of the given ICQ user. + * + * @param sess The oscar session. + * @param sn The UIN of the user of whom you wish to request info. + * @param type The type of info you wish to request. This should be the current + * state of the user, as one of the AIM_ICQ_STATE_* defines. + * @return Return 0 if no errors, otherwise return the error number. + */ +faim_export int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type) +{ + aim_conn_t *conn; + int i; + fu8_t ck[8]; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn) + return -EINVAL; + + for (i = 0; i < 8; i++) + aimutil_put8(ck+i, (fu8_t) rand()); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* Cookie */ + aimbs_putraw(&fr->data, ck, 8); + + /* Channel (2) */ + aimbs_put16(&fr->data, 0x0002); + + /* Dest sn */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, sn, strlen(sn)); + + /* TLV t(0005) - Encompasses almost everything below. */ + aimbs_put16(&fr->data, 0x0005); /* T */ + aimbs_put16(&fr->data, 0x005e); /* L */ + { /* V */ + aimbs_put16(&fr->data, 0x0000); + + /* Cookie */ + aimbs_putraw(&fr->data, ck, 8); + + /* Put the 16 byte server relay capability */ + aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 0x0036); + { /* V */ + aimbs_putle16(&fr->data, 0x001b); /* L */ + aimbs_putle16(&fr->data, 0x0008); /* AAA - Protocol version */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle16(&fr->data, 0x0003); /* Client features? */ + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle8(&fr->data, 0x00); /* Unkizown */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + + aimbs_putle16(&fr->data, 0x000e); /* L */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + + /* The type of status message being requested */ + if (type & AIM_ICQ_STATE_CHAT) + aimbs_putle16(&fr->data, 0x03ec); + else if(type & AIM_ICQ_STATE_DND) + aimbs_putle16(&fr->data, 0x03eb); + else if(type & AIM_ICQ_STATE_OUT) + aimbs_putle16(&fr->data, 0x03ea); + else if(type & AIM_ICQ_STATE_BUSY) + aimbs_putle16(&fr->data, 0x03e9); + else if(type & AIM_ICQ_STATE_AWAY) + aimbs_putle16(&fr->data, 0x03e8); + + aimbs_putle16(&fr->data, 0x0000); /* Status? */ + aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */ + aimbs_putle16(&fr->data, 0x0001); /* L? */ + aimbs_putle8(&fr->data, 0x00); /* Null termination? */ + } /* End TLV t(2711) */ + } /* End TLV t(0005) */ + + /* TLV t(0003) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** * This can be used to send an ICQ authorization reply (deny or grant). It is the "old way." * The new way is to use SSI. I like the new way a lot better. This seems like such a hack, * mostly because it's in network byte order. Figuring this stuff out sometimes takes a while, * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people * were taking when they merged the two protocols. * - * sn is the destination screen name - * type is the type of message. 0x0007 for authorization denied. 0x0008 for authorization granted - * message is the message you want to send, it should be null terminated + * @param sn The destination screen name. + * @param type The type of message. 0x0007 for authorization denied. 0x0008 for authorization granted. + * @param message The message you want to send, it should be null terminated. + * @return Return 0 if no errors, otherwise return the error number. */ faim_export int aim_send_im_ch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message) { @@ -1859,7 +1970,11 @@ return ret; } -static int clienterr(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +/* + * Receive the response from an ICQ status message request. This contains the + * ICQ status message. Go figure. + */ +static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) { int ret = 0; aim_rxcallback_t userfunc; @@ -1873,8 +1988,58 @@ sn = aimbs_getstr(bs, snlen); reason = aimbs_get16(bs); - if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) - ret = userfunc(sess, rx, channel, sn, reason); + switch (reason) { + case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ + fu8_t statusmsgtype, *msg; + fu16_t len; + fu32_t state; + + len = aimbs_getle16(bs); /* Should be 0x001b */ + free(aimbs_getraw(bs, len)); /* Unknown */ + + len = aimbs_getle16(bs); /* Should be 0x000e */ + free(aimbs_getraw(bs, len)); /* Unknown */ + + statusmsgtype = aimbs_getle8(bs); + switch (statusmsgtype) { + case 0xe8: + state = AIM_ICQ_STATE_AWAY; + break; + case 0xe9: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; + break; + case 0xea: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT; + break; + case 0xeb: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; + break; + case 0xec: + state = AIM_ICQ_STATE_CHAT; + break; + default: + state = 0; + break; + } + + aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + + len = aimbs_getle16(bs); + msg = aimbs_getraw(bs, len); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason, state, msg); + + free(msg); + } break; + + default: { + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason); + } break; + } /* end switch */ free(ck); free(sn); @@ -1916,7 +2081,7 @@ else if (snac->subtype == 0x000a) return missedcall(sess, mod, rx, snac, bs); else if (snac->subtype == 0x000b) - return clienterr(sess, mod, rx, snac, bs); + return clientautoresp(sess, mod, rx, snac, bs); else if (snac->subtype == 0x000c) return msgack(sess, mod, rx, snac, bs); diff -r 3ef95e625bbc -r 56746b0868db src/protocols/oscar/oscar.c --- a/src/protocols/oscar/oscar.c Sun May 05 18:43:20 2002 +0000 +++ b/src/protocols/oscar/oscar.c Sun May 05 19:02:08 2002 +0000 @@ -243,6 +243,7 @@ static int gaim_parse_offgoing (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...); static int gaim_parse_misses (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_clientauto (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_user_info (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_motd (aim_session_t *, aim_frame_t *, ...); static int gaim_chatnav_info (aim_session_t *, aim_frame_t *, ...); @@ -691,6 +692,7 @@ 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_MSG, AIM_CB_MSG_CLIENTAUTORESP, gaim_parse_clientauto, 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); @@ -1630,6 +1632,83 @@ return 1; } +static char *gaim_icq_status(int state) { + /* Make a cute little string that shows the status of the dude or dudet */ + if (state & AIM_ICQ_STATE_CHAT) + return g_strdup_printf("Free For Chat"); + else if (state & AIM_ICQ_STATE_DND) + return g_strdup_printf("Do Not Disturb"); + else if (state & AIM_ICQ_STATE_OUT) + return g_strdup_printf("Not Available"); + else if (state & AIM_ICQ_STATE_BUSY) + return g_strdup_printf("Occupied"); + else if (state & AIM_ICQ_STATE_AWAY) + return g_strdup_printf("Away"); + else if (state & AIM_ICQ_STATE_WEBAWARE) + return g_strdup_printf("Web Aware"); + else if (state & AIM_ICQ_STATE_INVISIBLE) + return g_strdup_printf("Invisible"); + else + return g_strdup_printf("Online"); +} + +static int gaim_parse_clientauto(aim_session_t *sess, aim_frame_t *fr, ...) { + struct gaim_connection *gc = sess->aux_data; + va_list ap; + fu16_t chan, reason; + char *who; + + va_start(ap, fr); + chan = (fu16_t)va_arg(ap, unsigned int); + who = va_arg(ap, char *); + reason = (fu16_t)va_arg(ap, unsigned int); + + switch(reason) { + case 0x0003: { /* Reply from an ICQ status message request */ + int state = (int)va_arg(ap, fu32_t); + char *msg = va_arg(ap, char *); + char *status_msg = gaim_icq_status(state); + char *dialog_msg, **splitmsg; + struct oscar_data *od = gc->proto_data; + GSList *l = od->evilhack; + gboolean evilhack = FALSE; + + /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */ + splitmsg = g_strsplit(msg, "\r\n", 0); + + /* If who is in od->evilhack, then we're just getting the away message, otherwise this + * will just get appended to the info box (which is already showing). */ + while (l) { + char *x = l->data; + if (!strcmp(x, normalize(who))) { + evilhack = TRUE; + g_free(x); + od->evilhack = g_slist_remove(od->evilhack, x); + break; + } + l = l->next; + } + + if (evilhack) + dialog_msg = g_strdup_printf(_("UIN: %s
Status: %s


%s
"), who, status_msg, g_strjoinv("
", splitmsg)); + else + dialog_msg = g_strdup_printf(_("Status: %s


%s
"), status_msg, g_strjoinv("
", splitmsg)); + g_show_info_text(gc, who, 2, dialog_msg, NULL); + + g_free(status_msg); + g_free(dialog_msg); + g_strfreev(splitmsg); + } break; + + default: { + debug_printf("Received an unknown client auto-response from %s. Type 0x%04x\n", who, reason); + } break; + } + va_end(ap); + + return 1; +} + static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) { va_list ap; fu16_t reason; @@ -2397,6 +2476,7 @@ static int gaim_simpleinfo(aim_session_t *sess, aim_frame_t *fr, ...) { struct gaim_connection *gc = sess->aux_data; + struct buddy *budlight; va_list ap; struct aim_icq_simpleinfo *info; char buf[16 * 1024]; @@ -2411,13 +2491,31 @@ "UIN: %lu
" "Nick: %s
" "Name: %s %s
" - "Email: %s\n", + "Email: %s
\n", info->uin, info->nick, info->first, info->last, info->email); - g_show_info_text(gc, who, FALSE, buf, NULL); + /* If the contact is away, then we also want to get their status message + * and show it in the same window as info. g_show_info_text gets the status + * message if the third arg is 0 (this seems really gross to me). The + * parse-icq-status-message function knows if it is putting it's message in + * an info window because the name will _not_ be in od->evilhack. For getting + * only the away message the contact's UIN is put in od->evilhack. */ + if ((budlight = find_buddy(gc, who)) && ((budlight->uc >> 7) & (AIM_ICQ_STATE_AWAY || AIM_ICQ_STATE_DND || AIM_ICQ_STATE_OUT || AIM_ICQ_STATE_BUSY || AIM_ICQ_STATE_CHAT))) { + if (budlight->caps & AIM_CAPS_ICQSERVERRELAY) + g_show_info_text(gc, who, 0, buf, NULL); + else { + char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7); + g_show_info_text(gc, who, 2, buf, "Status: ", state_msg, "
\n

Remote client does not support sending status messages.
\n", NULL); + free(state_msg); + } + } else { + char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7); + g_show_info_text(gc, who, 2, buf, "Status: ", state_msg, NULL); + free(state_msg); + } return 1; } @@ -2459,7 +2557,7 @@ at += g_snprintf(buf + at, len - at, "%s has the following screen names:
", address); for (i = 0; i < num; i++) at += g_snprintf(buf + at, len - at, "%s
", &SNs[i * (MAXSNLEN + 1)]); - g_show_info_text(NULL, NULL, FALSE, buf, NULL); + g_show_info_text(NULL, NULL, 2, buf, NULL); g_free(buf); return 1; @@ -2635,10 +2733,22 @@ aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE); } -static void oscar_get_away(struct gaim_connection *g, char *name) { +static void oscar_get_away(struct gaim_connection *g, char *who) { struct oscar_data *odata = (struct oscar_data *)g->proto_data; - if (!odata->icq) - aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO); + if (odata->icq) { + struct buddy *budlight = find_buddy(g, who); + if (budlight) + if ((budlight->uc & 0xff80) >> 7) + if (budlight->caps & AIM_CAPS_ICQSERVERRELAY) + aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7); + else + debug_printf("Error: Remote client does not support retrieval of status messages.\n"); + else + debug_printf("Error: The user %s has no status message, therefore not requesting.\n", who); + else + debug_printf("Error: Could not find %s in local contact list, therefore unable to request status message.\n", who); + } else + aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_GENERALINFO); } static void oscar_get_caps(struct gaim_connection *g, char *name) { @@ -2729,13 +2839,13 @@ aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); gc->away = ""; } else if (!strcmp(state, "Do Not Disturb")) { - aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_DND); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY); gc->away = ""; } else if (!strcmp(state, "Not Available")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY); gc->away = ""; } else if (!strcmp(state, "Occupied")) { - aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_BUSY); + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY); gc->away = ""; } else if (!strcmp(state, "Free For Chat")) { aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT); @@ -3236,12 +3346,12 @@ return icon_offline_xpm; if (uc & AIM_ICQ_STATE_CHAT) return icon_ffc_xpm; + if (uc & AIM_ICQ_STATE_DND) + return icon_dnd_xpm; + if (uc & AIM_ICQ_STATE_OUT) + return icon_na_xpm; if (uc & AIM_ICQ_STATE_BUSY) return icon_occ_xpm; - if (uc & AIM_ICQ_STATE_OUT) - return icon_na_xpm; - if (uc & AIM_ICQ_STATE_DND) - return icon_dnd_xpm; if (uc & AIM_ICQ_STATE_AWAY) return icon_away_xpm; return icon_online_xpm; @@ -3433,7 +3543,30 @@ static void oscar_get_away_msg(struct gaim_connection *gc, char *who) { struct oscar_data *od = gc->proto_data; od->evilhack = g_slist_append(od->evilhack, g_strdup(normalize(who))); - oscar_get_info(gc, who); + if (od->icq) { + struct buddy *budlight = find_buddy(gc, who); + if (budlight) + if ((budlight->uc >> 7) & (AIM_ICQ_STATE_AWAY || AIM_ICQ_STATE_DND || AIM_ICQ_STATE_OUT || AIM_ICQ_STATE_BUSY || AIM_ICQ_STATE_CHAT)) + if (budlight->caps & AIM_CAPS_ICQSERVERRELAY) + aim_send_im_ch2_geticqmessage(od->sess, who, (budlight->uc & 0xff80) >> 7); + else { + char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7); + char *dialog_msg = g_strdup_printf(_("UIN: %s
Status: %s


Remote client does not support sending status messages.
"), who, state_msg); + g_show_info_text(gc, who, 2, dialog_msg, NULL); + free(state_msg); + free(dialog_msg); + } + else { + char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7); + char *dialog_msg = g_strdup_printf(_("UIN: %s
Status: %s


User has no status message.
"), who, state_msg); + g_show_info_text(gc, who, 2, dialog_msg, NULL); + free(state_msg); + free(dialog_msg); + } + else + do_error_dialog("Could not find contact in local list, therefore unable to request status message.\n", "Gaim - Error"); + } else + oscar_get_info(gc, who); } static GList *oscar_buddy_menu(struct gaim_connection *gc, char *who) { @@ -3448,7 +3581,13 @@ pbm->gc = gc; m = g_list_append(m, pbm); - if (!odata->icq) { + if (odata->icq) { + pbm = g_new0(struct proto_buddy_menu, 1); + pbm->label = _("Get Status Msg"); + pbm->callback = oscar_get_away_msg; + pbm->gc = gc; + m = g_list_append(m, pbm); + } else { pbm = g_new0(struct proto_buddy_menu, 1); pbm->label = _("Get Away Msg"); pbm->callback = oscar_get_away_msg;