changeset 22337:a8c025929245

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)
author Mark Doliner <mark@kingant.net>
date Wed, 27 Feb 2008 09:18:06 +0000
parents 382bc33e8824
children a298447b2d66 969a2aeae461
files ChangeLog libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/family_icq.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h
diffstat 5 files changed, 68 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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,
--- 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;
--- 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);
--- 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);