/** * answers status message requests * @param sess the oscar session * @param sender the guy whos asking * @param cookie message id which we are answering for * @param message away message * @param state our current away state the way icq requests it (0xE8 for away, 0xE9 occupied, ...) * @return 0 if no error */ int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie, const char *message, const guint8 state, const guint16 dc) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+2+0x1d+0x10+9+strlen(message)+1))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid); aimbs_putraw(&fr->data, cookie, 8); aimbs_put16(&fr->data, 0x0002); /* channel */ aimbs_put8(&fr->data, strlen(sender)); aimbs_putraw(&fr->data, (guint8 *)sender, strlen(sender)); aimbs_put16(&fr->data, 0x0003); /* reason: channel specific */ aimbs_putle16(&fr->data, 0x001b); /* length of data SEQ1 */ aimbs_putle16(&fr->data, 0x0008); /* protocol version */ aimbs_putle32(&fr->data, 0x0000); /* no plugin -> 16 times 0x00 */ aimbs_putle32(&fr->data, 0x0000); aimbs_putle32(&fr->data, 0x0000); aimbs_putle32(&fr->data, 0x0000); aimbs_putle16(&fr->data, 0x0000); /* unknown */ aimbs_putle32(&fr->data, 0x0003); /* client features */ aimbs_putle8(&fr->data, 0x00); /* unknown */ aimbs_putle16(&fr->data, dc); /* Sequence number? XXX - This should decrement by 1 with each request */ /* end of SEQ1 */ aimbs_putle16(&fr->data, 0x000e); /* Length of SEQ2 */ aimbs_putle16(&fr->data, dc); /* Sequence number? same as above * 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 */ /* end of SEQ2 */ /* now for the real fun */ aimbs_putle8(&fr->data, state); /* away state */ aimbs_putle8(&fr->data, 0x03); /* msg-flag: 03 for states */ aimbs_putle16(&fr->data, 0x0000); /* status code ? */ aimbs_putle16(&fr->data, 0x0000); /* priority code */ aimbs_putle16(&fr->data, strlen(message) + 1); /* message length + termination */ aimbs_putraw(&fr->data, (guint8 *) message, strlen(message) + 1); /* null terminated string */ aim_tx_enqueue(sess, fr); return 0; }
/** * Subtype 0x0004 - Request someone's icon. * * @param sess The oscar session. * @param conn The icon connection for this session. * @param sn The screen name of the person who's icon you are requesting. * @param iconcsum The MD5 checksum of the icon you are requesting. * @param iconcsumlen Length of the MD5 checksum given above. Should be 10 bytes. * @return Return 0 if no errors, otherwise return the error number. */ faim_export int aim_bart_request(aim_session_t *sess, const char *sn, fu8_t iconcsumtype, const fu8_t *iconcsum, fu16_t iconcsumlen) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 1+strlen(sn) + 4 + 1+iconcsumlen))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0010, 0x0004, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0010, 0x0004, 0x0000, snacid); /* Screen name */ aimbs_put8(&fr->data, strlen(sn)); aimbs_putraw(&fr->data, sn, strlen(sn)); /* Some numbers. You like numbers, right? */ aimbs_put8(&fr->data, 0x01); aimbs_put16(&fr->data, 0x0001); aimbs_put8(&fr->data, iconcsumtype); /* Icon string */ aimbs_put8(&fr->data, iconcsumlen); aimbs_putraw(&fr->data, iconcsum, iconcsumlen); aim_tx_enqueue(sess, fr); return 0; }
/** * Subtype 0x0002 - Upload your icon. * * @param sess The oscar session. * @param conn The icon connection for this session. * @param icon The raw data of the icon image file. * @param iconlen Length of the raw data of the icon image file. * @return Return 0 if no errors, otherwise return the error number. */ faim_export int aim_bart_upload(aim_session_t *sess, const fu8_t *icon, fu16_t iconlen) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0010)) || !icon || !iconlen) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 2 + 2+iconlen))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0010, 0x0002, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0010, 0x0002, 0x0000, snacid); /* The reference number for the icon */ aimbs_put16(&fr->data, 1); /* The icon */ aimbs_put16(&fr->data, iconlen); aimbs_putraw(&fr->data, icon, iconlen); aim_tx_enqueue(sess, fr); return 0; }
faim_export int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, fu16_t infotype) { struct aim_priv_inforeq privdata; aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !conn || !sn) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn)))) return -ENOMEM; strncpy(privdata.sn, sn, sizeof(privdata.sn)); privdata.infotype = infotype; snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq)); aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid); aimbs_put16(&fr->data, infotype); aimbs_put8(&fr->data, strlen(sn)); aimbs_putraw(&fr->data, sn, strlen(sn)); aim_tx_enqueue(sess, fr); return 0; }
/* * Inverse of aim_extractuserinfo() */ int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) { aim_tlvlist_t *tlvlist = NULL; if (!bs || !info) return -EINVAL; aimbs_put8(bs, strlen(info->sn)); aimbs_putraw(bs, (guint8 *)info->sn, strlen(info->sn)); aimbs_put16(bs, info->warnlevel); aim_addtlvtochain16(&tlvlist, 0x0001, info->flags); aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); #if ICQ_OSCAR_SUPPORT if (atoi(info->sn) != 0) { aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status); aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr); } #endif aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities); aim_addtlvtochain32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); aimbs_put16(bs, aim_counttlvchain(&tlvlist)); aim_writetlvchain(bs, &tlvlist); aim_freetlvchain(&tlvlist); return 0; }
faim_export int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, fu16_t exchange) { static const char ck[] = {"create"}; static const char lang[] = {"en"}; static const char charset[] = {"us-ascii"}; aim_frame_t *fr; aim_snacid_t snacid; aim_tlvlist_t *tl = NULL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid); /* exchange */ aimbs_put16(&fr->data, 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 completly * 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. */ aimbs_put8(&fr->data, strlen(ck)); aimbs_putraw(&fr->data, ck, strlen(ck)); /* * instance * * Setting this to 0xffff apparently assigns the last instance. * */ aimbs_put16(&fr->data, 0xffff); /* detail level */ aimbs_put8(&fr->data, 0x01); aim_addtlvtochain_raw(&tl, 0x00d3, strlen(name), name); aim_addtlvtochain_raw(&tl, 0x00d6, strlen(charset), charset); aim_addtlvtochain_raw(&tl, 0x00d7, strlen(lang), lang); /* tlvcount */ aimbs_put16(&fr->data, aim_counttlvchain(&tl)); aim_writetlvchain(&fr->data, &tl); aim_freetlvchain(&tl); aim_tx_enqueue(sess, fr); return 0; }
/* * 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 * screen names in the form "Screen Name One&ScreenNameTwo&" 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. */ faim_export int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist) { aim_frame_t *fr; int packlen = 0; fu16_t 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 = strdup(denylist); listcount = aimutil_itemcnt(localcpy, '&'); packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) { free(localcpy); return -ENOMEM; } snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid); for (i = 0; (i < (listcount - 1)) && (i < 99); i++) { tmpptr = aimutil_itemindex(localcpy, i, '&'); aimbs_put8(&fr->data, strlen(tmpptr)); aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr)); free(tmpptr); } free(localcpy); aim_tx_enqueue(sess, fr); 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, * and Gaim 0.60 and newer. * */ int aim_im_sendmtn(aim_session_t *sess, guint16 type1, const char *sn, guint16 type2) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002))) return -EINVAL; if (!sn) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid); /* * 8 days of light * Er, that is to say, 8 bytes of 0's */ aimbs_put16(&fr->data, 0x0000); aimbs_put16(&fr->data, 0x0000); aimbs_put16(&fr->data, 0x0000); aimbs_put16(&fr->data, 0x0000); /* * Type 1 (should be 0x0001 for mtn) */ aimbs_put16(&fr->data, type1); /* * Dest sn */ aimbs_put8(&fr->data, strlen(sn)); aimbs_putraw(&fr->data, (const guint8*)sn, strlen(sn)); /* * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn) */ aimbs_put16(&fr->data, type2); aim_tx_enqueue(sess, fr); return 0; }
faim_internal int aim_putcap(aim_bstream_t *bs, fu32_t caps) { int i; if (!bs) return -EINVAL; for (i = 0; aim_bstream_empty(bs); i++) { if (aim_caps[i].flag == AIM_CAPS_LAST) break; if (caps & aim_caps[i].flag) aimbs_putraw(bs, aim_caps[i].data, 0x10); } return 0; }
/* * Subtype 0x0004 - 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. * */ faim_export int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list) { aim_frame_t *fr; aim_snacid_t snacid; int len = 0; char *localcpy = NULL; char *tmpptr = NULL; if (!buddy_list || !(localcpy = strdup(buddy_list))) return -EINVAL; for (tmpptr = strtok(localcpy, "&"); tmpptr;) { faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); len += 1 + strlen(tmpptr); tmpptr = strtok(NULL, "&"); } if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + len))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); strncpy(localcpy, buddy_list, strlen(buddy_list) + 1); for (tmpptr = strtok(localcpy, "&"); tmpptr;) { faimdprintf(sess, 2, "---adding: %s (%d)\n", tmpptr, strlen(tmpptr)); aimbs_put8(&fr->data, strlen(tmpptr)); aimbs_putraw(&fr->data, tmpptr, strlen(tmpptr)); tmpptr = strtok(NULL, "&"); } aim_tx_enqueue(sess, fr); free(localcpy); return 0; }
/* * Inverse of aim_extractuserinfo() */ faim_internal int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) { aim_tlvlist_t *tlvlist = NULL; if (!bs || !info) return -EINVAL; aimbs_put8(bs, strlen(info->sn)); aimbs_putraw(bs, info->sn, strlen(info->sn)); aimbs_put16(bs, info->warnlevel); if (info->present & AIM_USERINFO_PRESENT_FLAGS) aim_addtlvtochain16(&tlvlist, 0x0001, info->flags); if (info->present & AIM_USERINFO_PRESENT_MEMBERSINCE) aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); if (info->present & AIM_USERINFO_PRESENT_IDLE) aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); #if ICQ_OSCAR_SUPPORT if (atoi(info->sn) != 0) { if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status); if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr); } #endif if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities); if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) aim_addtlvtochain32(&tlvlist, (fu16_t)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); aimbs_put16(bs, aim_counttlvchain(&tlvlist)); aim_writetlvchain(bs, &tlvlist); aim_freetlvchain(&tlvlist); return 0; }
/* * Subtype 0x0002 * */ faim_export int aim_search_address(aim_session_t *sess, aim_conn_t *conn, const char *address) { aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !conn || !address) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address)))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, strdup(address), strlen(address)+1); aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid); aimbs_putraw(&fr->data, address, strlen(address)); aim_tx_enqueue(sess, fr); return 0; }
/** * Change your ICQ password. * * @param sess The oscar session * @param passwd The new password. If this is longer than 8 characters it * will be truncated. * @return Return 0 if no errors, otherwise return the error number. */ faim_export int aim_icq_changepasswd(aim_session_t *sess, const char *passwd) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; int bslen, passwdlen; if (!passwd) return -EINVAL; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) return -EINVAL; passwdlen = strlen(passwd); if (passwdlen > MAXICQPASSLEN) passwdlen = MAXICQPASSLEN; bslen = 2+4+2+2+2+2+passwdlen+1; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); /* For simplicity, don't bother using a tlvlist */ aimbs_put16(&fr->data, 0x0001); aimbs_put16(&fr->data, bslen); aimbs_putle16(&fr->data, bslen - 2); aimbs_putle32(&fr->data, atoi(sess->sn)); aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ aimbs_putle16(&fr->data, snacid); /* eh. */ aimbs_putle16(&fr->data, 0x042e); /* shrug. */ aimbs_putle16(&fr->data, passwdlen+1); aimbs_putraw(&fr->data, passwd, passwdlen); aimbs_putle8(&fr->data, '\0'); aim_tx_enqueue(sess, fr); return 0; }
/* * Huh? What is this? */ faim_export int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn) { aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !conn || !sn) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid); aimbs_put8(&fr->data, strlen(sn)); aimbs_putraw(&fr->data, sn, strlen(sn)); aim_tx_enqueue(sess, fr); return 0; }
faim_export int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; int bslen; if (!xml || !strlen(xml)) return -EINVAL; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) return -EINVAL; bslen = 2 + 10 + 2 + strlen(xml) + 1; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); /* For simplicity, don't bother using a tlvlist */ aimbs_put16(&fr->data, 0x0001); aimbs_put16(&fr->data, bslen); aimbs_putle16(&fr->data, bslen - 2); aimbs_putle32(&fr->data, atoi(sess->sn)); aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ aimbs_putle16(&fr->data, snacid); /* eh. */ aimbs_putle16(&fr->data, 0x0998); /* shrug. */ aimbs_putle16(&fr->data, strlen(xml) + 1); aimbs_putraw(&fr->data, xml, strlen(xml) + 1); aim_tx_enqueue(sess, fr); return 0; }
static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance) { fu8_t *buf; int buflen; aim_bstream_t bs; buflen = 2 + 1 + strlen(roomname) + 2; if (!(buf = malloc(buflen))) return 0; aim_bstream_init(&bs, buf, buflen); aimbs_put16(&bs, exchange); aimbs_put8(&bs, strlen(roomname)); aimbs_putraw(&bs, roomname, strlen(roomname)); aimbs_put16(&bs, instance); aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); free(buf); return 0; }
/* * Send a warning to destsn. * * Flags: * AIM_WARN_ANON Send as an anonymous (doesn't count as much) * * returns -1 on error (couldn't alloc packet), 0 on success. * */ int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags) { aim_frame_t *fr; aim_snacid_t snacid; guint16 outflags = 0x0000; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(destsn)+13))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1); aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid); if (flags & AIM_WARN_ANON) outflags |= 0x0001; aimbs_put16(&fr->data, outflags); aimbs_put8(&fr->data, strlen(destsn)); aimbs_putraw(&fr->data, (guint8 *)destsn, strlen(destsn)); aim_tx_enqueue(sess, fr); return 0; }
/** * aim_writetlvchain - Write a TLV chain into a data buffer. * @buf: Destination buffer * @buflen: Maximum number of bytes that will be written to buffer * @list: Source TLV chain * * Copies a TLV chain into a raw data buffer, writing only the number * of bytes specified. This operation does not free the chain; * aim_freetlvchain() must still be called to free up the memory used * by the chain structures. * * XXX clean this up, make better use of bstreams */ faim_internal int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list) { int goodbuflen; aim_tlvlist_t *cur; /* do an initial run to test total length */ for (cur = *list, goodbuflen = 0; cur; cur = cur->next) { goodbuflen += 2 + 2; /* type + len */ goodbuflen += cur->tlv->length; } if (goodbuflen > aim_bstream_empty(bs)) return 0; /* not enough buffer */ /* do the real write-out */ for (cur = *list; cur; cur = cur->next) { aimbs_put16(bs, cur->tlv->type); aimbs_put16(bs, cur->tlv->length); if (cur->tlv->length) aimbs_putraw(bs, cur->tlv->value, cur->tlv->length); } return 1; /* XXX this is a nonsensical return */ }
faim_export int aim_icq_sendsms(aim_session_t *sess, const char *dest, const char *body) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; int bslen; int a; char *xml; int xmllen; char timestr[30]; time_t t; struct tm *tm; if (!body || !strlen(body)) return -EINVAL; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) return -EINVAL; time(&t); tm = gmtime(&t); strftime(timestr, 30, "%a, %d %b %Y %T %Z", tm); xmllen = 17 + 13 + strlen(dest) + 14 + 6 + strlen(body) + 7 + 25 + 13 + strlen(sess->sn) + 14 + 14 + strlen(sess->sn /* should be nick */) + 15 + 18 + 2 + 19 + 6 + strlen(timestr) + 7 + 18 + 1; bslen = 2 + 4 + 2 + 2 + 2 + 2 + 2 + 16 + 4 + xmllen; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) return -ENOMEM; xml = (char *) malloc(xmllen); snprintf(xml, xmllen, "<icq_sms_message><destination>%s</destination>"\ "<text>%s</text><codepage>1252</codepage>"\ "<senders_UIN>%s</senders_UIN>"\ "<senders_name>%s</senders_name>"\ "<delivery_receipt>No</delivery_receipt>"\ "<time>%s</time></icq_sms_message>", dest, body, sess->sn, sess->sn /* should be nick */, timestr); snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); /* For simplicity, don't bother using a tlvlist */ aimbs_put16(&fr->data, 0x0001); aimbs_put16(&fr->data, bslen); aimbs_putle16(&fr->data, bslen - 2); aimbs_putle32(&fr->data, atoi(sess->sn)); aimbs_putle16(&fr->data, 2000); /* I command thee. */ aimbs_putle16(&fr->data, snacid); /* eh. */ aimbs_put16(&fr->data, 0x8214); /* SMS send subtype */ aimbs_put16(&fr->data, 0x0001); aimbs_put16(&fr->data, 0x0016); for(a = 0; a < 16; a++) aimbs_put8(&fr->data, 0x00); aimbs_put32(&fr->data, xmllen); /* yes, this has to be 32 bits */ aimbs_putraw(&fr->data, xml, xmllen); aim_tx_enqueue(sess, fr); free(xml); 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 */ faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const char *msg, int msglen, const char *encoding, const char *language) { int i; aim_frame_t *fr; aim_msgcookie_t *cookie; aim_snacid_t snacid; fu8_t ckstr[8]; aim_tlvlist_t *otl = NULL, *itl = NULL; if (!sess || !conn || !msg || (msglen <= 0)) return 0; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid); /* * Cookie * * XXX mkcookie should generate the cookie and cache it in one * operation to preserve uniqueness. */ for (i = 0; i < 8; i++) ckstr[i] = (fu8_t)rand(); cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL); cookie->data = NULL; /* XXX store something useful here */ aim_cachecookie(sess, cookie); /* ICBM Header */ aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */ aimbs_put16(&fr->data, 0x0003); /* Channel */ /* * Type 1: Flag meaning this message is destined to the room. */ aim_tlvlist_add_noval(&otl, 0x0001); /* * Type 6: Reflect */ if (!(flags & AIM_CHATFLAGS_NOREFLECT)) aim_tlvlist_add_noval(&otl, 0x0006); /* * Type 7: Autoresponse */ if (flags & AIM_CHATFLAGS_AWAY) aim_tlvlist_add_noval(&otl, 0x0007); /* * SubTLV: Type 1: Message */ aim_tlvlist_add_raw(&itl, 0x0001, msglen, msg); /* * SubTLV: Type 2: Encoding */ if (encoding != NULL) aim_tlvlist_add_raw(&itl, 0x0002, strlen(encoding), encoding); /* * SubTLV: Type 3: Language */ if (language != NULL) aim_tlvlist_add_raw(&itl, 0x0003, strlen(language), 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(&otl, 0x0005, &itl); aim_tlvlist_write(&fr->data, &otl); aim_tlvlist_free(&itl); aim_tlvlist_free(&otl); aim_tx_enqueue(sess, fr); return 0; }
/* * Send an ICBM (instant message). * * * Possible flags: * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse * AIM_IMFLAGS_ACK -- Requests that the server send an ack * when the message is received (of type 0x0004/0x000c) * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are * online (probably ICQ only). * AIM_IMFLAGS_UNICODE--Instead of ASCII7, the passed message is * made up of UNICODE duples. If you set * this, you'd better be damn sure you know * what you're doing. * AIM_IMFLAGS_ISO_8859_1 -- The message contains the ASCII8 subset * known as ISO-8859-1. * * Generally, you should use the lowest encoding possible to send * your message. If you only use basic punctuation and the generic * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7 * characters, but they are all clearly defined in ISO-8859-1, then * use that. Keep in mind that not all characters in the PC ASCII8 * character set are defined in the ISO standard. For those cases (most * notably when the (r) symbol is used), you must use the full UNICODE * encoding for your message. In UNICODE mode, _all_ characters must * occupy 16bits, including ones that are not special. (Remember that * the first 128 UNICODE symbols are equivelent to ASCII7, however they * must be prefixed with a zero high order byte.) * * I strongly discourage the use of UNICODE mode, mainly because none * of the clients I use can parse those messages (and besides that, * wchars are difficult and non-portable to handle in most UNIX environments). * If you really need to include special characters, use the HTML UNICODE * entities. These are of the form ߪ where 2026 is the hex * representation of the UNICODE index (in this case, UNICODE * "Horizontal Ellipsis", or 133 in in ASCII8). * * Implementation note: Since this is one of the most-used functions * in all of libfaim, it is written with performance in mind. As such, * it is not as clear as it could be in respect to how this message is * supposed to be layed out. Most obviously, tlvlists should be used * instead of writing out the bytes manually. * * XXX more precise verification that we never send SNACs larger than 8192 * XXX check SNAC size for multipart * */ int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args) { static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 }; aim_conn_t *conn; int i, msgtlvlen; aim_frame_t *fr; aim_snacid_t snacid; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) return -EINVAL; if (!args) return -EINVAL; if (args->flags & AIM_IMFLAGS_MULTIPART) { if (args->mpmsg->numparts <= 0) return -EINVAL; } else { if (!args->msg || (args->msglen <= 0)) return -EINVAL; if (args->msglen >= MAXMSGLEN) return -E2BIG; } /* Painfully calculate the size of the message TLV */ msgtlvlen = 1 + 1; /* 0501 */ if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) msgtlvlen += 2 + args->featureslen; else msgtlvlen += 2 + sizeof(deffeatures); if (args->flags & AIM_IMFLAGS_MULTIPART) { aim_mpmsg_section_t *sec; for (sec = args->mpmsg->parts; sec; sec = sec->next) { msgtlvlen += 2 /* 0101 */ + 2 /* block len */; msgtlvlen += 4 /* charset */ + sec->datalen; } } else { msgtlvlen += 2 /* 0101 */ + 2 /* block len */; msgtlvlen += 4 /* charset */ + args->msglen; } if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128))) return -ENOMEM; /* XXX should be optional */ snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1); aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); /* * Generate a random message cookie * * We could cache these like we do SNAC IDs. (In fact, it * might be a good idea.) In the message error functions, * the 8byte message cookie is returned as well as the * SNAC ID. * */ for (i = 0; i < 8; i++) aimbs_put8(&fr->data, (guint8) rand()); /* * Channel ID */ aimbs_put16(&fr->data, 0x0001); /* * Destination SN (prepended with byte length) */ aimbs_put8(&fr->data, strlen(args->destsn)); aimbs_putraw(&fr->data, (guint8 *)args->destsn, strlen(args->destsn)); /* * Message TLV (type 2). */ aimbs_put16(&fr->data, 0x0002); aimbs_put16(&fr->data, msgtlvlen); /* * Features * */ aimbs_put8(&fr->data, 0x05); aimbs_put8(&fr->data, 0x01); if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) { aimbs_put16(&fr->data, args->featureslen); aimbs_putraw(&fr->data, args->features, args->featureslen); } else { aimbs_put16(&fr->data, sizeof(deffeatures)); aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures)); } if (args->flags & AIM_IMFLAGS_MULTIPART) { aim_mpmsg_section_t *sec; for (sec = args->mpmsg->parts; sec; sec = sec->next) { aimbs_put16(&fr->data, 0x0101); aimbs_put16(&fr->data, sec->datalen + 4); aimbs_put16(&fr->data, sec->charset); aimbs_put16(&fr->data, sec->charsubset); aimbs_putraw(&fr->data, sec->data, sec->datalen); } } else { aimbs_put16(&fr->data, 0x0101); /* * Message block length. */ aimbs_put16(&fr->data, args->msglen + 0x04); /* * Character set. */ if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) { aimbs_put16(&fr->data, args->charset); aimbs_put16(&fr->data, args->charsubset); } else { if (args->flags & AIM_IMFLAGS_UNICODE) aimbs_put16(&fr->data, 0x0002); else if (args->flags & AIM_IMFLAGS_ISO_8859_1) aimbs_put16(&fr->data, 0x0003); else aimbs_put16(&fr->data, 0x0000); aimbs_put16(&fr->data, 0x0000); } /* * Message. Not terminated. */ aimbs_putraw(&fr->data, (guint8 *)args->msg, args->msglen); } /* * Set the Request Acknowledge flag. */ if (args->flags & AIM_IMFLAGS_ACK) { aimbs_put16(&fr->data, 0x0003); aimbs_put16(&fr->data, 0x0000); } /* * Set the Autoresponse flag. */ if (args->flags & AIM_IMFLAGS_AWAY) { aimbs_put16(&fr->data, 0x0004); aimbs_put16(&fr->data, 0x0000); } if (args->flags & AIM_IMFLAGS_OFFLINE) { aimbs_put16(&fr->data, 0x0006); aimbs_put16(&fr->data, 0x0000); } /* * Set the I HAVE A REALLY PURTY ICON flag. */ if (args->flags & AIM_IMFLAGS_HASICON) { aimbs_put16(&fr->data, 0x0008); aimbs_put16(&fr->data, 0x000c); aimbs_put32(&fr->data, args->iconlen); aimbs_put16(&fr->data, 0x0001); aimbs_put16(&fr->data, args->iconsum); aimbs_put32(&fr->data, args->iconstamp); } /* * Set the Buddy Icon Requested flag. */ if (args->flags & AIM_IMFLAGS_BUDDYREQ) { aimbs_put16(&fr->data, 0x0009); aimbs_put16(&fr->data, 0x0000); } aim_tx_enqueue(sess, fr); if (!(sess->flags & AIM_SESS_FLAGS_DONTTIMEOUTONICBM)) aim_cleansnacs(sess, 60); /* clean out SNACs over 60sec old */ return 0; }
/* * conn must be a BOS connection! */ faim_export int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance) { int i; aim_frame_t *fr; aim_msgcookie_t *cookie; struct aim_invite_priv *priv; fu8_t ckstr[8]; aim_snacid_t snacid; aim_tlvlist_t *otl = NULL, *itl = NULL; fu8_t *hdr; int hdrlen; aim_bstream_t hdrbs; if (!sess || !conn || !sn || !msg || !roomname) return -EINVAL; if (conn->type != AIM_CONN_TYPE_BOS) return -EINVAL; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) return -ENOMEM; snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1); aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); /* * Cookie */ for (i = 0; i < sizeof(ckstr); i++) aimutil_put8(ckstr, (fu8_t) rand()); /* XXX should be uncached by an unwritten 'invite accept' handler */ if ((priv = malloc(sizeof(struct aim_invite_priv)))) { priv->sn = strdup(sn); priv->roomname = strdup(roomname); priv->exchange = exchange; priv->instance = instance; } if ((cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_INVITE, priv))) aim_cachecookie(sess, cookie); else free(priv); for (i = 0; i < sizeof(ckstr); i++) aimbs_put8(&fr->data, ckstr[i]); /* * 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) * * Everything else is inside this TLV. * * Sigh. AOL was rather inconsistent right here. So we have * to play some minor tricks. Right inside the type 5 is some * raw data, followed by a series of TLVs. * */ hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2; hdr = malloc(hdrlen); aim_bstream_init(&hdrbs, hdr, hdrlen); aimbs_put16(&hdrbs, 0x0000); /* Unknown! */ aimbs_putraw(&hdrbs, ckstr, sizeof(ckstr)); /* I think... */ aim_putcap(&hdrbs, AIM_CAPS_CHAT); aim_addtlvtochain16(&itl, 0x000a, 0x0001); aim_addtlvtochain_noval(&itl, 0x000f); aim_addtlvtochain_raw(&itl, 0x000c, strlen(msg), msg); aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance); aim_writetlvchain(&hdrbs, &itl); aim_addtlvtochain_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); aim_writetlvchain(&fr->data, &otl); free(hdr); aim_freetlvchain(&itl); aim_freetlvchain(&otl); aim_tx_enqueue(sess, fr); return 0; }
/* * Send an SMS message. This is the non-US way. The US-way is to IM * their cell phone number (+19195551234). * * We basically construct and send an XML message. The format is: * <icq_sms_message> * <destination>full_phone_without_leading_+</destination> * <text>message</text> * <codepage>1252</codepage> * <senders_UIN>self_uin</senders_UIN> * <senders_name>self_name</senders_name> * <delivery_receipt>Yes|No</delivery_receipt> * <time>Wkd, DD Mmm YYYY HH:MM:SS TMZ</time> * </icq_sms_message> * * Yeah hi Peter, whaaaat's happening. If there's any way to use * a codepage other than 1252 that would be great. Thaaaanks. */ faim_export int aim_icq_sendsms(aim_session_t *sess, const char *name, const char *msg, const char *alias) { aim_conn_t *conn; aim_frame_t *fr; aim_snacid_t snacid; int bslen, xmllen; char *xml, timestr[30]; time_t t; struct tm *tm; if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) return -EINVAL; if (!name || !msg || !alias) return -EINVAL; time(&t); tm = gmtime(&t); strftime(timestr, 30, "%a, %d %b %Y %T %Z", tm); /* The length of xml included the null terminating character */ xmllen = 225 + strlen(name) + strlen(msg) + strlen(sess->sn) + strlen(alias) + strlen(timestr) + 1; if (!(xml = (char *)malloc(xmllen*sizeof(char)))) return -ENOMEM; snprintf(xml, xmllen, "<icq_sms_message>\n" "\t<destination>%s</destination>\n" "\t<text>%s</text>\n" "\t<codepage>1252</codepage>\n" "\t<senders_UIN>%s</senders_UIN>\n" "\t<senders_name>%s</senders_name>\n" "\t<delivery_receipt>Yes</delivery_receipt>\n" "\t<time>%s</time>\n" "</icq_sms_message>\n", name, msg, sess->sn, alias, timestr); bslen = 37 + xmllen; if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) { free(xml); return -ENOMEM; } snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); /* For simplicity, don't bother using a tlvlist */ aimbs_put16(&fr->data, 0x0001); aimbs_put16(&fr->data, bslen); aimbs_putle16(&fr->data, bslen - 2); aimbs_putle32(&fr->data, atoi(sess->sn)); aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ aimbs_putle16(&fr->data, snacid); /* eh. */ /* From libicq200-0.3.2/src/SNAC-SRV.cpp */ aimbs_putle16(&fr->data, 0x8214); aimbs_put16(&fr->data, 0x0001); aimbs_put16(&fr->data, 0x0016); aimbs_put32(&fr->data, 0x00000000); aimbs_put32(&fr->data, 0x00000000); aimbs_put32(&fr->data, 0x00000000); aimbs_put32(&fr->data, 0x00000000); aimbs_put16(&fr->data, 0x0000); aimbs_put16(&fr->data, xmllen); aimbs_putraw(&fr->data, xml, xmllen); aim_tx_enqueue(sess, fr); free(xml); return 0; }