/* * Subtype 0x0004 - Set your client's capabilities. */ int aim_locate_setcaps(OscarData *od, guint64 caps) { FlapConnection *conn; PurpleAccount *account = purple_connection_get_account(od->gc); PurplePresence *presence = purple_account_get_presence(account); PurpleStatus *status = purple_presence_get_status(presence, "mood"); const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); ByteStream bs; aim_snacid_t snacid; GSList *tlvlist = NULL; if (!(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) return -EINVAL; aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood); byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0); aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/* * Subtype 0x0004 - Set your client's capabilities. */ int aim_locate_setcaps(OscarData *od, guint32 caps) { FlapConnection *conn; ByteStream bs; aim_snacid_t snacid; GSList *tlvlist = NULL; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) return -EINVAL; aim_tlvlist_add_caps(&tlvlist, 0x0005, caps); byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0); aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs); byte_stream_destroy(&bs); return 0; }
static void peer_proxy_send(PeerConnection *conn, ProxyFrame *frame) { size_t length; ByteStream bs; purple_debug_info("oscar", "Outgoing peer proxy frame with " "type=0x%04hx, unknown=0x%08x, " "flags=0x%04hx, and payload length=%hd\n", frame->type, frame->unknown, frame->flags, frame->payload.len); length = 12 + frame->payload.len; byte_stream_new(&bs, length); byte_stream_put16(&bs, length - 2); byte_stream_put16(&bs, PEER_PROXY_PACKET_VERSION); byte_stream_put16(&bs, frame->type); byte_stream_put32(&bs, frame->unknown); byte_stream_put16(&bs, frame->flags); byte_stream_putraw(&bs, frame->payload.data, frame->payload.len); peer_connection_send(conn, &bs); byte_stream_destroy(&bs); }
static void sendframe_flap(FlapConnection *conn, FlapFrame *frame) { ByteStream bs; int payloadlen, bslen; payloadlen = byte_stream_curpos(&frame->data); byte_stream_new(&bs, 6 + payloadlen); /* FLAP header */ byte_stream_put8(&bs, 0x2a); byte_stream_put8(&bs, frame->channel); byte_stream_put16(&bs, frame->seqnum); byte_stream_put16(&bs, payloadlen); /* Payload */ byte_stream_rewind(&frame->data); byte_stream_putbs(&bs, &frame->data, payloadlen); bslen = byte_stream_curpos(&bs); byte_stream_rewind(&bs); flap_connection_send_byte_stream(&bs, conn, bslen); byte_stream_destroy(&bs); }
/** * Create a rendezvous "init recv" packet and send it on its merry way. * This is the first packet sent to the proxy server by the second client * involved in this rendezvous proxy session. * * @param conn The peer connection. * @param pin The 2 byte PIN sent to us by the other user. This acts * as our passcode when establishing the proxy session. */ static void peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin) { ProxyFrame frame; PurpleAccount *account; const gchar *bn; guint8 bn_length; memset(&frame, 0, sizeof(ProxyFrame)); frame.type = PEER_PROXY_TYPE_JOIN; frame.flags = 0x0000; account = purple_connection_get_account(conn->od->gc); bn = purple_account_get_username(account); bn_length = strlen(bn); byte_stream_new(&frame.payload, 1 + bn_length + 2 + 8 + 20); byte_stream_put8(&frame.payload, bn_length); byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length); byte_stream_put16(&frame.payload, pin); byte_stream_putraw(&frame.payload, conn->cookie, 8); byte_stream_put16(&frame.payload, 0x0001); /* Type */ byte_stream_put16(&frame.payload, 16); /* Length */ byte_stream_putcaps(&frame.payload, conn->type); /* Value */ peer_proxy_send(conn, &frame); }
/* * Subtype 0x0008 */ int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name, guint16 exchange) { static const char ck[] = {"create"}; static const char lang[] = {"en"}; static const char charset[] = {"us-ascii"}; ByteStream bs; aim_snacid_t snacid; GSList *tlvlist = NULL; byte_stream_new(&bs, 1142); snacid = aim_cachesnac(od, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, NULL, 0); /* exchange */ byte_stream_put16(&bs, exchange); /* * This looks to be a big hack. You'll note that this entire * SNAC is just a room info structure, but the hard room name, * here, is set to "create". * * Either this goes on the "list of questions concerning * why-the-hell-did-you-do-that", or this value is completely * ignored. Without experimental evidence, but a good knowledge of * AOL style, I'm going to guess that it is the latter, and that * the value of the room name in create requests is ignored. */ byte_stream_put8(&bs, strlen(ck)); byte_stream_putstr(&bs, ck); /* * instance * * Setting this to 0xffff apparently assigns the last instance. * */ byte_stream_put16(&bs, 0xffff); /* detail level */ byte_stream_put8(&bs, 0x01); aim_tlvlist_add_str(&tlvlist, 0x00d3, name); aim_tlvlist_add_str(&tlvlist, 0x00d6, charset); aim_tlvlist_add_str(&tlvlist, 0x00d7, lang); /* tlvcount */ byte_stream_put16(&bs, aim_tlvlist_count(tlvlist)); aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/** * Allocate a new FLAP frame. * * @param channel The FLAP channel. This is almost always 2. */ FlapFrame * flap_frame_new(OscarData *od, guint16 channel, int datalen) { FlapFrame *frame; frame = g_new0(FlapFrame, 1); frame->channel = channel; if (datalen > 0) byte_stream_new(&frame->data, datalen); return frame; }
/* * Stubtypes 0x0005, 0x0006, 0x0007, and 0x0008 - Modify permit/deny lists. * * Changes your visibility depending on changetype: * * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again * * list should be a list of "Buddy Name One&BuddyNameTwo&" etc. * * Equivelents to options in WinAIM: * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD * with only your name on it. * - Allow only users on my Buddy List: Send an * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your * buddy list * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD * with everyone listed that you want to see you. * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only * yourself in the list * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with * the list of users to be blocked * * XXX ye gods. */ int aim_bos_changevisibility(OscarData *od, FlapConnection *conn, int changetype, const char *denylist) { ByteStream bs; int packlen = 0; guint16 subtype; char *localcpy = NULL, *tmpptr = NULL; int i; int listcount; aim_snacid_t snacid; if (!denylist) return -EINVAL; if (changetype == AIM_VISIBILITYCHANGE_PERMITADD) subtype = 0x05; else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE) subtype = 0x06; else if (changetype == AIM_VISIBILITYCHANGE_DENYADD) subtype = 0x07; else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE) subtype = 0x08; else return -EINVAL; localcpy = g_strdup(denylist); listcount = aimutil_itemcnt(localcpy, '&'); packlen = aimutil_tokslen(localcpy, 99, '&') + listcount-1; byte_stream_new(&bs, packlen); for (i = 0; (i < (listcount - 1)) && (i < 99); i++) { tmpptr = aimutil_itemindex(localcpy, i, '&'); byte_stream_put8(&bs, strlen(tmpptr)); byte_stream_putstr(&bs, tmpptr); g_free(tmpptr); } g_free(localcpy); snacid = aim_cachesnac(od, SNAC_FAMILY_BOS, subtype, 0x0000, NULL, 0); flap_connection_send_snac(od, conn, SNAC_FAMILY_BOS, subtype, 0x0000, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/* * Subtype 0x0009 - Set directory profile data. * * This is not the same as aim_location_setprofile! * privacy: 1 to allow searching, 0 to disallow. * */ int aim_locate_setdirinfo(OscarData *od, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy) { FlapConnection *conn; ByteStream bs; aim_snacid_t snacid; GSList *tlvlist = NULL; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) return -EINVAL; aim_tlvlist_add_16(&tlvlist, 0x000a, privacy); if (first) aim_tlvlist_add_str(&tlvlist, 0x0001, first); if (last) aim_tlvlist_add_str(&tlvlist, 0x0002, last); if (middle) aim_tlvlist_add_str(&tlvlist, 0x0003, middle); if (maiden) aim_tlvlist_add_str(&tlvlist, 0x0004, maiden); if (state) aim_tlvlist_add_str(&tlvlist, 0x0007, state); if (city) aim_tlvlist_add_str(&tlvlist, 0x0008, city); if (nickname) aim_tlvlist_add_str(&tlvlist, 0x000c, nickname); if (zip) aim_tlvlist_add_str(&tlvlist, 0x000d, zip); if (street) aim_tlvlist_add_str(&tlvlist, 0x0021, street); byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, NULL, 0); aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/** * Write the given OdcFrame to a ByteStream and send it out * on the established PeerConnection. */ static void peer_odc_send(PeerConnection *conn, OdcFrame *frame) { GaimAccount *account; const char *username; size_t length; ByteStream bs; gaim_debug_info("oscar", "Outgoing ODC frame to %s with " "type=0x%04x, flags=0x%04x, payload length=%u\n", conn->sn, frame->type, frame->flags, frame->payload.len); account = gaim_connection_get_account(conn->od->gc); username = gaim_account_get_username(account); memcpy(frame->sn, username, strlen(username)); memcpy(frame->cookie, conn->cookie, 8); length = 76; byte_stream_new(&bs, length + frame->payload.len); byte_stream_putraw(&bs, conn->magic, 4); byte_stream_put16(&bs, length); byte_stream_put16(&bs, frame->type); byte_stream_put16(&bs, frame->subtype); byte_stream_put16(&bs, 0x0000); byte_stream_putraw(&bs, frame->cookie, 8); byte_stream_put16(&bs, 0x0000); byte_stream_put16(&bs, 0x0000); byte_stream_put16(&bs, 0x0000); byte_stream_put16(&bs, 0x0000); byte_stream_put32(&bs, frame->payload.len); byte_stream_put16(&bs, 0x0000); byte_stream_put16(&bs, frame->encoding); byte_stream_put16(&bs, 0x0000); byte_stream_put16(&bs, frame->flags); byte_stream_put16(&bs, 0x0000); byte_stream_put16(&bs, 0x0000); byte_stream_putraw(&bs, frame->sn, 32); byte_stream_putraw(&bs, frame->payload.data, frame->payload.len); peer_connection_send(conn, &bs); g_free(bs.data); }
/* * Subtype 0x0004 (SNAC_SUBTYPE_BUDDY_ADDBUDDY) - Add multiple buddies to your buddy list. * * This just builds the "set buddy list" command then queues it. * * buddy_list = "Screen Name One&ScreenNameTwo&"; * * XXX Clean this up. * */ int aim_buddylist_set(OscarData *od, FlapConnection *conn, const char *buddy_list) { ByteStream bs; aim_snacid_t snacid; int len = 0; char *localcpy = NULL; char *tmpptr = NULL; if (!buddy_list || !(localcpy = g_strdup(buddy_list))) return -EINVAL; for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { purple_debug_misc("oscar", "---adding: %s (%" G_GSIZE_FORMAT ")\n", tmpptr, strlen(tmpptr)); len += 1 + strlen(tmpptr); tmpptr = strtok(NULL, "&"); } byte_stream_new(&bs, len); strncpy(localcpy, buddy_list, strlen(buddy_list) + 1); for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { purple_debug_misc("oscar", "---adding: %s (%" G_GSIZE_FORMAT ")\n", tmpptr, strlen(tmpptr)); byte_stream_put8(&bs, strlen(tmpptr)); byte_stream_putstr(&bs, tmpptr); tmpptr = strtok(NULL, "&"); } snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, NULL, 0); flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs); byte_stream_destroy(&bs); g_free(localcpy); return 0; }
/** * Send client-to-client IM over an established direct connection. * To send a direct IM, call this just like you would aim_send_im. * * @param conn The already-connected ODC connection. * @param msg Null-terminated string to send. * @param len The length of the message to send, including binary data. * @param encoding See the AIM_CHARSET_* defines in oscar.h * @param autoreply TRUE if this is any auto-reply. */ void peer_odc_send_im(PeerConnection *conn, const char *msg, int len, int encoding, gboolean autoreply) { OdcFrame frame; g_return_if_fail(msg != NULL); g_return_if_fail(len > 0); memset(&frame, 0, sizeof(OdcFrame)); frame.type = 0x0001; frame.subtype = 0x0006; frame.payload.len = len; frame.encoding = encoding; frame.flags = autoreply; byte_stream_new(&frame.payload, len); byte_stream_putraw(&frame.payload, (guint8 *)msg, len); peer_odc_send(conn, &frame); g_free(frame.payload.data); }
/* * Subtype 0x0004 (SNAC_SUBTYPE_BUDDY_ADDBUDDY) - Add buddy to list. * * Adds a single buddy to your buddy list after login. * XXX This should just be an extension of setbuddylist() * */ int aim_buddylist_addbuddy(OscarData *od, FlapConnection *conn, const char *sn) { ByteStream bs; aim_snacid_t snacid; if (!sn || !strlen(sn)) return -EINVAL; byte_stream_new(&bs, 1+strlen(sn)); byte_stream_put8(&bs, strlen(sn)); byte_stream_putstr(&bs, sn); snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, sn, strlen(sn)+1); flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/* * Subtype 0x0015 - Request the info a user using the short method. This is * what iChat uses. It normally is VERY leniently rate limited. * * @param sn The screen name whose info you wish to request. * @param flags The bitmask which specifies the type of info you wish to request. * 0x00000001 - Info/profile. * 0x00000002 - Away message. * 0x00000004 - Capabilities. * 0x00000008 - Certification. * @return Return 0 if no errors, otherwise return the error number. */ int aim_locate_getinfoshort(OscarData *od, const char *sn, guint32 flags) { FlapConnection *conn; ByteStream data; aim_snacid_t snacid; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !sn) return -EINVAL; byte_stream_new(&data, 4 + 1 + strlen(sn)); byte_stream_put32(&data, flags); byte_stream_put8(&data, strlen(sn)); byte_stream_putstr(&data, sn); snacid = aim_cachesnac(od, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1); flap_connection_send_snac(od, conn, 0x0002, 0x0015, 0x0000, snacid, &data); g_free(data.data); return 0; }
/* * Subtype 0x0015 - Request the info of a user using the short method. This is * what iChat uses. It normally is VERY leniently rate limited. * * @param bn The buddy name whose info you wish to request. * @param flags The bitmask which specifies the type of info you wish to request. * 0x00000001 - Info/profile. * 0x00000002 - Away message. * 0x00000004 - Capabilities. * 0x00000008 - Certification. * @return Return 0 if no errors, otherwise return the error number. */ int aim_locate_getinfoshort(OscarData *od, const char *bn, guint32 flags) { FlapConnection *conn; ByteStream bs; aim_snacid_t snacid; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn) return -EINVAL; byte_stream_new(&bs, 4 + 1 + strlen(bn)); byte_stream_put32(&bs, flags); byte_stream_put8(&bs, strlen(bn)); byte_stream_putstr(&bs, bn); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, bn, strlen(bn)+1); flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, snacid, &bs, FALSE); byte_stream_destroy(&bs); return 0; }
/* * Subtype 0x000f * * XXX pass these in better * */ int aim_locate_setinterests(OscarData *od, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy) { FlapConnection *conn; ByteStream bs; aim_snacid_t snacid; GSList *tlvlist = NULL; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) return -EINVAL; /* ?? privacy ?? */ aim_tlvlist_add_16(&tlvlist, 0x000a, privacy); if (interest1) aim_tlvlist_add_str(&tlvlist, 0x0000b, interest1); if (interest2) aim_tlvlist_add_str(&tlvlist, 0x0000b, interest2); if (interest3) aim_tlvlist_add_str(&tlvlist, 0x0000b, interest3); if (interest4) aim_tlvlist_add_str(&tlvlist, 0x0000b, interest4); if (interest5) aim_tlvlist_add_str(&tlvlist, 0x0000b, interest5); byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, NULL, 0); aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/* * Subtype 0x000b - Huh? What is this? */ int aim_locate_000b(OscarData *od, const char *bn) { FlapConnection *conn; ByteStream bs; aim_snacid_t snacid; return -EINVAL; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)) || !bn) return -EINVAL; byte_stream_new(&bs, 1+strlen(bn)); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, NULL, 0); byte_stream_put8(&bs, strlen(bn)); byte_stream_putstr(&bs, bn); flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/* * Subtype 0x0005 - Send a Chat Message. * * Possible flags: * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages * should be sent to their sender. * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse * (Note that WinAIM does not honor this, * and displays the message as normal.) * * XXX convert this to use tlvchains */ int aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar *msg, int msglen, const char *encoding, const char *language) { int i; ByteStream bs; IcbmCookie *cookie; aim_snacid_t snacid; guint8 ckstr[8]; GSList *tlvlist = NULL, *inner_tlvlist = NULL; if (!od || !conn || !msg || (msglen <= 0)) return 0; byte_stream_new(&bs, 1142); snacid = aim_cachesnac(od, SNAC_FAMILY_CHAT, 0x0005, 0x0000, NULL, 0); /* * Cookie * * XXX mkcookie should generate the cookie and cache it in one * operation to preserve uniqueness. */ for (i = 0; i < 8; i++) ckstr[i] = (guint8)rand(); cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL); cookie->data = NULL; /* XXX store something useful here */ aim_cachecookie(od, cookie); /* ICBM Header */ byte_stream_putraw(&bs, ckstr, 8); /* Cookie */ byte_stream_put16(&bs, 0x0003); /* Channel */ /* * Type 1: Flag meaning this message is destined to the room. */ aim_tlvlist_add_noval(&tlvlist, 0x0001); /* * Type 6: Reflect */ if (!(flags & AIM_CHATFLAGS_NOREFLECT)) aim_tlvlist_add_noval(&tlvlist, 0x0006); /* * Type 7: Autoresponse */ if (flags & AIM_CHATFLAGS_AWAY) aim_tlvlist_add_noval(&tlvlist, 0x0007); /* * SubTLV: Type 1: Message */ aim_tlvlist_add_raw(&inner_tlvlist, 0x0001, msglen, (guchar *)msg); /* * SubTLV: Type 2: Encoding */ if (encoding != NULL) aim_tlvlist_add_str(&inner_tlvlist, 0x0002, encoding); /* * SubTLV: Type 3: Language */ if (language != NULL) aim_tlvlist_add_str(&inner_tlvlist, 0x0003, language); /* * Type 5: Message block. Contains more TLVs. * * This could include other information... We just * put in a message TLV however. * */ aim_tlvlist_add_frozentlvlist(&tlvlist, 0x0005, &inner_tlvlist); aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(inner_tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, snacid, &bs); byte_stream_destroy(&bs); return 0; }
/* * Subtype 0x0004 * * Gives BOS your profile. * * profile_encoding and awaymsg_encoding MUST be set if profile or * away are set, respectively, and their value may or may not be * restricted to a few choices. I am currently aware of: * * us-ascii Just that * unicode-2-0 UTF-16BE * * profile_len and awaymsg_len MUST be set similarly, and they MUST * be the length of their respective strings in bytes. * * To get the previous behavior of awaymsg == "" un-setting the away * message, set awaymsg non-NULL and awaymsg_len to 0 (this is the * obvious equivalent). * */ int aim_locate_setprofile(OscarData *od, const char *profile_encoding, const gchar *profile, const int profile_len, const char *awaymsg_encoding, const gchar *awaymsg, const int awaymsg_len) { FlapConnection *conn; ByteStream bs; aim_snacid_t snacid; GSList *tlvlist = NULL; char *encoding; static const char defencoding[] = {"text/aolrtf; charset=\"%s\""}; if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) return -EINVAL; if (!profile && !awaymsg) return -EINVAL; if ((profile && profile_encoding == NULL) || (awaymsg && awaymsg_len && awaymsg_encoding == NULL)) { return -EINVAL; } /* Build the packet first to get real length */ if (profile) { /* no + 1 here because of %s */ encoding = g_malloc(strlen(defencoding) + strlen(profile_encoding)); snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding); aim_tlvlist_add_str(&tlvlist, 0x0001, encoding); aim_tlvlist_add_raw(&tlvlist, 0x0002, profile_len, (const guchar *)profile); g_free(encoding); } /* * So here's how this works: * - You are away when you have a non-zero-length type 4 TLV stored. * - You become unaway when you clear the TLV with a zero-length * type 4 TLV. * - If you do not send the type 4 TLV, your status does not change * (that is, if you were away, you'll remain away). */ if (awaymsg) { if (awaymsg_len) { encoding = g_malloc(strlen(defencoding) + strlen(awaymsg_encoding)); snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding); aim_tlvlist_add_str(&tlvlist, 0x0003, encoding); aim_tlvlist_add_raw(&tlvlist, 0x0004, awaymsg_len, (const guchar *)awaymsg); g_free(encoding); } else aim_tlvlist_add_noval(&tlvlist, 0x0004); } byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0); aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(tlvlist); flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs); byte_stream_destroy(&bs); return 0; }