# HG changeset patch # User Mark Doliner # Date 1204103886 0 # Node ID a8c025929245f3dc8a1bc6a2463c6172e5f1d451 # Parent 382bc33e88249aba54bbc668e180dfa2a095fc5c Add support for offline messages for AIM, thanks to some info from Matthew Goldstein. The new protocol bits used for offline messaging for AIM are very nice. It fits in with the rest of the oscar protocol very well (the old, ICQ-style offline messages were a bit of a hack). The offline messages arrive in the same way as every other message (except that they have a timestamp), so we can use the same message parsing that we use for all other IMs. This means that all our encoding stuff works just as well with offline messages. AND the new offline message stuff works for ICQ accounts, too. So I switched ICQ over to use it, which fixes offline message timestamps (if that was still a problem, I'm not sure) and offline message encoding bugs. Fixes #1229 (feature request for AIM offline messages) Fixes #1761 (ICQ offline message timestamp is wrong) Fixes #4300 (crash when receiving offline messages) Fixes #4464 (can't send IMs to invisible users) diff -r 382bc33e8824 -r a8c025929245 ChangeLog --- a/ChangeLog Wed Feb 27 04:49:20 2008 +0000 +++ b/ChangeLog Wed Feb 27 09:18:06 2008 +0000 @@ -2,6 +2,8 @@ version 2.4.0 (??/??/2008): libpurple: + * Added support for offline messages for AIM accounts (thanks to + Matthew Goldstein) * Fixed various problems with loss of status messages when going or returning from idle on MySpaceIM. * Eliminated unmaintained Howl backend implementation for the diff -r 382bc33e8824 -r a8c025929245 libpurple/protocols/oscar/family_icbm.c --- a/libpurple/protocols/oscar/family_icbm.c Wed Feb 27 04:49:20 2008 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Wed Feb 27 09:18:06 2008 +0000 @@ -221,7 +221,7 @@ params.maxrecverwarn = byte_stream_get16(bs); params.minmsginterval = byte_stream_get32(bs); - params.flags = 0x0000000b; + params.flags = 0x0000000b | AIM_IMPARAM_FLAG_SUPPORT_OFFLINEMSGS; params.maxmsglen = 8000; params.minmsginterval = 0; @@ -1626,7 +1626,10 @@ } else if (type == 0x0006) { /* Message was received offline. */ - /* XXX - not sure if this actually gets sent. */ + /* + * This flag is set on incoming offline messages for both + * AIM and ICQ accounts. + */ args.icbmflags |= AIM_IMFLAGS_OFFLINE; } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */ @@ -1657,6 +1660,14 @@ args.icbmflags |= AIM_IMFLAGS_TYPINGNOT; + } else if (type == 0x0016) { + + /* + * UTC timestamp for when the message was sent. Only + * provided for offline messages. + */ + args.timestamp = byte_stream_get32(bs); + } else if (type == 0x0017) { if (length > byte_stream_empty(bs)) @@ -2589,6 +2600,30 @@ } /* + * Subtype 0x0010 - Request any offline messages that are waiting for + * us. This is the "new" way of handling offline messages which is + * used for both AIM and ICQ. The old way is to use the ugly + * aim_icq_reqofflinemsgs() function, but that is no longer necessary. + * + * We set the 0x00000100 flag on the ICBM message parameters, which + * tells the oscar servers that we support offline messages. When we + * set that flag the servers do not automatically send us offline + * messages. Instead we must request them using this function. This + * should happen after sending the 0x0001/0x0002 "client online" SNAC. + */ +int aim_im_reqofflinemsgs(OscarData *od) +{ + FlapConnection *conn; + + if (!od || !(conn = flap_connection_findbygroup(od, 0x0002))) + return -EINVAL; + + aim_genericreq_n(od, conn, 0x0004, 0x0010); + + return 0; +} + +/* * Subtype 0x0014 - Send a mini typing notification (mtn) packet. * * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer, diff -r 382bc33e8824 -r a8c025929245 libpurple/protocols/oscar/family_icq.c --- a/libpurple/protocols/oscar/family_icq.c Wed Feb 27 04:49:20 2008 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Wed Feb 27 09:18:06 2008 +0000 @@ -25,6 +25,7 @@ #include "oscar.h" +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS int aim_icq_reqofflinemsgs(OscarData *od) { FlapConnection *conn; @@ -86,6 +87,7 @@ return 0; } +#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware) @@ -559,6 +561,7 @@ purple_debug_misc("oscar", "icq response: %d bytes, %ld, 0x%04x, 0x%04x\n", cmdlen, ouruin, cmd, reqid); if (cmd == 0x0041) { /* offline message */ +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS struct aim_icq_offlinemsg msg; aim_rxcallback_t userfunc; @@ -585,6 +588,7 @@ if ((userfunc = aim_callhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE))) ret = userfunc(od, conn, frame); +#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ } else if (cmd == 0x07da) { /* information */ guint16 subtype; diff -r 382bc33e8824 -r a8c025929245 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Wed Feb 27 04:49:20 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.c Wed Feb 27 09:18:06 2008 +0000 @@ -180,8 +180,10 @@ static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_memrequest (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_selfinfo (OscarData *, FlapConnection *, FlapFrame *, ...); +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS static int purple_offlinemsg (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_offlinemsgdone (OscarData *, FlapConnection *, FlapFrame *, ...); +#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ static int purple_icqalias (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_icqinfo (OscarData *, FlapConnection *, FlapFrame *, ...); static int purple_popup (OscarData *, FlapConnection *, FlapFrame *, ...); @@ -1275,8 +1277,10 @@ oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_ERROR, purple_parse_msgerr, 0); oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, purple_parse_mtn, 0); oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_ACK, purple_parse_msgack, 0); +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSG, purple_offlinemsg, 0); oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_OFFLINEMSGCOMPLETE, purple_offlinemsgdone, 0); +#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_ALIAS, purple_icqalias, 0); oscar_data_addhandler(od, SNAC_FAMILY_ICQ, SNAC_SUBTYPE_ICQ_INFO, purple_icqinfo, 0); oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0); @@ -2079,7 +2083,8 @@ g_datalist_clear(&attribs); } - serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL)); + serv_got_im(gc, userinfo->sn, tmp, flags, + (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL)); g_free(tmp); return 1; @@ -2264,6 +2269,8 @@ } } } + + oscar_free_name_data(data); } static void @@ -3639,8 +3646,13 @@ presence = purple_status_get_presence(status); aim_srv_setidle(od, !purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence)); + /* Request offline messages for AIM and ICQ */ + aim_im_reqofflinemsgs(od); + if (od->icq) { +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS aim_icq_reqofflinemsgs(od); +#endif oscar_set_extendedstatus(gc); aim_icq_setsecurity(od, purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION), @@ -3664,6 +3676,7 @@ return 1; } +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS static int purple_offlinemsg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { va_list ap; struct aim_icq_offlinemsg *msg; @@ -3692,6 +3705,7 @@ aim_icq_ackofflinemsgs(od); return 1; } +#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ static int purple_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { @@ -4258,7 +4272,7 @@ g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi); } - args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES; + args.flags = AIM_IMFLAGS_ACK | AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_OFFLINE; if (od->icq) { /* We have to present different "features" (whose meaning is unclear and are merely a result of protocol inspection) @@ -4275,7 +4289,6 @@ args.features = features_icq_offline; args.featureslen = sizeof(features_icq_offline); } - args.flags |= AIM_IMFLAGS_OFFLINE; } else { args.features = features_aim; args.featureslen = sizeof(features_aim); diff -r 382bc33e8824 -r a8c025929245 libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Wed Feb 27 04:49:20 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.h Wed Feb 27 09:18:06 2008 +0000 @@ -679,8 +679,9 @@ #define AIM_TRANSFER_DENY_DECLINE 0x0001 #define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002 -#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED 0x00000001 -#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED 0x00000002 +#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED 0x00000001 +#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED 0x00000002 +#define AIM_IMPARAM_FLAG_SUPPORT_OFFLINEMSGS 0x00000100 /* This is what the server will give you if you don't set them yourself. */ #define AIM_IMPARAM_DEFAULTS { \ @@ -844,6 +845,7 @@ /* Always provided */ aim_mpmsg_t mpmsg; guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */ + time_t timestamp; /* Only set for offline messages */ /* Only provided if message has a human-readable section */ gchar *msg; @@ -948,6 +950,7 @@ /* 0x0006 */ int aim_im_sendch4(OscarData *od, const char *sn, guint16 type, const char *message); /* 0x0008 */ int aim_im_warn(OscarData *od, FlapConnection *conn, const char *destsn, guint32 flags); /* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *sn, const guchar *cookie, guint16 code); +/* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od); /* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2); void aim_icbm_makecookie(guchar* cookie); gchar *oscar_encoding_extract(const char *encoding); @@ -1256,6 +1259,7 @@ #define AIM_ICQ_INFO_UNKNOWN 0x100 #define AIM_ICQ_INFO_HAVEALL 0x1ff +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS struct aim_icq_offlinemsg { guint32 sender; @@ -1266,6 +1270,7 @@ char *msg; int msglen; }; +#endif /* OLDSTYLE_ICQ_OFFLINEMSGS */ struct aim_icq_info { @@ -1330,8 +1335,10 @@ char *status_note_title; }; +#ifdef OLDSTYLE_ICQ_OFFLINEMSGS int aim_icq_reqofflinemsgs(OscarData *od); int aim_icq_ackofflinemsgs(OscarData *od); +#endif int aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware); int aim_icq_changepasswd(OscarData *od, const char *passwd); int aim_icq_getsimpleinfo(OscarData *od, const char *uin);